Domanda

Supponiamo di avere un processo che genera esattamente un processo figlio. Ora, quando il processo genitore termina per qualsiasi motivo (normalmente o in modo anomalo, uccidendo, ^ C, affermando il fallimento o qualsiasi altra cosa) voglio che il processo figlio muoia. Come farlo correttamente?


Qualche domanda simile su StackOverflow:


Qualche domanda simile su StackOverflow per Windows :

È stato utile?

Soluzione

Il bambino può chiedere al kernel di fornire SIGHUP (o altro segnale) quando il genitore muore specificando l'opzione PR_SET_PDEATHSIG in prctl () in basso come questo :

prctl (PR_SET_PDEATHSIG, SIGHUP);

Vedi man 2 prctl per i dettagli.

Modifica: questo è solo Linux

Altri suggerimenti

Sto cercando di risolvere lo stesso problema e poiché il mio programma deve essere eseguito su OS X, la soluzione solo per Linux non ha funzionato per me.

Sono arrivato alla stessa conclusione delle altre persone in questa pagina: non esiste un modo compatibile con POSIX per avvisare un bambino quando un genitore muore. Quindi ho raccolto la cosa migliore da fare: avere il sondaggio per bambini.

Quando un processo genitore muore (per qualsiasi motivo), il processo genitore del figlio diventa processo 1. Se il figlio esegue semplicemente il polling periodicamente, può verificare se il suo genitore è 1. In caso affermativo, il figlio dovrebbe uscire.

Questo non è eccezionale, ma funziona ed è più semplice delle soluzioni di polling TCP socket / lockfile suggerite altrove in questa pagina.

L'ho raggiunto in passato eseguendo il "quot" originale " codice nel "quot" child " e il "generato" " codice nel "genitore" (ovvero: inverti il ??solito senso del test dopo fork () ). Quindi intrappola SIGCHLD nel "generato" " codice ...

Potrebbe non essere possibile nel tuo caso, ma carino quando funziona.

Se non riesci a modificare il processo figlio, puoi provare qualcosa del tipo:

int pipes[2];
pipe(pipes)
if (fork() == 0) {
    close(pipes[1]); /* Close the writer end in the child*/
    dup2(0, pipes[0]); /* Use reader end as stdin */
    exec("sh -c 'set -o monitor; child_process & read dummy; kill %1'")
}

close(pipes[0]); /* Close the reader end in the parent */

Questo esegue il figlio all'interno di un processo di shell con il controllo del lavoro abilitato. Il processo figlio viene generato in background. La shell attende una nuova linea (o un EOF) quindi uccide il bambino.

Quando il genitore muore, non importa quale sia la ragione, chiuderà la sua estremità del tubo. La shell figlio otterrà un EOF dalla lettura e procederà all'eliminazione del processo figlio in background.

Per completezza. Su macOS puoi usare kqueue:

void noteProcDeath(
    CFFileDescriptorRef fdref, 
    CFOptionFlags callBackTypes, 
    void* info) 
{
    // LOG_DEBUG(@"noteProcDeath... ");

    struct kevent kev;
    int fd = CFFileDescriptorGetNativeDescriptor(fdref);
    kevent(fd, NULL, 0, &kev, 1, NULL);
    // take action on death of process here
    unsigned int dead_pid = (unsigned int)kev.ident;

    CFFileDescriptorInvalidate(fdref);
    CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example

    int our_pid = getpid();
    // when our parent dies we die as well.. 
    LOG_INFO(@"exit! parent process (pid %u) died. no need for us (pid %i) to stick around", dead_pid, our_pid);
    exit(EXIT_SUCCESS);
}


void suicide_if_we_become_a_zombie(int parent_pid) {
    // int parent_pid = getppid();
    // int our_pid = getpid();
    // LOG_ERROR(@"suicide_if_we_become_a_zombie(). parent process (pid %u) that we monitor. our pid %i", parent_pid, our_pid);

    int fd = kqueue();
    struct kevent kev;
    EV_SET(&kev, parent_pid, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL);
    kevent(fd, &kev, 1, NULL, 0, NULL);
    CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, noteProcDeath, NULL);
    CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
    CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
    CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
    CFRelease(source);
}

Il processo figlio ha una pipe da / verso il processo parent? In tal caso, riceveresti un SIGPIPE se scrivi o ottieni EOF durante la lettura: queste condizioni potrebbero essere rilevate.

