В чем разница между fork
и exec
?
Различия между fork и exec
Ответ 1
Использование 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
Ответ 2
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
выполнили в текущем процессе целевой исполняемый файл.
Ответ 3
Я думаю, что некоторые концепции из "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 гораздо более подробного ответа)
Ответ 4
Вилка создает копию вызывающего процесса. обычно следует за структурой
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 call unix-ядро очистит дочерний процесс, текст, данные, стек и заполняет с помощью файла/данных, связанных с процессом foo), таким образом, дочерний процесс имеет другой код (код foo {не совпадает с родительским))
Ответ 5
Они используются вместе для создания нового дочернего процесса. Во-первых, вызов 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
}
Ответ 6
fork() создает копию текущего процесса с выполнением в новом дочернем элементе, начиная с сразу после вызова fork(). После fork() они идентичны, за исключением возвращаемого значения функции fork(). (RTFM для более подробной информации.) Два процесса могут затем расходиться еще дальше, причем один не может вмешиваться в другой, за исключением, возможно, любых общих файлов.
exec() заменяет текущий процесс новым. Это не имеет никакого отношения к fork(), за исключением того, что exec() часто следует fork(), когда требуется запустить другой дочерний процесс, а не заменять текущий.
Ответ 7
Создает копию текущего процесса. Выполняемый процесс называется родительским процессом, а вновь созданный процесс называется дочерним процессом. Способ дифференцировать два состоит в том, чтобы посмотреть возвращаемое значение:
-
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;
}
Ответ 8
Ярким примером для понимания концепции fork()
и exec()
является shell, программа интерпретатора команд, которую пользователи обычно выполняют после входа в систему. Оболочка интерпретирует первое слово командной строки как имя команды
Для многих команд оболочка forks и дочерний процесс выполняет команду, связанную с именем, обрабатывающим остальные слова в командной строке в качестве параметров команды.
Оболочка допускает три типа команд. Во-первых, команда может быть исполняемый файл, который содержит объектный код, созданный компиляцией исходного кода (например, программа C). Во-вторых, команда может быть исполняемым файлом, который содержит последовательность командных строк оболочки. Наконец, команда может быть внутренней командой оболочки (вместо исполняемого файла ex → cd, ls и т.д.)