Использование вызова wait() демонстрируется в листинге 26.1. В этой программе создается несколько дочерних процессов — по одному на каждый (целочисленный) аргумент командной строки. Каждый потомок ожидает определенное количество секунд, указанное в соответствующем аргументе, и затем завершается. Тем временем после создания всех потомков родительский процесс начинает их отслеживать, последовательно выполняя вызовы wait(). Этот цикл завершается, когда wait() возвращает –1 (это не единственный из возможных вариантов: например, мы могли бы выходить из цикла, когда число завершенных потомков, numDead, сравнивается с числом созданных потомков). В журнале сессии, представленном ниже, показано, что происходит, когда мы используем данную программу для создания трех дочерних процессов:
$ ./multi_wait 7 1 4
[13:41:00] child 1 started with PID 21835, sleeping 7 seconds
[13:41:00] child 2 started with PID 21836, sleeping 1 seconds
[13:41:00] child 3 started with PID 21837, sleeping 4 seconds
[13:41:01] wait() returned child PID 21836 (numDead=1)
[13:41:04] wait() returned child PID 21837 (numDead=2)
[13:41:07] wait() returned child PID 21835 (numDead=3)
No more children — bye!
Если в какой-то момент у нас имеется несколько завершенных потомков, порядок, в котором они будут обработаны вызовами wait(), является неопределенным (согласно стандарту SUSv3). Это означает, что порядок обработки зависит от реализации. Он может меняться даже в разных версиях ядра Linux.
Листинг 26.1. Создание нескольких потомков и ожидание их завершения
procexec/multi_wait.c
#include
#include
#include "curr_time.h" /* Объявление currTIme() */
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
int numDead; /* Количество завершенных потомков на данный момент */
pid_t childPid; /* PID завершенного потомка */
int j;
if (argc < 2 || strcmp(argv[1], "-help") == 0)
usageErr("%s sleep-time…\n", argv[0]);
setbuf(stdout, NULL); /* Отключаем буферизацию стандартного вывода */
for (j = 1; j < argc; j++) { /* Создаем по одному потомку для каждого аргумента */
switch (fork()) {
case –1:
errExit("fork");
case 0: /* Потомок ожидает какое-то время, затем завершается */
printf("[%s] child %d started with PID %ld, sleeping %s "
"seconds\n", currTime("%T"), j, (long) getpid(), argv[j]);
sleep(getInt(argv[j], GN_NONNEG, "sleep-time"));
_exit(EXIT_SUCCESS);
default: /* Родитель продолжает выполнение цикла */
break;
}
}
numDead = 0;
for (;;) { /* Родитель ждет завершения каждого потомка */
childPid = wait(NULL);
if (childPid == –1) {
if (errno == ECHILD) {
printf("No more children — bye!\n");
exit(EXIT_SUCCESS);
} else { /* Какая-то другая (непредвиденная) ошибка */
errExit("wait");
}
}
numDead++;
printf("[%s] wait() returned child PID %ld (numDead=%d)\n",
currTime("%T"), (long) childPid, numDead);
}
}
procexec/multi_wait.c
26.1.2. Системный вызов waitpid()
Системный вызов waitpid() снимает ряд ограничений, присущих вызову wait().
• Если родительский процесс создал несколько потомков, wait() не позволяет ожидать завершения конкретного из них; мы можем отслеживать завершение работы только следующего дочернего процесса.
• Если ни один из потомков еще не был завершен, вызов wait() всегда блокируется. Иногда имеет смысл организовать неблокирующее ожидание, чтобы, если все потомки все еще работают, иметь возможность немедленно узнать об этом факте.
• С помощью wait() можно отслеживать только потомки, которые завершились. Если потомок был остановлен (по сигналам SIGSTOP или SIGTTIN) или возобновил свою работу (по сигналу SIGCONT), мы об этом не узнаем.
#include
pid_t waitpid(pid_t
Возвращает идентификатор потомка: 0 (см. далее) или –1, если возникла ошибка
Возвращаемое значение и аргумент status у вызова waitpid() служат тем же целям, что и wait() (описание значений аргумента status ищите в разделе 26.1.3). Аргумент pid позволяет выбрать потомков, которые будут отслеживаться. Делается это следующим образом.
• Если pid больше 0, ожидаем потомка, чей
• Если pid равен 0, ожидаем потомка, входящего
• Если pid меньше –1, ожидаем любого потомка, идентификатор
• Если pid равен –1, ожидаем
Аргумент options представляет собой битовую маску, которая может быть пустой или содержать какое-то количество следующих флагов (каждый из которых входит в стандарт SUSv3), скомбинированных с помощью побитового ИЛИ.
• WUNTRACED — позволяет узнать не только о завершенных дочерних процессах, но и о потомках,