Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » Linux программирование в примерах - Арнольд Роббинс

Linux программирование в примерах - Арнольд Роббинс

Читать онлайн Linux программирование в примерах - Арнольд Роббинс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 72 73 74 75 76 77 78 79 80 ... 165
Перейти на страницу:

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()».

1 ... 72 73 74 75 76 77 78 79 80 ... 165
Перейти на страницу:
Тут вы можете бесплатно читать книгу Linux программирование в примерах - Арнольд Роббинс.
Комментарии