在Unix/Linux中,进程组中的进程是否应该随其父进程一起终止?

6
我有一种情况,其中一个父进程可能会生成许多子进程。 我想要实现的是,如果父进程被杀死或退出,则所有子进程应该随着父进程一起终止。
在下面的帖子中(链接如下),我发现了通过使父进程成为组长来实现这一点的建议。 如果我理解正确,这也是进程组的主要目的。我对吗? 帖子还提到了prctl(PR_SET_PDEATHSIG,SIGHUP);和其他一些方法,但它们都是特定于操作系统的,或者以其他方式不太优雅。
我编写了一个小型演示程序,试图更好地理解事情,但它并没有按照我预期的方式工作。我做错了什么?
//https://www.andrew.cmu.edu/course/15-310/applications/homework/homework4/terminalgroups1.html
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/termios.h>

int main()
{
    int status;
    int cpid;
    int ppid;

    ppid = getpid();
    printf("parent: %d\n", ppid);

    if (!(cpid=fork()))
    {
        printf("child: %d\n", getpid());
        if(setpgid(0,ppid) == -1)
           printf("child setpgid errno %s\n", strerror(errno));
        else
           printf("child gid %d\n", getpgid(0));
        pause();
        printf("child exited\n");
        exit (-1);
     }

     if (cpid < 0)
         exit(-1);

     setpgid(0, ppid);
     if(setpgid(0,0) == -1)
         printf("parent setpgid erno %s\n", strerror(errno));
     else
         printf("parrent gid %d\n", getpgid(0));


     sleep(7);
     printf("parent exit\n");
     exit(0);
}

这篇文章与以下建议有关: * 如何使子进程在父进程退出后自动终止?


它为什么不按照你的期望工作?你期望什么? - Some programmer dude
父进程在7秒后终止,但子进程仍然存在并被init进程孤立。我期望子进程能够接收到某些信号并随着父进程一起终止。 - user2979375
http://man7.org/linux/man-pages/man3/exit.3.html 上说,但我不知道在这种情况下是否实际:如果进程是会话领导者并且其控制终端是会话的控制终端,则该控制终端的前台进程组中的每个进程都会收到一个SIGHUP信号,并且该终端与此会话分离,从而允许其被新的控制进程获取。 - user2979375
谢谢,我会尝试的。 - user2979375
setsid 返回“不允许的操作”错误。 - user2979375
显示剩余3条评论
2个回答

6
请注意,只有在非常有限的情况下才会向子进程发送信号。根据POSIX的规定:
  • 如果该进程是控制进程,则应将SIGHUP信号发送到属于调用进程的控制终端的前台进程组中的每个进程。
  • 如果该进程是控制进程,则与该会话关联的控制终端应从该会话中分离出来,从而可以由新的控制进程获取该终端。
  • 如果该进程的退出导致一个进程组变为孤儿,并且如果任何新孤儿进程组的成员被停止,则将向新孤儿进程组中的每个进程发送一个SIGHUP信号,然后是一个SIGCONT信号。
控制进程的定义是:

建立与控制终端的连接的会话领导者。如果终端随后不再是该会话的控制终端,则会话领导者将停止成为控制进程。

一般来说,你的进程不会成为建立与控制终端的连接的会话领导者(通常是你的shell)。
如果有其他适用于POSIX的部分,请告知我。
我对你的代码进行了测试,以下是修改后的代码:termkids.c
#include "posixver.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

static void sigcatcher(int signum)
{

  printf("%d: Signal caught: %d\n", (int)getpid(), signum);
  exit(1);
}

int main(void)
{
    int cpid;
    int ppid;

    ppid = getpid();
    printf("Parent  PID:  %d\n", ppid);
    printf("Initial PGID: %d\n", (int)getpgid(0));
    if (setpgid(0, 0) != 0)
    {
      fprintf(stderr, "setpgid() failed (%d: %s)\n", errno, strerror(errno));
      return 1;
    }
    printf("Revised PGID: %d\n", (int)getpgid(0));

    if ((cpid=fork()) < 0)
    {
      fprintf(stderr, "fork() failed (%d: %s)\n", errno, strerror(errno));
      return 1;
    }
    else if (cpid == 0)
    {
        cpid = getpid();
        printf("Child PID:  %d\n", cpid);
        printf("Child PGID: %d\n", (int)getpgid(0));
        (void)signal(SIGTERM, sigcatcher);
        (void)signal(SIGHUP,  sigcatcher);

        pause();
        printf("%d: child exited\n", cpid);
        return(-1);
     }

     printf("Parent - sleeping\n");
     sleep(7);
     printf("Parent exits\n");
     return(0);
}

示例输出:

$ ./termkids
Parent  PID:  17701
Initial PGID: 17701
Revised PGID: 17701
Parent - sleeping
Child PID:  17702
Child PGID: 17701
Parent exits
$ ps
  PID TTY          TIME CMD
  388 pts/5    00:00:00 bash
17702 pts/5    00:00:00 termkids
17707 pts/5    00:00:00 ps
$ kill 17702
17702: Signal caught: 15
$

请注意,kill 17702 命令是在父进程完成几分钟后发送的。

2
您可以使用atexit注册一个函数,该函数向具有相同进程组ID的所有进程发送SIGHUP信号。这将产生向所有子进程发送信号的效果,当父进程退出时。但是,请注意,子进程中的SIGHUP信号处理程序将导致子进程立即退出,而不会从pause()返回并打印子进程退出消息。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/termios.h>
#include <sys/types.h>

void killall() 
{
    kill(0, SIGHUP);
}

int main()
{
    int status;
    int cpid;
    int ppid;

    if (atexit(killall) != 0)
    {
        fprintf(stderr, "atexit failed with %d", errno);
        exit(-1);
    }

    ppid = getpid();
    printf("parent: %d\n", ppid);

    if (!(cpid=fork()))
    {
       printf("child: %d\n", getpid());
       if(setpgid(0,ppid) == -1)
           printf("child setpgid errno %s\n", strerror(errno));
       else
           printf("child gid %d\n", getpgid(0));
       pause();
       printf("child exited\n");
       exit (-1);
     }

     if (cpid < 0)
         exit(-1);

     setpgid(0, ppid);
     if(setpgid(0,0) == -1)
         printf("parent setpgid erno %s\n", strerror(errno));
     else
         printf("parent gid %d\n", getpgid(0));


     sleep(7);
     printf("parent exit\n");
     exit(0);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接