Почему дочерний элемент vfork или fork должен вызывать _exit () вместо exit ()?

12

Со страницы руководства vfork():

vfork () отличается от fork () тем, что родительский элемент приостанавливается до тех пор, пока дочерний элемент не вызовет execve (2) или _exit (2). Дочерний узел совместно использует всю память со своим родителем, включая стек, до тех пор, пока дочерний объект не выпустит execve (). Дочерний объект не должен возвращаться из текущей функции или вызывать exit (), но может вызывать _exit ().

Почему ребенок должен использовать, _exit()а не просто звонить exit()? Я надеюсь, что это применимо к обоим vfork()и fork().

сен
источник
2
если вы заинтересованы в более раннем обсуждении онтопичности вызовов Unix C API
xenoterracide

Ответы:

11

Как было показано ранее , vforkдочерний процесс не разрешает доступ к памяти родителя. exitявляется функцией библиотеки C (вот почему она часто пишется как exit(3)) Он выполняет различные задачи очистки, такие как очистка и закрытие потоков C (файлы открываются с помощью функций, объявленных в stdio.h) и выполнение пользовательских функций, зарегистрированных в atexit. Все эти задачи включают чтение и запись в память процесса.

_exitвыходит без очистки. Это непосредственно системный вызов (именно поэтому он записан как _exit(2)), обычно реализуемый путем помещения номера системного вызова в регистр процессора и выполнения определенной инструкции процессора (переход к обработчику системного вызова). Для этого не требуется доступ к памяти процесса, поэтому это безопасно сделать после vfork.

После forkэтого такого ограничения нет: родительский и дочерний процессы теперь полностью автономны.

Жиль "ТАК - прекрати быть злым"
источник
vfork не разрешает дочернему процессу доступ к памяти родителя ? Но я думал, что они используют одно и то же адресное пространство, поэтому ребенок может получить доступ к адресному пространству родителей. Было ли это понимание неправильно?
Сен
После ветвления такого ограничения нет: родительский и дочерний процессы теперь полностью автономны. Означает ли это, что я могу вызвать exit () от потомка вилки?
Сен
1
@Sen: ребенок не имеет доступа к памяти родителя. Если вы попробуете это, это может сработать, потому что ядро ​​не защитит вас. Но эффект не может быть тем, что вы намереваетесь; например, если ваш компилятор решит оставить какое-то значение в регистре, родительский процесс не увидит его.
Жиль "ТАК - перестать быть злым"
@Sen: После разветвления вы можете сделать выход из вызова или любую другую функцию. Каждый процесс запускается после разветвления (в Linux даже начальный процесс initразветвляется ядром).
Жиль "ТАК - перестань быть злым"
3

exitвыполнять дополнительную очистку, например, вызывая функции, зарегистрированные, atexitследовательно, он получает доступ к данным вне скопированной части. _exitвыполняет syscall напрямую, без очистки, кроме как в ядре.

Мацей Печотка
источник
... и следует отметить, что fork () копирует все, поэтому вы можете вызывать exit () и можете определенно вернуться из текущей функции.
Дероберт
3

У вас есть дочерний вызов _exit (), чтобы избежать очистки буферов stdio (или других) при выходе из дочернего процесса. Поскольку дочерний процесс представляет собой точную копию родительского процесса, дочерний процесс все еще имеет то, что родительский процесс имел в «stdout» или «stderr», буферы из <stdio.h>. Вы можете (и будете в неподходящее время) получать двойные выходные данные, вызывая exit (), один из обработчиков atexit дочернего процесса и один из родительского, когда буферы в родительском процессе заполняются и очищаются.

Я понимаю, что приведенный выше ответ концентрируется на особенностях stdio.h, но эта идея, вероятно, переносится на другие буферизованные операции ввода-вывода, как указывает один из ответов выше.

Брюс Эдигер
источник
1

exit(): - выполняет некоторую задачу очистки, например, закрывает потоки ввода-вывода и многие другие, а затем возвращает ядро. _exit(): - напрямую попадает в ядро ​​(не выполнять никаких задач по очистке).

fork() : у parent и child есть разные таблицы файлов, поэтому изменения, внесенные потомками, не влияют на параметры среды parent и наоборот.

vfork(): parent и child используют одну и ту же таблицу файлов, поэтому изменение, внесенное child, влияет на параметры среды parent. например, некоторая переменная var=10, теперь запускаемая var++потомком, а затем запускающая родителя, вы также можете увидеть эффект var++в выводе parent.

Как я уже говорил , если вы используете exit()в vfork()том всем I / O уже закрыт. Таким образом, даже если родительский элемент работает правильно, вы не можете получить правильный вывод, потому что все переменные сброшены и все потоки закрыты.

user2670535
источник