- •Міністерство освіти і науки України
- •Жуковецька с.Л., Шестопалов с.В.
- •Анотація
- •Введення
- •Лабораторна робота №1 Технологія розробки програм у середовищі Linux
- •1. Команди роботи з командним інтерпретатором ос unix
- •2. Створення програми
- •3. Функції введення-виведення.
- •4. Приклад програми
- •5. Основні керуючі конструкції мови с
- •6. Індівідуальні завдання
- •Лабораторна робота № 2. Автоматизація пакетних завдань
- •1. Компіляція багатомодульною програми
- •2. Автоматизація пакетних завдань
- •4. Завдання
- •Лабораторна робота № 3. Взаємодія програми з середовищем виконання
- •1. Аргументи командного рядка
- •2. Змінні оточення
- •3. Індівідуальні завдання
- •Лабораторна робота №4. Контроль виконання програми
- •1. Коди завершення програми
- •2. Функції обробки значення errno
- •3. Функція atexit()
- •4. Макрос asssert()
- •5. Індівідуальні завдання
- •Лабораторна робота №5 Файлові api.
- •1 Основні поняття
- •2. Права доступу до файлів
- •3. Інтерфейси для файлового введення / виводу
- •4. Системні виклики роботи з файлами
- •5. Індівідуальні завдання
- •Варіанти завдань
- •Лабораторна робота №6 Робота з каталогами та посиланнями
- •Методичні вказівки
- •1. Створення та видалення каталогу
- •2. Читання каталогу
- •3. Зміна каталогу
- •4. Управління жорсткими посиланнями
- •5. Управління символічними посиланнями
- •6. Перейменування файлів і каталогів
- •7. Індівідуальні завдання
- •Лабораторна робота №7. Одержання і відображення метаданих файлу.
- •Методичні вказівки
- •1. Механізми управління файлами ос unix
- •2. Отримання даних індексного дескриптору
- •3. Додаткові функції маніпулювання даними індексних дескрипторів
- •4. Отримання даних про відкриті файли
- •5. Індівідуальні завдання
- •Лабораторна робота №8. Керування процесами
- •Методичні вказівки
- •1. Загальні поняття
- •2. Створення поцесу
- •4. Перезавантаження програми процесу
- •5. Завершення виконання процесу
- •6. Системні виклики wait, waitpid
- •7. Індівідуальні завдання
- •Лабораторна робота №9. Використання каналів
- •Методичні вказівки
- •1. Заганьні відомості
- •2. Використання каналів
- •3. Індівідуальні завдання
- •Варіанти завдань
- •Лабораторна робота №10 Використання повідомлень
- •Методичні вказівки
- •1. Загальні відомості
- •2. Використання повідомлень
- •3. Індівідуальні завдання
- •Література
- •Системне програмування Посібник до виконання лабораторних та самостійних робіт
- •65082, Одеса, вул. Дворянська, 1/3
2. Створення поцесу
Новий процес створюється системним викликом 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