Каковы различия между fork
и exec
?
fork
в основном клонирование: O
Каковы различия между fork
и exec
?
fork
в основном клонирование: O
Ответы:
Использование 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
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
выполните в текущем процессе целевой исполняемый файл.
pid < 0
и fork()
вызов не удался
Я думаю, что некоторые концепции из «Расширенного программирования Unix» Марка Рочкинда были полезны для понимания различных ролей fork()
/ exec()
, особенно для человека, привыкшего к CreateProcess()
модели Windows :
Программа представляет собой набор инструкций и данных , которые хранятся в обычном файле на диске. (из 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
вызывается из дочернего процесса, чтобы «заменить» копию родительского процесса новым процессом.
Процесс идет примерно так:
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()
и 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()
возвращает идентификатор процесса (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()
концепцией является оболочкой , программа командного интерпретатора , что пользователи , как правило , выполняются после входа в system.The оболочку интерпретирует первое слово командной строки в качестве команды имени
Для многих команд оболочка вилки и дочерний процесс Execs команды , связанная с именем обработки оставшихся слов в командной строке в качестве параметров команды.
Оболочки позволяет использовать три типа команд. Во-первых, команда может быть исполняемым файлом, который содержит объектный код, созданный путем компиляции исходного кода (например, программа на C). Во-вторых, команда может быть исполняемым файлом, который содержит последовательность командных строк оболочки. Наконец, команда может быть командой внутренней оболочки. (Вместо исполняемого файла ex-> cd , ls и т. Д.)