Esiste una variante UNIX in cui un processo figlio muore con il suo genitore?


41

Studio il comportamento del kernel Linux da un po 'di tempo ormai, ed è sempre stato chiaro per me che:

Quando un processo muore, tutti i suoi figli vengono restituiti al initprocesso (PID 1) fino a quando non muoiono.

Tuttavia, recentemente, qualcuno con molta più esperienza di me con il kernel mi ha detto che:

Quando un processo termina, anche tutti i suoi figli muoiono (a meno che tu non usi NOHUPnel qual caso tornano init).

Ora, anche se non ci credo, ho comunque scritto un semplice programma per assicurarmene. So che non dovrei fare affidamento sul tempo ( sleep) per i test poiché tutto dipende dalla pianificazione del processo, ma per questo semplice caso, penso che sia abbastanza.

int main(void){
    printf("Father process spawned (%d).\n", getpid());
    sleep(5);

    if(fork() == 0){
        printf("Child process spawned (%d => %d).\n", getppid(), getpid());
        sleep(15);
        printf("Child process exiting (%d => %d).\n", getppid(), getpid());
        exit(0);
    }

    sleep(5);
    printf(stdout, "Father process exiting (%d).\n", getpid());
    return EXIT_SUCCESS;
}

Ecco l'output del programma, con l'Associated psrisultato ogni volta che printfparla:

$ ./test &
Father process spawned (435).

$ ps -ef | grep test
myuser    435    392   tty1    ./test

Child process spawned (435 => 436).

$ ps -ef | grep test
myuser    435    392   tty1    ./test
myuser    436    435   tty1    ./test

Father process exiting (435).

$ ps -ef | grep test
myuser    436    1     tty1    ./test

Child process exiting (436).

Ora, come puoi vedere, questo si comporta come mi sarei aspettato. Il processo orfano (436) viene restituito a init(1) fino alla sua morte.

Tuttavia, esiste un sistema basato su UNIX su cui questo comportamento non si applica per impostazione predefinita? Esiste un sistema su cui la morte di un processo innesca immediatamente la morte di tutti i suoi figli?

Risposte:


68

Quando un processo termina, anche tutti i suoi figli muoiono (a meno che tu non usi NOHUP nel qual caso tornano a init).

Questo è sbagliato. Morto sbagliato. La persona che diceva che si sbagliava o confondeva una situazione particolare con il caso generale.

Esistono due modi in cui la morte di un processo può causare indirettamente la morte dei suoi figli. Sono collegati a ciò che accade quando un terminale è chiuso. Quando un terminale scompare (storicamente perché la linea seriale è stata tagliata a causa di un riaggancio del modem, oggigiorno generalmente perché l'utente ha chiuso la finestra dell'emulatore di terminale), un segnale SIGHUP viene inviato al processo di controllo in esecuzione in quel terminale - in genere, la shell iniziale è iniziata in quel terminale. Le conchiglie normalmente reagiscono a questo uscendo. Prima di uscire, le shell destinate all'uso interattivo inviano HUP a ciascun lavoro avviato.

L'avvio di un lavoro da una shell nohupinterrompe quella seconda fonte di segnali HUP perché il lavoro ignorerà quindi il segnale e quindi non gli verrà detto di morire quando il terminale scompare. Altri modi per interrompere la propagazione dei segnali HUP dalla shell ai lavori includono l'uso dell'integrato della shell disownse ne ha uno (il lavoro viene rimosso dall'elenco dei lavori della shell) e il doppio fork (la shell avvia un figlio che avvia un figlio di per sé ed esce immediatamente; la shell non ha conoscenza del nipote).

Ancora una volta, i lavori avviati nel terminale muoiono non perché il loro processo genitore (la shell) muore, ma perché il loro processo genitore decide di ucciderli quando gli viene detto di ucciderli. E la shell iniziale nel terminale non muore perché il suo processo genitore muore, ma perché il suo terminale scompare (il che può essere o meno coincidente perché il terminale è fornito da un emulatore di terminale che è il processo genitore della shell).


1
C'è un terzo modo, qualcosa con i gruppi di controllo. Tutto quello che so è che systemd lo usa per imporre uccisioni pulite.
o11c,

