Когда вы вызываете vfork()
, создается новый процесс, и этот новый процесс заимствует образ процесса родительского процесса, за исключением стека. Дочернему процессу присваивается собственная новая звезда стека, однако он не допускается return
из вызывающей функции vfork()
.
Пока дочерний процесс выполняется, родительский процесс блокируется, поскольку дочерний процесс занял адресное пространство родительского процесса.
Независимо от того, что вы делаете, все, что просто обращается к стеку, изменяет только частный стек дочернего элемента. Однако если вы изменяете глобальные данные, это изменяет общие данные и, таким образом, также влияет на родительские данные.
Вещи, которые изменяют глобальные данные, например:
вызов malloc () или free ()
используя stdio
изменение настроек сигнала
изменение переменных, которые не являются локальными для вызывающей функции vfork()
.
...
Когда вы звоните _exit()
(важно, никогда не звоните exit()
), дочерний процесс прекращается, и контроль возвращается родителю.
Если вы вызываете какую-либо функцию из exec*()
семейства, создается новое адресное пространство с новым программным кодом, новыми данными и частью стека от родительского (см. Ниже). Как только это будет готово, дочерний элемент больше не заимствует адресное пространство у дочернего элемента, а использует собственное адресное пространство.
Элемент управления возвращается родителю, так как его адресное пространство больше не используется другим процессом.
Важно: в Linux нет реальной vfork()
реализации. Linux, скорее, реализует vfork()
на основе fork()
концепции «Копировать при записи», введенной SunOS-4.0 в 1988 году. Чтобы заставить пользователей поверить, что они используют vfork()
, Linux просто устанавливает общие данные и приостанавливает родительский процесс, пока ребенок не вызвал _exit()
или одну из exec*()
функций.
Поэтому Linux не извлекает выгоду из того факта, что реальному vfork()
не нужно устанавливать описание адресного пространства для дочернего элемента в ядре. Это приводит к тому, vfork()
что это не быстрее, чем fork()
. В системах, которые реализуют реальный vfork()
, он обычно в 3 раза быстрее fork()
и влияет на производительность используемых оболочек vfork()
- ksh93
, последних Bourne Shell
и csh
.
Причина, по которой вы никогда не должны звонить exit()
от vfork()
дочернего exit()
элемента ed, заключается в том, что сбрасывает stdio в случае, если за время до вызова были получены незаполненные данные vfork()
. Это может привести к странным результатам.
Кстати: posix_spawn()
реализован поверх vfork()
, поэтому vfork()
не будет удален из ОС. Было упомянуто, что Linux не использует vfork()
для posix_spawn()
.
Что касается стека, документации мало, вот что написано на странице руководства Solaris:
The vfork() and vforkx() functions can normally be used the
same way as fork() and forkx(), respectively. The calling
procedure, however, should not return while running in the
child's context, since the eventual return from vfork() or
vforkx() in the parent would be to a stack frame that no
longer exists.
Так что реализация может делать все что угодно. Реализация Solaris использует общую память для стекового фрейма вызова функции vfork()
. Ни одна реализация не предоставляет доступ к более старым частям стека от родительского.