Различия между форком и exec

199

Каковы различия между forkи exec?

Sashi
источник
3
Хорошее, подробное описание функций управления процессами fork, exec и других процессов можно найти по
Джонатан Фингланд,
9
@Justin, потому что мы хотим , чтобы стать SO местом для программирования вопросов.
paxdiablo
4
@ Polaris878: о, сейчас! : D
Януш Ленар
так forkв основном клонирование: O
Себастьян Ходжас

Ответы:

364

Использование forkи execпример духа UNIX в том смысле, что он обеспечивает очень простой способ запуска новых процессов.

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

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

execВызов является способом в основном заменить весь текущий процесс с новой программой. Он загружает программу в текущее пространство процесса и запускает ее из точки входа.

Таким образом, forkи execчасто используются в последовательности , чтобы получить новую программу , работающую как ребенок текущего процесса. Оболочки обычно делают это всякий раз, когда вы пытаетесь запустить программу наподобие: findоболочка разветвляется, затем дочерний процесс загружает findпрограмму в память, настраивая все аргументы командной строки, стандартный ввод-вывод и так далее.

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

Точно так же, программы, которые знают, что они закончили и просто хотят запустить другую программу, не нуждаются fork, execа затем waitдля ребенка. Они могут просто загрузить ребенка прямо в свое пространство процесса.

Некоторые реализации UNIX имеют оптимизированную систему, forkкоторая использует то, что они называют копированием при записи. Это хитрость, чтобы отложить копирование пространства процесса forkдо тех пор, пока программа не попытается что-то изменить в этом пространстве. Это полезно для тех программ, которые используют только, forkа не execпотому, что им не нужно копировать все пространство процесса.

Если exec вызывается следующим образом fork(и это происходит в основном), это вызывает запись в пространство процесса, а затем копируется для дочернего процесса.

Обратите внимание , что существует целое семейство execвызовов ( execl, execle, execveи так далее) , но execв контексте здесь означает любое из них.

Следующая диаграмма иллюстрирует типичную fork/execоперацию, в которой bashоболочка используется для вывода каталога с помощью lsкоманды:

+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V
paxdiablo
источник
52

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

 int pid = fork();

 if (pid == 0)
 {
     printf("I'm the child");
 }
 else
 {
     printf("I'm the parent, my child is %i", pid);
     // here we can kill the child, but that's not very parently of us
 }

Это может взорвать ваш разум. Теперь у вас есть один кусок кода с почти одинаковым состоянием, выполняемый двумя процессами. Дочерний процесс наследует весь код и память процесса, который только что его создал, в том числе начиная с того места, где был fork()прерван вызов. Единственная разница заключается в fork()коде возврата, который сообщает вам, являетесь ли вы родителем или ребенком. Если вы родитель, возвращаемое значение - это идентификатор ребенка.

execэто немного легче понять, вы просто указываете execвыполнить процесс, используя целевой исполняемый файл, и у вас нет двух процессов, выполняющих один и тот же код или наследующих одно и то же состояние. Как говорит @Steve Hawkins, execможет использоваться после того, как вы forkвыполните в текущем процессе целевой исполняемый файл.

Дуг Т.
источник
6
есть также условие, когда pid < 0и fork()вызов не удался
Джонатан Фингланд
3
Это совсем не поражает воображение :-) Один фрагмент кода, выполняемый двумя процессами, происходит каждый раз, когда используется общая библиотека или DLL.
paxdiablo
31

Я думаю, что некоторые концепции из «Расширенного программирования Unix» Марка Рочкинда были полезны для понимания различных ролей fork()/ exec(), особенно для человека, привыкшего к CreateProcess()модели Windows :

Программа представляет собой набор инструкций и данных , которые хранятся в обычном файле на диске. (из 1.1.2 Программы, процессы и потоки)

,

Чтобы запустить программу, ядру сначала предлагается создать новый процесс , представляющий собой среду, в которой выполняется программа. (также из 1.1.2 Программы, процессы и потоки)

,

Невозможно понять системные вызовы exec или fork без полного понимания различия между процессом и программой. Если эти условия являются новыми для вас, вы можете вернуться и просмотреть раздел 1.1.2. Если вы готовы продолжить сейчас, мы суммируем различие в одном предложении: процесс - это среда выполнения, которая состоит из сегментов команд, пользовательских данных и системных данных, а также множества других ресурсов, полученных во время выполнения. тогда как программа - это файл, содержащий инструкции и данные, которые используются для инициализации сегментов команд и пользовательских данных процесса. (из 5.3 execСистемные вызовы)

Как только вы поймете разницу между программой и процессом, поведение fork()и exec()функции можно будет обобщить следующим образом:

  • fork() создает дубликат текущего процесса
  • exec() заменяет программу в текущем процессе другой программой

(это, по сути, упрощенная версия «для чайников» гораздо более подробного ответа Паксдиабло )

Майкл Берр
источник
29

Форк создает копию вызывающего процесса. как правило, следует структуре введите описание изображения здесь

int cpid = fork( );

if (cpid = = 0) 
{

  //child code

  exit(0);

}

//parent code

wait(cpid);

// end

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

EXEC заменяет текущий процесс новым кодом процесса, данными, стеком. как правило, следует структуре введите описание изображения здесь

int cpid = fork( );

if (cpid = = 0) 
{   
  //child code

  exec(foo);

  exit(0);    
}

//parent code

wait(cpid);

// end

(после вызова exec ядро ​​Unix очищает текст, данные, стек дочернего процесса и заполняет текст / данные, относящиеся к процессу foo), таким образом, дочерний процесс имеет другой код (код foo {не совпадает с родительским})

