Scroll to navigation

fork(2) System Calls Manual fork(2)

ИМЯ

fork - создаёт дочерний процесс

LIBRARY

Standard C library (libc, -lc)

СИНТАКСИС

#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 они не могут этого делать.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set to indicate the error.

ОШИБКИ

Возникло системного ограничение на количество нитей. Есть несколько ограничений, которые могут вызвать эту ошибку:
  • 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).
Вызывающий работает по алгоритму планирования SCHED_DEADLINE и у него не установлен флаг сброса-при-fork (reset-on-fork). Смотрите sched(7).
Вызов fork() завершился с ошибкой из-за невозможности разместить необходимые структуры ядра, потому что слишком мало памяти.
Была попытка создания дочерний процесс в пространстве имён PID, чей процесс «init» завершил работу. Смотрите pid_namespaces(7).
Вызов fork() не поддерживается на этой платформе (например, из-за того, что аппаратное обеспечение не содержит блока управления памятью (MMU)).
Системный вызов был прерван сигналом и перезапущен (может быть замечено только при трассировке).

СТАНДАРТЫ

POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.

ЗАМЕЧАНИЯ

В Linux, fork() реализован с помощью «копирования страниц при записи» (copy-on-write, COW), поэтому расходы на вызов состоят из времени и памяти, требуемой на копирование страничных таблиц родителя и создания уникальной структуры, описывающей задачу.

Отличия между библиотекой C и ядром

Since glibc 2.3.3, rather than invoking the kernel's fork() system call, the glibc fork() wrapper that is provided as part of the NPTL threading implementation invokes clone(2) with flags that provide the same effect as the traditional system call. (A call to fork() is equivalent to a call to clone(2) specifying flags as just SIGCHLD.) The glibc wrapper invokes any fork handlers that have been established using pthread_atfork(3).

ПРИМЕРЫ

See pipe(2) and wait(2) for more examples.

#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(void)
{

pid_t pid;
if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
perror("signal");
exit(EXIT_FAILURE);
}
pid = fork();
switch (pid) {
case -1:
perror("fork");
exit(EXIT_FAILURE);
case 0:
puts("Child exiting.");
exit(EXIT_SUCCESS);
default:
printf("Child is PID %jd\n", (intmax_t) pid);
puts("Parent exiting.");
exit(EXIT_SUCCESS);
} }

СМ. ТАКЖЕ

clone(2), execve(2), exit(2), setrlimit(2), unshare(2), vfork(2), wait(2), daemon(3), pthread_atfork(3), capabilities(7), credentials(7)

ПЕРЕВОД

Русский перевод этой страницы руководства был сделан Azamat Hackimov <azamat.hackimov@gmail.com>, Dmitry Bolkhovskikh <d20052005@yandex.ru>, Yuri Kozlov <yuray@komyakino.ru> и Иван Павлов <pavia00@gmail.com>

Этот перевод является бесплатной документацией; прочитайте Стандартную общественную лицензию GNU версии 3 или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на man-pages-ru-talks@lists.sourceforge.net.

4 декабря 2022 г. Linux man-pages 6.02