Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Метвказівки для Астистовой1.doc
Скачиваний:
2
Добавлен:
08.11.2019
Размер:
288.26 Кб
Скачать
  1. Ієрархія процесів.

В деяких системах батьківський і синівський процеси залишаються пов’язаними між собою певним чином. Синівський процес також може створювати процеси, формуючи ієрархію процесів.

В UNIX процес, всі його діти і подальші нащадки створюють групу процесів. Сигнал з клавіатури досягає всіх членів групи, які взаємодіють з клавіатурою в даний момент (це звичайно всі активні процеси, які створені в поточному вікні). Кожний з процесів може перехватити сигнал, ігнорувати його чи виконати іншу дію по замовчуванню. Розглянемо приклад ініціалізації UNIX при запуску. При завантаженні присутній спеціальний процес init. Він зчитує файл з інформацією про кількість терміналів. Потім процес розгалуджується таким чином, що кожному терміналу відповідає один процес. Процеси чекають появи будь-якого користувача в системі. Якщо пароль вірний, процес входа в систему запускає оболонку для обробки команд користувача, які, в свою чергу, можуть запускати процеси. Таким чином, всі процеси в системі належать єдиному дереву, яке починається з процеса init.

Навпаки, в ОС WINDOWS не існує поняття ієрархії процесів, а всі процеси рівноправні. Єдине, що може бути чимось схожим на ієрархію процесів – створення процеса, в якому батьківський процес одержує спеціальний маркер (так званий дескриптор), який дозволяє контролювати синівський процес. Але маркер можна передати іншому процесу, порушуючи ієрархію. В UNIX це неможливо.

Існує 3 можливих стани процесів:

  1. Працюючий (використовує в даний момент процесор);

  2. Готовий до роботи ( тимчасово призупинений для виконання іншого процесу);

  3. Заблокований (не може бути запущений перш ніж не здійсниться деяка зовнішня подія).

З точки зору логіки перші 2 стани однакові. В обох випадках процес може бути запущений, тільки в 2-му випадку недоступний процесор. Третій стан запустити неможливо, незалежно від завантаженості процесора.

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

  1. Реалізація процесів.

Для реалізації процесів ОС має таблицю процесів (масив структур), з одним елементом для кожного процесу. Ці елементи називають блоками керування процесом. В блоці знаходиться інформація про стан процесу, лічильник команд, вказівник стеку, розподіл пам’яті, розподіл і використання ресурсів, а також вся інша інформація, яка повинна бути збережена при переключенні із стану готовності чи блокування в стан запуску. Які саме поля будуть в таблиці процесів, залежить від конкретної системи.

4. Системні виклики для роботи з процесами

