Опубликован: 15.06.2004 | Доступ: свободный | Студентов: 2557 / 712 | Оценка: 4.35 / 3.96 | Длительность: 27:47:00
ISBN: 978-5-9556-0011-6
Лекция 7:

Процессы

< Лекция 6 || Лекция 7: 123456 || Лекция 8 >

Аргумент options задается как побитное ИЛИ следующих флагов, определенных в заголовочном файле <sys/wait.h>.

WNOHANG

Функция waitpid() не приостанавливает выполнение вызывающего потока управления, если запрашиваемый статус не доступен немедленно.

WUNTRACED

Наряду со статусом завершения запрашивать статус остановленных, но еще не опрошенных процессов.

Если запрос статуса порожденного процесса завершился успешно, функции wait() и waitpid() возвращают идентификатор этого процесса и размещают по указателю stat_loc (если он отличен от NULL ) значение, которое будет нулевым тогда и только тогда, когда выдан статус порожденного процесса, завершившегося по одной из трех причин:

  • произошел возврат из функции main с нулевым результатом;
  • порожденный процесс вызвал функцию _exit() или exit() с нулевым аргументом;
  • завершились все потоки управления порожденного процесса.

Для анализа целочисленного значения stat_val, на которое указывает аргумент stat_loc, в файле <sys/wait.h> определен набор макросов. Например, значение WIFEXITED (stat_val ) будет ненулевым в случае нормального завершения порожденного процесса; при этом WEXITSTATUS (stat_val) возвращает младший байт статуса. Макрос WIFSTOPPED (stat_val) возвращает ненулевое значение, если получен статус остановленного процесса.

Процесс может вызвать собственное завершение, обратившись к функциям семейства exit() (см. листинг 7.24).

#include <stdlib.h>
void exit (int status);
void _Exit (int status);
#include <unistd.h>
void _exit (int status);
7.24. Описание функций семейства exit().

Значением аргумента status могут служить константы 0, EXIT_SUCCESS, EXIT_FAILURE или любое другое значение, хотя ожидающему родительскому процессу будет доступен только младший байт статуса ( status & 0377 ).

Функция exit(), в отличие от _Exit() и _exit(), производит аккуратное завершение: выполняет зарегистрированные функции (см. ниже описание функции atexit() ), выталкивает буфера, закрывает потоки, удаляет временные файлы и т.д. Функции _Exit() и _exit() эквивалентны. Все функции семейства exit() закрывают файловые дескрипторы, открытые вызывающим процессом.

Если родительский процесс выполняет функции wait() или waitpid(), ему передается младший байт значения status. Если родительский процесс этого не делает, функции семейства exit() переводят вызывающий процесс в состояние зомби.

Если у завершающегося процесса были потомки, родительским для них становится системный процесс, определяемый реализацией.

Функция atexit() (см. листинг 7.25) позволяет зарегистрировать функции, которые будут вызываться, если процесс завершается, обращаясь к exit() или возвращаясь из main().

#include <stdlib.h>
int atexit (void (*func) (void));
7.25. Описание функции atexit().

Реализация должна поддерживать регистрацию по крайней мере тридцати двух функций и вызывать их в обратном порядке.

В листинге 7.26 приведен пример использования функций порождения и завершения процессов.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>

static void atefunc (void) {
/* Перед завершением выдадим информацию о */
/* процессах */
printf ("Ситуация перед завершением
родительского процесса\n");
(void) system ("ps -o pid,ppid,vsz,args");
}

int main (void) {
	int pid;
	int stat;
	/* Отменим буферизацию стандартного вывода */
	setbuf (stdout, NULL);
	/* Зарегистрируем обработчик завершения процесса */
	if (atexit (atefunc) != 0) {
		perror ("ATEXIT");
		exit (1);
	}
	/* Создадим новый процесс */
	if ((pid = fork ()) < 0) {
		perror ("FORK");
		exit (2);
	} else if (pid == 0) {
		/* Порожденный процесс */
		/* Выполним служебную программу ps */
		printf ("Ситуация с точки зрения порожденного
			процесса\n");
		(void) execl ("/bin/ps", "ps", "-o",
			"pid,ppid,args", (char *) 0);
		perror ("EXEC");
		exit (3); /* execl() завершился неудачей */
	} else {
		/* Родительский процесс */
		sleep (1);
		/* Вероятно, порожденный процесс уже */
		/* завершился */
		/* Посмотрим на текущую ситуацию */
		printf ("Ситуация перед вызовом waitpid() в
			родительском процессе\n");
		(void) system ("ps -o pid,ppid,vsz,args");
		(void) waitpid (pid, &stat, 0);
		printf ("Статус завершения порожденного
		процесса с идентификатором %d: %d\n", pid, stat);
	}
	return 0;
}
7.26. Пример использования функций порождения и завершения процессов.

Результат работы этой программы может выглядеть так, как показано в листинге 7.27 (выполнимый файл программы имеет имя prog30 ).

Ситуация с точки зрения порожденного процесса
PID PPID COMMAND
6123 1072 -sh
29568 6123 prog30
29569 29568 ps -o pid,ppid,args
Ситуация перед вызовом waitpid() в родительском
	процессе
PID PPID VSZ COMMAND
6123 1072 2260 -sh
29568 6123 1340 prog30
29569 29568 0 [ps <defunct>]
29570 29568 2584 ps -o pid,ppid,vsz,args
Статус завершения порожденного процесса с
	идентификатором 29569: 0
Ситуация перед завершением родительского процесса
PID PPID VSZ COMMAND
6123 1072 2260 -sh
29568 6123 1340 prog30
29571 29568 2584 ps -o pid,ppid,vsz,args
7.27. Возможный результат работы программы, использующей функции порождения и завершения процессов.

Обратим внимание на информацию о зомби-процессе (с пометкой <defunct> ), а также на то, что функция, зарегистрированная с помощью atexit(), вызвана в результате возврата из main().

Для терминирования процессов извне предназначена служебная программа kill, вызванная следующим образом:

kill -s TERM идентификатор_процесса ...

Если это не помогло, можно применить более сильный вариант:

kill -s KILL идентификатор_процесса ...

Разумеется, попытки терминирования процессов подвержены контролю прав доступа.

< Лекция 6 || Лекция 7: 123456 || Лекция 8 >
Антон Коновалов
Антон Коновалов

В настоящее время актуальный стандарт - это POSIX 2008 и его дополнение POSIX 1003.13
Планируется ли актуализация материалов данного очень полезного курса?