Почему зомби ждет своего ребенка?

11

Я копаюсь в разных источниках, но не могу найти хорошее описание анатомии пожинающих детей. Это простой случай того, что я хотел бы понять.

$ 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

Почему зомби ждут ребенка?

Вы можете объяснить это? Нужно ли мне знать C и читать исходный код Bash, чтобы получить более полное представление об этом, или есть какая-либо документация? Я уже консультировался:

GNU bash, версия 4.3.42 (1) -релиз (x86_64-pc-linux-gnu)

Linux 4.4.0-31-generic # 50-Ubuntu SMP Ср 13 июля 00:07:12 UTC 2016 x86_64 x86_64 x86_64 GNU / Linux


источник
2
Следует заметить, что это не имеет ничего общего с bash (кроме того факта, что если вы решите использовать bash в качестве оболочки, он запустит много процессов). Другие оболочки (tcsh, ksh, zsh и т. Д.) Все запускают процессы и по сути выполняют те же функции ОС, чтобы справиться с ними.
jamesqf
@jamesqf Интересно. Если вы хотите расширить свой комментарий до полноценного ответа, это было бы здорово.
1
За исключением того, что это на самом деле не ответ, а просто указание на то, что вы искали ответ не в тех местах :-) Любая хорошая книга по * nix системному программированию должна дать гораздо лучший ответ, чем я мог бы написать.
jamesqf

Ответы:

17

Зомби не ждет своего ребенка. Как и любой процесс зомби, он остается, пока его родитель не соберет его.

Вы должны отобразить все вовлеченные процессы, чтобы понять, что происходит, а также посмотреть на PPID. Используйте эту командную строку:

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

Родитель процесса, который вы убиваете cat. В результате bash запускает фоновую команду cat <( sleep 100 & wait )в подоболочке. Поскольку единственное, что делает этот подоболочка, это настроить некоторое перенаправление и затем выполнить внешнюю команду, эта подоболочка заменяется внешней командой. Вот краткое изложение:

  • Исходный bash (12126) вызывает forkвыполнение фоновой команды cat <( sleep 100 & wait )в дочернем элементе (14247).
    • Дочерний объект (14247) вызывает pipeсоздание канала, а затем forkсоздает дочерний элемент для запуска процесса подстановки sleep 100 & wait.
      • Внук (14248) призывает forkбежать sleep 100в фоновом режиме. Поскольку внук не является интерактивным, фоновый процесс не выполняется в отдельной группе процессов. Затем внук ждет sleepвыхода.
    • Потомок (14247) вызывает setpgid(это фоновое задание в интерактивной оболочке, поэтому он получает свою собственную группу процессов), а затем execveзапускается cat. (Я немного удивлен, что подстановка процессов не происходит в группе фоновых процессов.)
  • Вы убиваете внука (14248). Работает его родитель cat, который ничего не знает ни о каком дочернем процессе и не имеет деловых вызовов wait. Так как родитель внука не пожинает его, внук остается зомби.
  • В конце концов, catзавершается - либо потому, что вы его убили, либо потому, что sleepвозвращает и закрывает канал, чтобы catувидеть конец его ввода. В этот момент родитель зомби умирает, поэтому зомби собирается init, а init пожинает его.

Если вы измените команду на

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

затем catзапускается в отдельном процессе, а не в дочернем элементе исходного процесса bash: первый дочерний элемент должен остаться позади, чтобы запустить echo done. В этом случае, если вы убьете внука, он не останется зомби, потому что ребенок (который все еще работает с bash) пожинает его.

Смотрите также Как Linux справляется с процессом зомби и могут ли зомби иметь сирот? Будут ли потревожены дети-сироты, пожиная зомби?

Жиль "ТАК - перестань быть злым"
источник
Я тоже был удивлен, что процесс группы. Похоже, это была ошибка, и теперь она исправлена ​​в ветке мастера bash.
PSkocik
«Оригинальный bash ждет своего ребенка (14247)». Почему или каким образом? Предполагается, что ребенок работает в фоновом режиме, и нет явного вызова. В чем разница между исходным bash (14246), ожидающим 14247 и 14247 (который работает cat), не ожидающим 14248 (ожидание sleep)? Есть ли какая-то память о том, кто кого ждет, которого потерял ребенок (14247), а оригинальный bash (14246) не потерял, или, возможно, список сигналов, таких как SIGCHLD, о которых следует вызывать, и 14247 (в данный момент запущенных bash) отписался с помощью С уважением к 14248?
1
@tomas Я имел в виду, что оригинальный bash вызывает waitсвоего потомка, то есть пожинает его. Я вижу, как это может сбить с толку, я удалил это предложение, которое не было даже в нужном месте в хронологическом смысле. Информация о том, что процесс умер, переходит к его родителю, процесс не может «подписаться» на получение информации о смерти какого-либо другого процесса.
Жиль "ТАК - перестань быть злым"
6

Зомби не ждет ребенка. Вместо этого, зомби - это процесс, который уже умер (сам по себе или был убит - как в вашем примере), когда его код, данные и стек были освобождены, и теперь содержит только свой код выхода, ожидая, когда его родитель вызовет wait(2)его извлечение. (и, таким образом, окончательно очистить запись процесса полностью из таблицы процессов)

В вашем примере, когда сон заканчивается (или убивается), родитель прочитает статусы выхода и пожнет зомби. Смотрите выше wait(2)для деталей.

Матия Налис
источник