In Linux, è possibile installare un segnale di morte del genitore nel figlio, ad esempio:

#include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
#include <signal.h> // signals
#include <unistd.h> // fork()
#include <stdio.h>  // perror()

// ...

pid_t ppid_before_fork = getpid();
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
    ; // continue parent execution
} else {
    int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
    if (r == -1) { perror(0); exit(1); }
    // test in case the original parent exited just
    // before the prctl() call
    if (getppid() != ppid_before_fork)
        exit(1);
    // continue child execution ...

Si noti che la memorizzazione dell'ID del processo padre prima del fork e il test nel figlio dopo prctl () elimina una condizione di competizione tra prctl () e l'uscita del processo che ha chiamato il figlio.

Si noti inoltre che il segnale di morte del genitore del figlio viene cancellato nei propri figli appena creati. Non è influenzato da un execve () .

Tale test può essere semplificato se siamo certi che il processo di sistema incaricato di adottare tutti gli orfani ha PID 1:

pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
    ; // continue parent execution
} else {
    int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
    if (r == -1) { perror(0); exit(1); }
    // test in case the original parent exited just
    // before the prctl() call
    if (getppid() == 1)
        exit(1);
    // continue child execution ...

Affidarsi al fatto che quel processo di sistema sia init e che abbia PID 1 non è portatile, comunque. POSIX.1-2008 specifica :

  

L'ID del processo padre di tutti i processi figlio esistenti e i processi zombie del processo chiamante devono essere impostati sull'ID processo di un processo di sistema definito dall'implementazione. Cioè, questi processi devono essere ereditati da un processo di sistema speciale.

Tradizionalmente, il processo di sistema che adotta tutti gli orfani è PID 1, ovvero init - che è l'antenato di tutti i processi.

Su sistemi moderni come Linux o FreeBSD un altro processo potrebbe avere quel ruolo. Ad esempio, su Linux, un processo può chiamare prctl (PR_SET_CHILD_SUBREAPER , 1) per affermarsi come processo di sistema che eredita tutti gli orfani di uno qualsiasi dei suoi discendenti (cfr. An esempio su Fedora 25).

Ispirato da un'altra risposta qui, ho trovato la seguente soluzione all-POSIX. L'idea generale è quella di creare un processo intermedio tra genitore e figlio, che abbia uno scopo: notare quando il genitore muore e uccidere esplicitamente il figlio.

Questo tipo di soluzione è utile quando il codice nel figlio non può essere modificato.

int p[2];
pipe(p);
pid_t child = fork();
if (child == 0) {
    close(p[1]); // close write end of pipe
    setpgid(0, 0); // prevent ^C in parent from stopping this process
    child = fork();
    if (child == 0) {
        close(p[0]); // close read end of pipe (don't need it here)
        exec(...child process here...);
        exit(1);
    }
    read(p[0], 1); // returns when parent exits for any reason
    kill(child, 9);
    exit(1);
}

Esistono due piccoli avvertimenti con questo metodo:

  • Se uccidi deliberatamente il processo intermedio, il bambino non verrà ucciso alla morte del genitore.
  • Se il figlio esce prima del genitore, il processo intermedio proverà a eliminare il pid figlio originale, che ora potrebbe riferirsi a un processo diverso. (Questo potrebbe essere corretto con più codice nel processo intermedio.)

A parte questo, il vero codice che sto usando è in Python. Eccolo per completezza:

def run(*args):
    (r, w) = os.pipe()
    child = os.fork()
    if child == 0:
        os.close(w)
        os.setpgid(0, 0)
        child = os.fork()
        if child == 0:
            os.close(r)
            os.execl(args[0], *args)
            os._exit(1)
        os.read(r, 1)
        os.kill(child, 9)
        os._exit(1)
    os.close(r)

Non credo sia possibile garantire che utilizzando solo chiamate POSIX standard. Come nella vita reale, una volta generato un bambino, ha una vita propria.

È possibile che il processo genitore rilevi il maggior numero possibile di eventi di terminazione e tenti di terminare il processo figlio in quel momento, ma ce ne sono sempre alcuni che non possono essere catturati.

Ad esempio, nessun processo può catturare un SIGKILL . Quando il kernel gestisce questo segnale, ucciderà il processo specificato senza alcuna notifica a quel processo.

Estendere l'analogia - l'unico altro modo standard per farlo è che il bambino si suicidi quando scopre che non ha più un genitore.

Esiste un modo solo Linux di farlo con prctl (2) - vedi altre risposte.

Come altre persone hanno sottolineato, basarsi sul pid del genitore per diventare 1 quando il genitore esce non è portatile. Invece di attendere un ID processo genitore specifico, attendi che l'ID cambi:

pit_t pid = getpid();
switch (fork())
{
    case -1:
    {
        abort(); /* or whatever... */
    }
    default:
    {
        /* parent */
        exit(0);
    }
    case 0:
    {
        /* child */
        /* ... */
    }
}

/* Wait for parent to exit */
while (getppid() != pid)
    ;

Aggiungi un micro-sleep come desiderato se non vuoi eseguire il polling alla massima velocità.

Questa opzione mi sembra più semplice che usare una pipe o fare affidamento sui segnali.

Installa un gestore di trappole per catturare SIGINT, che interrompe il processo figlio se è ancora in vita, anche se altri poster hanno ragione che non catturerà SIGKILL.

Apri un file .lock con accesso esclusivo e fai in modo che il sondaggio figlio su di esso provi ad aprirlo - se l'apertura ha esito positivo, il processo figlio dovrebbe uscire

Questa soluzione ha funzionato per me:

  • Passa la pipe stdin al figlio - non devi scrivere alcun dato nello stream.
  • Il bambino legge indefinitamente da stdin fino a EOF. Un EOF segnala che il genitore se n'è andato.
  • Questo è un modo infallibile e portatile per rilevare quando il genitore se ne è andato. Anche se il genitore si arresta in modo anomalo, il sistema operativo chiuderà la pipe.

Questo era per un processo di tipo operaio la cui esistenza aveva senso solo quando il genitore era vivo.

Penso che un modo rapido e sporco sia quello di creare una pipe tra figlio e genitore. Quando il genitore esce, i figli riceveranno un SIGPIPE.

Alcuni poster hanno già menzionato pipe e kqueue . In effetti, puoi anche creare una coppia di socket di dominio Unix collegati mediante la chiamata socketpair () . Il tipo di socket dovrebbe essere SOCK_STREAM .

Supponiamo di avere i due descrittori di file socket fd1, fd2. Ora fork () per creare il processo figlio, che erediterà il file fds. Nel genitore chiudi fd2 e nel figlio chiudi fd1. Ora ogni processo può poll () il fd aperto rimanente alla sua estremità per l'evento POLLIN . Fintanto che ciascuna parte non esplicitamente chiude () il suo fd durante la normale vita, puoi essere abbastanza sicuro che un flag POLLHUP dovrebbe indicare la fine dell'altro (non importa pulito o no). Una volta informato di questo evento, il bambino può decidere cosa fare (ad es. Morire).

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <stdio.h>

int main(int argc, char ** argv)
{
    int sv[2];        /* sv[0] for parent, sv[1] for child */
    socketpair(AF_UNIX, SOCK_STREAM, 0, sv);

    pid_t pid = fork();

    if ( pid > 0 ) {  /* parent */
        close(sv[1]);
        fprintf(stderr, "parent: pid = %d\n", getpid());
        sleep(100);
        exit(0);

    } else {          /* child */
        close(sv[0]);
        fprintf(stderr, "child: pid = %d\n", getpid());

        struct pollfd mon;
        mon.fd = sv[1];
        mon.events = POLLIN;

        poll(&mon, 1, -1);
        if ( mon.revents & POLLHUP )
            fprintf(stderr, "child: parent hung up\n");
        exit(0);
    }
}

Puoi provare a compilare il codice di prova del concetto sopra riportato ed eseguirlo in un terminale come ./a.out & amp; . Hai circa 100 secondi per sperimentare l'uccisione del PID padre con vari segnali, o semplicemente uscirà. In entrambi i casi, dovresti visualizzare il messaggio " child: parent ha riattaccato " ;.

Rispetto al metodo che utilizza il gestore SIGPIPE , questo metodo non richiede di provare la chiamata write () .

Questo metodo è anche simmetrico , ovvero i processi possono utilizzare lo stesso canale per monitorare l'esistenza reciproca.

Questa soluzione chiama solo le funzioni POSIX. Ho provato questo in Linux e FreeBSD. Penso che dovrebbe funzionare su altri Unix ma non ho ancora testato.

Vedi anche:

  • unix (7) delle pagine man di Linux, unix (4) per FreeBSD, poll (2) , socketpair ( 2) , socket (7) su Linux.

In POSIX , exit () , _exit () e _Exit () sono definite in:

  • Se il processo è un processo di controllo, il segnale SIGHUP deve essere inviato a ciascun processo nel gruppo di processi in primo piano del terminale di controllo appartenente al processo di chiamata.

Quindi, se si organizza che il processo padre sia un processo di controllo per il suo gruppo di processi, il figlio dovrebbe ricevere un segnale SIGHUP quando il padre esce. Non sono assolutamente sicuro che ciò accada quando il genitore si blocca, ma penso che lo faccia. Certamente, per i casi non crash, dovrebbe funzionare bene.

Tieni presente che potresti dover leggere un sacco di caratteri minuscoli, tra cui la sezione Definizioni di base (Definizioni), nonché le informazioni sui servizi di sistema per exit () e setsid ( ) e setpgrp () - per ottenere il quadro completo. (Anche io!)

Se invii un segnale al pid 0, usando ad esempio

kill(0, 2); /* SIGINT */

quel segnale viene inviato all'intero gruppo di processo, uccidendo così efficacemente il bambino.

Puoi provarlo facilmente con qualcosa del tipo:

(cat && kill 0) | python

Se poi premi ^ D, vedrai il testo " Terminated " come un'indicazione che l'interprete Python è stato effettivamente ucciso, invece che appena uscito a causa della chiusura di stdin.

Nel caso in cui sia rilevante per qualcun altro, quando ho generato istanze JVM in processi figlio biforcuti da C ++, l'unico modo in cui ho potuto far terminare correttamente le istanze JVM dopo il completamento del processo genitore era quello di fare quanto segue. Speriamo che qualcuno possa fornire feedback nei commenti se questo non è il modo migliore per farlo.

1) Chiama prctl (PR_SET_PDEATHSIG, SIGHUP) sul processo figlio biforcato come suggerito prima di avviare l'app Java tramite execv e

