Разница между fork(), vfork(), exec() и clone()

Я искал, чтобы найти разницу между этими четырьмя в Google, и я ожидал, что там будет огромный объем информации об этом, но действительно не было никакого твердого сравнения между четырьмя вызовами.

Я приступил к попытке скомпилировать некий базовый взгляд на различия между этими системными вызовами, и вот что я получил. Является ли вся эта информация правильной/я не вижу ничего важного?

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

Новый процесс (дочерний элемент) получает идентификатор процесса (PID) и имеет идентификатор PID старого процесса (родителя) в качестве родительского PID (PPID). Поскольку в двух процессах теперь выполняется точно такой же код, они могут определить, что из-за кода возврата fork - ребенок получает 0, родитель получает PID дочернего элемента. Это все, конечно, предполагается, что работает вызов fork - если нет, ни один дочерний элемент не создается, а родитель получает код ошибки.

Vfork: Основное различие между vfork и fork заключается в том, что при создании нового процесса с помощью vfork() родительский процесс временно приостанавливается, а дочерний процесс может занять родительское адресное пространство. Это странное положение дел продолжается до тех пор, пока дочерний процесс не выйдет или не вызовет execve(), и в этот момент родительский процесс продолжается.

Это означает, что дочерний процесс vfork() должен быть осторожным, чтобы избежать неожиданной модификации переменных родительского процесса. В частности, дочерний процесс не должен возвращаться из функции, содержащей вызов vfork(), и он не должен вызывать exit() (если ему нужно выйти, он должен использовать _exit(), на самом деле это также верно для дочернего нормального fork()).

Exec : Вызов exec - это способ полностью заменить весь текущий процесс новой программой. Он загружает программу в текущее пространство процесса и запускает ее из точки входа. exec() заменяет текущий процесс на исполняемый файл, указанный функцией. Элемент управления никогда не возвращается к исходной программе, если нет ошибки exec().

Clone : Клон, как fork, создает новый процесс. В отличие от fork эти вызовы позволяют дочернему процессу совместно использовать части контекста выполнения с вызывающим процессом, таким как пространство памяти, таблицу файловых дескрипторов и таблицу обработчиков сигналов.

Когда дочерний процесс создается с помощью клона, он выполняет приложение функции fn (arg). (Это отличается от fork, где выполнение продолжается в дочернем элементе с точки исходного вызова fork.) Аргумент fn является указателем на функцию, которая вызывается дочерним процессом в начале ее выполнения. Аргумент arg передается функции fn.

Когда приложение функции fn (arg) возвращается, дочерний процесс завершается. Целое число, возвращаемое fn, является кодом выхода для дочернего процесса. Детский процесс также может быть закончен явно, вызывая exit (2) или после получения фатального сигнала.

Полученная информация:

Спасибо, что нашли время, чтобы прочитать это!:)

Ответ 1

  • vfork() - устаревшая оптимизация. Перед хорошим управлением памятью fork() была сделана полная копия родительской памяти, поэтому было довольно дорого. так как во многих случаях за fork() следовал exec(), который отбрасывает текущую карту памяти и создает новую, это был ненужный расход. В настоящее время fork() не копирует память; он просто устанавливается как "копировать при записи", поэтому fork() + exec() так же эффективен, как vfork() + exec().

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

Ответ 2

  • execve() заменяет текущее исполняемое изображение другим, загруженным из исполняемого файла.
  • fork() создает дочерний процесс.
  • vfork() - это исторически оптимизированная версия fork(), предназначенная для использования, когда execve() вызывается непосредственно после fork(). Оказалось, что он хорошо работает в системах, отличных от MMU (где fork() не может работать эффективным образом), а при fork() процессах с огромным объемом памяти для запуска небольшой программы (подумайте Java Runtime.exec()). POSIX стандартизовал posix_spawn(), чтобы заменить эти последние два более современных использования vfork().
  • posix_spawn() делает эквивалент a fork()/execve(), а также позволяет использовать fd juggling между ними. Он должен заменить fork()/execve(), главным образом для платформ, отличных от MMU.
  • pthread_create() создает новый поток.
  • clone() - это конкретный Linux-вызов, который можно использовать для реализации чего-либо от fork() до pthread_create(). Это дает большой контроль. Вдохновленный rfork().
  • rfork() - это конкретный вызов Plan-9. Он должен быть общим вызовом, позволяющим несколько степеней совместного использования, между полными процессами и потоками.

