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

<p>я пытался найти разницу между этими четырьмя в google, и я ожидал, что по этому поводу будет огромное количество информации, но между этими четырьмя звонками действительно не было четкого сравнения.</p><p>я попытался скомпилировать некий базовый взгляд на различия между этими системными вызовами и вот что я получил. вся эта информация верна/я что-то упускаю?</p><p><code>fork</code>: в результате вызова fork в основном создается копия текущего процесса, идентичная почти во всех отношениях (не все копируются, например, из-за ограничений ресурсов в некоторых реализациях, но идея состоит в том, чтобы создать максимально близкую копию).</p><p>новый процесс (дочерний) получает другой идентификатор процесса (pid) и имеет pid старого процесса (родителя) в качестве родительского pid (ppid). поскольку два процесса теперь выполняют точно один и тот же код, они могут определить, какой именно, по коду возврата fork - дочерний элемент получает 0, родительский получает pid дочернего элемента. конечно, это все, если предположить, что вызов fork работает - если нет, дочерний элемент не создается, а родительский код получает код ошибки.</p><p><code>vfork</code>: основное различие между vfork и fork состоит в том, что при создании нового процесса с помощью vfork () родительский процесс временно приостанавливается, и дочерний процесс может занимать адресное пространство родительского процесса. это странное положение вещей продолжается до тех пор, пока дочерний процесс не завершится или не вызовет execve (), после чего родительский процесс продолжится.</p><p>это означает, что дочерний процесс vfork () должен быть осторожен, чтобы избежать неожиданного изменения переменных родительского процесса. в частности, дочерний процесс не должен возвращаться из функции, содержащей вызов vfork (), и он не должен вызывать exit () (если ему нужно выйти, он должен использовать \_exit (); на самом деле, это также верно для дочернего процесса нормальной вилки ()).</p><p><code>exec :</code> вызов exec - это способ в основном заменить весь текущий процесс новой программой. он загружает программу в текущее пространство процесса и запускает ее из точки входа. exec () заменяет текущий процесс исполняемым файлом, указанным функцией. элемент управления никогда не возвращается к исходной программе, если не существует ошибки exec ().</p><p><code>clone :</code> clone, как fork, создает новый процесс. в отличие от fork эти вызовы позволяют дочернему процессу совместно использовать часть своего контекста выполнения с вызывающим процессом, например пространство памяти, таблицу дескрипторов файлов и таблицу обработчиков сигналов.</p><p>когда дочерний процесс создается с помощью clone, он выполняет функцию приложения fn (arg). (это отличается от fork, где выполнение продолжается в дочернем элементе с точки исходного вызова fork.) аргумент fn является указателем на функцию, которая вызывается дочерним процессом в начале его выполнения. аргумент arg передается в функцию fn.</p><p>когда приложение функции fn (arg) возвращается, дочерний процесс завершается. целое число, возвращаемое fn, является кодом выхода для дочернего процесса. дочерний процесс может также явно завершиться с помощью вызова exit (2) или после получения фатального сигнала.</p><p>информация полученная форма:</p><ul> <li> <a href="https://stackoverflow.com/questions/1653340/exec-and-fork">различия между форком и exec</a> </li> <li> <a href="http://www.allinterview.com/showanswers/59616.html">http://www.allinterview.com/showanswers/59616.html</a> </li> <li> <a href="http://www.unixguide.net/unix/programming/1.1.2.shtml">http://www.unixguide.net/unix/programming/1.1.2.shtml</a> </li> <li> <a href="http://linux.about.com/library/cmd/blcmdl2\_clone.htm">http://linux.about.com/library/cmd/blcmdl2\_clone.htm</a> </li> </ul><p>спасибо, что нашли время, чтобы прочитать это! :)</p>

