При порождении процесса, который впоследствии может загрузить новую программу, "родителю" может быть небезынтересно узнать о завершении выполнения "потомка". Например, после того как запущена утилита ls(1) , командный интерпретатор приостанавливает свое выполнение до завершения работы утилиты и только после этого выдает свое приглашение на экран. Можно привести еще множество ситуаций, когда процессам необходимо синхронизировать свое выполнение с выполнением других процессов. Одним из способов такой синхронизации является обработка родителем сигнала SIGCHLD
, отправляемого ему при "смерти" потомка. Механизм сигналов мы рассмотрим в следующем разделе. Сейчас же остановимся на другом подходе.
Операционная система предоставляет процессу ряд функций, позволяющих ему контролировать выполнение потомков. Это функции wait(2) , waitid(2) и waitpid(2) :
#include
#include
pid_t wait(int* stat_loc);
int waitpid(idtype_t idtype, id_t id,
siginfo_t * infop, int options);
pid_t waitpid(pid_t pid, int *stat_loc, int options);
Первый из этих вызовов wait(2) обладает самой ограниченной функциональностью — он позволяет заблокировать выполнение процесса, пока кто-либо из его непосредственных потомков не прекратит существование. Вызов wait(2) немедленно возвратит состояние уже завершившегося дочернего процесса в переменной stat_loc
, если последний находится в состоянии зомби. Значение stat_loc
может быть проанализировано с помощью следующих макроопределений:
WIFEXITED(status) |
Возвращает истинное (ненулевое) значение, если процесс завершился нормально. |
WEXITSTATUS(status) |
Если WIFEXITED(status) не равно нулю, определяет код возврата завершившегося процесса (аргумент функции exit(2) ). |
WIFSIGNALLED(status) |
Возвращает истину, если процесс завершился по сигналу. |
WTERMSIG(status) |
Если WIFSIGNALLED(status) не равно нулю, определяет номер сигнала, вызвавшего завершение выполнения процесса. |
WCOREDUMP(status) |
Если WIFSIGNALLED(status) не равно нулю, макрос возвращает истину в случае создания файла core. |
Системный вызов waitid(2) предоставляет больше возможностей для контроля дочернего процесса. Аргументы idtype
и id
определяют, за какими из дочерних процессов требуется следить:
Значение аргумента idtype |
Описание |
P_PID |
waitid(2) блокирует выполнение процесса, следя за потомком, PID которого равен id . |
P_PGID |
waitid(2) блокирует выполнение процесса, следя за потомками, идентификаторы группы которых равны id . |
P_ALL |
waitid(2) блокирует выполнение процесса, следя за всеми непосредственными потомками. |
Аргумент options
содержит флаги, объединенные логическим ИЛИ, определяющие, за какими изменениями в состоянии потомков следит waitid(2) :
Флаги аргумента options |
Описание |
WEXITED |
Предписывает ожидать завершения выполнения процесса. |
WTRAPPED |
Предписывает ожидать ловушки (trap) или точки останова (breakpoint) для трассируемых процессов. |
WSTOPPED |
Предписывает ожидать останова процесса из-за получения сигнала. |
WCONTINUED |
Предписывает вернуть статус процесса, выполнение которого было продолжено после останова. |
WNOHANG |
Предписывает завершить свое выполнение, если отсутствует статусная информация (т.е. отсутствует ожидаемое событие). |
WNOWAIT |
Предписывает получить статусную информацию, но не уничтожать ее, оставив дочерний процесс в состоянии ожидания. |
Аргумент infop
указывает на структуру siginfo_t
, которая будет заполнена информацией о потомке. Мы рассмотрим эту структуру в следующем разделе.
Функция waitpid(2) , как и функции wait(2) и waitid(2) , позволяет контролировать определенное множество дочерних процессов.
В заключение для иллюстрации описанных в этом разделе системных вызовов приведем схему работы командного интерпретатора при запуске команды.
...
/* Вывести приглашение shell*/
write(1, "$ ", 2);
/* Считать пользовательский ввод */
get_input(inputbuf);
/* Произвести разбор ввода: выделить команду cmd
и ее аргументы arg[] */
parse_input(inputbuf, and, arg);
/* Породить процесс */
pid = fork();
if (pid == 0) {
/* Запустить программу */
execvp(cmd, arg);
Читать дальше