2) Aggiungi un hook di arresto all'applicazione Java che esegue il polling fino a quando il suo PID padre è uguale a 1, quindi esegui un Runtime.getRuntime (). halt (0) . Il polling viene eseguito avviando una shell separata che esegue il comando ps (Vedi: Come posso trovare il mio PID in Java o JRuby su Linux? ).

MODIFICA 130118:

Sembra che non sia stata una soluzione solida. Faccio ancora fatica a capire le sfumature di ciò che sta succedendo, ma a volte stavo ancora ottenendo processi JVM orfani quando eseguivo queste applicazioni in sessioni schermo / SSH.

Invece di eseguire il polling per il PPID nell'app Java, ho semplicemente avuto l'hook dell'arresto per eseguire la pulizia seguito da una brusca interruzione come sopra. Quindi mi sono assicurato di invocare waitpid nell'app genitore C ++ sul processo figlio generato quando era il momento di terminare tutto. Questa sembra essere una soluzione più solida, in quanto il processo figlio ne assicura la chiusura, mentre il genitore utilizza riferimenti esistenti per assicurarsi che i suoi figli terminino. Confrontalo con la soluzione precedente che aveva interrotto il processo genitore ogni volta che lo desiderava e che i bambini cercavano di capire se erano rimasti orfani prima di terminare.

