Лабораторна робота №8. Керування процесами



Мета:Оволодіння технологією використання API для роботи із процесами ОС UNIX. Застосування отриманих знань для написання програм керування процесами.

 

Завдання для самостійної підготовки:

Вивчити:

− організацію процесів ОС UNIX;

− основні атрибути процесів ОС UNIX;

− засоби міжпроцесної взаємодії;

− системні виклики керування процесами ОС UNIX;

 

Методичні вказівки

Загальні поняття

 

Процес - це фундаментальне поняття ОС Unix. Під процесом розуміється програма під час її виконання. Процес є єдиною активною сутністю в системі Unix. Процес - унікальним образом идентифицируемая програма, що має потребу в одержанні доступу до ресурсів комп'ютера.

Процес в UNIX має ряд атрибутів, що дозволяють операційній системі управляти його роботою. Основні атрибути:

1. Ідентифікатор процесу (PID).

2. Ідентифікатор батьківського процесу (PPID).

3. Поточний пріоритет PRI.

4. Виправлення пріоритету (NICE) - відносний пріоритет.

5. Термінальна лінія (TTY).

6. Реальний (UID) і ефективний (EUID) ідентифікатори користувача.

7. Реальний (GID) і ефективний (EGID) ідентифікатори групи.

 

Unix надає ряд системних викликів, за допомогою яких можна довідатися реальні й діючі ідентифікатори користувача й групи:

 

getuid - повертає реальний ідентифікатор користувача

#include <unistd.h>

uid_t getuid(void);

 

Повертає ідентифікатор користувача (коди помилок не передбачені)

#include <unistd.h>

uid_t geteuid(void);

 

Повертає ідентифікатор користувача (коди помилок не передбачені

 

getgid - повертає реальний ідентифікатор групи

#include <unistd.h>

gid_t getgid(void);

 

Повертає ідентифікатор групи (коди помилок не передбачені)

 

getegid - повертає діючий ідентифікатор групи

#include <unistd.h>

gid_t getegid(void);

Повертає ідентифікатор групи (коди помилок не передбачені)

 

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

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

 

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

 

#include <sys/types.h>

#include <unistd.h>

pid_t fork (void);

 

Виклик fork не має аргументів і повертає

−   у батьківський процес >=0 ідентифікатор породженого процесу

−   у породжений процес =0

−  -1 у випадку помилки, новий процес при цьому не створюється

Системний виклик fork завершується невдачею й новим процесом не породжується, якщо:

− створити процес забороняє системне обмеження на загальну кількість процесів,

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

− загальна кількість системної пам'яті, наданої для фізичного уведення-висновку, тимчасово виявилося недостатнім.

 

Fork реалізує схему паралельного програмування.

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

 

Приклад 1

Нижче наведено приклад, який ілюструє це поняття. Малюнок розбитий на дві частини: До і Після. Частина малюнка До показує стан до виклику fork. Існує єдиний процес А. Стрілка, позначена РС (Program counter - програмний лічильник), вказує на виконується в даний момент оператор. Спочатку виводиться повідомлення One.

 

 

Рисунок 2 – Ілюстрація роботи виклику fork

 

Частина малюнка Після показує ситуацію відразу ж після виклику fork. Тепер існують два виконуваних одночасно процесу: А і В. Процес А - це той же самий процес, що і в частині малюнка До. Процес В - це новий процес, породжений викликом fork. Цей процес є копією процесу А, крім одного важливого виключення - він має інше значення ідентифікатора (процесу pid), але виконує ту ж саму програму, що і процес А, т. е. ті ж три рядки вихідного коду, наведені на малюнку. Відповідно до введеної вище термінологією процес А є батьківським процесом, а процес В - дочірнім. Дві стрілки з написом РС в цій частині малюнка показують, що наступним оператором, який виконується батьком і нащадком після виклику fork, є виклик printf. Іншими словами, обидва процеси А і В продовжують виконання з тієї ж точки коду програми, хоча процес В і є новим процесом для системи. Тому повідомлення Two виводиться двічі.    

 

Приклад 2

# include <unistd.h>

main ()

{pid_t pid; / * process-id у батьківському процесі * /

printf ("Поки всього один процес \ n");

printf ("Виклик fork ... \ n");

pid = fork (); / * Створення нового процесу * /

if (pid = = 0)

printf ("Дочірній процес \ n");

 else if (pid> 0)

printf ("Батьківський процес, pid нащадка% d \ n, pid ");

else

printf (" Помилка виклику fork, нащадок не створено \ n");}

 

Оператор if, наступний за викликом fork, має три гілки. Перша визначає дочірній процес, що відповідає нульовому значенню змінної pid. Друга задає дії для батьківського процесу, відповідаючи позитивному значенню змінної pid. Третя гілка неявно відповідає негативному (а насправді одно -1) значенням змінної pid, яке повертається, якщо викликом fork не вдається створити дочірній процес. Оскільки обидва процеси, створених програмою, будуть виконуватися одночасно без синхронізації, то немає гарантії, що висновок батьківського і дочірнього процесів не буде змішуватися.

 

Приклад 3

# Include <unistd.h>

# include <sys/types.h>

# include <stdio.h>

main ()

{int pid, tmppid; / / оголошуємо змінні

pid = fork (); / / створюємо нашого першого нащадка (Child 1)

if (pid == 0) / / відокремлюємо нащадка від основного процесу            

{/ / ось тут починається код першого нащадка (Child 1)

  pid = fork (); / / створюємо наступного нащадка (Child 2),

причому з коду попереднього

     if (pid == 0) / / поділяємо нащадків, тепер Child 1 теж батько

     {/ / виконуваний код Child 2

         pid = fork (); / / створюємо третього нащадка (Child 3)

       if (pid == 0) / / поділяємо нащадків, тепер і Child 2 батько

          {printf ("I'm child 3 process with pid:% d \ n, getpid ());

          / / і наостанок код третоьго нащадка, він же

         / / виконуваний }

       else

        {printf ("I'm child 2 process with pid:% d \ n", getpid ());

/ / тут виконуваний код Child 2 , знову tmppid}}

     else / / далі йде код другого нащадка (Child 2)

   {printf ("I'm child 1 process with pid:% d \ n", getpid ());

      / / тут виконуваний код Child 1, ось потрібен tmppid}}

else

{printf ("I'm main process with pid:% d \ n", getpid ());

   / / виводимо назву основного процесу}}

 

Висновок повинен бути такий:

I'm main process with pid: 328

I'm child 1 process with pid: 329

I'm child 2 process with pid: 330

I'm child 3 process with pid: 331

 


Дата добавления: 2018-04-05; просмотров: 419; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!