Fork vs Clone на 2.6 ядре Linux

37

У меня есть некоторая путаница относительно вилки и клона. Я видел это:

  • форк для процессов и клон для потоков

  • форк просто вызывает клон, клон используется для всех процессов и потоков

Являются ли эти из них точными? В чем разница между этими 2 системными вызовами с ядром Linux 2.6?

Грегг Левенталь
источник

Ответы:

52

fork()был оригинальный системный вызов UNIX. Он может использоваться только для создания новых процессов, а не потоков. Кроме того, это портативный.

В Linux clone()это новый, универсальный системный вызов, который можно использовать для создания нового потока выполнения. В зависимости от переданных параметров новый поток выполнения может придерживаться семантики процесса UNIX, потока POSIX, чего-то промежуточного или чего-то совершенно другого (например, другого контейнера). Вы можете указать все виды опций, определяющих, будут ли совместно использоваться или копироваться память, файловые дескрипторы, различные пространства имен, обработчики сигналов и т. Д.

Поскольку clone()это системный вызов superset, реализация fork()оболочки системных вызовов в glibc фактически вызывает clone(), но это деталь реализации, о которой программистам не нужно знать. Реальный реальный fork()системный вызов все еще существует в ядре Linux по причинам обратной совместимости, даже если он стал избыточным, потому что программы, которые используют очень старые версии libc, или другой libc, кроме glibc, могут использовать его.

clone()также используется для реализации pthread_create()функции POSIX для создания потоков.

Портативные программы должны звонить, fork()а pthread_create()не clone().

Celada
источник
2
posix_spawn - еще одна важная функция, которая в некоторых отношениях более и менее переносима, чем fork.
Random832
10

Похоже, clone()в Linux 2.6 есть две вещи

Есть системный вызов:

int clone(int (*fn)(void *), void *child_stack,
          int flags, void *arg, ...
          /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

Это «clone ()», описанный при выполнении man 2 clone.

Если вы прочитаете эту справочную страницу достаточно близко, вы увидите это:

It is actually a library function layered on top of the
underlying clone() system call.

По-видимому, вы должны реализовать многопоточность, используя «библиотечную функцию», наложенную на системно вызывающий вызов с однозначным названием.

Я написал короткую программу:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int
main(int ac, char **av)
{
    pid_t cpid;
    switch (cpid = fork()) {
    case 0:   // Child process
        break;
    case -1:  // Error
        break;
    default:  // parent process
        break;
    }
    return 0;
}

Скомпилировал его с помощью:, c99 -Wall -Wextraи запустил, strace -fчтобы увидеть, что на самом деле делают системные вызовы разветвления. Я получил это straceна машине Linux 2.6.18 (процессор x86_64):

20097 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x2b4ee9213770) = 20098
20097 exit_group(0)                     = ?
20098 exit_group(0)

В straceвыводе не появляется вызов "fork" . clone()Вызов , который проявляется в straceвыходе имеет очень разные аргументы от человека-страницы-клона. child_stack=0как первый аргумент отличается от int (*fn)(void *).

Похоже, что fork(2)системный вызов реализован в терминах реального clone() , точно так же, как clone()реализована «библиотечная функция» . Реального clone() имеет другой набор аргументов от человека-страницы-клона.

Проще говоря, оба ваших кажущихся противоречивыми высказывания о fork()и clone()являются правильными. Вовлеченный «клон» отличается, однако.

Брюс Эдигер
источник
9
«На самом деле это библиотечная функция, расположенная поверх основного системного вызова clone ()». - в общем, это относится к каждому системному вызову. Программисты фактически всегда вызывают функции в libc, которые названы в честь системного вызова. Это связано с тем, что для выполнения реального реального системного вызова непосредственно из C требуется магия, зависящая от платформы (обычно путем принудительной установки некоторой ловушки ЦП, зависит от архитектуры ABI) и машинный код, который лучше всего оставить делегированным в libc.
Селада
1
@Celada - да, согласился. Просто это man 2 cloneформулирует это именно так, что, как мне показалось, запутало проблему и помешало спрашивающему получить хороший ответ.
Брюс Эдигер
2
Я считаю , что средства Manpage , чтобы указать , что список аргументов из cloneбиблиотеки функций существенно отличается от списка аргументов , принятого базовой системы вызова. В частности, системный вызов всегда возвращается дважды в одном стеке, как это forkделает традиционный ; все аргументы, относящиеся к дочернему стеку, обрабатываются строго в пространстве пользователя. Смотрите, например, sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/…
zwol
1
Я хотел дать ваш ответ лучший ответ, потому что он качается, но стадо меня поразило и пошло с первым ответом. Она получает очки за время отклика. Спасибо за ваше объяснение.
Грегг Левенталь
6

fork()это просто определенный набор флагов для системного вызова clone(). clone()достаточно общего, чтобы создать «процесс» или «поток», или даже странные вещи, которые находятся где-то между процессами и потоками (например, разные «процессы», которые совместно используют одну и ту же таблицу дескрипторов файлов).

По сути, для каждого «типа» информации, связанной с контекстом выполнения в ядре, clone()вы можете выбрать псевдоним этой информации или ее копирование. Потоки соответствуют псевдонимам, процессы соответствуют копированию. Указывая промежуточные комбинации флагов для clone(), вы можете создавать странные вещи, которые не являются потоками или процессами. Обычно вы не должны этого делать, и я полагаю, что во время разработки ядра Linux возникли споры о том, должен ли он предусматривать такой общий механизм, как clone().

Atsby
источник