【问题标题】:How to kill all child processes after parent process termination?父进程终止后如何杀死所有子进程?
【发布时间】:2023-09-03 02:26:01
【问题描述】:

我可以通过父进程杀死一个子进程。但是如果父进程有多个子进程会怎样呢?

例如在下面的代码中,有 1 个父进程和 6 个子进程。父进程终止后如何立即杀死其他六个子进程?

如果您运行此代码,父进程将在 5 秒后终止。在该子进程再过 5 秒(总共 10 秒)后终止。

但是我想在父进程终止后立即杀死6个子进程。所以父进程和6个子进程应该在5秒后终止。

#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>

int main() 
{ 
    for(int i=0;i<6;i++) // loop will run 6 times(there are 6 child processes.) 
    { 
        if(fork() == 0) 
        { 
            printf("Started [son] pid %d from [parent] pid %d\n",getpid(),getppid()); 

            sleep(10); //child waits 10 seconds,then it exitted.

            printf("Exitted [son] pid %d from [parent] pid %d\n",getpid(),getppid()); 

            exit(0); 
        } 
    } 

    //parent
    sleep(5);  //parent will wait 5 seconds than it will exit
    printf("Parent terminated\n");
    exit(0); //parent terminated.(how can I exit the the other 6 child processes too?)

} 

【问题讨论】:

  • 阅读手册等待2。
  • @purec 我已经读过,我不希望父母等待孩子。
  • 好的...但是当父母等待孩子终止时这是一种常见的做法。

标签: c linux process posix kill-process


【解决方案1】:

这是一个可能更便携的解决方案。

fork(2) 系统调用会返回你的子进程的 PID,你可以存储 PID,然后你可以使用kill(2) 向子进程发送信号并终止它们。

注意SIGKILLSIGTERM 信号可能需要父进程的某些权限。如果它没有这样的权限,你可以向子进程发送SIGCONT,并在你的子进程中修改SIGCONT信号处理程序。

!!!警告标志

来自使用exit() 的信号处理程序是不安全的。我刚刚检查了手册man 7 signal,发现它不是异步安全的。您可以使用_exit_Exitabort

一些伪代码:

#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void* handler(int sig){
    _exit(0);
}
int main() 
{ 
    pid_t children[6];
    for(int i=0;i<6;i++) // loop will run 6 times(there are 6 child processes.) 
    { 
        if((children[i] = fork()) == 0) 
        { 
            signal(SIGCONT,handler);
            printf("Started [son] pid %d from [parent] pid %d\n",getpid(),getppid()); 

            sleep(10); //child waits 10 seconds,then it exitted.

            printf("Exitted [son] pid %d from [parent] pid %d\n",getpid(),getppid()); 

            exit(0); 
        } 
    } 

    //parent
    sleep(5);  //parent will wait 5 seconds than it will exit
    for(int i=0;i<6;i++)
        kill(children[I],SIGCONT);
    printf("Parent terminated\n");
    exit(0); //parent terminated.(how can I exit the the other 6 child processes too?)

}

【讨论】:

  • 如果你可以发送一个信号,你应该可以发送任何一个。
  • @PSkocik 这是来自 kill(2) BSD 手册的内容。 “对于有权向 pid 指定的进程发送信号的进程,接收进程的真实或有效用户 ID 必须与发送进程的用户 ID 匹配,或者用户必须具有适当的权限(例如由 set- “然而,使用 SIGCONT 是一种解决方法(在语义上并不完全正确)。
  • 现在让我来点滴滴! :D exit 不是异步信号安全的。你应该退出_exit。无论如何,很好的答案! +1d。
  • 好吧,我在阅读您的帖子之前才意识到这一点并急于修改答案哈哈,无论如何谢谢;)
  • 想出了一种不需要明确杀死的 POSIX 方式。
【解决方案2】:

在 Linux 上,您可以使用 prctl 请求通过信号通知您父母的死亡(错误检查已跳过)。

#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/prctl.h> //<<<<<<<<
#include <signal.h> //<<<<<<<<

int main() 
{ 
    for(int i=0;i<6;i++) // loop will run 6 times(there are 6 child processes.) 
    { 
        if(fork() == 0) 
        { 
            prctl(PR_SET_PDEATHSIG, SIGTERM); //<<<<<<

            printf("Started [son] pid %d from [parent] pid %d\n",getpid(),getppid()); 

            sleep(2);

            printf("Exitted [son] pid %d from [parent] pid %d\n",getpid(),getppid()); 

            exit(0); 
        } 
    } 

    //parent
    sleep(1);
    printf("Parent terminated\n");
    exit(0);
   //<<< Linux auto-sends the deathsignal to all children

} 

对于不要求父进程在死亡时显式杀死其子进程的 POSIX 兼容解决方案,您可以使用 async-IO 管道。

Async-IO 依赖于在文件描述符事件上发送的信号。在这种情况下,只要您确保自动关闭关闭对管道端文件的最后引用(跳过错误检查),您就可以获得由内核自动关闭垂死进程的文件描述符引起的关闭事件的通知:

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>

#include <signal.h>
#include <fcntl.h>
#include <sys/ioctl.h>

int main()
{

    int pipes[6][2];
    for(int i=0;i<6;i++) // loop will run 6 times(there are 6 child processes.)
    {
        pipe(pipes[i]); //create a pipe

        if(fork() == 0)
        {
            //get notified on an event on the read-end (we're aiming for the EOF event)
            fcntl(pipes[i][0],F_SETOWN,getpid()); 
            ioctl(pipes[i][0], FIOASYNC, &(int){1});

            for(int j=0; j<=i; j++) close(pipes[j][1]); //close all write-end ends so the refcount is 1 and the parent has the last ref

            printf("Started [son] pid %d from [parent] pid %d\n",getpid(),getppid());

            sleep(2);

            printf("Exitted [son] pid %d from [parent] pid %d\n",getpid(),getppid());

            exit(0);
        }
    }

    //parent
    sleep(1);
    printf("Parent terminated\n");
    exit(0); 
    //<<<this closes all the last write ends of the pipes and so the children will get notified with a signal 
    //the signal is SIGIO by default, whose default disposition is to kill the process (this can be changed by fcntl(fd,F_SETSIG,TheSignal))



}

【讨论】: