Должны ли процессы в группе процессов завершаться вместе со своими родителями в Unix/Linux?

У меня есть ситуация, когда один родительский процесс может порождать много дочерних процессов. Чего я хочу добиться, так это того, что если родительский процесс уничтожен или завершен, все его дочерние элементы должны завершаться вместе с родительским процессом.

В посте (ссылка ниже) я нашел предложение архивировать это, сделав родительский процесс лидером группы. Если я правильно понимаю, это также основная цель групп процессов. Я прав?
В сообщении также упоминается 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);
}

Этот пост относится к предложению, сделанному в: * Как заставить дочерний процесс умереть после выхода из родительского процесса?

2 ответа

Обратите внимание, что сигнал отправляется дочерним процессам только при очень ограниченном наборе обстоятельств. POSIX говорит:

  • Если процесс является процессом управления, сигнал SIGHUP должен отправляться каждому процессу в группе процессов переднего плана управляющего терминала, принадлежащего вызывающему процессу.

  • Если процесс является процессом управления, управляющий терминал, связанный с сеансом, должен быть отсоединен от сеанса, позволяя ему получить новый процесс управления.

  • Если выход из процесса приводит к тому, что группа процессов становится осиротевшей, и если какой-либо член вновь осиротевшей группы процессов остановлен, то сигнал SIGHUP, за которым следует сигнал SIGCONT, должен быть отправлен каждому процессу в вновь осиротевшей группе процессов.,

Определение процесса управления:

Руководитель сеанса, который установил соединение с управляющим терминалом. Если терминал впоследствии перестает быть управляющим терминалом для этого сеанса, лидер сеанса перестает быть процессом управления.

В общем, ваш процесс не будет лидером сеанса, который установил соединение с управляющим терминалом (который обычно будет вашей оболочкой).

Если есть другая часть 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 был отправлен через несколько минут после завершения родительского процесса.

Вы можете использовать atexit для регистрации функции, которая посылает сигнал 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);
}
Другие вопросы по тегам