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