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

183

В чем разница между fork и exec?

  • 87
    ... и мы бы хотели, чтобы Google связался здесь в следующий раз, когда кто-нибудь погуглит это ... так что это правильный вопрос.
  • 1
    Мех. У Google это есть. Зачем засорять его большим количеством результатов, чтобы мы могли сказать, что есть еще один SO ответ, который оценивается?
Показать ещё 4 комментария
Теги:
exec
fork

8 ответов

309
Лучший ответ

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

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

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

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

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

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

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

В некоторых реализациях UNIX есть оптимизированный fork, который использует то, что они называют copy-on-write. Это трюк, чтобы задержать копирование пространства процесса в 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
48

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 not very parently of us
 }

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

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

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

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

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

.

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

.

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

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

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

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

24

Вилка создает копию вызывающего процесса. обычно следует за структурой Изображение 4816

int cpid = fork( );

if (cpid = = 0) 
{

  //child code

  exit(0);

}

//parent code

wait(cpid);

// end

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

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

int cpid = fork( );

if (cpid = = 0) 
{

  //child code

  exec(foo);

  exit(0);

}

//parent code

wait(cpid);

// end

(после того, как exec call 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
}
  • 0
    IIUC, что может не работать из процессов с GUI?
  • 1
    В 7-й строке упоминается, что функция exec () создает дочерний процесс. Действительно ли это так, потому что fork () уже создал дочерний процесс, а вызов exec () просто заменяет программу только что созданного нового процесса
4

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

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

1

Изображение 4818 fork():

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

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

  • 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() является shell, программа интерпретатора команд, которую пользователи обычно выполняют после входа в систему. Оболочка интерпретирует первое слово командной строки как имя команды

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню