Czy istnieje wariant UNIX, w którym proces potomny umiera wraz z rodzicem?

41

Od dłuższego czasu badam zachowanie jądra Linuksa i zawsze było dla mnie jasne, że:

Kiedy proces umiera, wszystkie jego dzieci są zwracane do initprocesu (PID 1), aż w końcu umrą.

Jednak ostatnio ktoś z dużo większym doświadczeniem z jądrem powiedział mi, że:

Kiedy proces się kończy, wszystkie jego dzieci również umierają (chyba że użyjesz, NOHUPw którym przypadku wrócą init).

Teraz, chociaż w to nie wierzę, wciąż napisałem prosty program, aby się tego upewnić. Wiem, że nie powinienem polegać na czasie ( sleep) na testach, ponieważ wszystko zależy od planowania procesu, ale w tym prostym przypadku myślę, że to wystarczy.

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;
}

Oto wynik programu, z powiązanym pswynikiem za każdym razem, gdy printfmówi:

$ ./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).

Teraz, jak widać, zachowuje się tak, jak bym się spodziewał. Proces osierocony (436) jest zwracany do init(1) aż do śmierci.

Czy jednak istnieje system oparty na systemie UNIX, w którym takie zachowanie nie jest domyślnie stosowane? Czy istnieje system, w którym śmierć procesu natychmiast powoduje śmierć wszystkich jego dzieci?

John WH Smith
źródło

Odpowiedzi:

68

Kiedy proces się kończy, wszystkie jego dzieci również umierają (chyba że użyjesz NOHUP, w którym to przypadku wrócą do init).

To jest źle. Całkiem nieprawidłowy. Osoba, która to powiedziała, albo się pomyliła, albo pomyliła określoną sytuację z ogólnym przypadkiem.

Istnieją dwa sposoby, w jakie śmierć procesu może pośrednio spowodować śmierć jego dzieci. Są one powiązane z tym, co dzieje się, gdy terminal jest zamknięty. Kiedy terminal znika (w przeszłości, ponieważ linia szeregowa została odcięta z powodu zawieszenia modemu, obecnie zwykle dlatego, że użytkownik zamknął okno emulatora terminala), sygnał SIGHUP jest wysyłany do procesu sterującego działającego w tym terminalu - zazwyczaj rozpoczyna się początkowa powłoka w tym terminalu. Pociski zwykle reagują na to, wychodząc. Przed wyjściem powłoki przeznaczone do użytku interaktywnego wysyłają HUP do każdego uruchomionego zadania.

Rozpoczęcie zadania od powłoki z nohupprzerwami w drugim źródle sygnałów HUP, ponieważ zadanie zignoruje sygnał, a tym samym nie zostanie poproszone o śmierć, gdy terminal zniknie. Inne sposoby na przerwanie propagacji sygnałów HUP z powłoki do zadań obejmują użycie disownwbudowanej powłoki, jeśli ma taką (zadanie jest usuwane z listy zadań powłoki) i podwójne rozwidlenie (powłoka uruchamia dziecko, które uruchamia dziecko sam z siebie i wychodzi natychmiast; skorupa nie ma wiedzy o swoim wnuku).

Ponownie zadania rozpoczęte w terminalu giną nie dlatego, że umiera ich proces nadrzędny (powłoka), ale dlatego, że proces nadrzędny decyduje się je zabić, kiedy nakazuje się je zabić. I początkowa powłoka w terminalu umiera nie dlatego, że umiera proces macierzysty, ale dlatego, że terminal znika (co może być przypadkowe, ponieważ terminal jest dostarczany przez emulator terminalu, który jest procesem macierzystym powłoki).

