Самая простая функция в семействе называется wait()
. Она блокирует вызывающий процесс до тех пор, пока один из его дочерних процессов не завершится (или не произойдет ошибка). Код состояния потомка возвращается через аргумент, являющийся указателем на целое число. В этом коде зашифрована различная информация о потомке. Например, макрос WEXITSTATUS()
возвращает код завершения дочернего процесса. Макрос WIFEXITED()
позволяет узнать, как именно завершился процесс: обычным образом (с помощью функции exit()
или оператора return
функции main()
) либо аварийно вследствие получения сигнала. В последнем случае макрос WTERMSIG()
извлекает из кода завершения номер сигнала.
Ниже приведена доработанная версия функции main()
из файла fork-exec.c
. На этот раз программа вызывает функцию wait()
, чтобы дождаться завершения дочернего процесса, в котором выполняется команда ls
.
int main() {
int child_status;
/* Список аргументов, передаваемых команде ls. */
char* arg_list[] = {
"ls", /* argv[0] — имя программы. */
"-l",
"/",
NULL /* Список аргументов должен оканчиваться указателем
NULL. */
};
/* Порождаем дочерний процесс, который выполняет команду ls.
Игнорируем возвращаемый идентификатор дочернего процесса. */
spawn("ls*, arg_list);
/* Дожидаемся завершения дочернего процесса. */
wait(&child_status);
if (WTFEXITED(child_status));
printf("the child process exited normally, with exit code %d\n",
WEXITSTATUS(child_status));
else
printf("the child process exited abnormally\n");
return 0;
}
Расскажем о других функциях семейства. Функция waitpid()
позволяет дождаться завершения конкретного дочернего процесса, а не просто любого. Функция wait3()
возвращает информацию о статистике использования центрального процессора завершившимся дочерним процессом. Функция wait4()
позволяет задать дополнительную информацию о том, завершения каких процессов следует дождаться.
3.4.3. Процессы-зомби
Если дочерний процесс завершается в то время, когда родительский процесс заблокирован функцией wait()
, он успешно удаляется и его код завершения передается предку через функцию wait()
. Но что произойдет, если потомок завершился, а родительский процесс так и не вызвал функцию wait()
? Дочерний процесс просто исчезнет? Нет, ведь в этом случае информация о его завершении (было ли оно аварийным или нет и каков код завершения) пропадет. Вместо этого дочерний процесс становится процессом-зомби.
wait()
тоже это делает, поэтому перед ее вызовом не нужно проверять, продолжает ли выполняться требуемый дочерний процесс. Предположим, к примеру, что программа создает дочерний процесс, выполняет нужные вычисления и затем вызывает функцию wait()
. Если к тому времени дочерний процесс еще не завершился, функция wait()
заблокирует программу. В противном случае процесс на некоторое время превратится в зомби. Тогда функция wait()
извлечет код его завершения, система удалит процесс и функция немедленно завершится.
Что же всё-таки случится, если родительский процесс не удалит своих потомков? Они останутся в системе в виде зомби. Программа, показанная в листинге 3.6, порождает дочерний процесс, который немедленно завершается, тогда как родительский процесс берет минутную паузу, после чего тоже заканчивает работу, так и не позаботившись об удалении потомка.
#include «stdlib.h>
#include
#include
int main() {
pid_t child_pid;
/* Создание дочернего процесса. */
child_pid = fork();
if (child_pid > 0) {
/* Это родительский процесс — делаем минутную паузу. */
sleep(60);
} else {
/* Это дочерний процесс — немедленно завершаем работу. */
exit(0);
}
return 0;
}
Скомпилируйте этот файл и запустите программу. Пока программа работает, перейдите в другое окно и просмотрите список процессов с помощью следующей команды:
% ps -е -o pid,ppid,stat,cmd