Ответ
Fork (вилочное разветвление) — это системный вызов в ядре Linux (fork()), который создаёт новый процесс (дочерний) путём полного дублирования текущего процесса (родительского). Это основной способ создания процессов в Unix-подобных системах.
Что происходит при вызове fork():
- Ядро создаёт почти точную копию родительского процесса: копирует сегменты памяти (стек, куча, данные, текст), таблицу файловых дескрипторов, контекст выполнения.
- Новому процессу присваивается уникальный PID (Process ID).
- В родительский процесс возвращается PID дочернего процесса.
- В дочерний процесс возвращается
0. Это ключевой момент для различения процессов в коде. - Оба процесса продолжают выполнение со следующей инструкции после
fork().
Типичный паттерн: Fork + Exec
Дочерний процесс редко выполняет тот же код, что и родитель. Обычно он сразу вызывает семейство системных вызовов exec() (например, execve()), которое заменяет образ памяти дочернего процесса на новый исполняемый файл (например, /bin/ls).
Пример на C:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork(); // Критическая точка разветвления
if (pid == -1) {
perror("fork failed");
return 1;
}
if (pid == 0) {
// Этот код выполняется ТОЛЬКО в дочернем процессе
printf("Child: My PID is %d, Parent PID is %dn", getpid(), getppid());
// Заменяем себя программой 'ls'
execlp("ls", "ls", "-la", NULL);
// Если execlp вернул управление — значит, ошибка
perror("execlp failed");
return 1;
} else {
// Этот код выполняется ТОЛЬКО в родительском процессе
printf("Parent: My PID is %d, Child PID is %dn", getpid(), pid);
int status;
wait(&status); // Ожидаем завершения дочернего процесса
printf("Child exited with status %dn", WEXITSTATUS(status));
}
return 0;
}
Значение для DevOps/системного администрирования:
- Так работают демоны и серверные процессы (например,
nginxmaster процесс форкает worker процессы). - Понимание форка необходимо для отладки, анализа потребления памяти (Copy-On-Write механизм) и написания скриптов, управляющих процессами.
- Неконтролируемое создание процессов через форк может привести к "fork bomb" — атаке типа "отказ в обслуживании".
Ответ 18+ 🔞
А, ну это же классика, ёпта! Сидишь такой, пишешь код, и тут — бац! — тебе надо новый процесс запустить. В Linux, блядь, для этого есть системный вызов fork(). Представь: твой текущий процесс, как Змей Горыныч, берет и отпочковывает своего клона, прям один в один, ебать мои старые костыли.
Что творится под капотом, когда ты вызываешь fork():
- Ядро, этот хитрая жопа, создаёт почти полную копию родительского процесса. Всё копирует: память, какие файлы открыты, контекст — овердохуища информации.
- Новому отродью выдают его собственный, уникальный PID (идентификатор процесса).
- И вот тут самый сок, чувак. После разветвления в родительский процесс возвращается PID дочернего, а в дочерний процесс возвращается
0. Это как метка, чтобы понять, кто ты теперь: папаша или дитё. - И оба этих процесса, блядь, начинают выполнять код со следующей строчки после
fork(). Удивление пиздец, да? Два одинаковых потока выполнения из одной точки.
Но обычно дочернему процессу неинтересно делать то же самое. Поэтому стандартный финт ушами — это Fork + Exec. Дитё сразу вызывает exec() (например, execve()), который выгружает его текущую программу из памяти и загружает на её место совершенно новую, например, /bin/bash или /usr/bin/python3. Представь, как будто человек переоделся в костюм другого человека, да так, что стал им. Вот это exec.
Смотри, как это выглядит в коде на C:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork(); // Вот она, точка невозврата! Разделение реальности!
if (pid == -1) {
perror("fork failed"); // Если форк сломался — ну, бывает, ядрёна вошь.
return 1;
}
if (pid == 0) {
// Ага! Мы тут, если вернулся 0. Значит, мы — дитё.
printf("Child: My PID is %d, Parent PID is %dn", getpid(), getppid());
// А теперь, хуй с горы, заменяем себя на 'ls -la'
execlp("ls", "ls", "-la", NULL);
// Сюда мы попадём ТОЛЬКО если execlp обосрался.
perror("execlp failed");
return 1;
} else {
// А сюда попадёт только родитель, потому что pid у него — номер ребёнка.
printf("Parent: My PID is %d, Child PID is %dn", getpid(), pid);
int status;
wait(&status); // Сидим, ждём, пока дитё своё дело сделает и откинется.
printf("Child exited with status %dn", WEXITSTATUS(status));
}
return 0;
}
А тебе, как будущему деплоячему или админу, это зачем?
- Да всё так работает, блядь! Демоны, веб-серверы вроде nginx — master-процесс плодит worker-процессы именно через
fork(). Без этого никуда. - Чтобы не быть полным распиздяем, нужно понимать, как процессы плодятся и память жрут (там, кстати, умная оптимизация Copy-On-Write, чтобы не копировать всё сразу).
- И главное — терпения ноль ебать к коду, который может устроить
fork bomb. Это когда процесс плодится бесконтрольно, пока система не накрывается медным тазом. Одна строчка в shell::(){ :|:& };:— и привет, ребутать сервак.