(Из черновика XPG4 / SUSv2 / POSIX)
Функция
vfork()
аналогична
fork()
за тем исключением, что поведение процесса, созданного
vfork(),
неопределено, если он модифицирует любые данные, кроме переменной
типа pid_t, используемой в качестве значения, возвращаемого
vfork(),
или возвращается из функции, в которой была вызвана функция
vfork(),
или вызывает любую функцию до удачного исполнения
_exit()
или одной из функций семейства
exec.
НАЙДЕННЫЕ ОШИБКИ
EAGAIN
В системе слишком много процессов - попробуйте еще раз.
ENOMEM
Не хватает памяти для создания нового процесса.
ОПИСАНИЕ LINUX
vfork,
так же, как и
fork(2),
создает дочерний процесс для вызывающего.
Подробности, возвращаемые значения и ошибки смотрите в
fork(2).
vfork()
это специальный вариант
clone(2).
Он используется для создания новых процессов без копирования таблиц страниц
родительского процесса. Это может использоваться в приложениях, критичных
к производительности, для создания дочерних процессов, сразу же запускающих
execve().
vfork()
отличается от fork тем, что родительский процесс блокируется до тех пор,
пока дочерний процесс не вызовет
execve(2)
или
_exit(2).
Дочерний процесс разделяет всю память с родительским, включая стек,
до тех пор, пока не вызовет
execve().
Дочерний процесс не должен выходить из текущей функции или
вызывать
exit(),
но может вызвать
_exit().
Обработчики прерываний наследуются, но не разделяются. Сигналы передаются
родительскому процессу после того, как дочерний процесс разблокирует
его работу.
ПРИМЕЧАНИЯ ПО ИСТОРИИ
В Linux функция
fork()
реализована при помощи страниц "копирования при записи" (copy-on-write), поэтому единственная
задержка, возникающая при вызове
fork(),
- это время, необходимое для создания копии таблиц страниц родительского
процесса и структуры задания дочернего процесса.
Однако, в прошлом
fork()
мог требовать создания полной копии пространства данных
вызывающего процесса, что обычно не требовалось, так как сразу за
fork()
следовало
exec().
Поэтому для большей эффективности BSD предложило системный вызов
vfork,
который не копировал адресное пространство процесса,
а использовал то же самое пространство, блокируя родительский
процесс до вызова
execve()
или до прекращения своей работы.
Использование vfork было похоже на трюк: например, сохранность
данных родительсокго процесса зависела от того, какие
переменные содержатся в регистрах.
НАЙДЕННЫЕ ОШИБКИ
Скажем прямо, не очень хорошо, что в Linux существует столь тяжелый
пережиток прошлого.
В man-странице BSD написано следующее:
"Этот системный вызов будет устранен после того, как будут реализованы
соответствующие механизмы разделения ресурсов системы. Пользователи не
должны опираться на существующую семантику разделения памяти
vfork,
то есть программа должна быть аналогична программе с
fork."
Говоря проще, стандартное описание не позволяет использовать
vfork(),
потому что, если последующий за ним
exec
может не исполниться, что произойдет дальше - не определено.
Обработка сигналов еще более сложна и различается от системы к системе.
В man-странице BSD написано следующее:
"Для исключения возможности остановки системы процессы, являющиеся
дочерними после исполнения
vfork,
никогда не получают сигналов SIGTTOU или SIGTTIN; вместо этого вывод или
ioctl
всегда разрешены, а попытки ввода приводят к ситуации "EOF".
На текущий момент (Linux 2.3.25)
strace(1)
не может следовать за
vfork()
и требует изменений в коде ядра.
ПРИМЕЧАНИЯ ПО ИСТОРИИ
Системный вызов
vfork()
впервые появился в 3.0BSD.
В BSD 4.4 он стал синонимом
fork(),
но NetBSD ввела его снова:
http://www.netbsd.org/Documentation/kernel/vfork.html .
В Linux этот системный вызов был эквивалентом
fork()
примерно до ядра 2.2.0-pre6. Начиная с 2.2.0-pre9 (на i386 и немного
позже на других архитектурах), он стал независимым системным
вызовом. Его поддержка быда добавлена в glibc 2.0.112.
СООТВЕТСТВИЕ СТАНДАРТАМ
Системный вызов
vfork
может быть похожим на системные вызовы с тем же именем в
других ОС. Требования, предъявляемые стандартами к
vfork
не такие жесткие, как те, которые предъявляются к
fork,
поэтому будет достаточно тех реализаций, в которых эти два вызова являются
синонимами.
В частности, программист не может полагаться на действия
родительского процесса до вызова
execve()
или
_exit()
и не может полагаться на специфичесоке поведение т.н.
"возникшей разделяемой памяти".