【问题标题】:Should processes in process group terminate together with their parent in Unix/Linux?进程组中的进程是否应该与 Unix/Linux 中的父进程一起终止?
【发布时间】:2014-06-30 17:41:12
【问题描述】:

我有一个父进程可能会产生许多子进程的情况。 我想要实现的是,如果父进程被杀死或退出,那么它的所有子进程都应该与父进程一起终止。

在帖子(下面的链接)中,我发现了通过使父进程成为组长来存档此内容的建议。 如果我理解正确,这也是流程组的主要目的。我说的对吗?
帖子还提到 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);
}

这篇文章与以下建议有关: * How to make child process die after parent exits?

【问题讨论】:

  • 如何它没有按您期望的方式工作?你期望做什么
  • 父进程在 7 秒后终止,但子进程仍然存在并被 init 进程孤立。我希望孩子收到一些信号并与父母终止。
  • man7.org/linux/man-pages/man3/exit.3.html 说但我不知道这在这种情况下是否真实:如果进程是会话领导者并且其控制终端是会话的控制终端,那么每个进程在这个控制终端的前台进程组被发送一个 SIGHUP 信号,并且终端与这个会话解除关联,允许它被一个新的控制进程获取。
  • 你有没有想过在分叉之前调用setsid,以确保父进程是进程组负责人并且你有一个新的会话?
  • 谢谢,我会试试的。

标签: c linux unix process fork


【解决方案1】:

您可以使用 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);
}

【讨论】:

    【解决方案2】:

    请注意,仅在非常有限的情况下才会向子进程发送信号。 POSIX 说:

    • 如果进程是控制进程,则应向属于调用进程的控制终端的前台进程组中的每个进程发送SIGHUP信号。

    • 如果进程是控制进程,则与会话关联的控制终端应与会话解除关联,允许它被新的控制进程获取。

    • 如果进程的退出导致进程组成为孤立进程,并且如果新孤立进程组的任何成员停止,则应向新进程组中的每个进程发送一个 SIGHUP 信号和一个 SIGCONT 信号。孤立的进程组。

    controlling process的定义是:

    与控制终端建立连接的会话负责人。如果终端随后不再是该会话的控制终端,则会话领导者不再是控制进程。

    一般来说,您的进程不会成为与控制终端(通常是您的 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 是在父进程完成几分钟后发送的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多