Ожидание процессов в C

Я читал документацию по wait() и waitpid() и до сих пор немного не понимаю, как они работают (я понял, что wait(&status) эквивалентно waitpid(-1, &status, 0);). Ниже приведены небольшие фрагменты кода, над которым я работаю. Пожалуйста, помогите мне понять, правильно ли написаны эти фрагменты, и если нет, то почему.

Цель 1. Пожинать всех детей-зомби.

int reapedPid;
do {
    reapedPid = waitpid(-1,NULL,WNOHANG);
} while (reapedPid > 0);

То, что я пытаюсь сделать здесь, это перебрать всех дочерних элементов, собрать дочерний элемент, если он завершен, позволить ему продолжать работу, если нет, и когда у меня закончатся дочерние элементы, тогда reapedPid == -1 и цикл завершится. Причина, по которой я здесь запутался, заключается в том, что я не понимаю, как waitpid() должен знать, какие дочерние элементы уже проверены, а какие нет. Делает ли он такую ​​проверку? Или такой подход не сработает?

Цель 2. Подождать, пока закончат все дочерние элементы.

int pid;
do {
    pid = wait(NULL);
} while (pid != -1);

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

Цель 3. Разветвить дочерний элемент и дождаться его завершения.

int pid = fork();
if (pid < 0) {
    // handle error.
}
else if (pid == 0) {
    // execute child command
}
else {
    int status;
    int waitedForPid = waitpid(pid,&status,0);
    assert(waitedForPid == pid);
}

Здесь я просто пытаюсь разветвить процесс и заставить родителя дождаться завершения дочернего процесса. Я не совсем уверен, должен ли я передать здесь параметр 0, но мне показалось, что WNOHANG, WUNTRACED и WCONTINUED не имеют отношения к моей цели.


person Kvass    schedule 19.11.2013    source источник


Ответы (2)


  1. Работа ядра заключается в отслеживании процессов. Отслеживание мертвых процессов тривиально. Ядро может определить, какие дочерние процессы умерли, но еще не ожидаются, и будет возвращать один из этих мертвых дочерних процессов при каждом вызове, пока не останется ни одного, о котором можно было бы сообщить. (Из-за опции WNOHANG могут остаться дочерние элементы, которых нужно ждать, но ни один из оставшихся дочерних элементов пока не мертв.)

  2. Этот второй цикл также прекрасен и почти эквивалентен первому. Разница в том, что он будет зависать, ожидая, пока все дети умрут, прежде чем вернуть -1.

  3. Этот третий фрагмент в порядке; утверждение будет верным за исключением чрезвычайных обстоятельств (типа другой поток в программе тоже дождался ребенка и собрал труп). Однако, если вы где-то запустили другой процесс и позволили ему работать в фоновом режиме, возможно, вы собираете зомби, тогда как с модификацией других циклов вы можете собирать зомби и все еще ждать правильного потомка:

    int pid = fork();
    
    if (pid < 0)
    {
        // handle error.
    }
    else if (pid == 0)
    {
        // execute child command
    }
    else
    {
        int status;
        int corpse;
        while ((corpse = waitpid(-1, &status, 0)) > 0)
            if (corpse == pid)
                break;
    }
    
person Jonathan Leffler    schedule 19.11.2013
comment
Цикл, который вы предоставили для № 3, является интересным средством многозадачности. Что касается № 2, вы сказали, что он эквивалентен № 1, но я пытаюсь достичь двух разных целей в этих случаях - тогда № 2 неверен? Во втором случае я хочу дождаться, когда все закончится, зомби или нет. В первом я действительно не хочу ничего тянуть, просто пожинаю все, что могу, а затем иду дальше. - person Kvass; 19.11.2013
comment
Ты прав; Я не уделял должного внимания параметрам в первом цикле. Я обновил свой ответ; ваше понимание в значительной степени на месте. Если вы пишете оболочку, вы вполне можете разрешить пользователям выполнять задания в фоновом режиме (&?); тогда цикл, который я показываю, становится важным — хотя вы, вероятно, захотите записать смерть отдельного ребенка (например, чтобы вы могли сообщить пользователю, что его фоновые задания выполнены), или чтобы вы могли обрабатывать статусы выхода каждой команды в трубопроводе или... - person Jonathan Leffler; 19.11.2013

Для большинства из них вы сможете легко написать несколько примеров программ и проверить свое понимание.

Цель 1. Пожинать всех детей-зомби.

Причина, по которой я здесь запутался, заключается в том, что я не понимаю, как waitpid() должен знать, какие дочерние элементы уже проверены, а какие нет. Делает ли он такую ​​проверку?

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

Для целей 2 и 3, опять же, я считаю необходимым упражнением написать пример, чтобы увидеть, как это работает. Для # 2 я бы вместо этого предложил, чтобы ваш код всегда отслеживал всех разветвленных дочерних элементов, чтобы он мог точно знать, на кого wait. Ваш код для № 3 выглядит хорошо; options не требуется. Не забудьте использовать макросы WEXITSTATUS и друзей, чтобы получить информацию от status.

Смотрите также:

person Jonathon Reinhart    schedule 19.11.2013
comment
В случае, если я не могу отслеживать всех разветвленных дочерних элементов (из-за определенных ограничений в спецификациях), является ли вариант № 2 жизнеспособным? - person Kvass; 19.11.2013
comment
@Kvass Думаю, да. Но я бы закодировал что-нибудь маленькое и убедился. - person Jonathon Reinhart; 19.11.2013
comment
В порядке. Кроме того, всегда ли верно утверждение в моем примере 3? - person Kvass; 19.11.2013