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

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

Процес завершує роботу при виконанні системного виклику exit.

Процес може сам завершити свою роботу, відповідно до алгоритму, або може бути припинений ядром.

При завершенні процесу послідовно виконуються наступні дії:

вимикаються усі сигнали.

У викликала процесі закриваються всі дескриптори відкритих файлів.

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

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

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

Якщо ідентифікатор процесу, термінальна лінія і ідентифікатор групи процесів у завершується процесу збігаються, то всім процесам з тим же ідентифікатором групи процесів посилається сигнал SIGHUP. Тим самим, завершуються і всі породжені в пріоритетному режимі процеси.

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

6. Системні виклики wait, waitpid

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

waitpid - очікує зміни стану дочірнього процесу

# include <sys/wait.h>

pid_t waitpid (pid_t pid, int * statusp, int options)

pid-ідентифікатор процесу або групи процесів

statusp - покажчик на статус або NULL

options - прапори;

У разі успіху повертає ідентифікатор процесу або 0, у разі помилки -1 (код помилки у змінній errno)

Аргумент pid може приймати такі

  • значення:>0 Очікувати зміна стану дочірнього процесу з вказаним ідентифікатором.

  • -1 Очікувати зміна стану будь-якого дочірнього процесу.

  • 0 Очікувати зміна стану будь-якого дочірнього процесу, що належить до тієї ж групи процесів, що і викликає.

  • <-1 Очікувати зміна стану будь-якого дочірнього процесу, що належить до групи процесів з ідентифікатором-pid.

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

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

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

Системний виклик waitpid може повернути стан змінив його дочірнього процесу тільки один раз (такий дочірній процес називається очікуваний). Або іншими тільки один раз словами: очікуваний нащадок перестає бути очікуваним, якщо звіт про зміну його стану вже отримано. Це означає наступне: якщо в одній точці програми було отримано стан нащадка і раптом виявилося, що це не той нащадок, якого очікували, то немає ніякого способу повернути

Аргумент options може містити один або більше прапорів, об'єднаних опера цією АБО:

WEXITED Повідомляти про завершення нащадка (завжди мається на увазі в waitpid, який нс має такого прапора).

WSTOPPED Повідомляти про припинення нащадка (подібно waitpid з прапором WUNTRACED).

WCONTINUED Повідомляти про відновлення роботи нащадком (аналогічний прапор передбачений і в waitpid).

WNOHANG Не очікувати зміни стану нащадка. Якщо воно ще не змінилося - повертати значення 0 (аналогічний прапор передбачений і в waitpid).

WNOWAiT Залишити нащадка очікуваним. Таким чином, відстежити зміну стану нащадка можна буде декількома викликами waitpid.

Приклад

1. Очікувати завершення нащадка pid і отримати код завершення.

Ec_neg1 (waitpid (pid, & status, 0))

2. Чекати завершення будь-якого з нащадків, без отримання коду завершення. * /

Ec_neg1 (pid = waitpid (-1, NULL, 0))

3. Отримати від будь-якого з нащадків по груповому ідентифікатору pgid повідомлення про завершення або про припинення і отримати код стану. Не чекати, якщо нащадок ще не змінив стан. * /

Ec_neg1 (pid = waitpid (-pgid, & status, WNOHANG | WUNTRACED))

Другий представник групи системних викликів wait представляє собою спроще щенний варіант waitpid і відповідає виклику останнього з аргументом pid, рав ним -1, і з аргументом options рівним нулю:

wait - очікує завершення дочірнього процесу

# include <sys/wait.h>

pid_t wait (int * statusp,

statusp - покажчик на статус або NULL;

Повертає ідентифікатор процесу або -1 у випадку помилки (код помилки-у змінній errno).

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

Припустимо, що процес має двох нащадків і заздалегідь не відомо, який із них завершить роботу першим. Якщо нам потрібно тільки дочекатися завершення обох, можна спочатку почекати завершення будь-якого з нащадків, а потім викликати waitpid для очікування завершення іншого. Або батьківський процес може виконувати будь-яку роботу і періодично викликати waitpid з прапором WNOHANG для кожного з нащадків. Але, як ми вже говорили, ніколи не використовуйте waitpid c аргументом pid, рівним -1, якщо додатком не гарантується відсутність інших нащадків. Взагалі, у великих і складних проектах, де над окремими частинами працюють цілі групи розробників (наприклад, бібліотеки для роботи із зображеннями або бібліотеки для взаємодії з базами даних) таких гарантій дати ніхто не може. Було б просто чудово, якби існувала така різно видність системного виклику wait, якому можна було б передати цілий масив ідентифікаторів, але, на жаль, такого виклику немає.

У розглянутому прикладі після породження процесу - нащадка, батьківський процес видає виводить на термінал ідентифікатор породженого процесу, затримується на 5 секунд і викликає функцію для опитування стану процесу - нащадка. Породжений процес виводить повідомлення, що містить значення змінної x. Варто звернути увагу на те, що значення цієї змінної збігаються й у батька, і в нащадка.

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <errno.h>

int main()

{

int x, pid;

x=2;

printf("Single process, x=%d\n",x);

pid=fork();

if(pid == 0)

printf("New, x=%d\n",x); // Нащадок

else if(pid > 0)

{ // Батько

printf("Old, pid=%d, x=%d\n",pid,x);

sleep(5);

wait(pid);

}

else

{ perror("Fork error ");

return -1;

}

return 0;

}

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

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <errno.h>

main()

{

pid_t pid;

switch (pid = fork())

{

case -1:

printf(“Помилка виклику fork”);

break;

case 0: /* Нащадок викликає exec */

execl (“/bin/ls”, “ls”, “-l”, (char *)0);

printf(“Помилка виклику exec”);

break;

default: /* Батьківський процес викликає wait для припинення */

/* роботи до завершення дочірнього процесу. */

wait ( (int *)0);

printf (“ Програма ls завершилася\n”);

exit (0);

}

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]