Какие системные вызовы Linux используются для создания процесса?

«Какие системные вызовы Linux используются для создания процесса?» — вопрос из категории Linux, который задают на 24% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Linux для создания нового процесса традиционно используется комбинация двух системных вызовов: fork() и exec().

  1. fork(): Создает почти точную копию (дочерний процесс) вызывающего процесса (родительского). Дочерний процесс получает копию памяти, файловых дескрипторов и других атрибутов. Для оптимизации используется механизм Copy-On-Write (COW): память физически копируется только при попытке записи.
  2. exec() семейство (например, execve): Заменяет текущий образ процесса (код, данные, стек) новым исполняемым файлом, загружаемым с диска. Этот вызов обычно выполняется в дочернем процессе после fork(), чтобы запустить другую программу.

Типичная последовательность в оболочке (shell) или демоне:

#include <unistd.h>
#include <sys/wait.h>

pid_t pid = fork(); // Создаем копию процесса
if (pid == 0) {
    // Код выполняется в дочернем процессе
    char *args[] = {"/bin/ls", "-l", NULL};
    execve(args[0], args, NULL); // Заменяем образ на /bin/ls
    // Если execve вернул управление - это ошибка
    _exit(EXIT_FAILURE);
} else if (pid > 0) {
    // Код выполняется в родительском процессе
    int status;
    waitpid(pid, &status, 0); // Ожидаем завершения дочернего процесса
}
  1. clone(): Более гибкий низкоуровневый вызов, на котором основаны fork() и механизмы создания потоков (threads). Позволяет точно контролировать, какие ресурсы (память, таблица файлов, пространство имен) будут разделяться между родителем и потомком. Именно clone() с определенными флагами используется для создания контейнеров (namespaces, cgroups).

С точки зрения DevOps, понимание этих вызовов важно для отладки проблем с порождением процессов в контейнерах, написания своих демонов или скриптов мониторинга.