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

Урок № 85

(згідно навчальної робочої програми)

Тема: «Керування процесами в UNIX та LINUX»

Питання:

  1. Ідентифікаційна інформація та атрибути безпеки процесу.

  2. Образ процесу.

  3. Керуючий блок процесу.

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

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

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

Образ процесу

В UNIX-системах образ процесу містить такі компоненти:

  • керуючий блок процесу;

  • код програми, яку виконує процес;

  • стек процесу, де зберігаються тимчасові дані (параметри процедур, повернені значення, локальні змінні тощо);

  • глобальні дані, спільні для всього процесу.

Для кожного компонента образу процесу виділяють окрему ділянку пам’яті.

Ідентифікаційна інформація та атрибути безпеки процесу

Із кожним процесом у системі пов’язана ідентифікаційна інформація.

Ідентифікатор процесу (pid) є унікальним у межах усієї системи, і його вико­ристовують для доступу до цього процесу. Ідентифікатор процесу іnit завжди до­рівнює одиниці.

Ідентифікатор процесу-предка (ppid) задають під час його створення. Будь- який процес має мати доступ до цього ідентифікатора. Так в UNIX-системах обов’язково підтримується зв’язок «предок-нащадок». Якщо предок процесу Р завершує виконання, предком цього процесу автоматично стає іnit, тому ppid для Р дорівнюватиме одиниці.

Із процесом також пов’язаний набір атрибутів безпеки.

  • Реальні ідентифікатори користувача і групи процесу (uid, gid) відповідають користувачеві, що запустив програму, внаслідок чого в системі з’явився відпо­відний процес.

  • Ефективні ідентифікатори користувача і групи процесу (euid, egid) викори­стовують у спеціальному режимі виконання процесу — виконанні з правами власника.

Керуючий блок процесу

Керуючий блок процесу в Linux відображається структурою даних task_struct. До найважливіших полів цієї структури належать поля, що містять таку інформацію:

  • ідентифікаційні дані (зокрема pid— ідентифікатор процесу);

  • стан процесу (виконання, очікування тощо);

  • покажчики на структури предка і нащадків;

  • час створення процесу та загальний час виконання (так звані таймери процесу);

  • стан процесора (вміст регістрів і лічильник інструкцій);

  • атрибути безпеки процесу (uid, gid, euid, egid).

Зазначимо, що в ядрі Linux відсутня окрема структура даних для потоку, тому інформація про стан процесора міститься в керуючому блоці процесу.

Крім перелічених вище, task_struct має кілька полів спеціалізованого призна­чення, необхідних для різних підсистем Linux:

  • відомості для обробки сигналів;

  • інформація для планування процесів;

  • інформація про файли і каталоги, пов’язані із процесом;

  • структури даних для підсистеми керування пам’яттю.

Дані полів task_struct можуть спільно використовувати декілька процесів спе­ціалізованого призначення, у цьому випадку такі процеси фактично є потоками.

  • Керуючі блоки процесу зберігаються в ядрі у спеціальній структурі даних.

Тепер обмеження на максимальну кількість процесів перевіряється тільки все­редині реалізації функції fork() і залежить від обсягу доступної пам’яті. Реалізація керуючого блоку в Linux відрізняється від його традиційної реалі­зації в UNIX-системах. У більшості версій UNIX (SystemV, BSD) керуючий блок процесу складається з двох структур даних - структури процесу (ргос) і структу­ри користувача (и).

Структура процесу містить інформацію, необхідну протягом усього часу існу­вання процесу (зокрема, коли він вивантажений на диск). Це такі дані, як pid, ефективний uid, пріоритет, покажчик на структуру користувача. Структури про­цесу поєднуються в черзі процесів у системі. Структура користувача містить ін­формацію, необхідну тільки під час виконання процесу.

Створення процесу

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

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

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

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

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

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

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

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

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

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

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

# include<unistd.h>

pid_t fork(): // тип pid_t0 цілим

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

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

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

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

  1. Стан процесу стає TASK_ZOMBIE(зомбі, про цей стан див. нижче).

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

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

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

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

#іnclude<unistd.h>

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

#include<stdlib.h>

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

exit (128): // вихід з кодом повернення 128

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

Контрольні питання:

  1. Що включає в себе ідентифікаційна інформація та атрибути безпеки процесу?

  2. Що включає в себе керуючий блок процесу?

  3. Яким чином здійснюється створення процесу?

  4. Як використовується fork() у прикладних програмах?

  5. Яким чином здійснюється завершення процесу.

Література: Шеховцов В.А. Операційні системи. – К.: Видавнича група BHV, 2005. – 576 с.: іл., стор. 61-65.