使用信号处理程序 Pause/Resume 子进程

Using Signal Handlers to Pause/Resume a Child Process

我目前正在尝试使用 C 中的信号来控制使用 fork() 方法创建的子进程。本质上,我有一个子进程 运行 从 linux 终端执行 "yes" 命令(此命令只打印 "y" 和一个换行符,直到它终止)。我希望能够使用 CTRL-Z pause/resume 这个过程。这就是我现在得到的:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
pid_t CHILD_PROCESS;
pid_t PARENT_PROCESS;
int isPaused;
void pause_handler(int signo){
  if(!isPaused){  
    printf("Ctrl-Z pressed. Pausing child.\n");
    isPaused = 1;
    kill(CHILD_PROCESS,SIGSTOP);
  }
  else if(isPaused){
   printf("\nCtrl-Z pressed. Resuming child.\n");
   kill(CHILD_PROCESS,SIGCONT);
   isPaused = 0;
  }
}

int main(int argc, char** argv){
  pid_t pid;
  PARENT_PROCESS = getpid();
  pid = fork();
  if(pid == 0){
    system("yes");
  }
  isPaused = 0;
  if(pid > 0){
    signal(SIGTSTP, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);
    CHILD_PROCESS = pid;
    while(1){
      if(signal(SIGTSTP,pause_handler) == SIG_ERR){
        printf("Signal Failure");
      }
    }
  }
}

当我 运行 这样做时,我可以通过按 CTRL-Z "Ctrl-Z pressed. Pausing child." 打印到控制台,我可以通过按 CTRL- "Ctrl-Z pressed. Resuming child." 打印到控制台又是Z。但是,它实际上并没有一遍又一遍地恢复打印 "y" 。关于为什么子进程没有恢复的任何想法?

事实证明,system 中有一个隐式的 fork 调用,因此存储在 CHILD_PROCESS 中的 PID 实际上不是子进程,而是一个中间进程。

来自man 3 system

   The  system()  library  function uses fork(2) to create a child process
   that executes the shell command specified in command using execl(3)  as
   follows:

       execl("/bin/sh", "sh", "-c", command, (char *) 0);

   system() returns after the command has been completed.

所以,如果我们用 execl("/bin/sh", "sh", "-c", "yes", NULL) 替换 system("yes") 调用,那么我们就避免了这个额外的分支,并且程序可以按预期运行。


唯一的另一个问题是,根据我的评论,我发现 ,在信号处理程序中使用 printf 是未定义的行为。这不是一个需要担心的问题,但在以后的代码中要记住一些事情!