Подтвердить что ты не робот

Почему у меня есть `wait()` для дочерних процессов?

Даже несмотря на то, что страница linux man wait 1 очень хорошо объясняет, что вам нужно wait() для дочерних процессов, чтобы они не превращались в зомби, он не говорит, почему вообще.

Я планировал свою программу (которая является моей первой многопоточной, так что извините мою наивность) вокруг цикла for(;;) ever, который запускает дочерние процессы, которые получают exec() ed и обязательно завершатся самостоятельно.

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

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

Тем не менее вопрос:

Почему Linux держит зомби вообще? Почему я должен ждать своих детей? Это для обеспечения дисциплины в отношении родительских процессов? В течение десятилетий использования Linux у меня никогда не было ничего полезного из процессов зомби, я не совсем понимаю полезность зомби как "функции".

Если ответ заключается в том, что родительские процессы должны иметь способ узнать, что случилось с их детьми, то ради бога нет оснований считать зомби нормальными процессами и запретить создание процессов без зомби только потому, что там слишком много зомби. В системе, которую я сейчас разрабатываю, я могу только создавать от 400 до 500 процессов, прежде чем все перестанет останавливаться (это плохо поддерживается CentOS-системой, работающей на самом дешевом VServer, который я мог найти, - но все же 400 зомби составляют менее нескольких килобайт информации )

4b9b3361

Ответ 1

Мне, вероятно, придется добавить таблицу процессов, в которой хранятся дочерние элементы и должны использовать waitpid - не очень, но через некоторое время прошло - что является проблемой, поскольку время работы детей варьируется от нескольких микросекунд до нескольких минут. Если я использую waitpid рано, мой родительский процесс будет заблокирован

Ознакомьтесь с документацией для waitpid. Вы можете сказать waitpid НЕ блокировать (т.е. Немедленно возвращаться, если нет детей, чтобы пожать) с помощью параметра WNOHANG. Кроме того, вам не нужно давать waitpid PID. Вы можете указать -1, и он будет ждать любого дочернего элемента. Поэтому вызов waitpid, как показано ниже, соответствует вашему ограничению без блокировки и ограничению без сохранения-pids:

waitpid( -1, &status, WNOHANG );

Если вы действительно не хотите правильно обрабатывать создание процесса, тогда вы можете передать ответственность за init, дважды нажав, пожиная ребенка и передав exec внуку:

pid_t temp_pid, child_pid;
temp_pid = fork();
if( temp_pid == 0 ){
    child_pid = fork();
    if( child_pid == 0 ){
        // exec()
        error( EXIT_FAILURE, errno, "failed to exec :(" );
    } else if( child_pid < 0 ){
        error( EXIT_FAILURE, errno, "failed to fork :(" );
    }
    exit( EXIT_SUCCESS );
} else if( temp_pid < 0 ){
    error( EXIT_FAILURE, errno, "failed to fork :(" );
} else {
    wait( temp_pid );
}

В приведенном выше фрагменте кода дочерний процесс выдает свой собственный дочерний элемент, сразу же существует, а затем сразу же извлекается родителем. Внук сирота, принятый init, и будет получен автоматически.

Почему Linux держит зомби вообще? Почему я должен ждать своего дети? Это для обеспечения дисциплины в отношении родительских процессов? В десятилетия использования Linux у меня никогда не было ничего полезного из зомби процессов, я не совсем понимаю полезность зомби как "функции". Если ответ заключается в том, что родительские процессы должны иметь способ узнать что случилось с их детьми, тогда ради бога нет причина считать зомби обычными процессами и запретить создание не зомби-процессы только потому, что слишком много зомби.

Как еще вы предлагаете, можно эффективно получить код выхода процесса? Проблема в том, что отображение кода PID <= > exit (et al.) Должно быть один к одному. Если ядро ​​выпустило PID процесса, как только оно выйдет, будет получено или нет, а затем новый процесс наследует тот же PID и выйдет, как бы вы справились с сохранением двух кодов для одного PID? Как бы заинтересованный процесс извлекал код выхода для первого процесса? Не предполагайте, что никто не заботится о кодах выхода просто потому, что вы этого не делаете. Вы считаете, что это неприятность/ошибка, считаются полезными и чистыми.

В системе, которую я сейчас разрабатываю, я могу создавать только 400-500 перед тем, как все перестанет останавливаться (это плохо поддерживается Система CentOS, работающая на самом дешевом VServer, который я мог найти - но все же 400 зомби меньше, чем несколько килобайт информации)

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

Как правило, максимальное количество процессов ограничено только вашей памятью. Вы можете увидеть свой предел с помощью:

cat /proc/sys/kernel/threads-max

Ответ 2

Ваше рассуждение обратное: ядро ​​держит зомби, потому что они сохраняют состояние, которое вы можете получить с помощью wait() и связанных системных вызовов.

Правильный способ обработки асинхронного дочернего завершения состоит в том, чтобы иметь обработчик SIGCHLD, который выполняет wait() для очистки дочерних процессов.

Ответ 3

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

Ответ 4

Хотя сохранение мертвого pid в таблице процессов в основном для обеспечения его последующего кода для родителя,

Мне приходится жаловаться на то, что там есть дизайн bad (но уже стал историческим и неизменным).

1. Нельзя предварительно объявить, что i_don_care_status_of( pid )

В ОС Windows у нас есть close( processHandle ), чтобы добиться этого эффекта.

HANDLE aProcessHandle = CreateProcess(.....);
CloseHandle( aProcessHandle )

Чтобы преодолеть это, существуют некоторые не совершенные методы (из Wiki):

В современных UNIX-подобных системах (которые соответствуют спецификации SUSv3 в этом отношении) применяется следующий специальный случай: если родитель явно игнорирует SIGCHLD, установив свой обработчик на SIG_IGN (а не просто игнорируя сигнал по умолчанию) или флаг SA_NOCLDWAIT, вся информация о статусе дочернего выхода будет удалена, и никакие процессы зомби не будут оставлены. [1]

2. Нет обработки опорных счетчиков pid.

Когда процесс мертв, если нет ссылки на pid, ядро ​​может немедленно удалить его.

3. Не удается получить код выхода несвязанного pid

Только родитель может получить код выхода pid, это смешно. Нет надежного способа ждать несвязанного pid.

(Использование NETLINK + PROC_CONNECTOR может прослушивать событие выхода любого асинхронного pid).

В Windows это можно сделать с помощью WaitForSingleObject

HANDLE aProcessHandle = OpenProcess( pid... );
WaitForSingleObject(aProcessHandle, ...);

Эти недостатки, по-видимому, существуют, но дизайн Unix/Linux очень прост, поэтому мы должны его обнажить.

Ответ 5

Чтобы предоставить вам "exitcode" процесса, система должна сохранить для вас "базу данных процесса". Такая база данных с только кодом выхода называется "зомби". Вы можете использовать отдельный процесс, который будет периодически запрашивать "процессы зомби" для их "exitcode", тем самым эффективно освобождая эту память. То же самое можно сказать о Windows и других операционных системах. Linux здесь не особо особенный. Вам не нужно ждать процесса, просто спросите его "код выхода" после завершения процесса.