kill(child_pid, SIGTERM);
Для использования функции kill()
необходимо включить в программу файлы и .
По существующему соглашению код завершения указывает на то, успешно ли выполнилась программа. Нулевой код говорит о том, что все в порядке, ненулевой код свидетельствует об ошибке. В последнем случае конкретное значение кода может подсказать природу ошибки. Подобным образом функционируют все компоненты GNU/Linux. Например, на это рассчитывает интерпретатор команд, когда в командных сценариях вызовы программ объединяются с помощью операторов &&
(логическое умножение) и ||
(логическое сложение) Таким образом, функция main()
должна явно возвращать 0 при отсутствии ошибок.
Помните о следующем ограничении: несмотря на то что тип параметра функции exit()
, как и тип возвращаемого значения функции main()
, равен int
, операционная система Linux записывает код завершения лишь в младший из четырех байтов. Это означает, что значение кода должно находиться в диапазоне от 0 до 127. Коды, значение которых больше 128, интерпретируются особым образом: когда процесс уничтожается вследствие получения сигнала, его код завершения равен 128 плюс номер сигнала.
3.4.1. Ожидание завершения процесса
Читатели, запускавшие программу fork-exec
(см. листинг 3.4), должно быть, обратили внимание на то, что вывод команды ls
часто появляется после того, как основная программа уже завершила свою работу. Это связано с тем, что дочерний процесс, в котором выполняется команда ls
, планируется независимо от родительского процесса. Linux — многозадачная операционная система, процессы в ней выполняются одновременно, поэтому нельзя заранее предсказать, кто — предок или потомок — завершится раньше.
Но бывают ситуации, когда родительский процесс должен дождаться завершения одного или нескольких своих потомков. Это можно сделать с помощью функций семейства wait()
. Они позволяют родительскому процессу получать информацию о завершении дочернего процесса. В семейство входят четыре функции, различающиеся объемом возвращаемой информации, а также способом задания дочернего процесса.
3.4.2. Системные вызовы wait()
Самая простая функция в семействе называется wait()
. Она блокирует вызывающий процесс до тех пор, пока один из его дочерних процессов не завершится (или не произойдет ошибка). Код состояния потомка возвращается через аргумент, являющийся указателем на целое число. В этом коде зашифрована различная информация о потомке. Например, макрос WEXITSTATUS()
возвращает код завершения дочернего процесса. Макрос WIFEXITED()
позволяет узнать, как именно завершился процесс: обычным образом (с помощью функции exit()
или оператора return
функции main()
) либо аварийно вследствие получения сигнала. В последнем случае макрос WTERMSIG()
извлекает из кода завершения номер сигнала.
Ниже приведена доработанная версия функции main()
из файла fork-exec.c
. На этот раз программа вызывает функцию wait()
, чтобы дождаться завершения дочернего процесса, в котором выполняется команда ls
.
int main() {
int child_status;
/* Список аргументов, передаваемых команде ls. */
char* arg_list[] = {
"ls", /* argv[0] — имя программы. */
"-l",
"/",
NULL /* Список аргументов должен оканчиваться указателем
NULL. */
};
/* Порождаем дочерний процесс, который выполняет команду ls.
Игнорируем возвращаемый идентификатор дочернего процесса. */
spawn("ls*, arg_list);
/* Дожидаемся завершения дочернего процесса. */
wait(&child_status);
if (WTFEXITED(child_status));
printf("the child process exited normally, with exit code %d\n",
WEXITSTATUS(child_status));
else
printf("the child process exited abnormally\n");
return 0;
}
Расскажем о других функциях семейства. Функция waitpid()
позволяет дождаться завершения конкретного дочернего процесса, а не просто любого. Функция wait3()
возвращает информацию о статистике использования центрального процессора завершившимся дочерним процессом. Функция wait4()
позволяет задать дополнительную информацию о том, завершения каких процессов следует дождаться.
Если дочерний процесс завершается в то время, когда родительский процесс заблокирован функцией wait()
, он успешно удаляется и его код завершения передается предку через функцию wait()
. Но что произойдет, если потомок завершился, а родительский процесс так и не вызвал функцию wait()
? Дочерний процесс просто исчезнет? Нет, ведь в этом случае информация о его завершении (было ли оно аварийным или нет и каков код завершения) пропадет. Вместо этого дочерний процесс становится процессом-зомби.
Читать дальше