Какова цель fork ()? - PullRequest
80 голосов
/ 12 июня 2009

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

Ответы [ 15 ]

102 голосов
/ 12 июня 2009

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

Вот несколько примеров использования fork:

  1. Ваша оболочка использует fork для запуска программ, которые вы вызываете из командной строки.
  2. Веб-серверы типа apache используют fork для создания нескольких серверных процессов, каждый из которых обрабатывает запросы в своем собственном адресном пространстве. Если один из них умирает или утекает из памяти, другие остаются без изменений, поэтому он функционирует как механизм обеспечения отказоустойчивости.
  3. Google Chrome использует fork для обработки каждой страницы в отдельном процессе. Это предотвратит сбой всего браузера на стороне клиента на одной странице.
  4. fork используется для запуска процессов в некоторых параллельных программах (например, написанных с использованием MPI ). Обратите внимание, что это отличается от использования потоков , которые не имеют своего собственного адресного пространства и существуют в процессе.
  5. Языки сценариев используют fork косвенно для запуска дочерних процессов. Например, каждый раз, когда вы используете команду вроде subprocess.Popen в Python, вы fork дочерний процесс и читаете его вывод. Это позволяет программам работать вместе.

Типичное использование fork в оболочке может выглядеть примерно так:

int child_process_id = fork();
if (child_process_id) {
    // Fork returns a valid pid in the parent process.  Parent executes this.

    // wait for the child process to complete
    waitpid(child_process_id, ...);  // omitted extra args for brevity

    // child process finished!
} else {
    // Fork returns 0 in the child process.  Child executes this.

    // new argv array for the child process
    const char *argv[] = {"arg1", "arg2", "arg3", NULL};

    // now start executing some other program
    exec("/path/to/a/program", argv);
}

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

Если вам нужно больше примеров и более длинное объяснение, Wikipedia имеет приличное резюме. И вот несколько слайдов о том, как процессы, потоки и параллелизм работают в современных операционных системах.

13 голосов
/ 12 июня 2009

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

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

$ telnetd &

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

9 голосов
/ 12 июня 2009

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

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

http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

6 голосов
/ 12 июня 2009

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

pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process

этим вы можете предоставлять различные действия для родителя и ребенка и использовать функцию многопоточности.

6 голосов
/ 12 июня 2009

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

4 голосов
/ 26 декабря 2013

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

Если fork () возвращает отрицательное значение, создание дочернего процесса завершилось неудачно. fork () возвращает ноль недавно созданному дочернему процессу. fork () возвращает положительное значение, идентификатор процесса дочернего процесса, родителю. Возвращенный идентификатор процесса имеет тип pid_t, определенный в sys / types.h. Обычно идентификатор процесса является целым числом. Более того, процесс может использовать функцию getpid () для получения идентификатора процесса, назначенного этому процессу. Поэтому после системного вызова fork () простой тест может определить, какой процесс является дочерним. Обратите внимание, что Unix сделает точную копию адресного пространства родителя и передаст ее ребенку. Поэтому родительский и дочерний процессы имеют отдельные адресные пространства.

Давайте разберемся с этим на примере, чтобы прояснить вышесказанное. Этот пример не различает родительский и дочерний процессы.

#include  <stdio.h>
#include  <string.h>
#include  <sys/types.h>

#define   MAX_COUNT  200
#define   BUF_SIZE   100

void  main(void)
{
     pid_t  pid;
     int    i;
     char   buf[BUF_SIZE];

     fork();
     pid = getpid();
     for (i = 1; i <= MAX_COUNT; i++) {
          sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
          write(1, buf, strlen(buf));
     } 
}

Предположим, что вышеуказанная программа выполняется до точки вызова fork ().

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

pid = .....;

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

В чем причина использования write, а не printf? Это потому, что printf () "буферизован", что означает, что printf () сгруппирует выходные данные процесса вместе. При буферизации вывода для родительского процесса дочерний процесс может также использовать printf для распечатки некоторой информации, которая также будет буферизована. В результате, поскольку вывод не будет немедленно отправлен на экран, вы можете не получить правильный порядок ожидаемого результата. Хуже того, выходные данные двух процессов могут смешиваться странным образом. Чтобы преодолеть эту проблему, вы можете использовать «небуферизованную» запись.

Если вы запустите эту программу, вы можете увидеть на экране следующее:

................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
     ................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
     ................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
     ................

Идентификатор процесса 3456 может быть тот, который назначен родителю или ребенку. Из-за того, что эти процессы выполняются одновременно, их выходные строки смешиваются довольно непредсказуемым образом. Более того, порядок этих строк определяется планировщиком ЦП. Следовательно, если вы снова запустите эту программу, вы можете получить совершенно другой результат.

4 голосов
/ 17 апреля 2011

Форк создает новые процессы. Без fork у вас была бы система Unix, которая могла бы запускать только init.

4 голосов
/ 12 июня 2009

Вам, вероятно, не нужно использовать форк в повседневном программировании, если вы пишете приложения.

Даже если вы хотите, чтобы ваша программа запускала другую программу для выполнения какой-либо задачи, существуют другие более простые интерфейсы, которые используют fork за кулисами, такие как «system» в C и perl.

Например, если вы хотите, чтобы ваше приложение запускало другую программу, такую ​​как bc, для выполнения некоторых вычислений, вы можете использовать 'system' для ее запуска. Система делает 'форк' для создания нового процесса, затем 'exec', чтобы превратить этот процесс в bc. После завершения bc система возвращает управление вашей программе.

Вы также можете запускать другие программы асинхронно, но я не помню как.

Если вы пишете серверы, оболочки, вирусы или операционные системы, вы, скорее всего, захотите использовать fork.

3 голосов
/ 12 января 2012

Fork () используется для создания новых процессов, как написано в каждом теле.

Вот мой код, который создает процессы в виде двоичного дерева ....... Он попросит отсканировать количество уровней, до которых вы хотите создать процессы в двоичном дереве

#include<unistd.h> 
#include<fcntl.h> 
#include<stdlib.h>   
int main() 
{
int t1,t2,p,i,n,ab;
p=getpid();                
printf("enter the number of levels\n");fflush(stdout);
scanf("%d",&n);                
printf("root %d\n",p);fflush(stdout);
for(i=1;i<n;i++)    
{        
    t1=fork();

    if(t1!=0)
        t2=fork();        
    if(t1!=0 && t2!=0)        
        break;            
    printf("child pid %d   parent pid %d\n",getpid(),getppid());fflush(stdout);
}   
    waitpid(t1,&ab,0);
    waitpid(t2,&ab,0);
return 0;
}

OUTPUT

  enter the number of levels
  3
  root 20665
  child pid 20670   parent pid 20665
  child pid 20669   parent pid 20665
  child pid 20672   parent pid 20670
  child pid 20671   parent pid 20670
  child pid 20674   parent pid 20669
  child pid 20673   parent pid 20669
3 голосов
/ 12 июня 2009

Многопроцессорность занимает центральное место в вычислительной технике. Например, ваш IE или Firefox может создать процесс для загрузки файла для вас, пока вы еще просматриваете Интернет. Или, пока вы распечатываете документ в текстовом процессоре, вы все равно можете просматривать разные страницы и по-прежнему редактировать его.

...