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

Я пытался найти разницу между этими четырьмя в 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 эти вызовы позволяют дочернему процессу совместно использовать часть своего контекста выполнения с вызывающим процессом, например пространство памяти, таблицу дескрипторов файлов и таблицу обработчиков сигналов.

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

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

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

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

5 ответов

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

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

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

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

Различия между fork() и vfork() Разница между fork-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, что ожидается. Но в следующий раз в родительском процессе он напечатал какое-то мусорное значение.

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

Другие вопросы по тегам