Se il genitore muore, il PPID degli orfani cambia in 1 - devi solo controllare il tuo PPID. In un certo senso, questo è il polling, menzionato sopra. ecco un pezzo di shell per questo:

check_parent () {
      parent=`ps -f|awk '$2=='$PID'{print $3 }'`
      echo "parent:$parent"
      let parent=$parent+0
      if [[ $parent -eq 1 ]]; then
        echo "parent is dead, exiting"
        exit;
      fi
}


PID=$
cnt=0
while [[ 1 = 1 ]]; do
  check_parent
  ... something
done

Ho trovato 2 soluzioni, entrambe non perfette.

1. Uccidi tutti i bambini uccidendo (-pid) quando riceve il segnale SIGTERM.
Ovviamente, questa soluzione non è in grado di gestire "kill -9", ma funziona nella maggior parte dei casi e molto semplice perché non è necessario ricordare tutti i processi figlio.


    var childProc = require('child_process').spawn('tail', ['-f', '/dev/null'], {stdio:'ignore'});

    var counter=0;
    setInterval(function(){
      console.log('c  '+(++counter));
    },1000);

    if (process.platform.slice(0,3) != 'win') {
      function killMeAndChildren() {
        /*
        * On Linux/Unix(Include Mac OS X), kill (-pid) will kill process group, usually
        * the process itself and children.
        * On Windows, an JOB object has been applied to current process and children,
        * so all children will be terminated if current process dies by anyway.
        */
        console.log('kill process group');
        process.kill(-process.pid, 'SIGKILL');
      }

      /*
      * When you use "kill pid_of_this_process", this callback will be called
      */
      process.on('SIGTERM', function(err){
        console.log('SIGTERM');
        killMeAndChildren();
      });
    }

