Как убить все дочерние процессы после завершения родительского процесса? - PullRequest
0 голосов
/ 13 апреля 2019

Я могу убить дочерний процесс родительским процессом. Но что произойдет, если родительский процесс имеет более одного дочернего процесса?

Например, в приведенном ниже коде есть один родительский процесс и 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 ]

2 голосов
/ 13 апреля 2019

Вот, вероятно, более портативное решение.

Системный вызов fork(2) вернет PID ваших дочерних процессов, вы можете сохранить PID, а затем вы можете использовать kill(2), чтобы отправить сигнал дочерним процессам и завершить их.

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

!!! Предупреждающий знак

Из обработчика сигнала, использующего exit(), небезопасно. Я только что проверил руководство man 7 signal и обнаружил, что оно небезопасно. Вы можете использовать _exit, _Exit или abort

Какой-то псевдокод:

#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?)

}
2 голосов
/ 13 апреля 2019

В 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))



}
...