Различия между форком и exec
Каковы различия между fork
а также exec
?
9 ответов
Использование fork
а также exec
демонстрирует дух UNIX в том смысле, что он обеспечивает очень простой способ запуска новых процессов.
fork
call в основном создает дубликаты текущего процесса, идентичные почти во всех отношениях (не все копируются, например, ограничения ресурсов в некоторых реализациях, но идея состоит в том, чтобы создать максимально близкую копию).
Новый процесс (дочерний) получает другой идентификатор процесса (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
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
выполнить в текущем процессе целевой исполняемый файл.
Я думаю, что некоторые концепции из "Расширенного программирования Unix" Марка Рочкинда были полезны для понимания различных ролей fork()
/exec()
специально для кого то привык к винде CreateProcess()
модель:
Программа представляет собой набор инструкций и данных, которые хранятся в обычном файле на диске. (из 1.1.2 Программы, процессы и потоки)
,
Чтобы запустить программу, ядру сначала предлагается создать новый процесс, представляющий собой среду, в которой выполняется программа. (также из 1.1.2 Программы, процессы и потоки)
,
Невозможно понять системные вызовы exec или fork без полного понимания различия между процессом и программой. Если эти условия являются новыми для вас, вы можете вернуться и просмотреть раздел 1.1.2. Если вы готовы приступить сейчас, мы суммируем различие в одном предложении: процесс - это среда выполнения, которая состоит из сегментов команд, пользовательских данных и системных данных, а также множества других ресурсов, полученных во время выполнения. тогда как программа - это файл, содержащий инструкции и данные, которые используются для инициализации сегментов команд и пользовательских данных процесса. (из 5.3
exec
Системные звонки)
Как только вы поймете разницу между программой и процессом, поведение fork()
а также exec()
Функция может быть обобщена как:
fork()
создает дубликат текущего процессаexec()
заменяет программу в текущем процессе другой программой
(это, по сути, упрощенная версия "для чайников" гораздо более подробного ответа Паксдиабло)
Форк создает копию вызывающего процесса. как правило, следует структуре
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 {не совпадает с родительским})
Основное различие между 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.‖
Они используются вместе, чтобы создать новый дочерний процесс. Сначала звоню 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
}
fork() создает копию текущего процесса с выполнением в новом дочернем элементе, начиная сразу после вызова fork(). После fork() они идентичны, за исключением возвращаемого значения функции fork(). (RTFM для более подробной информации.) Затем эти два процесса могут еще больше расходиться, при этом один не может мешать другому, за исключением, возможно, каких-либо общих файловых дескрипторов.
exec () заменяет текущий процесс новым. Он не имеет ничего общего с fork(), за исключением того, что exec () часто следует за 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;
}
Главный пример, чтобы понять fork()
а также exec()
Концепция - это оболочка, программа интерпретатора команд, которую пользователи обычно выполняют после входа в систему. Оболочка интерпретирует первое слово командной строки как имя команды
Для многих команд оболочка и дочерний процесс исполняют команду, связанную с именем, обрабатывая оставшиеся слова в командной строке в качестве параметров команды.
Оболочка допускает три типа команд. Во-первых, команда может быть исполняемым файлом, который содержит объектный код, созданный путем компиляции исходного кода (например, программа на C). Во-вторых, команда может быть исполняемым файлом, который содержит последовательность командных строк оболочки. Наконец, команда может быть внутренней командой оболочки (вместо исполняемого файла ex-> cd, ls и т. Д.)