Життєвий цикл процесу

Поняття процесу в UNIX істотно відрізняється від аналогічного поняття в інших популярних ОС. Якщо в MS-DOS, Windows і інших системах новий процес створюється тільки при запуску програми, то в UNIX створення процесу і запуск програми - це два абсолютно різних дії. При цьому процес в деякому роді «більш первинний», ніж програма. Якщо за стандартним визначенням (див. п. 4.2.1) процес є робота по виконанню програми, то в UNIX буде більш доречно сказати, що програма - це один з ресурсів, що використовуються процесом.

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

повертає значення 0. Перевірка повернутого значення - найпростіший спосіб для процесу визначити, «хто він такий» - батько або нащадок.

Типовий фрагмент програми на C може виглядати приблизно так:

 

pid = fork (); / / Створення нового процесу

if (pid == -1) / / Процес не створений

{Обробка помилки створення процесу}

else if (pid == 0) / / Це породжений процес

{Оператори, що виконуються процесом-нащадком}

else / / Це процес-батько

{Оператори, що виконуються процесом-батьком}

У принципі, обидва процесу можуть надалі виконувати команди з одного і того ж файлу програми. Частіше, однак, процес незабаром після створення починає виконувати іншу програму. Для цього використовується одна з функцій сімейства exec. Декілька функцій, імена яких починаються з exec, розрізняються деталями - в якому форматі передаються параметри командного рядка, чи варто використовувати пошук файлу програми по змінній PATH і т.п. Кожна з цих функцій запускає вказаний файл програми, однак при цьому не породжується новий процес, просто поточний процес змінює виконувану програму. При цьому повністю перебудовується заново контекст процесу.

Механізм створення нових процесів один і той же як для процесів користувача, так і для системних процесів. Єдиним винятком є ​​найперший процес, що має ідентифікатор 0. Він породжує Друга системна процес, який називається INIT і має ідентифікатор 1. Всі інші процеси в системі є нащадками INIT.

Для нормального завершення процесу використовується функція exit (status). Ціле число status означає код завершення, заданий програмістом, при цьому значення 0 означає успішне завершення, а ненульові значення - різного роду помилки та нестандартні випадки.

Процес-нащадок повністю незалежний від батька, і завершуються ці процеси незалежно один від одного. Тим не менше, процес-батько має можливість синхронізуватися з моментом завершення нащадка (простіше кажучи, почекати цього завершення). Для цього батько виконує виклик функції wait:

pid = wait (& status);

Ця блокуюча функція переводить викликає процес в очікування до моменту завершення будь-якого з нащадків, породжених цим процесів. Так працює, наприклад, інтерпретатор команд UNIX, який запускає команду, введену з консолі, і очікує її завершення. Функція wait повертає pid завершився нащадка, а в змінній status передає код завершення.

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

Можлива також ситуація, коли процес-батько завершується до того, як буде завершено його нащадок. Оскільки процес не повинен залишатися без батька, «сирота» буде «усиновлена» системним процесом INIT.

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