Назва

vfork — створити процес-нащадок, блокуючи батьківський процес.

Синопсис

#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);

Стандартний опис

(З XPG4/SUSv2/чернетки POSIX) Функція vfork() працює так само, як і fork(), за винятком того, що її поведінка є невизначеною, якщо процес, створений викликом vfork(), або змінює будь-які дані, крім змінної типу pid_t, в котрій зберігається значення, повернене vfork(), або повертається з функції, в якій викликався vfork(), або ж викликає будь-яку іншу функцію перед тим, як успішно викликати ? exit (2) чи одну з функцій родини exec.

Помилки

EAGAIN : забагато процесів — спробуйте ще.

ENOMEM : недостатньо пам'яті підсисання (swap) для нового процесу.

Опис Linux

vfork, як і fork (2), створює процес — нащадок викликача. За подробицями й значеннями, що повертаються при цьому, дивіться сторінку fork (2).

vfork() є особливим випадком ?clone (2). Його використовують для створення нових процесів без копіювання таблиць сторінок батьківського процесу. Він може бути корисним для чутливих до швидкодії застосувань, де відбрунькований процес одразу ж викликає execve (2).

vfork() різниться від fork (2) тим, що батьківський процес затримується, аж доки нащадок не викличе execve (2) чи ? exit (2). Нащадок має спільну з батьком пам'ять, включаючи стек, аж доки не викличе execve (2). Нащадок не повинен повертатися з поточної функції чи викликати exit(), проте може викликати _exit().

Обробники сигналів, хоч і успадковуються, проте не є спільними. Сигнали прибувають до батьківського процесу після того, як його відпустить нащадок.

Історичний опис

Під Linux fork() реалізовано при допомозі сторінок пам'яті, що копіюються при запису до них — отже, ціною брунькування є лише час і пам'ять, витрачені на створення копії батьківських таблиць сторінок і унікальної структури задачі для нащадка. Однак, у погані старі часи fork() міг вимагати повного копіювання цілого простору даних викликача, часто без усякої потреби, бо зазвичай одразу ж після того настає виклик exec(). Отож, для покращення швидкодії BSD запропонували системний виклик vfork, котрий не копіював повністю простір адрес батька, а позичав його пам'ять і потік керування аж до виклику exec() чи виходу. Батьківський процес затримувався, доки нащадок користувався його ресурсами. Використання vfork було хитромудрим — наприклад, незмінність даних батьківського процесу залежала від знання того, які змінні містяться в регістрах.

Вади

Факт, що Linux оживив цього привида з минулого, є радше невтішним. Сторінка підручника BSD заявляє: «Цей системний виклик буде викоренено, коли реалізуються відповідні механізми системного розподілу. Користувачам не слід залежати від семантики vfork, оскільки він у цьому випадку стане синонімом для fork».

Висловлюючись формально, стандартний опис, поданий вище, не дозволяє використовувати vfork(), бо наступний за ним exec() може завершитись невдало, а що тоді повинно статись, не визначено.

Подробиці обробки сигналів туманні й різняться від системи до системи. Сторінка підручника BSD говорить: «Для уникнення можливої безвиході процесам, що відбрунькувались через vfork, ніколи не надсилаються сигнали SIGTTOU чи SIGTTIN; радше, дозволяється виведення чи ioctl, а спроби введення завжди вказуватимуть кінець файлу».

Наразі (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, тож реалізації, де ці два виклики є синонімами — відповідні. Зокрема, програміст не повинен вважати, що батьківський процес залишатиметься заблокованим аж до виклику exit() чи execve()_, а також не повинен покладатись не якусь специфічну поведінку щодо спільної пам'яті.

Дивіться також

?clone (2), execve (2), fork(2), ?wait (2).