Арнольд Роббинс - Linux программирование в примерах
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 «Родительский надзор: три различные стратегии» мы увидим, что это необязательно. Скорее, сигналы предоставляют ряд механизмов для использования уведомлениями родителей о завершении порожденных процессов.
9.1.6.2. Использование функций BSD: wait3() и wait4()
Системные вызовы BSD wait3() и wait4() полезны, если вы интересуетесь ресурсами, использованными порожденным процессом. Функции нестандартны (что означает, что они не являются частью POSIX), но широко доступны, в том числе на GNU/Linux. Объявления следующие:
#include <sys/types.h> /* Обычный */
#include <sys/time.h>
/* Под GNU/Linux не нужно, но улучшает переносимость */
#include <sys/resource.h>
#include <sys/wait.h>
pid_t wait3(int *status, int options, struct rusage *rusage);
pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);
Переменная status та же, что и для wait() и waitpid(). Все описанные ранее макросы (WIFEXITED() и т.д.) могут использоваться и с ними.
Значение options также то же самое, что и для waitpid(): либо 0, либо побитовое ИЛИ с одним или обоими флагами WNOHANG и WUNTRACED.
wait3() ведет себя подобно wait(), получая сведения о первом доступном порожденном зомби, a wait4() подобна waitpid(), получая сведения об определенном процессе. Обе функции возвращают PID потомка, -1 при ошибке или 0, если нет доступных процессов и был использован флаг WNOHANG. Аргумент pid может принимать те же значения, что и аргумент pid для waitpid().
Ключевым отличием является указатель struct rusage. Если он не равен NULL, система заполняет ее сведениями о процессе. Эта структура описана в POSIX и в справочной странице getrusage(2):
struct rusage {
struct timeval ru_utime; /* используемое время пользователя */
struct timeval ru_stime; /* используемое системное время */
long ru_maxrss; /* максимальный размер резидентного набора */
long ru_ixrss; /* общий размер разделяемой памяти */
long ru_idrss; /* общий размер не разделяемых данных */
long ru_isrss; /* общий размер не разделяемого стека */
long ru_minflt; /* использование страниц */
long ru_majflt; /* ошибок страниц */
long ru_nswap; /* подкачек */
long ru_inblock; /* блочных операций ввода */
long ru_oublock; /* блочных операций вывода */
long ru_msgsnd; /* посланных сообщений */
long ru_msgrcv; /* полученных сообщений */
long ru_nsignals; /* полученных сигналов */
long ru_nvcsw; /* добровольных переключений контекста */
long ru_nivcsw; /* принудительных переключений контекста */
};
Чисто BSD системы (4.3 Reno и более поздние) поддерживают все поля. В табл. 9.2 описаны доступность различных полей struct rusage для POSIX и Linux.
Таблица 9.2. Доступность полей struct rusage
Поле POSIX Linux Поле POSIX Linux ru_utime √ ≥ 2.4 ru_nswap ≥2.4 ru_stime √ ≥2.4 ru_nvcsw ≥2.6 ru_minflt ≥2.4 ru_nivcsw ≥2.6 ru_majflt ≥2.4Стандартом определены лишь поля, помеченные «POSIX». Хотя Linux определяет полную структуру, ядро 2.4 поддерживает лишь поля времени пользователя и системного времени. Ядро 2.6 поддерживает также поля, связанные с переключением контекста.[92]
Наиболее интересными полями являются ru_utime и ru_stime, использование времени процессора в режиме пользователя и ядра соответственно. (Время процессора в режиме пользователя является временем, потраченным на исполнение кода уровня пользователя. Время процессора в режиме ядра является временем, потраченным в ядре в пользу процесса.)
Эти два поля используют struct timeval, которая содержит значения времени с точностью до микросекунд. Дополнительные сведения по этой структуре см. в разделе 14.3.1 «Время в микросекундах: gettimeofday()».