Системні виклики 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);

}

}

Індівідуальні завдання

1. Призупинити на 1 з батьківський процес. У дочірньому процесі за допомогою системного виклику system() виконати стандартну команду ps, перенаправивши вивод у файл номер 1. Слідом за цим завершити дочірній процес. У батьківському процесі викликати ps і перенаправляти у файл номер 2. Звільнити осередок таблиці процесів породженого процесу.

2. Призупинити на 1 з батьківський процес. Виконати в дочірньому процесі один із системних викликів exec(), передавши йому як параметр стандартну програму ps. Аналогічно виконати виклик ps у батьківському процесі. Результати роботи команд ps в обох процесах перенаправляти в той самий файл.

3. Визначити в програмі глобальну змінну var зі значенням, рівним 1. Перевизначити стандартний висновок і батьківського, і дочірнього процесів у той самий файл. До виконання розгалуження збільшити на 1 змінну var, причому вивести її значення, як до збільшення, так і після. У батьківському процесі збільшити значення змінної на 3, а в дочірньому на 5. Вивести значення змінної до збільшення й після нього усередині кожного із процесів. Результат пояснити.

4. Призупинити на 1 з дочірній процес. У дочірньому процесі за допомогою системного виклику system() виконати стандартну команду ps, перенаправивши вивод у файл номер 1. Слідом за цим завершити дочірній процес. У батьківському процесі викликати ps і перенаправляти у файл номер 2. Звільнити осередок таблиці процесів породженого процесу.

5. Призупинити на 1 з дочірній процес. Виконати в дочірньому процесі один із системних викликів exec(), передавши йому як параметр стандартну програму ps. Аналогічно виконати виклик ps у батьківському процесі. Результати роботи команд ps в обох процесах перенаправляти в той самий файл. Звільнити осередок таблиці процесів породженого процесу.

6. Програма породжує через кожні 2 секунди 5 нових процесів. Кожний із цих процесів виконується заданий час і зупиняється, сповіщаючи про це батькові. Програма-Батько виводить на екран всі повідомлення про зміни в процесах.

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


Дата добавления: 2018-04-05; просмотров: 553; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!