Розглянемо системні виклики, що використовуються при роботі з процесами в ОС UNIX, які описані в бібліотеці <fcntl.h>. Системний виклик fork створює новий процес, копіюючи той, що викликає; виклик exit завершує виконання процесу; wait дає можливість батьківському процесові синхронізувати своє продовження із завершенням породженого процесу, а sleep призупиняє на певний час виконання процесу. Системний виклик exec дає процесу можливість запускати на виконання іншу програму. FORK Створення нового процесу: int fork (void); pid = fork (); в ході виконання функції ядро ​​виконує наступну послідовність дій: 1. 1. Відводить місце в таблиці процесів під новий процес. 2. 2. Присвоює породжуваному процесу унікальний код ідентифікації. 3. 3. Робить копію контексту батьківського процесу. Оскільки ті чи інші складові процесу, такі як область команд, можуть розділятися іншими процесами, ядро ​​може іноді замість копіювання області в нову фізичну ділянку пам'яті просто збільшити значення лічильника посилань на область. 4. 4. Збільшує значення лічильника числа файлів, пов'язаних з процесом, як у таблиці файлів, так і в таблиці індексів. 5. 5. Повертає батьківському процесові код ідентифікації породженого процесу, а породженому процесу повертає - 0. В результаті виконання функції fork контекст користувача і першого, й другого процесів збігаються у всьому, крім значення, що повертається змінній pid. Якщо процес не може бути породженим, функція повертає від’ємний код помилки. Процес, що викликає функцію fork, називається батьківським (процес-батько або процес-предок), створюваний процес називається породженим (процес-нащадок). Процес-нащадок завжди має більш високий пріоритет, ніж процес-предок, так як пріоритет процесу є найвищим у момент породження і зменшується по мірі знаходження у стані виконання. Нульовий процес, що виникає всередині ядра при завантаженні системи, є єдиним процесом, не створюваним за допомогою функції fork.   EXIT Завершення виконання процесу: void exit (int status); де status - значення, що повертається функцією батьківського процесу.Процеси можуть викликати функцію exit як в явному, так і в неявному вигляді (по закінченні виконання програми функція exit викликається автоматично з параметром 0). Також ядро ​​може викликати функцію exit за своєю ініціативою, якщо процес не прийняв посланий йому сигнал. В цьому випадку значення параметра status співпадає з номером сигналу. Виконання виклику exit призводить до «припинення існування» процесу, звільненню ресурсів та ліквідації контексту.   WAIT Очікування завершення виконання процесу-нащадка: int wait (int * stat); pid = wait (stat_addr); де pid - значення коду ідентифікації (PID) нащадка, що завершився, stat_addr - адреса змінної цілого типу, в яку буде поміщено значення, повернене функцією exit. За допомогою цієї функції процес синхронізує продовження свого виконання з моментом завершення нащадка. Ядро веде пошук нащадків процесу, які припинили існування, і в разі їх відсутності повертає помилку. Якщо нащадок, що припинив існування, виявлений, ядро ​​передає його код ідентифікації і значення, що повертається через параметр функції exit, процесу, що викликав функцію wait. Таким чином, через параметр функції exit (status) процес, що завершається, може передавати різні значення, які в закодованому вигляді містять інформацію про причину завершення процесу, однак на практиці цей параметр використовується за призначенням досить рідко. Якщо процес, що виконує функцію wait, має нащадків, які продовжують існування, він зупиняється до отримання очікуваного сигналу. Ядро не поновлює за своєю ініціативою процес, призупинений за допомогою функції wait: такий процес може поновитися тільки в разі отримання сигналу про «загибель нащадка».   SLEEP Призупинення роботи процесу на певний час: void sleep (unsigned seconds); де seconds - кількість секунд, на яку потрібно призупинити роботу процесу. Спочатку ядро ​​підвищує пріоритет роботи процесу так, щоб заблокувати всі переривання, які могли б (шляхом створення конкуренції) перешкодити роботі з чергами припинених процесів, і запам'ятовує старий пріоритет, щоб відновити його, коли виконання процесу буде відновлено. Процес отримує позначку «припиненого», адреса призупинення і пріоритет запам'ятовуються в таблиці процесів, а процес поміщається в хеш-чергу припинених процесів. У простому випадку (коли призупинення не допускає переривань) процес виконує перемикання контексту і благополучно «засинає». Коли призупинений процес «пробуджується», ядро ​​починає планувати його запуск: процес повертає збережений в алгоритмі sleep контекст, відновлює старий пріоритет роботи процесу (який був у нього до початку виконання алгоритму) і повертає керування ядру. Таким чином, не можна гарантувати, що після закінчення заданого часу призупинений процес відразу відновить свою роботу: він може бути вивантажений на час призупинення і тоді потрібна його підкачка в пам'ять; в цей час на виконанні може знаходитися процес з більш високим пріоритетом або процес, який не допускає переривань (наприклад, що знаходиться в критичному інтервалі) і т.д. Параметр seconds встановлює мінімальний інтервал, протягом якого процес буде припинений, а реальний час призупинення в будь-якому випадку буде трохи більшим, хоча б за рахунок часу, необхідного для перемикання процесів.   EXEC Запуск програми. Системний виклик exec здійснює кілька бібліотечних функцій - execl, execv, execle та ін Наведемо формат однієї з них: int execv (char * path, char * argv []); res = execv (path, argv); де path - ім'я виконуваного файлу, argv - покажчик на масив параметрів, які передаються викликаній програмі. Цей масив аналогічний параметру argv командного рядка функції main. Список argv повинен містити мінімум два параметри: перший - ім'я програми, що підлягає виконанню (відображається в argv [0] функції main нової програми), другий - NULL (завершальний список аргументів). Системний виклик exec дає можливість процесу запускати іншу програму, при цьому відповідний цій програмі виконуваний файл буде розташовуватися в просторі пам'яті процесу. Вміст користувача контексту після виклику функції стає недоступним, за винятком переданих функції параметрів, які переписуються ядром зі старого адресного простору в новий. Виклик exec повертає 0 при успішному завершенні і -1 при аварійному. В останньому випадку управління повертається в програму, що викликає. Як приклад використання цього виклику можна привести роботу командного інтерпретатора shell: при виконанні команди він спочатку породжує свою копію за допомогою виклику fork, а потім запускає відповідну зазначеній команді програму системним викликом exec.

Новий процес створюється системним викликом fork(). При цьому породжуваний процес - нащадок є точною копією процесу - батька. Вони розрізняються тим, що нащадок має відмінні від батьківського процесу ідентифікатори (PID і PPID). Оскільки породжений процес має однаковий з батьківським процесом програмний код, для відмінності в алгоритмах виконання можна використати код, повернений функцією fork(). Для батьківського процесу при нормальному завершенні повертається ідентифікатор породженого процесу, процес - нащадок отримує від fork() код повернення, рівний нулю. При невдалому завершенні повертається код помилки, рівний - 1 і встановлюється значення errno.

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