Dlaczego zombie czeka na swoje dziecko?

11

Przeszukuję różne źródła, ale nie mogę znaleźć dobrego opisu anatomii zbierania dzieci. To prosty przypadek tego, co chciałbym zrozumieć.

$ cat <( sleep 100 & wait ) &
[1] 14247
$ ps ax -O pgid | grep $$
12126 12126 S pts/17   00:00:00 bash
14248 12126 S pts/17   00:00:00 bash
14249 12126 S pts/17   00:00:00 sleep 100
14251 14250 S pts/17   00:00:00 grep --color=auto 12126
$ kill -2 14248

$ ps ax -O pgid | grep $$
12126 12126 S pts/17   00:00:00 bash
14248 12126 Z pts/17   00:00:00 [bash] <defunct>
14249 12126 S pts/17   00:00:00 sleep 100
14255 14254 S pts/17   00:00:00 grep --color=auto 12126

Dlaczego zombie czeka na dziecko?

Czy możesz to wyjaśnić? Czy muszę znać C i czytać kod źródłowy Bash, aby lepiej to zrozumieć, czy też jest jakaś dokumentacja? Już skonsultowałem:

GNU bash, wersja 4.3.42 (1) -release (x86_64-pc-linux-gnu)

Linux 4.4.0-31-generic # 50-Ubuntu SMP Środa 13 lipca 00:07:12 UTC 2016 x86_64 x86_64 x86_64 GNU / Linux


źródło
2
Należy zauważyć, że tak naprawdę nie ma to nic wspólnego z bash (poza tym, że jeśli zdecydujesz się użyć bash jako powłoki, rozpocznie się wiele procesów). Inne powłoki (tcsh, ksh, zsh i c) wszystkie uruchamiają procesy i uruchamiają w zasadzie te same funkcje systemu operacyjnego, aby sobie z nimi poradzić.
jamesqf
@jamesqf Ciekawe. Jeśli chcesz rozszerzyć swój komentarz na pełnoprawną odpowiedź, byłoby świetnie.
1
Tyle, że tak naprawdę nie jest to odpowiedź, po prostu wskazując, że szukałeś odpowiedzi w niewłaściwych miejscach :-) Każda dobra książka na temat programowania systemów * nix powinna dać o wiele lepszą odpowiedź, niż mógłbym napisać.
jamesqf

Odpowiedzi:

17

Zombie nie czeka na swoje dziecko. Jak każdy proces zombie, pozostaje tam, dopóki jego rodzic go nie odbierze.

Powinieneś wyświetlić wszystkie zaangażowane procesy, aby zrozumieć, co się dzieje, a także spojrzeć na PPID. Użyj tego wiersza poleceń:

ps -t $(tty) -O ppid,pgid

Rodzicem procesu, który zabijasz, jest cat. Dzieje się tak, że bash uruchamia polecenie cat <( sleep 100 & wait )w tle w podpowłoce. Ponieważ jedyne, co robi ta podpowłoka, to skonfigurowanie przekierowania, a następnie uruchomienie zewnętrznego polecenia, ta podpowłoka jest zastępowana przez zewnętrzne polecenie. Oto podsumowanie:

  • Oryginalne wywołanie bash (12126) wywołuje forkpolecenie cat <( sleep 100 & wait )w tle u dziecka (14247).
    • Dziecko (14247) wywołuje, pipeaby utworzyć potok, a następnie forkdziecko, aby uruchomić podstawienie procesu sleep 100 & wait.
      • Wnuk (14248) wzywa forkdo biegania sleep 100w tle. Ponieważ wnuk nie jest interaktywny, proces w tle nie działa w osobnej grupie procesów. Wnuk czeka na sleepwyjście.
    • Dziecko (14247) wywołuje setpgid(jest to zadanie w tle w interaktywnej powłoce, więc otrzymuje własną grupę procesów), a następnie execveuruchamia się cat. (Jestem trochę zaskoczony, że podstawienie procesu nie występuje w grupie procesów w tle).
  • Zabijasz wnuka (14248). Jego rodzic działa cat, który nie wie nic o żadnym procesie potomnym i nie ma połączeń biznesowych wait. Ponieważ rodzic wnuka nie zbiera go, wnuk pozostaje zombie.
  • W końcu catwychodzi - albo dlatego, że go zabijesz, albo dlatego, że sleepwraca i zamyka potok, więc catwidzi koniec swojego wejścia. W tym momencie rodzic zombie umiera, więc zombie jest zbierane przez init i init zbiera je.

Jeśli zmienisz polecenie na

{ cat <( sleep 100 & wait ); echo done; } &

następnie caturuchamia się w osobnym procesie, a nie w potomku oryginalnego procesu bash: pierwsze dziecko musi zostać w tyle, aby uruchomić echo done. W takim przypadku, jeśli zabijesz wnuka, nie pozostanie on jako zombie, ponieważ dziecko (które w tym momencie nadal wykonuje bash) zbiera je.

Zobacz także Jak Linux obsługuje proces zombie i czy zombie może mieć sieroty? Czy sierocym dzieciom przeszkadza zbieranie zombie?

Gilles „SO- przestań być zły”
źródło
Byłem również zaskoczony sprawą grupy procesowej. Wygląda na to, że był to błąd i został teraz naprawiony w gałęzi master bash.
PSkocik,
„Oryginalny bash czeka na swoje dziecko (14247)”. Dlaczego lub w jaki sposób? Dziecko powinno biec w tle i nie ma wyraźnego połączenia. Jaka jest różnica między oryginalnym bashem (14246) czekającym na 14247 a 14247 (który jest uruchomiony cat), który nie czeka na 14248 (czekający sleep)? Czy jest jakaś pamięć o tym, kto na kogo czeka, którego dziecko (14247) straciło, a oryginalna bash (14246) nie, czy może lista sygnałów, takich jak SIGCHLD, o kim należy zadzwonić i 14247 (teraz uruchomiony bash) wypisać się z chodzi o 14248?
1
@tomas Miałem na myśli, że oryginalny bash wywołuje waitswoje dziecko, tzn. zbiera je. Widzę, jak byłoby to mylące, usunąłem to zdanie, które nie było nawet w odpowiednim momencie chronologicznie mówiąc. Informacja o tym, że proces umarł, trafia do jego rodzica, proces nie może „subskrybować”, aby otrzymywać informacje o śmierci innego procesu.
Gilles „SO- przestań być zły”
6

Zombie nie czeka na dziecko. Zamiast zombie jest proces, który już umarł (przez własną rękę, albo został zabity - jak w przykładzie), miał swój kod, dane i stos zwalniane, a teraz zawiera tylko jego kod wyjścia, czekając na jego rodzic zadzwonić , aby je odzyskać (a zatem w końcu całkowicie wyczyść wpis procesu z tabeli procesów)wait(2)

W twoim przykładzie, kiedy sen się skończy (lub zostanie zabity), rodzic przeczyta status wyjścia i zbierze zombie. Zobacz wyżej wymienione wait(2)szczegóły.

Matija Nalis
źródło