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

3 Керування процесами ос unix

3.1 Процеси ос unix

В ОС UNIX щораз, коли Ви виконуєте команду, запускається процес, що ідентифікується та відслідковується операційною системою. Характерною рисою ОС UNIX є те, що одні процеси можуть породжуватися іншими. Інформацію про процеси можна одержати за допомогою команди командного інтерпретатора shell ps -f. На екрані з'явиться приблизно наступне:

UID PID PPID C STIME TTY TIME COMMAND

userow 94 1 0 10:15:56 tty6 0:02 -sh

userow 116 94 0 10:16:25 tty6 0:00 -sh

userow 125 116 0 10:16:27 tty6 9:38 /dss/rk/rk.20.02

-hrk=/dss/rk/d.hrk -nocore -in=stdin -out=stdout -display=D211

userow 285 125 1 13:11:10 tty6 0:00 sh

userow 290 285 10 13:11:58 tty6 0:00 ps -f

Таким чином, у користувача userow, що проробило описані вище дії, є 5 активних процесів. Цікаво простежити перемикання в колонках ідентифікатор процесу (PID) і ідентифікатор батьківського процесу (PPID). Shell, запущений при вході користувача userow у систему, має ідентифікатор процесу 94; його предком є процес ініціалізації (його ідентифікатор дорівнює 1). Процес із ідентифікатором 116 породжений для виконання shell-процедури rk; він є предком процесу з ідентифікатором 125 і т.д.

Точно так само нові процеси породжуються при виконанні програм. Уведення команди запуску програми означає запит до shell'у на породження ще одного процесу, побудованого з виконуваного файлу з усіма функціями, поміщеними в нього редактором зв'язків.

3.2 Сисвизови створення процесів

Для створення процесів є три основних системних виклики:

fork(2) - створити копію даного процесу.

system(3S) - запит до shell'у на виконання команди.

exec(2) - завершити один процес і приступитися до виконання іншого.

3.2.1 fork(2)

Новий процес створюється системним викликом fork(). Не створюються цим викликом в UNIX тільки три процеси 0, 1 і 2, вони створюються в момент ініціалізації системи. Процес 0 - батько процесів 1 і 2; процес 1 - батько всіх процесів по ініціалізації діалогу (LOGIN) і таким чином батько взагалі всіх процесів; процес 2 - свопер.

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

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

  1. перевіряється чи досить простору в пам'яті для підкачування нового процесу,

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

  3. після створення таблиці входів newproc() створює підкачується копию, що, процесу-батька,

  4. далі ініціалізуються робочі параметри процесу-дитини у відведеній йому пам'яті.

У новому вході таблиці процесів ідентифікатор процесу-дитини встановлюється в p_pid. Ідентифікатор процесу-батька встановлюється в p_ppid. Ідентифікатор процесу - це число від 0 до MAXPID = 30000. Відразу ж підкреслимо, що ідентифікатор використовується тільки в межпроцессных взаємодіях. Наприкінці досліджується таблиця, щоб відзначити деякий певний процес за адресою або процес, що йде в цей момент, і вказати його значення в u_procp.

Звідси можна бачити, що newproc() є внутрішньою функцією fork() і робить роботи при дійсному створенні нового процесу:

  • одержує унікальний ідентифікатор процесу й знаходить вільний вхід у таблицю процесів,

  • заповнює вхід у таблицю процесу-дитини, копіюючи інформацію від процесу-батька й инициализируя потрібні поля,

  • приєднує пам'ять на пристроях покачки для свопингового зображення процесу-дитини,

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

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

Єдина істотна відмінність між цими двома процесами полягає в тім, що породжений процес має свій унікальний ідентифікатор. У випадку успішного завершення fork() повертає породженому процесу 0, а батьківському процесу - ідентифікатор породженого процесу. Ідея одержання двох однакових процесів може здатися трохи дивної, однак:

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

  • Породжений процес може перетворитися в зовсім інший процес за допомогою виклику exec().

  • Батьківський процес може за допомогою виклику wait(2) чекати завершення породженого процесу.

Таблиця 3

Значення осередків у входах таблиці нового процесу після завершення ініціалізації

Входи таблиці процесів

Значення для

