Завершение C-программы

Завершение C-программы

Существует несколько способов завершения программы. Основными являются возврат из функции main()[17] и вызов функций exit(2), оба приводят к завершению выполнения задачи. Заметим, что процесс может завершиться по не зависящим от него обстоятельствам, например, при получении сигнала, действие по умолчанию для большинства из которых приводит к завершению выполнения процесса[18] (см. раздел "Сигналы" далее в этой главе). В этом случае функция exit(2) будет вызвана ядром от имени процесса.

Системный вызов exit(2) выглядит следующим образом:

#include <unistd.h>

void exit(int status);

Аргумент status, передаваемый функции exit(2), возвращается родительскому процессу и представляет собой код возврата программы. По соглашению программа возвращает 0 в случае успеха и другую величину в случае неудачи. Значение кода неудачи может иметь дополнительную трактовку, определяемую самой программой. Например, программа grep(1), выполняющая поиск заданных подстрок в файлах, определяет следующие коды возврата:

0 совпадение было найдено 1 совпадений найдено не было 2 синтаксическая ошибка или недоступны файлы поиска

Наличие кода возврата позволяет программам взаимодействовать друг с другом. Например, следующая программа (назовем ее fail) может являться условием неудачи и использоваться в соответствующих синтаксических конструкциях shell:

main() {

 exit(1);

}

$ fail

$ echo $?           Выведем код возврата программы fail

1

$ fail || echo fail Конструкция shell, использующая условие неудачи fail

fail

Помимо передачи кода возврата, функция exit(2) производит ряд действий, в частности выводит буферизованные данные и закрывает потоки ввода/вывода. Альтернативой ей является функция _exit(2), которая не производит вызовов библиотеки ввода/вывода, а сразу вызывает системную функцию завершения ядра. Более подробно о процедурах завершения процесса см. раздел "Создание и управление процессами".

Задача может зарегистрировать обработчики выхода (exit handler), — функции, которые вызываются после вызова exit(2), но до окончательного завершения процесса. Эти обработчики, вызываемые по принципу LIFO (последний зарегистрированный обработчик будет вызван первым), запускаются только при "добровольном" завершении процесса. Например, при получении процессом сигнала обработчики выхода вызываться не будут. Для обработки таких ситуаций следует использовать специальные функции — обработчики сигналов (см. раздел "Сигналы" далее в этой главе).

Обработчики выхода регистрируются с помощью функции atexit(3C):

#include <stdlib.h>

int atexit(void(*func)(void));

Функцией atexit(1) может быть зарегистрировано до 32 обработчиков.

На рис. 2.7 проиллюстрированы возможные варианты запуска и завершения программы, написанной на языке С.

Рис. 2.7. Запуск и завершение C-программы

Данный текст является ознакомительным фрагментом.