Сандеш Кобаль
источник
1
Это немного не связано с вопросом, но разве этот код выше не вызывает условия гонки, если дочерний процесс завершает сначала свой код? В этом случае родительский процесс будет зависать вечно, ожидая, когда ребенок завершит свою работу, верно?
выход
7

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

Процесс идет примерно так:

child = fork();  //Fork returns a PID for the parent process, or 0 for the child, or -1 for Fail

if (child < 0) {
    std::cout << "Failed to fork GUI process...Exiting" << std::endl;
    exit (-1);
} else if (child == 0) {       // This is the Child Process
    // Call one of the "exec" functions to create the child process
    execvp (argv[0], const_cast<char**>(argv));
} else {                       // This is the Parent Process
    //Continue executing parent process
}
Стив Хокинс
источник
2
В 7-й строке упоминается, что функция exec () создает дочерний процесс. Действительно ли это так, потому что fork () уже создал дочерний процесс, а вызов exec () просто заменяет программу только что созданного нового процесса
cbinder,
4

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

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

Уоррен Янг
источник
3

Основное различие между fork()и exec()заключается в том, что

fork()Системный вызов создает клон запущенной в данный момент программы. Исходная программа продолжает выполнение со следующей строки кода после вызова функции fork (). Клон также начинает выполнение со следующей строки кода. Посмотрите на следующий код, который я получил с http://timmurphy.org/2014/04/26/using-fork-in-cc-a-minimum-working-example/

#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
    printf("--beginning of program\n");
    int counter = 0;
    pid_t pid = fork();
    if (pid == 0)
    {
        // child process
        int i = 0;
        for (; i < 5; ++i)
        {
            printf("child process: counter=%d\n", ++counter);
        }
    }
    else if (pid > 0)
    {
        // parent process
        int j = 0;
        for (; j < 5; ++j)
        {
            printf("parent process: counter=%d\n", ++counter);
        }
    }
    else
    {
        // fork failed
        printf("fork() failed!\n");
        return 1;
    }
    printf("--end of program--\n");
    return 0;
}

Эта программа объявляет переменную счетчика, установленную в ноль, перед fork()ing. После вызова fork у нас параллельно работают два процесса, каждый из которых увеличивает свою собственную версию счетчика. Каждый процесс будет завершен и завершен. Поскольку процессы выполняются параллельно, у нас нет возможности узнать, что закончится первым. Запуск этой программы напечатает что-то похожее на то, что показано ниже, хотя результаты могут отличаться от одного запуска к следующему.

--beginning of program
parent process: counter=1
parent process: counter=2
parent process: counter=3
child process: counter=1
parent process: counter=4
child process: counter=2
parent process: counter=5
child process: counter=3
--end of program--
child process: counter=4
child process: counter=5
--end of program--

exec()Семейство системных вызовов заменяет текущий код процесса с другим фрагментом кода. Процесс сохраняет свой PID, но становится новой программой. Например, рассмотрим следующий код:

#include <stdio.h> 
#include <unistd.h> 
main() {
 char program[80],*args[3];
 int i; 
printf("Ready to exec()...\n"); 
strcpy(program,"date"); 
args[0]="date"; 
args[1]="-u"; 
args[2]=NULL; 
i=execvp(program,args); 
printf("i=%d ... did it work?\n",i); 
} 

Эта программа вызывает execvp()функцию для замены ее кода программой даты. Если код хранится в файле с именем exec1.c, то при его выполнении выдается следующий вывод:

Ready to exec()... 
Tue Jul 15 20:17:53 UTC 2008 

Программа выводит строку adyReady to exec (). , , ‖ И после вызова функции execvp () заменяет ее код программой date. Обратите внимание, что линия -. , , сделал это работает? не отображается, потому что в этот момент код был заменен. Вместо этого мы видим результат выполнения ―date -u.‖

Абдулхаким Зейну
источник
1

введите описание изображения здесьfork():

Создает копию запущенного процесса. Запущенный процесс называется родительским процессом, а вновь созданный процесс называется дочерним процессом . Способ их различения заключается в просмотре возвращаемого значения:

  1. fork() возвращает идентификатор процесса (pid) дочернего процесса в родительском

  2. fork() возвращает 0 у ребенка.

exec():

Он инициирует новый процесс внутри процесса. Он загружает новую программу в текущий процесс, заменяя существующую.

fork()+ exec():

При запуске новой программы необходимо сначала fork()создать новый процесс, а затем exec()(т.е. загрузить в память и выполнить) двоичный файл программы, который предполагается запустить.

int main( void ) 
{
    int pid = fork();
    if ( pid == 0 ) 
    {
        execvp( "find", argv );
    }

    //Put the parent to sleep for 2 sec,let the child finished executing 
    wait( 2 );

    return 0;
}
Йогиш ХТ
источник
0

Главный пример, чтобы понять fork() и exec()концепцией является оболочкой , программа командного интерпретатора , что пользователи , как правило , выполняются после входа в system.The оболочку интерпретирует первое слово командной строки в качестве команды имени

Для многих команд оболочка вилки и дочерний процесс Execs команды , связанная с именем обработки оставшихся слов в командной строке в качестве параметров команды.

Оболочки позволяет использовать три типа команд. Во-первых, команда может быть исполняемым файлом, который содержит объектный код, созданный путем компиляции исходного кода (например, программа на C). Во-вторых, команда может быть исполняемым файлом, который содержит последовательность командных строк оболочки. Наконец, команда может быть командой внутренней оболочки. (Вместо исполняемого файла ex-> cd , ls и т. Д.)

krpra
источник