Allo stesso modo, è possibile installare il gestore 'exit' come sopra se si chiama process.exit da qualche parte. Nota: Ctrl + C e l'arresto improvviso sono stati elaborati automaticamente dal sistema operativo per eliminare il gruppo di processi, quindi non più qui.

2.Utilizzare chjj / pty.js per generare il processo con il controllo terminale allegato.
Quando uccidi il processo corrente anche uccidendo -9, anche tutti i processi figlio verranno automaticamente uccisi (dal sistema operativo?). Immagino che, poiché il processo corrente contiene un altro lato del terminale, quindi se il processo corrente muore, il processo figlio otterrà SIGPIPE, quindi morirà.


    var pty = require('pty.js');

    //var term =
    pty.spawn('any_child_process', [/*any arguments*/], {
      name: 'xterm-color',
      cols: 80,
      rows: 30,
      cwd: process.cwd(),
      env: process.env
    });
    /*optionally you can install data handler
    term.on('data', function(data) {
      process.stdout.write(data);
    });
    term.write(.....);
    */

Sono riuscito a realizzare una soluzione portatile e senza polling con 3 processi abusando del controllo terminale e delle sessioni. Questa è masturbazione mentale, ma funziona.

Il trucco è:

  • il processo A è avviato
  • il processo A crea una pipe P (e non legge mai da essa)
  • processo A fork nel processo B
  • il processo B crea una nuova sessione
  • il processo B alloca un terminale virtuale per quella nuova sessione
  • il processo B installa il gestore SIGCHLD in modo che muoia quando il bambino esce
  • il processo B imposta un gestore SIGPIPE
  • process B fork in process C
  • il processo C fa tutto ciò di cui ha bisogno (es. exec () s il binario non modificato o esegue qualsiasi logica)
  • il processo B scrive sulla pipe P (e blocca in questo modo)
  • process A wait () s sul processo B ed esce quando muore

In questo modo:

  • se il processo A muore: il processo B ottiene un SIGPIPE e muore
  • se il processo B muore: il processo A wait () ritorna e muore, il processo C ottiene un SIGHUP (perché quando il leader della sessione di una sessione con un terminale collegato muore, tutti i processi nel gruppo di processi in primo piano ottengono un SIGHUP)
  • se il processo C muore: il processo B ottiene un SIGCHLD e muore, quindi il processo A muore

Carenze:

  • il processo C non è in grado di gestire SIGHUP
  • il processo C verrà eseguito in una sessione diversa
  • il processo C non può utilizzare l'API del gruppo di sessioni / processi perché interromperà la fragile configurazione
  • creare un terminale per ogni operazione di questo tipo non è la migliore idea di sempre

Anche se sono passati 7 anni, ho appena riscontrato questo problema mentre sto eseguendo l'applicazione SpringBoot che deve avviare webpack-dev-server durante lo sviluppo e deve ucciderlo quando si arresta il processo di back-end.

Provo a usare Runtime.getRuntime (). addShutdownHook ma ha funzionato su Windows 10 ma non su Windows 7.

L'ho modificato per utilizzare un thread dedicato che attende che il processo venga chiuso o per InterruptedException che sembra funzionare correttamente su entrambe le versioni di Windows.

