Яке призначення fork ()?


87

У багатьох програмах та на сторінках інструкцій Linux я бачив використання коду fork(). Навіщо нам потрібно користуватися fork()і яке його призначення?


151
Щоб усі ті філософи-ресторани не голодували.
kenj0418

Відповіді:


107

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 .

Якщо ви хочете більше прикладів і більш довгих пояснень, Вікіпедія має гідний резюме. І ось кілька слайдів про те, як процеси, потоки та паралельність працюють у сучасних операційних системах.


Куля 5: "часто"? Тільки "часто"? Хто з них не використовує, або за яких обставин fork () не використовується - в системах, що підтримують fork (), тобто.
Джонатан Леффлер,

19
Як не дивно, це називається CreateProcess () - ці божевільні хлопці з Windows :-)
paxdiablo

2
ніколи досі не усвідомлював, що "оболонка використовує fork для запуску програм, які ви викликаєте з командного рядка"!
Лазер

1
Посилання на слайди порушено
piertoni

1
Всі відповіді говорять , що fork()це шлях , щоб створити новий процес в UNIX, але бути педантичним, є принаймні один інший: posix_spawn().
Девіслор,

15

fork () - це те, як Unix створює нові процеси. У той момент, коли ви викликали fork (), ваш процес клонується, і два різні процеси продовжують виконання звідти. Один з них, дитина, отримає fork () return 0. Інший, батько, буде fork () повернути PID (ідентифікатор процесу) дитини.

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

$ telnetd &

Що стосується причини створення нових процесів, саме так ваша операційна система може робити багато речей одночасно. Ось чому ви можете запустити програму і, поки вона працює, перейти в інше вікно і зробити щось інше.


@varDumper Хороший улов!
Daniel C. Sobral

9

fork () використовується для створення дочірнього процесу. Коли викликається функція fork (), буде створено новий процес, і виклик функції fork () поверне інше значення для дочірнього та батьківського елементів.

Якщо значення, яке повертається, дорівнює 0, ви знаєте, що ви є дочірнім процесом, а якщо значення, що повертається, є числом (яке є ідентифікатором дочірнього процесу), ви знаєте, що ви батьки. (а якщо це від’ємне число, вилка не вдалася, і дочірній процес не створювався)

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


1
Якщо значення повернення не дорівнює -1, у цьому випадку fork () не вдався.
Джонатан Леффлер,

8

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

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

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


6

fork () створить новий дочірній процес, ідентичний батьківському. Отже, все, що ви запускаєте в коді після цього, буде виконуватися обома процесами - дуже корисно, якщо у вас є, наприклад, сервер, і ви хочете обробляти кілька запитів.


чому ти створюєш дочірню дитину, яка є ідентичною батьківській, яка користь?

1
Це просто як побудова армії проти одного солдата. Ви розгалужуєтеся, щоб ваша програма могла обробляти більше запитів одночасно, а не по одному.
cloudhead

fork () повертає 0 для дитини та pid дитини для батьків. Потім дитина може використовувати такий виклик, як exec (), щоб замінити його стан новою програмою. Так запускаються програми.
Тодд Гамблін,

Процеси дуже близькі до однакових, але є багато тонких відмінностей. Кричущими різницями є поточний ПІД та батьківський ПІД. Є проблеми, пов’язані із затриманими замками та утримуваними семафорами. На сторінці посібника fork () для POSIX перелічено 25 відмінностей між батьком і дитиною.
Джонатан Леффлер,

2
@kar: Як тільки у вас є два процеси, вони можуть продовжуватися окремо від них, і один із них може повністю замінити себе (exex ()) іншою програмою.
Vatine

4

Ймовірно, вам не потрібно використовувати fork у повсякденному програмуванні, якщо ви пишете програми.

Навіть якщо ви хочете, щоб ваша програма запустила іншу програму, щоб виконати якесь завдання, існують інші простіші інтерфейси, які використовують форк за кадром, наприклад, "система" на C та perl.

Наприклад, якщо ви хотіли, щоб ваш додаток запустив іншу програму, таку як bc, щоб зробити для вас якийсь розрахунок, ви можете використовувати "систему" для її запуску. Система робить "форк", щоб створити новий процес, а потім "exec", щоб перетворити цей процес на bc. Після завершення bc система повертає управління вашою програмою.

Ви також можете запускати інші програми асинхронно, але я не пам’ятаю, як.

Якщо ви пишете сервери, оболонки, віруси чи операційні системи, ви, швидше за все, захочете використовувати fork.


Дякую за system(). Я читав про те, fork()що хочу, щоб мій код C запускав сценарій python.
Bean Taxi

4

Форк створює нові процеси. Без форка у вас була б система unix, яка могла б запускати лише init.


4

Форк системного виклику () використовується для створення процесів. Він не бере аргументів і повертає ідентифікатор процесу. Призначення 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 може бути таким, який присвоєний батькові або дочірній організації. Через те, що ці процеси виконуються одночасно, їх вихідні лінії змішуються досить непередбачувано. Більше того, порядок цих рядків визначається планувальником процесора. Отже, якщо ви знову запустите цю програму, ви можете отримати зовсім інший результат.


