В большинстве случаев при исполнении fork
и exec
для отдельной программы не нужно ничего наследовать, кроме дескрипторов файлов 0, 1 и 2. В этом случае можно вручную закрыть все другие открытые файлы в порожденном процессе после выполнения fork
и до выполнения exec
. В качестве альтернативы можно пометить дескриптор файла для автоматического закрытия системой при исполнении exec; эта последняя возможность обсуждается далее в главе (см раздел 9.4.3.1 «Флаг close-on-exec».)
9.1.5. Завершение процесса
Завершение процесса включает два шага: окончание процесса с передачей системе статуса завершения и восстановление информации родительским процессом.
9.1.5.1. Определение статуса завершения процесса
grep
использует 0 для указания, что образец был встречен по крайней мере один раз, 1 означает, что образец вообще не встретился, а 2 означает, что возникла ошибка.) Этот статус завершения доступен на уровне оболочки (для оболочек в стиле оболочки Борна) через специальную переменную $?
.
Стандарт С определяет две константы, которые следует использовать для полной переносимости на не-POSIX системы:
EXIT_SUCCESS
Программа завершилась без проблем. Для обозначения успеха может также использоваться ноль.
EXIT
_FAILURE
В программе была какая-нибудь проблема.
На практике использование лишь этих значений довольно ограничивает. Вместо этого следует выбрать небольшой набор кодов возврата, документировать их значения и использовать. (Например, 1 для ошибок опций командной строки и аргументов, 2 для ошибок ввода/вывода, 3 для ошибок данных и т.д.) Для удобочитаемости стоит использовать константы #define
или значения enum
. Слишком большой список ошибок делает их использование обременительным; в большинстве случаев вызывающая программа (или пользователь) интересуется лишь нулевым или ненулевым значением.
Когда достаточно двоичного разделения успех/неудача, педантичный программист использует EXIT_SUCCESS
и EXIT_FAILURE
. Наш собственный стиль более естественный, используя с return
и exit()
явные константы 0 или 1. Это настолько обычно, что рано заучивается и быстро становится второй натурой. Однако для своих проектов вы сами должны принять решение.
ЗАМЕЧАНИЕ. Для родительского процесса доступны лишь восемь наименее значимых битов значения. Поэтому следует использовать значения в диапазоне 0–255. Как мы вскоре увидим, у чисел 126 и 127 есть традиционные значения (помимо простого «неуспешно»), которых ваши программы должны придерживаться.
Поскольку имеют значение лишь восемь наименее значимых битов, вы никогда не должны использовать отрицательные статусы завершения. Когда из небольших отрицательных чисел выделяются восемь последних битов, они превращаются в большие положительные значения! (Например. -1 становится 255, а -5 становится 251.) Мы видели книги по программированию на С, в которых это понималось неправильно — не дайте сбить себя с толку
9.1.5.2. Возвращение из main()
Программа может естественно завершиться одним из двух способов: посредством использования одной из описанных далее функций или возвратившись из main()
. (Третий, более радикальный способ описан далее в разделе 12.4 «Совершение самоубийства: abort()
».) В последнем случае следует использовать явное возвращаемое значение вместо выпадения в конце функции:
/* Правильно */ /* Неправильно */