
- •Анотація
- •Лабораторна робота № 1 Перенаправлення уведення-виведення в oc unix. Конвеєри
- •Лабораторна робота № 2 Командна мова ос unix shell
- •Лабораторна робота №3 Процеси в unix
- •Теоретичні відомості:
- •Команди управління процесами
- •Лабораторна робота № 4 Асинхронне видалення дочірніх процесів за допомогою сигналу sigchld. Потоки, що повертають значення
- •Лабораторна робота № 5 Розробка багатофайлових програм Використання утиліти make
- •Лабораторна робота № 6 Взаємодія процесів. Розподілювана пам’ять. Семафори
- •Лабораторна робота № 7 Взаємодія процесів. Програмні канали
- •Лабораторна робота № 8 Розробка графічного інтерфейсу за допомогою бібліотеки Qt3. Створення в kDevelop базової структури додатку
- •Література
Лабораторна робота №3 Процеси в unix
Проведення заняття розраховано на 4 години
Мета роботи: Ознайомлення з процесами OC UNIX і з сигналами як засобом комунікації процесів, придбання знань і навичок навичок написання програм роботи з взаємодіючими процесами.
Теоретичні відомості:
Процес в UNIX (як і в UNIX) – це програма, яка виконується в окремому віртуальному адресному просторі. Коли користувач реєструється в системі, під нього автоматично створюється процес, в якому виконується оболонка (SHELL). У UNIX підтримується класична схема мультипрограмування. При цьому UNIX підтримує паралельне (або квазіпаралельне за наявності тільки одного процесора) виконання процесів користувача. Кожен процес виконується у власному віртуальному адресному просторі, тобто процеси захищені один від одного і крах одного процесу ніяк не вплине на процеси, що виконуються, і на всю систему в цілому.
Один процес не може прочитати що-небудь з пам'яті іншого процесу (або записати в неї) без «дозволу» на те іншого процесу. Санкціонована взаємодія процесів допускається системою.
Ядро надає системні виклики для створення нових процесів і для управління породженими процесами. Будь-яка програма може почати виконуватися, тільки якщо другий процес її запустить або відбудеться якесь переривання (наприклад, від зовнішнього пристрою).
У зв'язку з розвитком SMP (Symmetric Multiprocessor Architectures) в ядро UNIX був упроваджений механізм ниток або потоків управління (threads). Іншими словами, нитка – це процес, який виконується у віртуальній пам'яті, який використовується разом з іншими нитками одного і того ж «ваговитого» процесу, який володіє окремою віртуальною пам'яттю. Такий «ваговитий» процес може мати декілька «легковагих» процесів. Простіше кажучи, нитки – це паралельно виконувані частини однієї програми, що реалізовуються UNIX у вигляді декількох процесів з спеціальним прапором. Причому всі ці процеси виконуються в одному віртуальному адресному просторі (у одному середовищі) процесу-батька.
Потоки (або нитки) дозволяють вирішувати в рамках однієї програми одночасно декілька задач.
Операційна система надає програмі певний інтервал процесорного часу. Коли програма переходить в режим очікування якої-небудь події (наприклад, сигналу) або звільняє процесор, операційна система передає управління іншій програмі. Розподіляючи час центрального процесора, операційна система розподіляє його не між програмами, а між потоками. Виходячи зі всього цього, потоки – це набори команд, що мають можливість одержувати час процесора. Час процесора виділяється квантами. Квант – це мінімальний час, впродовж якого потік (нитка) може використовувати процесор.
Коли ви вводите команду, інтерпретатор проводить пошук вказаної програми в каталогах, які перераховані при визначенні змінної оточення PATH. При цьому буде виконана перша знайдена програма з вказаним ім'ям.
Якщо інтерпретатору (SHELL) зустрічається команда, відповідна виконуваному файлу, інтерпретатор виконує її, починаючи з точки входу (entry point). Для С-программ entry point - це функція main. Точка входу для кожного середовища розробки різна. Запущена програма також також може створити процес, тобто запустити якусь програму і її виконання також також почнеться з функції main.
Потім за допомогою системного виклику fork() створюється адресний простір – готується «місце» для нового процесу, а потім за допомогою виклику exec() у цей адресний простір завантажується програма. Таким чином, кожен новий процес виконується в своєму індивідуальному середовищі.
Для створення процесів використовується системний виклик fork(). Виклик fork() створює новий адресний простір, який повністю ідентичен адресному простору основного процесу. Іншими словами, fork() создає новий процес, після виконання цього системного виклику ви одержуєте два абсолютно однакових процеси – основний і породжений. Функція fork() повертає 0 в породженому процесі і PID (Process ID – ідентифікатор породженого процесу) - в основному. PID – це ціле число.
Тепер, коли процес створений, можна запустити в ньому програму за допомогою виклику exec(). Параметрами функції exec() є ім'я виконуваного файлу і, якщо потрібно, параметри, які будуть передані цій програмі. У адресний простір, породжений за допомогою fork() процесу буде завантажена програма і її виконання почнеться з точки входу (адреса функції main).
Як приклад розглянемо цей фрагмент програми:
if(fork()==0)
wait();
else
exec(«ls»,»l»,0); /*породжений процес*/
Тепер розглянемо детальніше. Що ж робиться при виконанні виклику fork():
Виділяється пам'ять для описувача нового процесу в таблиці процесів.
Призначається ідентифікатор процесу PID.
Створюється логічна копія процесу, який виконує fork() – повне копіювання вмісту віртуальної пам'яті батьківського процесу, копіювання складових ядерного статичного і динамічного контекстів процесу-предка.
Збільшуються лічильники відкриття файлів (породжений процес успадковує всі відкриті файли батьківського процесу).
Повертається PID в точку поверненняз системного виклику в батьківському процесі і 0 - в процесі-нащадку. У разі неможливості створити процес повертається негативне значення PID.
Загальна схема управління процесами.
Кожен процес може породжувати повністю ідентичний процес за допомогою fork(). Батьківський процес може чекати закінчення виконання всіх своїх процесів-нащадків за допомогою системного виклику wait().
У будь-який момент часу процес може змінити вміст свого образу пам'яті, використовуючи один з різновидів виклику exec(). Кожен процес реагує на сигнали і, природно, може встановити власну реакцію на сигнали, вироблювані операційною системою. Пріоритет процесу може бути змінений за допомогою системного виклику nice().
Сигнал – це спосіб інформування процесу ядром про випадок випадок якоїсь події. Якщо виникає декілька однотипних подій, процесу буде поданий тільки один сигнал. Сигнал означає, що відбулася подія, але ядро не повідомляє, скільки таких подій відбулося.
Приклади сигналів:
Закінчення породженого процесу (наприклад, із-за системного виклику exit()).
Виникнення виняткової ситуації.
Сигнали, що надходять надходять від користувача при натисненні певних клавіш.
Встановити реакцію на надходження сигналу можна за допомогою системного виклику signal():
func=signal(snum,function);
де:
snum –номер сигналу;
function – адреса функції, яка повинна бути виконана під час вступу вказаного сигналу.
Повертане значення – адреса функції, яка реагуватиме на надходження сигналу. Замість function можна вказати нуль або одиницю.
Якщо був вказаний нуль, то під час вступу сигналу snum виконання процесу буде перервано аналогічно виклику exit(). Якщо вказати одиницю, даний сигнал буде проігнорований, але це можливо не для всіх процесів.
За допомогою системного виклику kill() можна згенерувати сигнали і передати їх іншим процесам. Звичайно kill() використовується для того, щоб примусово завершити («вбити») процес.
kill(pid,snum);
де:
pid – ідентифікатор процесу;
snum – номер сигналу, який буде переданий процесу.
Номери і назви всіх допустимих сигналів описані у файлі signal.h. Назви і номери сигналів можна одержати з довідника.
Для нормального завершення процесу використовується виклик:
exit(status);
де:
status – ціле число, повертане процесу-предку для інформування його про причини завершення процесу-нащадка.
Виклик exit() може задаватися в будь-якій точці програми, але може бути і неявним, наприклад, при виході з функції main() (при програмуванні на С) оператор return 0; буде сприйнятий як системний виклик exit(0).