如何只杀死前台的子进程?

时间:2018-05-10 19:49:09

标签: c fork signal-handling sigaction

我正在尝试构建一个shell,并且我已经设法编写了大部分功能,但是我遇到了一个小问题。

说我输入firefox &。 Firefox将作为后台进程打开。 &激活BG标志,使父母不等待子进程。

然后我输入gedit。 Gedit将作为前台进程打开。这意味着当前父级正在等待进程关闭。

此时,父级有两个进程 - firefoxgedit。 Firefox尚未等待,目前处于后台,而我们目前正在等待Gedit完成。到目前为止一切都很好。

但是,如果我决定按ctrl-c发送SIGINT信号,则firefoxgedit都会关闭。不好,只有gedit应该关闭。

这是我的信号处理函数:

pid_t suspended_process[10];
int last_suspended = -1;

void signal_handler(int signo){
    pid_t process = currentpid();

    // Catches interrupt signal
    if(signo == SIGINT){
        int success = kill(process, SIGINT);
    }

    // Catches suspend signal
    else if(signo == SIGTSTP){
        int success = kill(process, SIGTSTP);
        resuspended = 1;
        suspended_process[last_suspended+1] = process;
        last_suspended++;
    }
}

这是fork-exec代码中的一部分,它可以等待进程,也可以继续运行。

  else if(pid > 0){ //Parent
    current_pid = pid;

    // Waits if background flag not activated.
    if(BG == 0){
      // WUNTRACED used to stop waiting when suspended
      waitpid(current_pid, &status, WUNTRACED);

        if(WIFEXITED(status)){
          setExitcode(WEXITSTATUS(status));
        }
        else if(WIFSIGNALED(status)){
          printf("Process received SIGNAL %d\n", WTERMSIG(status));
        }
    }
  }

如果我事先暂停某个流程,也会发生这种情况。例如,我运行firefox,然后按ctrl-z暂停它。然后我运行gedit并按ctrl-c关闭它。之后,如果我按fg恢复已暂停的firefox,则会立即关闭。

我找不到只将SIGINT信号发送到前台进程的方法,它总是将信号发送给除父级以外的所有子级,无论它们是在后台还是暂停。

以防万一,这是初始化信号处理程序的函数:

void init_handler(){
    struct sigaction sa;

    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;

    // If conditions for signal handling.
    // Also creates 2 signal handlers in memory for the SIGINT and SIGTSTP
    if(sigaction(SIGINT, &sa, NULL) == -1)
        printf("Couldn't catch SIGINT - Interrupt Signal\n");
    if(sigaction(SIGTSTP, &sa, NULL) == -1)
        printf("Couldn't catch SIGTSTP - Suspension Signal\n");
}

1 个答案:

答案 0 :(得分:2)

这很简单,但信号没有完成。相反,您必须使用名为流程组的功能。每个作业(可执行文件或管道等)都是一个单独的进程组。您可以使用setpgid(或在setpgrp的某些系统上)创建流程组。您可以在fork之后但exec之前设置子进程的进程组,然后将此作业的进程组ID存储到作业表中。

现在,前台中的进程组被设置为终端(shell的/dev/tty)的活动进程组tcsetpgrp - 这是将接收的进程组< KBD> CTRL + C 。那些属于同一会话但不属于设置为tcsetpgrp前景的组的进程组将完全忽略 CTRL + C