Системный вызов fork
Системный вызов fork — это системный вызов в Unix-подобных операционных системах, предназначенный для создания нового процесса, который является точной копией вызывающего (родительского) процесса. Новый процесс, называемый дочерним, получает собственный идентификатор (PID) и запускается с того же места в коде, что и родитель, но с возможностью выполнения отдельного пути выполнения. Системный вызов fork является одним из фундаментальных механизмов многозадачности в Unix-подобных системах, включая Linux, BSD и macOS, и лежит в основе создания большинства процессов, включая те, что запускаются через командную оболочку.
История
Системный вызов fork был впервые реализован в операционной системе Unix, разработанной в Bell Labs в начале 1970-х годов. Его создание приписывается Деннису Ритчи, Кену Томпсону и другим разработчикам, которые стремились создать простой и эффективный механизм для порождения процессов. В ранних версиях Unix, таких как Version 6 (1975), fork уже использовался для создания новых процессов, а затем, в сочетании с системным вызовом exec, позволял запускать новые программы. С тех пор fork стал стандартом для POSIX-совместимых систем и был включён в спецификацию POSIX.1 (IEEE Std 1003.1-1988). В современных системах, таких как Linux, fork реализован через более низкоуровневый системный вызов clone, который предоставляет более тонкую настройку разделения ресурсов.
Принцип работы
При вызове fork операционная система создаёт новый процесс, который является копией родительского. Этот процесс получает:
- Собственное адресное пространство (копия памяти родителя, включая код, данные, стек и кучу).
- Копии всех открытых файловых дескрипторов, сигналов и других ресурсов.
- Уникальный идентификатор процесса (PID) и идентификатор родительского процесса (PPID).
После вызова fork оба процесса (родительский и дочерний) продолжают выполнение с одной и той же точки — сразу после вызова. Различие между ними определяется возвращаемым значением:
- В родительском процессе
forkвозвращает PID дочернего процесса (положительное целое число). - В дочернем процессе
forkвозвращает 0. - В случае ошибки
forkвозвращает -1 и устанавливает код ошибки (например,EAGAINпри превышении лимита процессов).
Копирование при записи
В современных реализациях, таких как в Linux, fork использует механизм копирования при записи (copy-on-write, COW). Это означает, что адресное пространство родительского процесса не копируется немедленно. Вместо этого страницы памяти разделяются между родительским и дочерним процессами, но помечаются как доступные только для чтения. Если один из процессов пытается изменить данные на такой странице, ядро создаёт её копию для этого процесса. Это позволяет избежать избыточного копирования памяти, если дочерний процесс сразу вызывает exec для замены своего образа.
Использование
Системный вызов fork обычно используется в сочетании с exec для запуска новых программ. Типичный сценарий:
- Родительский процесс вызывает
fork, создавая дочерний процесс. - В дочернем процессе вызывается
exec(например,execve), который заменяет текущий образ процесса новым (например, программойlsилиgrep). - Родительский процесс может дождаться завершения дочернего процесса с помощью
waitилиwaitpid, чтобы получить его статус выхода.
Пример на языке C: ```c
include <unistd.h>
include <sys/wait.h>
include <stdio.h>
int main() { pid_t pid = fork();
if (pid == 0) { // Дочерний процесс execlp("/bin/ls", "ls", "-l", NULL); perror("exec failed"); return 1; } else if (pid > 0) { // Родительский процесс int status; waitpid(pid, &status, 0); printf("Child process finished with status %d\n", status); } else { perror("fork failed"); return 1; } return 0; } ```
Особенности и ограничения
Безопасность и производительность
- Копирование при записи значительно снижает накладные расходы, особенно при создании процессов, которые не требуют полного копирования памяти.
forkможет быть медленнее в системах с большим адресным пространством (например, 64-битных), но COW смягчает этот эффект.
Ограничения
- Лимиты процессов: В системе может быть ограничение на количество процессов на пользователя (
RLIMIT_NPROC) или на общее количество процессов в системе. - Ошибки:
forkможет завершиться ошибкой, если превышен лимит памяти или процессов, или если ядро не может выделить ресурсы.
Влияние на многопоточность
В многопоточных программах (например, с использованием pthreads) вызов fork создаёт дочерний процесс, который наследует только вызывающий поток. Другие потоки не копируются, что может привести к проблемам с синхронизацией, если они удерживали блокировки. POSIX рекомендует использовать fork только в однопоточных программах или сразу после вызова exec.
Альтернативы
В некоторых системах, особенно в Windows, используется другой механизм создания процессов — CreateProcess, который объединяет создание процесса и загрузку программы в один шаг. В Unix-подобных системах также существует системный вызов vfork, который создаёт дочерний процесс без копирования адресного пространства, предполагая, что дочерний процесс немедленно вызовет exec. Однако vfork считается устаревшим и не рекомендуется к использованию в новых программах из-за потенциальных проблем с безопасностью.
В Linux для более тонкого контроля над созданием процессов используется clone, который позволяет указать, какие ресурсы (память, файловые дескрипторы, пространство имён) разделять между родительским и дочерним процессами. clone лежит в основе реализации потоков (pthreads) и контейнеров.
Применение в реальных системах
- Командные оболочки: Например,
bashилиzshиспользуютforkдля выполнения команд, введённых пользователем. Послеforkдочерний процесс вызываетexecдля запуска утилиты (например,ls,cat). - Серверные приложения: Веб-серверы, такие как Apache (в режиме prefork), создают дочерние процессы для обработки входящих запросов, используя
fork. - Демоны: Многие демоны (фоновые службы) используют
forkдля отделения от терминала и перехода в фоновый режим.
Интересные факты
- В некоторых реализациях Unix, таких как Plan 9 от Bell Labs,
forkбыл заменён наrfork, который предоставляет более гибкие возможности для разделения ресурсов. - В Linux существует системный вызов
forkкак обёртка надclone, а такжеclone3для более современного интерфейса. - В ранних версиях Unix (до Version 7)
forkне поддерживал копирование при записи, что приводило к полному копированию памяти, что было крайне неэффективно для больших программ.
Источники
- Спецификация POSIX.1-2017:
fork— IEEE Std 1003.1-2017. - Книга «Advanced Programming in the UNIX Environment» (W. Richard Stevens, Stephen A. Rago).
- Документация Linux: man-страницы
fork(2),clone(2),exec(3). - Статья «The Evolution of the Unix Time-sharing System» (Dennis M. Ritchie).
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →