Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
СПОС_Конспект.doc
Скачиваний:
39
Добавлен:
02.05.2019
Размер:
1.13 Mб
Скачать

3. 5. 4Створення процесу

Реалізація fork() в Linux

В UNIX-сумісних системах процеси створює вже відомий нам системний виклик fork(). Розглянемо його реалізацію в Linux.

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

  2. Усі значення зі структури даних предка копіюють у структуру даних нащадка. Після цього поля, значення яких мають відрізнятися від вихідних, будуть змінені.

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

  1. Для процесу генерується ідентифікатор (pid), прицьому використовують спеціальний алгоритм, що гарантує унікальність.

  1. Для нащадка копіюють необхідні додаткові структури даних предка (таблицю дескрипторів файлів, відомості про поточний каталог, таблицю оброблювачів сигналів тощо).

  2. Формують адресний простір процесу.

  3. Структуру даних процесу поміщають у список і хеш-таблицю процесів системи.

  4. Процес переводять у стан готовності до виконання.

Використання fork() у прикладних програмах

Розглянемо приклад використання системного виклику fork() для створення процесу. Опис fork() відповідно до POSIX виглядає так:

#include<unistd.h>

pid_t fork(); //тип pid_t є цілим

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

Для нащадка fork() повертає нуль, а для предка – ідентифікатор (pid) створеного процесу-нащадка. Коли з якоїсь причини нащадок не був створений, fork() поверне –1. Тому звичайний код роботи і fork() має такий вигляд:

pid_t pid;

if((pid=fork())==-1)

{/* помилка, аварійне завершення */)}

if(pid==0)

{

// це нащадок

}

else

{

// це предок

printf(“Нащадок запущений з кодом %d\n”,pid);

}

Після створення процесу він може дістати доступу до ідентифікаційної інформації за допомогою системного виклику getpid(), який повертає ідентифікатор поточного процесу, і getppid(), що повертає ідентифікатор процесу-предка.

#include<unistd.h>

pid_t mypid, parent_pid;

mypid=getpid();

parent_pid=getppid();

3. 5. 5 Завершення процесу

Для завершення процесу в UNIX-системах використовують системний виклик _exit(). Розглянемо реалізацію цього системного виклику в Linux. Під час його виконання відбуваються такі дії.

  1. Стан процесу сиає TASK_ZOMBIE.

  2. Процес повідомляє предків і нащадків про те, що він завершився (за допомогою спеціальних сигналів).

  3. Звільняються ресурси, виділені під час виклику fork().

  4. Планувальника повідомляють про те, що контекст можна перемикати.

У прикладних програмах для завершення процесу можна використати безпосередньо системний виклик _exit() або його пакувальник – бібліотечну функцію exit(). Ця функція закриває всі потоки процесу, коректно вивільняє всі ресурси і викликає _exit() для фактичного його завершення:

#include<unistd.h>

void _exit(int status); //status задає код повернення

#include<stdlib.h>

void exit(int status); //status задає код повернення

exit(128);

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