3
Замість копіювання тексту ви могли б прокоментувати посилання: csl.mtu.edu/cs4411.ck/www/NOTES/process/fork/create.html
chaitanya lakkundi

3

Багатопроцесорна обробка є основною для обчислювальної техніки. Наприклад, ваш IE або Firefox можуть створити процес завантаження файлу для вас, поки ви все ще переглядаєте Інтернет. Або, поки ви роздруковуєте документ у текстовому процесорі, ви все ще можете переглядати різні сторінки та все одно редагувати разом із ним.


3

Форк () використовується для створення нових процесів, як писало кожне тіло.

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

#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;
}

ВИХІД

  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

2

Спочатку потрібно зрозуміти, що таке системний виклик fork (). Дозволь пояснити

  1. fork () Системний виклик створює точний дублікат батьківського процесу, робить дублікат батьківського стеку, купи, ініціалізованих даних, неініціалізованих даних та надає код у режимі лише для читання батьківському процесу.

  2. Системний виклик Fork копіює пам’ять на основі копіювання на запис, означає, що дитина робить сторінку віртуальної пам’яті, коли є необхідність копіювання.

Тепер Призначення fork ():

  1. Fork () можна використовувати там, де відбувається поділ роботи, як сервер повинен обробляти кілька клієнтів, тому батько повинен регулярно приймати з'єднання, тому сервер виконує форк для кожного клієнта для виконання читання-запису.

1

fork()використовується для породження дочірнього процесу. Зазвичай він використовується в подібних ситуаціях, як різьбонарізка, але є відмінності. На відміну від потоків, fork()створює цілі окремі процеси, а це означає, що дитина та батько, хоча вони є прямими копіями один одного в пункті, що fork()викликається, повністю відокремлені, і не можуть отримати доступ до простору пам'яті іншого (не переходячи до звичайних проблем Ви хочете отримати доступ до пам'яті іншої програми).

fork()як і раніше використовується деякими серверними програмами, переважно тими, що працюють як root на машині * NIX, що скидає дозволи перед обробкою запитів користувачів. Є ще деякі варіанти використання, але в основному люди зараз перейшли до багатопоточності.


2
Я не розумію сприйняття того, що "більшість людей" перейшли до багатопоточності. Процеси тут залишаються, а також нитки. Ніхто з них не "перейшов". При паралельному програмуванні найбільшими та найбільш одночасними кодами є багатопроцесорні програми з розподіленою пам'яттю (наприклад, MapReduce та MPI). Тим не менш, більшість людей обирають OpenMP або якусь парадигму спільної пам'яті для багатоядерної машини, і графічні процесори використовують потоки в наші дні, але є багато чого іншого. Однак я впевнений, що більше кодерів на цьому веб-сайті стикаються з паралельністю процесів на стороні сервера, ніж будь-що багатопоточне.
Тодд Гамблін,

1

Обґрунтування причини fork () проти простої функції exec () для ініціювання нового процесу пояснюється у відповіді на подібне запитання про біржу стеків unix .

По суті, оскільки форк копіює поточний процес, всі різні можливі параметри процесу встановлюються за замовчуванням, тому програміст не має їх постачати.

Навпаки, в операційній системі Windows програмістам доводиться використовувати функцію CreateProcess, яка набагато складніша і вимагає заповнення різноманітної структури для визначення параметрів нового процесу.

Отже, підсумовуючи, причиною форкінгу (проти виконання) є простота створення нових процесів.


0

Системний виклик Fork () використовується для створення дочірнього процесу. Це точна копія батьківського процесу. Форка копіює розділ стеку, розділ купи, розділ даних, змінну середовища, аргументи командного рядка від батьківського.

зверніться: http://man7.org/linux/man-pages/man2/fork.2.html


0

Функція fork () використовується для створення нового процесу шляхом дублювання існуючого процесу, з якого він викликається. Існуючий процес, з якого викликається ця функція, стає батьківським, а новостворений - дочірнім. Як уже зазначалося, дитина є копією батьків, але є деякі винятки.

  • У дитини є унікальний PID, як і будь-який інший процес, що працює в операційній системі.

  • У дитини є батьківський ідентифікатор процесу, який збігається з PID
    процесу, який його створив.

  • У дочірньому процесі використання ресурсів та лічильники часу процесора скидаються до нуля.

  • Набір сигналів, що очікують на розгляд, у дитини порожній.

  • Дочка не успадковує таймерів від батьків

Приклад:

    #include <unistd.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <stdio.h>
    #include <sys/wait.h>
    #include <stdlib.h>

    int var_glb; /* A global variable*/

int main(void)
{
    pid_t childPID;
    int var_lcl = 0;

    childPID = fork();

    if(childPID >= 0) // fork was successful
    {
        if(childPID == 0) // child process
        {
            var_lcl++;
            var_glb++;
            printf("\n Child Process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
        }
        else //Parent process
        {
            var_lcl = 10;
            var_glb = 20;
            printf("\n Parent process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
        }
    }
    else // fork failed
    {
        printf("\n Fork failed, quitting!!!!!!\n");
        return 1;
    }

    return 0;
}

Тепер, коли вищезгаданий код скомпільовано та запущено:

$ ./fork

Parent process :: var_lcl = [10], var_glb[20]

Child Process :: var_lcl = [1], var_glb[1]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.