ИМЯ
fork - создаёт
дочерний
процесс
ОБЗОР
#include <sys/types.h>
#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() не
наследуется.
- Память в
диапазонах
адресов,
которые
были
помечены с
помощью madvise(2)
флагом MADV_WIPEONFORK,
обнуляются
в потомке
после fork()
(флаг MADV_WIPEONFORK
остаётся в
потомке у
этих
диапазонов
адресов).
- Сигнал
завершения
работы
потомка
всегда SIGCHLD
(см. clone(2)).
- Биты прав
доступа к
порту,
установленные
с помощью
ioperm(2), не
наследуются
потомком;
потомок
должен
установить
все нужные
ему биты с
помощью
ioperm(2).
Также
стоит
учитывать
следующее:
- Процесс
потомка
создаётся
с
одиночной
нитью —
той,
которая
вызвала fork().
Всё
виртуальное
адресное
пространство
родителя
копируется
в потомок,
включая
состояние
мьютексов,
условных
переменных
и других
объектов pthreads;
в случае
проблем с
этим может
помочь
pthread_atfork(3).
- В
многонитевой
программе
после fork()
потомок
может
безопасно
вызывать
только
безопасные-асинхронные-сигнальные
функции
(смотрите
signal-safety(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)); или
- достигнуто
ограничение
на PID (pids.max),
наложенное
контроллером
cgroup на «номер
процесса»
(PID).
- EAGAIN
- Вызывающий
работает
по
алгоритму
планирования
SCHED_DEADLINE и у него
не
установлен
флаг
сброса-при-fork
(reset-on-fork).
Смотрите
sched(7).
- ENOMEM
- Вызов fork()
завершился
с ошибкой
из-за
невозможности
разместить
необходимые
структуры
ядра,
потому что
слишком
мало
памяти.
- ENOMEM
- Была
попытка
создания
дочерний
процесс в
пространстве
имён PID, чей
процесс
«init»
завершил
работу.
Смотрите
pid_namespaces(7).
- ENOSYS
- Вызов fork() не
поддерживается
на этой
платформе
(например,
из-за того,
что
аппаратное
обеспечение
не
содержит
блока
управления
памятью (MMU)).
- ERESTARTNOINTR
(начиная с Linux
2.6.17)
- Системный
вызов был
прерван
сигналом и
перезапущен
(может быть
замечено
только при
трассировке).
СООТВЕТСТВИЕ
СТАНДАРТАМ
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).
СМОТРИТЕ
ТАКЖЕ
clone(2), execve(2), exit(2),
setrlimit(2), unshare(2), vfork(2), wait(2),
daemon(3), pthread_atfork(3), capabilities(7),
credentials(7)