Gilles „SO- przestań być zły”
źródło
1
Jest trzeci sposób, coś z grupami kontrolnymi. Wiem tylko, że systemd używa go do wymuszania czystych zabójstw.
o11c
5
@ JanHudec Myślę, że mylisz nohupsię disown. disownjest wbudowany w jakąś powłokę, która usuwa działające zadanie z tabeli zadań (biorąc pod uwagę argument podobny %1), a głównym użytecznym efektem ubocznym jest to, że powłoka nie wysyła ZATRZYMANIA do podprocesu. nohupto zewnętrzne narzędzie, które ignoruje SIGHUP (i robi kilka innych rzeczy), a następnie uruchamia polecenie przekazane w wierszu polecenia.
Gilles „SO- przestań być zły”
@Gilles: Nie, nie byłem, ale patrzenie na strace nohupw rzeczywistości ignoruje sygnał przed wydaniem execpolecenia.
Jan Hudec
Dziękuję za Twoją odpowiedź! Szczególnie podobało mi się trochę historyczne tło dotyczące rozłączania modemów. Zaczynałem się martwić, że przez cały czas nie rozumiałem jądra ...
John WH Smith
3
Należy również zauważyć, że program kończy działanie po otrzymaniu SIGHUP, ponieważ domyślną procedurą obsługi sygnału dla SIGHUP jest wyjście. Ale każdy program może wdrożyć własną procedurę obsługi SIGHUP, co może spowodować, że nie zakończy się, gdy powłoka nadrzędna wyśle ​​SIGHUP.
slebetman
12

Kiedy proces się kończy, wszystkie jego dzieci również umierają (chyba że użyjesz NOHUP, w którym to przypadku wrócą do init).

Jest to prawidłowe, jeśli proces jest liderem sesji. Kiedy lider sesji umiera, SIGHUP jest wysyłany do wszystkich członków tej sesji. W praktyce oznacza to jego dzieci i ich potomków.

Proces staje się liderem sesji przez wywołanie setsid. Muszle tego używają.

Dennis
źródło
Właśnie przeprowadziłem test na ten temat, ale nie mogłem odtworzyć opisywanego zachowania ... Odrodziłem proces, dałem mu nową sesję ( forkzabij proces nadrzędny setsid) i rozwidliłem kolejne dziecko do tej nowej sesji. Jednak gdy proces nadrzędny (nowy lider sesji) zmarł, dziecko nie otrzymało SIGHUP i przywiązało się initdo niego, aż w końcu zakończyło pracę.
John WH Smith
Z setpgid(2)strony podręcznika: Jeśli sesja ma kontrolujący terminal, a flaga CLOCAL dla tego terminala nie jest ustawiona, a wystąpi rozłączenie terminala, lider sesji jest wysyłany SIGHUP. Jeśli lider sesji wyjdzie, wówczas sygnał SIGHUP zostanie również wysłany do każdego procesu w grupie procesów pierwszego planu terminala sterującego.
ninjalj
1
@ninjalj Zgaduję, że ta odpowiedź jest poprawna tylko wtedy, gdy lider sesji jest również procesem kontrolującym terminal. Standardowy lider sesji, niezależny od jakiegokolwiek terminala, nie zabije swoich dzieci. Czy to jest poprawne?
John WH Smith
-1

Tak więc powyższe plakaty mówią, że dzieci nie umierają, rodzic je zabija (lub wysyła im sygnał, na którym kończą). Możesz więc mieć to, o co prosisz, jeśli zaprogramujesz Rodzica, aby (1) prowadził rejestr wszystkich swoich dzieci i (2) wysyłał sygnał do wszystkich swoich dzieci.

To właśnie robi Shell i powinno to być to, co robi proces nadrzędny. Konieczne może być złapanie sygnału HUP u rodzica, aby nadal mieć wystarczającą kontrolę, aby zabić dzieci.

marco
źródło
Wiele przyczyn może spowodować śmierć rodzica. Nie ma sposobu, aby zapobiec przeżyciu dzieci, jeśli rodzic jest na przykład SIGKILLed.
Emil Jeřábek wspiera Monikę
-1

Brakuje mi trochę odpowiedzi, która jest nieco związana z umierającymi rodzicami: kiedy proces pisze na rurze, dla której nie ma już procesu czytania, otrzymuje SIGPIPE. Standardowe działanie dla SIGPIPE to wypowiedzenie.

To może rzeczywiście spowodować śmierć procesów. W rzeczywistości jest to standardowy sposób, w jaki program yesumiera.

Jeśli wykonam

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

w moim systemie odpowiedź brzmi:

y
y
y
y
y
y
y
y
y
y
141

a 141 to w rzeczywistości 128 + SIGPIPE:

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

z man 7 signal.

użytkownik86542
źródło
2
Ale proces odczytu z potoku niekoniecznie (a nawet zwykle) jego rodzic. Jest to tak naprawdę zupełnie inny przypadek niż pytanie.
Barmar