Операционная система предоставляет процессу ряд функций, позволяющих ему контролировать выполнение потомков. Это функции wait(2), waitid(2) и waitpid(2):
#include <sys/types.h>
#include <sys/wait.h>
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);
/* При нормальном запуске программы эта часть кода
выполняться уже не будет — можно смело выводить
сообщение об ошибке */
pexit(cmd);
} else
/* Родительский процесс (shell) ожидает завершения
выполнения потомка */
wait(&status);
...
Сигналы
Сигнал является способом передачи уведомления о некотором произошедшем событии между процессами или между ядром системы и процессами. Сигналы можно рассматривать, как простейшую форму межпроцессного взаимодействия, хотя на самом деле они больше напоминают программные прерывания, при которых нарушается нормальное выполнение процесса.
Сигналы появились уже в ранних версиях UNIX, но их реализация не была достаточно надежной. Сигнал мог быть "потерян", возникали также определенные сложности с отключением (блокированием) сигналов на время выполнения критических участков кода. В последующие версии системы, как BSD, так и System V, были внесены изменения, позволившие реализовать надежные (reliable) сигналы. Однако модель сигналов, принятая в версиях BSD, была несовместима с моделью версий System V. В настоящее время стандарт POSIX.1 вносит определенность в интерфейс надежных сигналов.