- •Міністерство освіти і науки України
- •Жуковецька с.Л., Шестопалов с.В.
- •Анотація
- •Введення
- •Лабораторна робота №1 Технологія розробки програм у середовищі Linux
- •1. Команди роботи з командним інтерпретатором ос unix
- •2. Створення програми
- •3. Функції введення-виведення.
- •4. Приклад програми
- •5. Основні керуючі конструкції мови с
- •6. Індівідуальні завдання
- •Лабораторна робота № 2. Автоматизація пакетних завдань
- •1. Компіляція багатомодульною програми
- •2. Автоматизація пакетних завдань
- •4. Завдання
- •Лабораторна робота № 3. Взаємодія програми з середовищем виконання
- •1. Аргументи командного рядка
- •2. Змінні оточення
- •3. Індівідуальні завдання
- •Лабораторна робота №4. Контроль виконання програми
- •1. Коди завершення програми
- •2. Функції обробки значення errno
- •3. Функція atexit()
- •4. Макрос asssert()
- •5. Індівідуальні завдання
- •Лабораторна робота №5 Файлові api.
- •1 Основні поняття
- •2. Права доступу до файлів
- •3. Інтерфейси для файлового введення / виводу
- •4. Системні виклики роботи з файлами
- •5. Індівідуальні завдання
- •Варіанти завдань
- •Лабораторна робота №6 Робота з каталогами та посиланнями
- •Методичні вказівки
- •1. Створення та видалення каталогу
- •2. Читання каталогу
- •3. Зміна каталогу
- •4. Управління жорсткими посиланнями
- •5. Управління символічними посиланнями
- •6. Перейменування файлів і каталогів
- •7. Індівідуальні завдання
- •Лабораторна робота №7. Одержання і відображення метаданих файлу.
- •Методичні вказівки
- •1. Механізми управління файлами ос unix
- •2. Отримання даних індексного дескриптору
- •3. Додаткові функції маніпулювання даними індексних дескрипторів
- •4. Отримання даних про відкриті файли
- •5. Індівідуальні завдання
- •Лабораторна робота №8. Керування процесами
- •Методичні вказівки
- •1. Загальні поняття
- •2. Створення поцесу
- •4. Перезавантаження програми процесу
- •5. Завершення виконання процесу
- •6. Системні виклики wait, waitpid
- •7. Індівідуальні завдання
- •Лабораторна робота №9. Використання каналів
- •Методичні вказівки
- •1. Заганьні відомості
- •2. Використання каналів
- •3. Індівідуальні завдання
- •Варіанти завдань
- •Лабораторна робота №10 Використання повідомлень
- •Методичні вказівки
- •1. Загальні відомості
- •2. Використання повідомлень
- •3. Індівідуальні завдання
- •Література
- •Системне програмування Посібник до виконання лабораторних та самостійних робіт
- •65082, Одеса, вул. Дворянська, 1/3
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);
}
}