<ul> <li> <p><code>vfork()</code> - это устаревшая оптимизация. до хорошего управления памятью <code>fork()</code> сделал полную копию памяти родителя, поэтому это было довольно дорого. поскольку во многих случаях за <code>fork()</code> следовала <code>exec()</code>, которая отбрасывает текущую карту памяти и создает новую, это было ненужным расходом. в настоящее время <code>fork()</code> не копирует память; он просто устанавливается как "копировать при записи", поэтому <code>fork()</code> + <code>exec()</code> так же эффективен, как <code>vfork()</code> + <code>exec()</code>.</p> </li> <li> <p><code>clone()</code> - системный вызов, используемый <code>fork()</code>. с некоторыми параметрами он создает новый процесс, с другими он создает поток. разница между ними заключается только в том, какие структуры данных (пространство памяти, состояние процессора, стек, pid, открытые файлы и т. д.) являются общими или нет.</p> </li> </ul>

<ul> <li><code>execve()</code> заменяет текущий исполняемый образ другим, загруженным из исполняемого файла.</li> <li><code>fork()</code> создает дочерний процесс.</li> <li><code>vfork()</code> является исторически оптимизированной версией <code>fork()</code>, предназначенной для использования, когда <code>execve()</code> вызывается непосредственно после <code>fork()</code>. оказалось, что он хорошо работает в не-mmu системах (где <code>fork()</code> не может работать эффективным образом) и когда <code>fork()</code>ing обрабатывает с огромным объемом памяти для запуска какой-то небольшой программы (вспомним java <code>runtime.exec()</code>). posix стандартизировал <code>posix\_spawn()</code>, чтобы заменить эти два более современных способа использования <code>vfork()</code>.</li> <li><code>posix\_spawn()</code> делает эквивалент <code>fork()/execve()</code>, а также допускает некоторое fd-манипулирование между ними. предполагается заменить <code>fork()/execve()</code>, в основном для не-mmu платформ.</li> <li><code>pthread\_create()</code> создает новый поток.</li> <li><code>clone()</code> - это специфичный для linux вызов, который можно использовать для реализации чего угодно, от <code>fork()</code> до <code>pthread\_create()</code>. это дает много контроля. вдохновлен <code>rfork()</code>.</li> <li><code>rfork()</code> - это специальный вызов plan-9. предполагается, что это будет общий вызов, обеспечивающий несколько степеней разделения между полными процессами и потоками.</li> </ul>

<ol> <li><code>fork()</code> - создает новый дочерний процесс, который является полной копией родительского процесса. дочерние и родительские процессы используют разные виртуальные адресные пространства, которые изначально заполнены одними и теми же страницами памяти. затем, когда оба процесса выполняются, виртуальные адресные пространства начинают все больше и больше различаться, поскольку операционная система выполняет отложенное копирование страниц памяти, которые записываются любым из этих двух процессов, и назначает независимые копии измененных страниц память для каждого процесса. этот метод называется копирование при записи (cow).</li> <li><code>vfork()</code> - создает новый дочерний процесс, который является "быстрой" копией родительского процесса. в отличие от системного вызова <code>fork()</code> дочерние и родительские процессы совместно используют одно и то же виртуальное адресное пространство. нота! используя одно и то же виртуальное адресное пространство, родительский и дочерний элементы используют один и тот же стек, указатель стека и указатель инструкции, как в случае классической <code>fork()</code>! чтобы предотвратить нежелательные помехи между родителем и дочерним элементом, которые используют один и тот же стек, выполнение родительского процесса приостанавливается до тех пор, пока дочерний элемент не вызовет <code>exec()</code> (создаст новое виртуальное адресное пространство и перейдет в другой стек) или <code>\_exit()</code> (завершение выполнение процесса). <code>vfork()</code> - это оптимизация <code>fork()</code> для модели "fork-and-exec". это может быть выполнено в 4-5 раз быстрее, чем <code>fork()</code>, потому что в отличие от <code>fork()</code> (даже с учетом cow), реализация системного вызова <code>vfork()</code> не включает создание нового адресного пространства (выделение и настройка нового страница каталогов).</li> <li><code>clone()</code> - создает новый дочерний процесс. различные параметры этого системного вызова указывают, какие части родительского процесса должны быть скопированы в дочерний процесс, а какие будут разделены между ними. в результате этот системный вызов может использоваться для создания всех видов исполняемых объектов, начиная с потоков и заканчивая совершенно независимыми процессами. фактически, системный вызов <code>clone()</code> является базой, которая используется для реализации <code>pthread\_create()</code> и всего семейства системных вызовов <code>fork()</code>.</li> <li><code>exec()</code> - сбрасывает всю память процесса, загружает и анализирует указанный исполняемый двоичный файл, устанавливает новый стек и передает управление точке входа загруженного исполняемого файла. этот системный вызов никогда не возвращает управление вызывающей стороне и служит для загрузки новой программы в уже существующий процесс. этот системный вызов с системным вызовом <code>fork()</code> образует классическую модель управления процессами unix, называемую "fork-and-exec".</li> </ol>