Ответ 3

  • fork() - создает новый дочерний процесс, который является полной копией родительского процесса. В дочерних и родительских процессах используются разные виртуальные адресные пространства, которые изначально заполняются теми же страницами памяти. Затем, когда выполняются оба процесса, виртуальные адресные пространства начинают все больше различаться, поскольку операционная система выполняет ленивое копирование страниц памяти, которые записываются одним из этих двух процессов, и назначает независимые копии измененных страниц памяти для каждого процесса. Этот метод называется Copy-On-Write (COW).
  • vfork() - создает новый дочерний процесс, который является "быстрой" копией родительского процесса. В отличие от системного вызова fork() дочерние и родительские процессы используют одно и то же виртуальное адресное пространство. ЗАМЕТКА! Используя одно и то же виртуальное адресное пространство, родительский и дочерний используют один и тот же стек, указатель стека и указатель команд, как в случае классического fork()! Чтобы предотвратить нежелательные помехи между родителями и дочерними элементами, которые используют один и тот же стек, выполнение родительского процесса замораживается до тех пор, пока ребенок не вызовет либо exec() (создайте новое виртуальное адресное пространство и переход в другой стек), либо _exit() (завершение выполнения процесса). vfork() - это оптимизация fork() для модели fork-and-exec. Это может быть выполнено в 4-5 раз быстрее, чем fork(), потому что в отличие от fork() (даже с учетом того, что COW хранится в уме) реализация системного вызова vfork() не включает создание нового адресного пространства ( размещение и настройка новых каталогов страниц).
  • clone() - создает новый дочерний процесс. Различные параметры этого системного вызова, укажите, какие части родительского процесса должны быть скопированы в дочерний процесс и какие части будут разделены между ними. В результате этот системный вызов можно использовать для создания всех видов исполнительных объектов, начиная с потоков и заканчивая полностью независимыми процессами. На самом деле системный вызов clone() является базой, которая используется для реализации pthread_create() и всего семейства системных вызовов fork().
  • exec() - сбрасывает всю память процесса, загружает и анализирует указанный исполняемый двоичный файл, устанавливает новый стек и передает управление точке входа загруженного исполняемого файла. Этот системный вызов никогда не возвращает управление вызывающему абоненту и служит для загрузки новой программы в уже существующий процесс. Этот системный вызов с системным вызовом fork() образуют классическую модель управления процессами UNIX под названием "fork-and-exec".

Ответ 4

Для fork(), vfork() и clone() все вызовы do_fork() выполняют реальную работу, но с разными параметрами.

asmlinkage int sys_fork(struct pt_regs regs)
{
    return do_fork(SIGCHLD, regs.esp, &regs, 0);
}

asmlinkage int sys_clone(struct pt_regs regs)
{
    unsigned long clone_flags;
    unsigned long newsp;

    clone_flags = regs.ebx;
    newsp = regs.ecx;
    if (!newsp)
        newsp = regs.esp;
    return do_fork(clone_flags, newsp, &regs, 0);
}
asmlinkage int sys_vfork(struct pt_regs regs)
{
    return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0);
}
#define CLONE_VFORK 0x00004000  /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_VM    0x00000100  /* set if VM shared between processes */

SIGCHLD means the child should send this signal to its father when exit.

Для fork у ребенка и отца есть независимая таблица страниц VM, но, поскольку эффективность, fork на самом деле не копирует какие-либо страницы, она просто устанавливает все доступные для записи страницы для дочернего процесса. Поэтому, когда дочерний процесс хочет что-то написать на этой странице, возникает исключение страницы, и ядро ​​будет выделять новую страницу, клонированную со старой страницы с разрешением на запись. Это называется "копировать при записи".

Для vfork виртуальная память точно определяется ребенком и отцом --- только из-за этого отец и ребенок не могут проснуться одновременно, поскольку они будут влиять друг на друга. Таким образом, отец будет спать в конце "do_fork()" и проснется, когда ребенок вызовет exit() или execve(), после чего он будет владеть новой таблицей страниц. Вот код (в do_fork()), который отец спит.

if ((clone_flags & CLONE_VFORK) && (retval > 0))
down(&sem);
return retval;

Вот код (в mm_release(), вызываемый exit() и execve()), которые пробуждают отца.

up(tsk->p_opptr->vfork_sem);

Для sys_clone() он более гибкий, поскольку вы можете вводить к нему любые clone_flags. Таким образом, pthread_create() вызывает этот системный вызов со многими clone_flags:

int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM);

Сводка: fork(), vfork() и clone() будут создавать дочерние процессы с различным монтированием ресурса совместного доступа с процессом отцов. Мы также можем сказать, что vfork() и clone() могут создавать потоки (на самом деле это процессы, поскольку они имеют независимый task_struct), поскольку они совместно используют таблицу страниц VM с отцовским процессом.

Ответ 5

Различия между fork() и vfork() Разница между fork-vfork the difference between fork and vfork

Поведение Vfork() более подробно описано в приведенной ниже программе.

[email protected] ~}$ cat vfork_advanced.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
    int n =10;
    pid_t pid = vfork(); //creating the child process
    if (pid == 0)          //if this is a chile process
    {
        printf("Child process started\n");
    }
    else//parent process execution
    {
        printf("Now i am coming back to parent process\n");
    }
    printf("value of n: %d \n",n); //sample printing to check "n" value
    return 0;
}
[email protected] ~}$ cc vfork_advanced.c
[email protected] ~}$ ./a.out
Child process started
value of n: 10
Now i am coming back to parent process
value of n: 594325573
a.out: cxa_atexit.c:100: __new_exitfn: Assertion 'l != NULL' failed.
Aborted

Примечание: опять же, если вы наблюдаете, результат vfork не определен. Значение "n" было напечатано впервые как 10, что ожидается. Но в следующий раз в родительском процессе он напечатал какое-то мусорное значение.

Ответ 6

в fork(), либо дочерний, либо родительский процесс будет выполняться на основе выбора cpu. Но в vfork(), конечно, ребенок будет выполняться первым. после того, как ребенок будет завершен, родитель выполнит.