Разница между fork (), vfork (), exec () и clone () - PullRequest
186 голосов
/ 01 февраля 2011

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

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

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

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

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

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

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

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

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

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

Информация, полученная форма:

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

Ответы [ 6 ]

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

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

74 голосов
/ 12 июля 2011
  • execve() заменяет текущий исполняемый образ другим, загруженным из исполняемого файла.
  • fork() создает дочерний процесс.
  • vfork() является исторически оптимизированнымверсия fork(), предназначенная для использования, когда execve() вызывается сразу после fork().Оказалось, что он хорошо работает в не-MMU системах (где fork() не может работать эффективно) и когда fork() обрабатывает процессы с огромным объемом памяти для запуска какой-то небольшой программы (например, Java Runtime.exec()).POSIX стандартизировал posix_spawn(), чтобы заменить эти два более современных способа использования vfork().
  • posix_spawn() эквивалентен fork()/execve(), а также допускает некоторое fd-жонглирование между ними.Предполагается, что он заменит fork()/execve(), в основном для не-MMU платформ.
  • pthread_create() создает новый поток.
  • clone() - это специфичный для Linux вызов, который можно использовать дляреализовать что-нибудь от fork() до pthread_create().Это дает много контроля.Вдохновленный rfork().
  • rfork() - это вызов для Plan-9.Предполагается, что это общий вызов, обеспечивающий несколько степеней разделения между полными процессами и потоками.
39 голосов
/ 12 августа 2014
  1. fork() - создает новый дочерний процесс, который является полной копией родительского процесса.Дочерние и родительские процессы используют разные виртуальные адресные пространства, которые изначально заполнены одними и теми же страницами памяти.Затем, когда оба процесса выполняются, виртуальные адресные пространства начинают все больше и больше различаться, поскольку операционная система выполняет отложенное копирование страниц памяти, которые записываются любым из этих двух процессов, и назначает независимые копии измененных страницпамять для каждого процесса.Этот метод называется Copy-On-Write (COW).
  2. vfork() - создает новый дочерний процесс, который является «быстрой» копией родительского процесса.В отличие от системного вызова fork(), дочерний и родительский процессы используют одно и то же виртуальное адресное пространство.НОТА!Используя одно и то же виртуальное адресное пространство, родительский и дочерний элементы используют один и тот же стек, указатель стека и указатель инструкций, как в случае классического fork()!Чтобы предотвратить нежелательное вмешательство между родительским и дочерним процессами, которые используют один и тот же стек, выполнение родительского процесса приостанавливается до тех пор, пока дочерний процесс не вызовет либо exec() (создаст новое виртуальное адресное пространство и перейдет в другой стек), либо _exit()(прекращение процесса выполнения).vfork() - это оптимизация fork() для модели "fork-and-exec".Это может быть выполнено в 4-5 раз быстрее, чем fork(), потому что в отличие от fork() (даже с учетом COW), реализация системного вызова vfork() не включает создание нового адресного пространства (выделение и настройка новых каталогов страниц).
  3. clone() - создает новый дочерний процесс.Различные параметры этого системного вызова указывают, какие части родительского процесса должны быть скопированы в дочерний процесс, а какие будут разделены между ними.В результате этот системный вызов может использоваться для создания всех видов исполняемых объектов, начиная с потоков и заканчивая совершенно независимыми процессами.Фактически системный вызов clone() является базой, которая используется для реализации pthread_create() и всех семейств системных вызовов fork().
  4. exec() - сбрасывает всю память процессазагружает и анализирует указанный исполняемый файл, устанавливает новый стек и передает управление точке входа загруженного исполняемого файла.Этот системный вызов никогда не возвращает управление вызывающей стороне и служит для загрузки новой программы в уже существующий процесс.Этот системный вызов с fork() системным вызовом вместе образует классическую модель управления процессами UNIX, называемую «fork-and-exec».
6 голосов
/ 09 августа 2014

Все функции 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 не будет на самом деле копировать какие-либо страницы, она просто установит все доступные для записи страницы только для чтения для дочернего процесса.Поэтому, когда дочерний процесс хочет что-то написать на этой странице, возникает исключение страницы, и ядро ​​выделяет новую страницу, клонированную со старой страницы с разрешением на запись.Это называется «копировать при записи».

Для 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 | 101 * CLS_S_S_NS; * CLS__SLE_S_SLE_S * *;), vfork () и clone () создадут дочерние процессы с разным монтированием общего ресурса с родительским процессом.Мы также можем сказать, что vfork () и clone () могут создавать потоки (на самом деле они являются процессами, поскольку они имеют независимую task_struct), поскольку они совместно используют таблицу страниц VM с родительским процессом.

3 голосов
/ 02 апреля 2019

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

Поведение Vfork () объяснено более подробно в следующей программе.

shashi@linuxtechi ~}$ 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;
}
shashi@linuxtechi ~}$ cc vfork_advanced.c
shashi@linuxtechi ~}$ ./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, что ожидается. Но в следующий раз в родительском процессе он напечатал какое-то мусорное значение.

0 голосов
/ 09 июля 2013

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

...