fork - создаёт дочерний процесс
fork(2)
создаёт дочерний процесс
ОБЗОР
#include <unistd.h>
pid_t fork(void);
ОПИСАНИЕ
Вызов fork() создаёт новый процесс посредством копирования вызывающего
процесса. Новый процесс считается дочерним процессом. Вызывающий процесс
считается родительским процессом.
Дочерний и родительский процессы находятся в отдельных пространствах
памяти. Сразу после fork() эти пространства имеют одинаковое
содержимое. Запись в память, отображение файлов (mmap(2)) и снятие
отображения (munmap(2)), выполненных в одном процессе, ничего не изменяет
в другом.
Дочерний процесс является точной копией родительского процесса за
исключением следующих моментов:
Потомок имеет свой уникальный идентификатор процесса, и этот PID
(идентификатор процесса) не совпадает ни с одним существующим
идентификатором группы процессов (setpgid(2)).
Идентификатор родительского процесса у потомка равен идентификатору
родительского процесса.
Потомок не наследует блокировки памяти родителя (mlock(2),
mlockall(2)).
Счётчики использования ресурсов (getrusage(2)) и времени ЦП у потомка
сброшены в 0.
Набор ожидающих сигналов потомка изначально пуст (sigpending(2)).
Потомок не наследует значения семафоров родителя (semop(2)).
Потомок не наследует связанные с процессом блокировки родителя (fcntl(2))
(с другой стороны, он наследует блокировки файловых описаний fcntl(2) и
блокировки flock(2)).
Потомок не наследует таймеры родителя (setitimer(2), alarm(2),
timer_create(2)).
Потомок не наследует ожидающие выполнения операции асинхронного ввода-вывода
(aio_read(3), aio_write(3)) и контексты асинхронного ввода-вывода
родителя (см. io_setup(2)).
Все перечисленные атрибуты указаны в POSIX.1. Родитель и потомок также
отличаются по следующим атрибутам процесса, которые есть только в Linux:
Потомок не наследует уведомления об изменении каталога (dnotify) родителя
(смотрите описание F_NOTIFY в fcntl(2)).
Настройка PR_SET_PDEATHSIG у prctl(2) сбрасывается, и поэтому потомок
не принимает сигнал о завершении работы родителя.
Резервное значение по умолчанию устанавливается равным родительскому
текущему резервному значению таймера. Смотрите описание PR_SET_TIMERSLACK
в prctl(2).
Отображение памяти, помеченное с помощью флага MADV_DONTFORK через
madvise(2), при fork() не наследуется.
Сигнал завершения работы потомка всегда SIGCHLD (см. clone(2)).
Биты прав доступа к порту, установленные с помощью ioperm(2), не
наследуются потомком; потомок должен установить все нужные ему биты с
помощью ioperm(2).
Также стоит учитывать следующее:
Процесс потомка создаётся с одиночной нитью — той, которая вызвала
fork(). Всё виртуальное адресное пространство родителя копируется в
потомок, включая состояние мьютексов, условных переменных и других объектов
pthreads; в случае проблем с этим может помочь pthread_atfork(3).
В многонитивой программе после fork(2) потомок может безопасно вызывать
только безопасные-асинхронные-сигнальные функции (смотрите signal(7)) до
тех пор, пока не вызовет execve(2).
Потомок наследует копии набора открытых файловых дескрипторов
родителя. Каждый файловый дескриптор в потомке ссылается на то же описание
файла что и родитель (смотрите open(2)). Это означает, что два файловых
дескриптора совместно используют флаги состояния открытого файла, текущее
смещение файла и атрибуты ввода-вывода, управляемые сигналами (смотрите
описание F_SETOWN и F_SETSIG в fcntl(2)).
Потомок наследует копии набора файловых дескрипторов открытых очередей
сообщений родителя (смотрите mq_overview(7)). Каждый файловый дескриптор
в потомке ссылается на то же описание открытой очереди сообщений что и
родитель. Это означает, что два файловых дескриптора совместно используют
флаги (mq_flags).
Потомок наследует копии набора потоков открытых каталогов родителя (смотрите
opendir(3)). В POSIX.1 сказано, что соответствующие потоки каталогов в
родителе и потомке могут совместно использовать позицию в потоке
каталога; в Linux/glibc они не могут этого делать.
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном завершении родителю возвращается PID процесса-потомка, а
процессу-потомку возвращается 0. При ошибке родительскому процессу
возвращается -1, процесс-потомок не создаётся, а значение errno
устанавливается в соответствующее значение.
ОШИБКИ
EAGAIN
Возникло системного ограничение на количество нитей. Есть несколько
ограничений, которые могут вызвать эту ошибку: был достигнут мягкий
ограничитель RLIMIT_NPROC (задаётся с помощью setrlimit(2)), который
ограничивает количество процессов и ните для реального ID пользователя; был
достигнут ядерный системный ограничитель на количество процессов и нитей,
/proc/sys/kernel/threads-max (смотрите proc(5)); был достигнуто
максимальное количество PID, /proc/sys/kernel/pid_max (смотрите
proc(5)).
EAGAIN
Вызывающий работает по алгоритму планирования SCHED_DEADLINE и у него не
установлен флаг сброса-при-fork (reset-on-fork). Смотрите sched(7).
ENOMEM
Вызов fork() завершился с ошибкой из-за невозможности разместить
необходимые структуры ядра, потому что слишком мало памяти.
ENOSYS
Вызов fork() не поддерживается на этой платформе (например, из-за того,
что аппаратное обеспечение не содержит блока управления памятью (MMU)).
СООТВЕТСТВИЕ СТАНДАРТАМ
POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.
ЗАМЕЧАНИЯ
В Linux, fork() реализован с помощью «копирования страниц при записи»
(copy-on-write, COW), поэтому расходы на вызов состоят из времени и памяти,
требуемой на копирование страничных таблиц родителя и создания уникальной
структуры, описывающей задачу.
Отличия между библиотекой C и ядром
Начиная с версии 2.3.3, вместо того, чтобы вызывать системный вызов
fork(), обёрточная функция fork() в glibc, как часть реализации нитей
NPTL, вызывает clone(2) с флагами, которые обеспечивают поведение
традиционного системного вызова (вызов fork() эквивалентен вызову
clone(2), если значение равно flags SIGCHLD). Обёртка в glibc
вызывает все обработчики при ветвлении (fork), которые были зарегистрированы
с помощью pthread_atfork(3).
ПРИМЕР
Смотрите pipe(2) и wait(2).