Книга: Linux программирование в примерах
9.1.6.1. Использование функций POSIX: wait() и waitpid()
9.1.6.1. Использование функций POSIX: wait()
и waitpid()
Первоначальным системным вызовом V7 был wait()
. Более новым вызовом POSIX, основанным на возможностях BSD, является waitpid()
. Объявления функций следующие:
#include <sys/types.h> /* POSIX */
ждет завершения любого порожденного процесса; сведения о том, как он завершился, возвращаются в
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
wait()*status
. (Вскоре мы обсудим, как интерпретировать *status
.) Возвращаемое значение является PID завершившегося процесса или -1, если возникла ошибка.
Если порожденных процессов нет, wait()
возвращает -1 с errno
, установленным в ECHILD
(отсутствует порожденный процесс). В противном случае, функция ждет завершения первого порожденного процесса или поступления сигнала.
Функция waitpid()
дает возможность ждать завершения определенного порожденного процесса. Она предоставляет значительную гибкость и является предпочтительной для использования функцией. Она также возвращает PID закончившегося процесса или -1 при возникновении ошибки. Аргументы следующие:
pid_t pid
Значение указывает, завершения какого порожденного процесса ждать как по-настоящему pid
, так и по группе процесса. Смысл значения pid
следующий:
pid < -1
Ждать завершения любого порожденного процесса с ID группы процесса, равной абсолютному значению pid
.
pid = -1
Ждать завершения любого порожденного процесса. Таким способом работает wait()
.
pid = 0
Ждать завершения любого порожденного процесса с ID группы процесса, равной ID группе родительского процесса.
pid > 0
Ждать завершения конкретного процесса с PID, равным pid
.
int *status
То же, что и для wait()
. <sys/wait.h>
определяет различные макросы, которые интерпретируют значение в *status
, которые мы вскоре опишем
int options
Этот параметр должен быть равен либо 0, либо побитовым ИЛИ одного или более из следующих флагов:
WNOHANG
Если ни один порожденный процесс не завершился, вернуться немедленно. Таким способом можно периодически проверять, не закончился ли какой- нибудь порожденный процесс. (Такая периодическая проверка известна как опрашивание события.)
WUNTRACED
Вернуть сведения о порожденном процессе, который остановился, но еще не завершился. (Например, для управления работой.)
WCONTINUED
(XSI.) Вернуть сведения о порожденном процессе, который продолжился, если его статус не сообщался с момента изменения. Это также для управления работой. Этот флаг является расширением XSI и не доступен под GNU/Linux.
С заполненным значением *status
работают несколько макросов, определяющие, что случилось. Они имеют тенденцию образовывать пары: один макрос для определения, что что-то случилось, и если этот макрос истинен, еще один макрос позволяет получить подробности. Макросы следующие:
WIFEXITED(status)
Этот макрос не равен нулю (true), если процесс завершился (в противоположность изменению состояния).
WEXITSTATUS(status)
Этот макрос дает статус завершения; он равен восьми наименее значимым битам значения, переданного exit()
или возвращенного из main()
. Этот макрос следует использовать лишь если WIFEXIDED(status)
равен true.
WIFSIGNALED(status)
Этот макрос не равен нулю, если процесс подвергся действию завершающего сигнала death-by-signal.
WTERMSIG(status)
Этот макрос предоставляет номер сигнала, который завершил процесс. Этот макрос следует использовать, лишь когда WIFSIGNALED(status)
равен true.
WIFSTOPPED(status)
Этот макрос не равен нулю, если процесс был остановлен.
WSTOPSIG(status)
Этот макрос предоставляет номер сигнала, который остановил процесс. (Процесс остановить могут несколько сигналов.) Этот макрос следует использовать лишь когда WIFSTOPPED(status)
равен true. Сигналы управления работами обсуждаются в разделе 10.8.2 «Сигналы управления работой».
WIFCONTINUED(status)
(XSI.) Этот макрос не равен нулю, если процесс был продолжен. Соответствующего макроса WCONTSIG()
нет, поскольку лишь один сигнал может вызвать продолжение процесса.
Обратите внимание, что этот макрос является расширением XSI и в частности, он недоступен в GNU/Linux. Следовательно, если вы хотите его использовать, заключите код внутри '#ifdef WIFCONTINUED ... #endif
'.
WCOREDUMP(status)
(Общий.) Этот макрос не равен нулю, если процесс создал снимок. Снимок процесса (core dump) является образом запущенного процесса в памяти, созданном при завершении процесса. Он предназначен для использования впоследствии при отладке. Системы Unix называют файл core
, тогда как системы GNU/Linux используют corе.pid
, где pid
является ID завершившегося процесса. Определенные сигналы завершают процесс и автоматически создают снимок процесса.
Обратите внимание, что этот макрос не стандартный. Системы GNU/Linux, Solaris и BSD его поддерживают, однако некоторые другие системы Unix нет. Поэтому и здесь, если нужно его использовать, заключите код внутрь '#ifdef WCOREDUMP ... #endif
'.
Большинство программ не интересуются, почему завершился порожденный процесс; им просто нужно, что он завершился, возможно, отметив, было завершение успешным или нет. Программа GNU Coreutils install
демонстрирует такое простое использование fork()
, execlp()
и wait()
. Опция -s
заставляет install
запустить для устанавливаемого двоичного исполняемого файла программу strip
. (strip
удаляет из исполняемого файла отладочную и прочую информацию. Это может сохранить значительное пространство. На современных системах с многогигабайтными жесткими дисками при установке редко бывает необходимо использовать strip
для исполняемых файлов.) Вот функция strip()
из install.с
:
513 /* Вырезать таблицу имен из файла PATH.
514 Мы могли бы сначала вытащить из файла магическое число
515 для определения, нужно ли вырезать, но заголовочные файлы и
516 магические числа варьируют от системы к системе так сильно, что
517 сделать его переносимым было бы очень трудно. Не стоит усилий. */
518
519 static void
520 strip (const char *path)
521 {
522 int status;
523 pid_t pid = fork();
524
525 switch (pid)
526 {
527 case -1:
528 error(EXIT_FAILURE, errno, _("fork system call failed"));
529 break;
530 case 0: /* Порожденный. */
531 execlp("strip", "strip", path, NULL);
532 error(EXIT_FAILURE, errno, _("cannot run strip"));
533 break;
534 default: /* Родитель. */
535 /* Родительский процесс. */
536 while (pid != wait(&status)) /* Ждать завершения потомка. */
537 /* Ничего не делать. */ ;
538 if (status)
539 error(EXIT_FAILURE, 0, _("strip failed"));
540 break;
541 }
542 }
Строка 523 вызывает fork()
. Затем оператор switch
предпринимает нужное действие для возвращения ошибки (строки 527–529), порожденного процесса (строки 530–533) и родительского процесса (строки 534–539).
Стиль строк 536–537 типичен; они ожидают завершения нужного порожденного процесса. Возвращаемое значение wait()
является PID этого потомка. Оно сравнивается с PID порожденного процесса, status
проверяется лишь на предмет равенства нулю (строка 538), в случае ненулевого результата потомок завершился неудачно. (Тест, хотя и правильный, грубый, но простой. Более правильным был бы тест наподобие 'if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
'.)
Из описаний и кода, представленных до сих пор, может показаться, что родительские программы должны выбрать определенный момент, чтобы ожидать завершения любого порожденного процесса, возможно, с опросом в цикле (как делает install.c
), ожидая всех потомков. В разделе 10.8.3 «Родительский надзор: три различные стратегии» мы увидим, что это необязательно. Скорее, сигналы предоставляют ряд механизмов для использования уведомлениями родителей о завершении порожденных процессов.
- 14.2.2. Блокировка POSIX: fcntl() и lockf()
- 9.1.6. Использование статуса завершения порожденного процесса
- 9.1.6.2. Использование функций BSD: wait3() и wait4()
- IEEE и POSIX
- 5.10. Функции wait и waitpid
- Преобразования типов при вызовах функций
- 5.7. Сигналы реального времени Posix
- POSIX IPC Awareness
- Восстановление с использованием инструмента gbak
- Типы страниц и их использование
- Использование констант
- Использование переменной окружения ISC_PATH