батька Значення для дитини

Статус процесу

Час роботи

Прапор статусу пам'яті

Ідентифікатор користувача

Груповий ідентифікатор

Значення "nice"

Ідентифікатор процесу

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

батька

Час у пам'яті

Пріоритет

p_stat

p_cpu

p_flag

p_uid

p_grp

p_nice

p_ppid

p_ppid

p_time

p_pri

SRUN

p_cpu

SLOAD

p_uid

p_grp

p_nice

новий унікальний ідентифікатор

p_pid

0 PUSER+p_nice-NZERO

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

З таблиці можна помітити, що процес-дитина автоматично успадковує той же ідентифікатор користувача, що був у батька, те ж значення p_nice, але не використовує p_cpu для обчислення пріоритету.

3.2.2 Еxec(2)

Системний виклик exec(), що заміщає частини тексту й даних у свопинговом зображенні відповідними значеннями файлу, що запускається на виконання. Точні кроки, що випливають за викликом exec(), такі:

  • ім'я нового виконуваного файлу передається для того,щоб установити відповідний INODE. Це завдання виконується через системний виклик namei(). Потім виконується проверкатого, що запуск, що зажадав, цього файлу користувач має на це право. Перевіряється розмір програми, щоб переконатися в можливості її завантаження в цей момент;

  • аргументи нової програми збираються й зберігаються на свопинговом пристрої. Перепідкачується ОЗУ для нової програми; зчитується й завантажується у відповідне місце пам'яті нового процесу сегмент тексту нової виконуваної програми, що не є тривіальним завданням, тому що, наприклад, у моделях з поділюваним текстом він може вже виявитися завантаженим і тоді просто необхідно відзначити додатковий нестаток у ньому;

  • потім завантажується сегмент даних з нового виконуваного файлу. Аркуш аргументів копіюється на свопинговое пристрій. Нарешті всі сигнали приймають "неявні" значення й переустановлюються тегистры.

Тепер може початися виконання нової програми.

exec() - це найменування цілого сімейства функцій:

execv(), execl(), execle(), execve(), execlp() і execvp().

Всі вони перетворюють процес, що викликав, в іншій. Відмінності між функціями полягають у способі подання аргументів. Наприклад, функція execl() може бути викликана в такий спосіб:

execl ("/bin/prog", "prog", arg1, arg2, (char*) 0);

Аргументи функції execl() мають наступний сенс:

/bin/prog Маршрутне ім'я нового виконуваного файлу.

prog Ім'я, що новий процес одержить як argv[0].

arg1, ... Покажчики на ланцюжки символів, що містять

аргументи програми prog.

(char *) 0 Порожнє посилання, що відзначає кінець списку

аргументів.

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

Щоб перекласти наведені міркування на мову C, можна включити в програму оператори, аналогічні наступної:

#include <errno.h>

int ch_stat, ch_pid, status;

char *progarg1;

char *progarg2;

void exit ();

extern int errno;

if ((ch_pid = fork ()) < 0)

{

/* Виклик fork завершився невдачею ... перевірка змінної errno */

}

else if (ch_pid == 0) {

/* Породжений процес */

(void) execl ("/bin/prog","prog",arg1,arg2,(char*)0);

exit (2);

/* execl() завершився невдачею */

}

else {

/* Батьківський процес */

while ((status = wait (&ch_stat)) != ch_pid) {

if (status < 0 && errno == ECHILD)

break;

errno = 0;

}

}

Оскільки при виклику exec() ідентифікатор породженого процесу успадковується новим процесом, батьківський процес знає цей ідентифікатор. Дія наведеного фрагмента програми зводиться до зупинки виконання однієї програми й запуску іншої, з наступним поверненням у ту крапку першої програми, де було зупинено її виконання. У точності т же саме відбувається при виклику функції system(3S). Дійсно, реалізація system() така, що при її виконанні викликаються fork(), exec() і wait().

Варто мати на увазі, що в даний приклад включена мінімальна кількість перевірок різних помилок. Так, необхідно проявляти обережність при спільній роботі з файлами. Крім іменованих файлів, новий процес, породжений викликом fork() або exec(), успадковує три відкритих файли: stdin, stdout і stderr.

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

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