5
@JanHudec Credo che tu stia confondendo nohupcon disown. disownè un builtin in alcune shell che rimuove un lavoro in esecuzione dalla tabella dei lavori (prendendo un argomento simile %1), e il principale effetto collaterale utile di ciò è che la shell non invierà un SIGHUP al sottoprocesso. nohupè un'utilità esterna che ignora SIGHUP (e fa poche altre cose) quindi avvia il comando passato sulla sua riga di comando.
Gilles 'SO- smetti di essere malvagio'

@Gilles: No, non lo ero, ma guardare lo strace nohupinfatti ignora il segnale prima che execsia il comando.
Jan Hudec

La ringrazio per la risposta! Mi è particolarmente piaciuto quel po 'di background storico relativo al riaggancio del modem. Stavo iniziando a preoccuparmi di aver frainteso il kernel per tutto questo tempo ...
John WH Smith

3
Inoltre, va notato che un programma termina quando riceve SIGHUP perché il gestore del segnale predefinito per SIGHUP deve uscire. Tuttavia, qualsiasi programma può implementare il proprio gestore SIGHUP che potrebbe non chiudersi quando la shell padre lo invia SIGHUP.
slebetman,

12

Quando un processo termina, anche tutti i suoi figli muoiono (a meno che tu non usi NOHUP nel qual caso tornano a init).

Questo è corretto se il processo è un leader di sessione. Quando un leader di sessione muore, un SIGHUP viene inviato a tutti i membri di quella sessione. In pratica ciò significa che i suoi figli e i loro discendenti.

Un processo si rende leader della sessione chiamando setsid. Le conchiglie usano questo.


Ho appena eseguito un test su questo, ma non sono riuscito a riprodurre il comportamento che stai descrivendo ... Ho generato un processo, ho dato una nuova sessione ( fork, ho ucciso il processo genitore setsid) e ho passato un altro bambino in questa nuova sessione. Tuttavia, quando il processo genitore (nuovo leader della sessione) è morto, il bambino non ha ricevuto SIGHUP e si è attaccato initfino alla fine.
John WH Smith,

Dalla setpgid(2)manpage: se una sessione ha un terminale di controllo e il flag CLOCAL per quel terminale non è impostato e si verifica un blocco del terminale, al leader della sessione viene inviato un SIGHUP. Se il leader della sessione esce, verrà inviato anche un segnale SIGHUP a ciascun processo nel gruppo di processi in primo piano del terminale di controllo.
ninjalj,

1
@ninjalj Quindi suppongo che questa risposta sia valida se e solo se il leader della sessione è anche il processo di controllo di un terminale. Un leader di sessione standard, indipendente da qualsiasi terminale, non ucciderà i suoi figli. È corretto?
John WH Smith,

-1

Quindi quello che dicono i poster sopra è che i bambini non muoiono, il genitore li uccide (o invia loro un segnale sul quale terminano). Quindi puoi avere quello che chiedi, se programmi il genitore a (1) tenere una registrazione di tutti i suoi figli e (2) inviare un segnale a tutti i suoi figli.

Questo è ciò che Shell fa e dovrebbe essere ciò che fa il processo genitore. Potrebbe essere necessario catturare il segnale HUP nel genitore in modo da avere ancora abbastanza controllo per uccidere i bambini.


Molte cause possono far morire il genitore. Non c'è modo di prevenire la sopravvivenza dei bambini se il genitore è SIGKILLed, per esempio.
Emil Jeřábek sostiene Monica il

-1

Mi manca un po 'le risposte che sono leggermente correlate ai genitori morenti: quando un processo scrive su una pipe per la quale non esiste più un processo di lettura, ottiene un SIGPIPE. L'azione standard per SIGPIPE è la risoluzione.

Ciò può effettivamente causare la morte dei processi. In effetti, è il modo standard in cui il programma yesmuore.

Se eseguo

(yes;echo $? >&2)|head -10

sul mio sistema, la risposta è

y
y
y
y
y
y
y
y
y
y
141

e 141 è davvero 128 + SIGPIPE:

   SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                 readers

da man 7 signal.


2
Ma il processo di lettura dalla pipe non è necessariamente (o addirittura di solito) suo padre. Quindi questo è davvero un caso molto diverso da quello che la domanda sta ponendo.
Barmar,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.