Fork (системний виклик)

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

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

Огляд

[ред. | ред. код]

Багатозадачна операційна система зазвичай повинна мати який-небудь механізм створення нового процесу — на основі вже запущеного, або повністю нового. У багатьох Unix-подібних операційних системах єдиним способом створення нового процесу і є системний виклик fork() (новіші системи можуть мати також posix_spawn). Для того, щоб процес розпочав виконання іншої програми, він спершу створює копію самого себе. Потім ця копія, яка називається "дочірній процес", виконує системний виклик exec для запуску в рамках себе іншої програми: він припиняє виконання своєї попередньої програми і починає виконувати іншу.

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

Така оптимізація має велике значення для загального випадку, коли fork використовується в поєднанні з викликом exec, для того, щоб почати виконання нової програми: зазвичай, дочірній процес виконує лише невеликий набір команд перед тим як він припиняє виконання своєї програми, на користь програми, яка буде запущена, і вона потребує дуже мало, якщо потребує, структур даних від свого батьківського процесу.

Коли процес викликає fork() його вважають батьківським процесом, і fork() повертає PID новоствореного дочірнього процесу. Всередині новоствореного процесу fork() повертає 0. У випадку невдачі повертається -1, а в змінну errno поміщається код помилки.[1] Після команди fork, обидва процеси не лише виконують одну і ту саму програму, але вони продовжують виконання так ніби обидві виконали системний виклик. Вони можуть перевіряти значення, які повертає виклик для визначення їхнього статусу, чи то дочірній процес, чи батьківський і діяти відповідно.

Системний виклик fork існував з першої версії операційної системи Unix,[2] що запозичений із ранньої системи GENIE.[3] Fork входить до стандарту POSIX.[1]

Комунікація

[ред. | ред. код]

Дочірній процес стартує із копією батьківських файлових дескрипторів.[1] Для спілкування між процесами, батьківський процес часто буде створювати конвеєр або декілька конвеєрів, а після породження процесів закриє кінці конвеєрів, які більше не потрібні.[4]

Використання у програмах

[ред. | ред. код]

Приклад на C

[ред. | ред. код]

Наступний варіант програми Hello World породжує за допомогою "fork" дочірній процес, який виводить на екран повідомлення і завершує виконання. Батьківський процес не виконує ніякої корисної роботи; він просто чекає на завершення дочірнього процесу.

#include <sys/types.h> /* pid_t          */
#include <sys/wait.h>  /* waitpid        */
#include <stdio.h>     /* printf, perror */
#include <stdlib.h>    /* exit           */
#include <unistd.h>    /* _exit, fork    */

int main(void)
{
    int st;
    pid_t pid = fork();
	
    switch (pid) {
      case -1:
        // fork() повертає -1, коли сталася помилка.
        perror("fork failed");
        exit(EXIT_FAILURE);
        break;
			
      case 0:
        // Коли fork() повертає 0, це означає, що був створений дочірній процес.
        printf("Hello from the child process!\n");
        _exit(EXIT_SUCCESS);  // exit() недоступна тут, тому треба використовувати _exit
        break;
			
      default:
        // Коли fork() повертає додатне число, ми у батьківському процесі
        (void) waitpid(pid, &st, 0); // а значення, яке повертається це PID (ідентифікатор процесу) новоствореного дочірнього процесу.
        break;
    }
	
    return EXIT_SUCCESS;
}

Примітки

[ред. | ред. код]
  1. а б в fork(2): create child process - Linux man page. Архів оригіналу за 14 березня 2016. Процитовано 13 березня 2016.
  2. Кен Томпсон; Денніс Рітчі (3 листопада 1971). SYS FORK (II) (PDF). UNIX Programmer's Manual (англ.). Bell Labs. Архів оригіналу (PDF) за 3 лютий 2015. Процитовано 4 березень 2015.
  3. Ritchie, Dennis M.; Thompson, Ken (July 1978). The UNIX Time-Sharing System (PDF). Bell System Technical Journal. AT&T. 57 (6): 1905—1929. doi:10.1002/j.1538-7305.1978.tb02136.x. Архів оригіналу (PDF) за 10 грудень 2005. Процитовано 22 квітня 2014.
  4. pipe(2) – сторінка довідки man про системні виклики GNU/Linux (англ.)