【问题标题】:Sending signal to a forked process that calls exec()向调用 exec() 的分叉进程发送信号
【发布时间】:2012-11-30 17:41:16
【问题描述】:

我有一个简单的 C 程序,它分叉一个进程并调用 exec 来运行如下命令:

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>

int fork_process(int sleep_interval) {
   char cmd[30];

   pid_t pid = fork();       
   if (pid > 0) {
       return pid;
   }
   else if (pid < 0) {
        printf("At parent. Couldn't create a child process!\n");
        return pid;
   }
   else { 
        sprintf(cmd, "sleep %d; %s", sleep_interval, "gzip a > a.gz");
        execlp("sh", "sh", "-c", cmd, (char *) 0);
   }
}

int main () {
   pid_t pid = fork_process(400);

   sleep (10);
   kill(pid, SIGTERM);

   return 1;
}

当我运行这个程序时,我注意到sh 在内部派生了一个进程来运行sleep 400

$ps x
  1428 pts/80   S+     0:00 ./kill_prog
  1429 pts/80   S+      0:00 sh -c sleep 400; gzip a > a.gz
  1430 pts/80   S+      0:00 sleep 400

现在,当SIGTERM 信号在程序中通过其pid(此处为1429)发送到子进程时,我注意到子进程终止但不是执行sleep 400 的进程(pid 1430 )。换句话说,执行sleep 400 的进程在完成之前会变成僵尸。

如何发送终止信号,以便将信号传播到子进程中派生的进程?我尝试将kill 中的进程组ID 用作kill(-1*pid, SIGTERM),但无济于事。

【问题讨论】:

    标签: c shell unix signals exec


    【解决方案1】:

    我终于找到了解决上述问题的方法。这是两个小的变化。

    我在 fork 一个孩子后添加到父级中执行此操作:

    pid_t pid = fork();   
    if (pid > 0) {
       // Make child process the leader of its own process group. This allows
       // signals to also be delivered to processes forked by the child process.
       setpgid(childpid, 0); 
       return pid;
    }
    

    最后,将信号发送到整个进程组:

    // Send signal to process group of child which is denoted by -ve value of child pid.
    // This is done to ensure delivery of signal to processes forked within the child. 
    kill((-1*pid), SIGTERM);
    

    【讨论】:

    • 您还应该在子进程中调用setpgid(0, 0),这样即使子进程在父进程的setpgid() 调用之前执行了execlp(),也会设置进程组。请注意,仍然需要在父进程中调用,以便在kill() 之前设置进程组,即使子进程在其setpgid() 调用之前延迟了很长时间。
    • 如果您以这种方式执行操作,它几乎适用于所有情况,但是,它可能会阻止诸如 bash 之类的操作,因为setsid 无法运行...如果您需要,您可以让进程setsid 本身,然后你可以调用kill( tcgetpgrp( ptsfilehandle ), SIGINT ); 之类的东西来让它发送实际的杀戮。
    【解决方案2】:

    真的很简单:只需在进程中添加一个 SIGTERM 信号处理程序即可:

    【讨论】:

    • 好点。我有一个稍微相似的想法——如果我在 kill() 中使用 SIGKILL(它不能接受信号处理程序)而不是 SIGTERM 会怎样。但是使用 SIGKILL 有与上述相同的观察结果。那么,在这种情况下,我需要做什么才能将信号传递给上面显示的所有进程?
    • 嗨 - 我不是在说“SIGKILL”。我是说“你必须制作自己的信号处理程序!”您可能还想研究另外两件事:Linux process groupskillpg
    • paulsm4,感谢您指出这两个链接。我明白你之前说的。我试图指出编写信号处理程序并不能解决所有情况下的问题。例如 - 如果我需要发送 SIGKILL,这种方法将无济于事。我尝试使用 kill 将信号发送到进程组(killpg() 是一个调用 kill(-pid,signo)) 的包装器,但这并没有像问题中提到的那样工作。
    • 请注意,这个SIGTERM 信号处理程序必须使用trap 在shell 中编写。要使其工作,您必须将sleep 放在后台并为其设置wait,以便shell 在等待时运行陷阱处理程序,除了具有sleep 作为内置函数的shell。
    猜你喜欢
    • 1970-01-01
    • 2020-04-08
    • 2016-03-21
    • 2017-01-20
    • 1970-01-01
    • 1970-01-01
    • 2020-03-25
    • 2015-05-11
    • 1970-01-01
    相关资源
    最近更新 更多