<p>все функции fork (), vfork () и clone () вызывают do\_fork () для реальной работы, но с разными параметрами.</p><pre><code>asmlinkage int sys\_fork(struct pt\_regs regs) { return do\_fork(sigchld, regs.esp, s, 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, s, 0); } asmlinkage int sys\_vfork(struct pt\_regs regs) { return do\_fork(clone\_vfork | clone\_vm | sigchld, regs.esp, s, 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. </code></pre><p>для fork дочерний и отец имеет независимую таблицу страниц vm, но, поскольку эффективность fork не будет на самом деле копировать ни одной страницы, он просто установит все доступные для записи страницы только для чтения для дочернего процесса. поэтому, когда дочерний процесс хочет что-то написать на этой странице, возникает исключение страницы, и ядро выделяет новую страницу, клонированную со старой страницы с разрешением на запись. это называется "копировать при записи".</p><p>для vfork виртуальная память принадлежит ребенку и отцу - именно поэтому отец и ребенок не могут бодрствовать одновременно, поскольку они будут влиять друг на друга. поэтому отец будет спать в конце do\_fork () и просыпаться, когда дочерний вызов вызывает exit () или execve (), с тех пор ему будет принадлежать новая таблица страниц. вот код (в do\_fork ()), который спит отец.</p><pre><code>if ((clone\_flags & clone\_vfork) && (retval > 0)) down(&sem); return retval; </code></pre><p>вот код (в mm\_release (), вызываемый exit () и execve ()), который пробуждает отца.</p><pre><code>up(tsk->p\_opptr->vfork\_sem); </code></pre><p>для sys\_clone () это более гибко, так как вы можете ввести в него любые clone\_flags. поэтому pthread\_create () вызывает этот системный вызов со многими clone\_flags:</p><p>int clone\_flags = (clone\_vm | clone\_fs | clone\_files | clone\_signal | clone\_settls | clone\_parent\_settid | clone\_child\_cleartid | clone\_sysvsem);</p><p>резюме: fork (), vfork () и clone () создадут дочерние процессы с различным монтированием ресурса совместного использования с родительским процессом. мы также можем сказать, что vfork () и clone () могут создавать потоки (фактически они являются процессами, поскольку они имеют независимую структуру task\_struct), поскольку они совместно используют таблицу страниц vm с родительским процессом.</p>

<p>различия между fork () и vfork () разница между fork-vfork <a href="https://i.stack.imgur.com/l8d3t.jpg"><img src="https://i.stack.imgur.com/l8d3t.jpg" alt="the difference between fork and vfork"></a> </p><p>поведение vfork () более подробно описано в приведенной ниже программе.</p><pre><code>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 startedn"); } else//parent process execution { printf("now i am coming back to parent processn"); } 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 </code></pre><p>примечание: опять же, если вы наблюдаете, результат vfork не определен. значение "n" было напечатано впервые как 10, что ожидается. но в следующий раз в родительском процессе он напечатал какое-то мусорное значение.</p>