В чем разница между использованием _exit() & exit() в обычном Linux fork-exec?
Я пытался выяснить, как механизм fork-exec используется внутри Linux. Все происходило по плану, пока некоторые веб-страницы не запутали меня.
Говорят, что дочерний процесс должен строго использовать _exit()
вместо простого exit()
или нормального возврата из main()
.
Как я знаю, Linux shell fork-execs выполняет каждую из внешних команд; предполагая, что я сказал выше, верно, вывод заключается в том, что ни одна из этих внешних команд или любое другое выполнение, происходящее внутри оболочки Linux, не может нормально работать!
Википедия и некоторые другие веб-страницы утверждают, что мы должны использовать _exit()
только для предотвращения дочернего процесса, вызывающего удаление родительских временных файлов, в то время как вероятная двойная промывка буферов stdio может произойти. хотя я понимаю первое, я не знаю, как двойная промывка буферов может быть вредной для системы Linux.
Я потратил весь день на это...
Спасибо за любое разъяснение.
Ответы
Ответ 1
Вы должны использовать _exit
(или его синоним _exit
), чтобы прервать дочернюю программу, когда exec
выходит из строя, поскольку в в этой ситуации дочерний процесс может влиять на внешние данные (файлы) родительского процесса, вызывая его обработчики atexit
, вызывая его обработчики сигналов и/или сбросные буферы.
По той же причине вы также должны использовать _exit
в любом дочернем процессе, который не выполняет exec
, но это редко.
Во всех остальных случаях просто используйте exit
. Как вы частично отметили, каждый процесс в Unix/Linux (кроме одного, init
) является дочерним элементом другого процесса, поэтому использование _exit
в каждом дочернем процессе означает, что exit
бесполезен вне init
.
switch (fork()) {
case 0:
// we're the child
execlp("some", "program", NULL);
_exit(1); // <-- HERE
case -1:
// error, no fork done ...
default:
// we're the parent ...
}
Ответ 2
exit()
очищает буферы io и выполняет некоторые другие функции, такие как функции запуска, зарегистрированные atexit()
. exit()
вызывает _end( )
_exit()
просто заканчивает процесс, не делая этого. Вы вызываете _exit()
из родительского процесса при создании демона, например.
Вы когда-нибудь замечали, что main()
является функцией? Вы когда-нибудь задумывались, как это называется?
Когда ac-программа запускает запущенную оболочку, вы получаете исполняемый путь к системному вызову "exec", и элемент управления передается ядру, которое, в свою очередь, вызывает функцию запуска каждого исполняемого файла _start()
, вызывает ваш main()
, когда main()
возвращает его, затем вызывает _end()
Некоторые реализации C используют несколько разные имена для _end()
и _start()
...
exit()
и _exit()
invoke _end()
Обычно - для каждого main()
должен быть один и только один вызов exit()
. (или вернуться в конце main()
)
Ответ 3
exit() находится в верхней части _exit(), используя обычную библиотеку C.
Существуют различия:
-
_exit() не будет удалять буфер stdio, а exit() удаляет буфер stdio до выхода.
-
_exit() не может выполнить процесс очистки, в то время как exit() может быть зарегистрирован с помощью некоторой функции (например, on_exit или at_exit) для выполнения некоторого процесса очистки, если
перед существованием программы требуется что-либо.
exit (status) просто передает статус выхода в _exit (статус). Рекомендуется, чтобы всякий раз, когда вы выполняли fork(), один из них между дочерним и родительским, один использовал _exit(), а другой использовал exit().