private void startWebpackDevServer() {
    String cmd = isWindows() ? "cmd /c gradlew webPackStart" : "gradlew webPackStart";
    logger.info("webpack dev-server " + cmd);

    Thread thread = new Thread(() -> {

        ProcessBuilder pb = new ProcessBuilder(cmd.split(" "));
        pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        pb.redirectError(ProcessBuilder.Redirect.INHERIT);
        pb.directory(new File("."));

        Process process = null;
        try {
            // Start the node process
            process = pb.start();

            // Wait for the node process to quit (blocking)
            process.waitFor();

            // Ensure the node process is killed
            process.destroyForcibly();
            System.setProperty(WEBPACK_SERVER_PROPERTY, "true");
        } catch (InterruptedException | IOException e) {
            // Ensure the node process is killed.
            // InterruptedException is thrown when the main process exit.
            logger.info("killing webpack dev-server", e);
            if (process != null) {
                process.destroyForcibly();
            }
        }

    });

    thread.start();
}

Storicamente, da UNIX v7, il sistema di processo ha rilevato l'orfanotrofio dei processi controllando l'id padre di un processo. Come ho detto, storicamente, il processo di sistema init (8) è un processo speciale per una sola ragione: non può morire. Non può morire perché l'algoritmo del kernel per gestire l'assegnazione di un nuovo ID del processo genitore dipende da questo fatto. quando un processo esegue la sua chiamata exit (2) (tramite una chiamata di sistema di processo o da un'attività esterna come invio di un segnale o simili) il kernel riassegna tutti i figli di questo processo con l'id del processo init come ID processo principale. Questo porta al test più semplice e al modo più portatile per sapere se un processo è diventato orfano. Basta controllare il risultato della chiamata di sistema getppid (2) e se è l'ID di processo del processo init (2) , il processo è diventato orfano prima della chiamata di sistema.

Da questo approccio emergono due problemi che possono portare a problemi:

  • per prima cosa, abbiamo la possibilità di modificare il processo init in qualsiasi processo utente, quindi Come possiamo assicurare che il processo init sarà sempre genitore di tutti i processi orfani? Bene, nel codice di chiamata di sistema exit c'è un controllo esplicito per vedere se il processo che esegue la chiamata è il processo init (il processo con pid uguale a 1) e se è il caso, il panico del kernel ( Non dovrebbe più essere in grado di mantenere la gerarchia dei processi), quindi non è consentito al processo init di effettuare una chiamata exit (2) .
  • secondo, c'è una condizione di gara nel test di base esposto sopra. L'id del processo di Init è considerato storicamente 1 , ma ciò non è garantito dall'approccio POSIX, che afferma (come esposto in altre risposte) che solo l'id di processo di un sistema è riservato a tale scopo. Quasi nessuna implementazione di posix lo fa, e puoi ipotizzare nei sistemi derivati ??unix originali che avere 1 come risposta della chiamata di sistema getppid (2) sia sufficiente per presumere che il processo sia orfano . Un altro modo per verificare è quello di creare un getppid (2) subito dopo il fork e confrontare quel valore con il risultato di una nuova chiamata. Questo semplicemente non funziona in tutti i casi, poiché entrambe le chiamate non sono atomiche insieme e il processo padre può morire dopo il fork (2) e prima del primo getppid (2) chiamata di sistema. Il processo ID genitore cambia solo una volta, quando il genitore esegue una chiamata exit (2) , quindi questo dovrebbe essere sufficiente per verificare se il risultato getppid (2) cambiato tra le chiamate per vedere che il processo genitore è uscito. Questo test non è valido per i figli effettivi del processo di init, perché sono sempre figli di init (8) `, ma puoi assumere in modo sicuro che questi processi non abbiano neanche un genitore (tranne quando sostituisci in un sistema il processo di init)

Un altro modo per fare ciò che è specifico di Linux è quello di creare il genitore in un nuovo spazio dei nomi PID. Sarà quindi PID 1 in quello spazio dei nomi e quando esce da esso tutti i suoi figli verranno immediatamente uccisi con SIGKILL .

Sfortunatamente, per creare un nuovo spazio dei nomi PID devi avere CAP_SYS_ADMIN . Tuttavia, questo metodo è molto efficace e non richiede modifiche reali al genitore o ai figli oltre il lancio iniziale del genitore.

Vedi clone (2) , pid_namespaces (7) e unshare (2) .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top