it-swarm.com.ru

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

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

182
Sashi

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

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

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

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

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

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

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

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

Если exec is вызывается после 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
334
paxdiablo

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 выполняете в текущем процессе целевой исполняемый файл.

49
Doug T.

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

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

,.

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

,.

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

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

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

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

29
Michael Burr

Форк создает копию вызывающего процесса. обычно следует структуре enter image description here

int cpid = fork( );

if (cpid = = 0) 
{

  //child code

  exit(0);

}

//parent code

wait(cpid);

// end

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

EXEC заменяет текущий процесс новым кодом процесса, данными, стеком , Как правило, следует структуре enter image description here

int cpid = fork( );

if (cpid = = 0) 
{

  //child code

  exec(foo);

  exit(0);

}

//parent code

wait(cpid);

// end

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

27
Sandesh Kobal

Они используются вместе, чтобы создать новый дочерний процесс. Во-первых, вызов 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
}
7
Steve Hawkins

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

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

4
Warren Young

Основное различие между 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(), чтобы заменить ее код программой date. Если код хранится в файле с именем exec1.c, то при его выполнении выдается следующий вывод: 

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

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

2
Abdulhakim Zeinu

 enter image description here 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;
}
1
Yogeesh H T

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

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

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

0
krpra