Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.

Предоплата всего

Подписываем
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Предоплата всего
Подписываем
Семафори в UNIX як засіб синхронізації процесів
Мета: Розглянути та практично опрацювати наступні питання: Семафори в UNIX. Відмінність операцій над UNIX-семафорами від класичних операцій. Створення масиву семафорів або доступ до вже існуючого масиву. Системний виклик semget(). Виконання операцій над семафорами. Системний виклик semop(). Видалення набору семафорів з системи за допомогою команди ipcrm або системного виклику semctl(). Поняття про POSIX-семафори.
В матеріалах попереднього заняття йшлося про необхідність синхронізації роботи процесів для їх коректної взаємодії через пам'ять, що розділяється. Як згадувалося в лекції 6, одним з перших механізмів, запропонованих для синхронізації поведінки процесів, стали семафори, концепцію яких описав Дейкстра (Dijkstra) в 1965 році. При розробці засобів System V IPC семафори увійшли до їх складу як невід'ємна частина. Слід зазначити, що набір операцій над семафорами System V IPC відрізняється від класичного набору операцій {P, V}, запропонованого Дейкстрой. Він включає три операції:
Спочатку всі IPC-семафори ініціюються нульовим значенням.
Ми бачимо, що класичній операції P(S) відповідає операція D(S,1), а класичній операції V(S) відповідає операція А(S,1). Аналогом ненульової ініціалізації семафорів Дейкстри значенням n може служити виконання операції А(S,n) відразу після створення семафора S, із забезпеченням атомарності створення семафора і її виконання за допомогою іншого семафора. Ми показали, що класичні семафори реалізуються через семафори System V IPC. Зворотне не є вірним. Використовуючи операції P(S) і V(S), ми не зуміємо реалізувати операцію Z(S).
Оскільки IPC-семафори є складовою частиною засобів System V IPC, то для них вірно все, що мовилося про ці засоби в матеріалах попереднього заняття. IPC-семафори є засобом зв'язку з непрямою адресацією, вимагають ініціалізації для організації взаємодії процесів і спеціальних дій для звільнення системних ресурсів після його закінчення. Простором імен IPC-семафорів є безліч значень ключа, що генеруються за допомогою функції ftok(). Для здійснення операцій над семафорами системним викликам як параметр передаються IPC- дескриптори семафорів, однозначно ідентифікуючих їх у всій обчислювальній системі, а вся інформація про семафори розташовується в адресному просторі ядра операційної системи. Це дозволяє організовувати через семафори взаємодію процесів, що навіть не знаходяться в системі одночасно.
В цілях економії системних ресурсів операційна система UNIX дозволяє створювати не по одному семафору для кожного конкретного значення ключа, а пов'язувати з ключем цілий масив семафорів (в Linux до 500 семафорів в масиві, хоча ця кількість може бути зменшене системним адміністратором). Для створення масиву семафорів, асоційованого з певним ключем, або доступу по ключу до вже існуючого масиву використовується системний виклик semget(), є аналогом системного виклику shmget() для пам'яті, що розділяється, який повертає значення IPC-дескриптора для цього масиву. При цьому застосовуються ті ж способи створення і доступу, що і для пам'яті, що розділяється. Знов створені семафори ініціюються нульовим значенням.
Системний виклик semget()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems int semflg);
Системний виклик semget призначений для виконання операції доступу до масиву IPC-семафорів і, у разі її успішного завершення, повертає дескриптор System V IPC для цього масиву (ціле ненегативне число, однозначно характеризуюче масив семафорів усередині обчислювальної системи і що використовується надалі для інших операцій з ним).
Параметр key є ключем System V IPC для масиву семафорів, тобто фактично його ім'ям з простору імен System V IPC. Як значення цього параметра може використовуватися значення ключа, одержане за допомогою функції ftok(), або спеціальне значення IPC_PRIVATE. Використовування значення IPC_PRIVATE завжди приводить до спроби створення нового масиву семафорів з ключем, який не співпадає із значенням ключа жодного з вже існуючих масивів і не може бути одержаний за допомогою функції ftok() ні при одній комбінації її параметрів.
Параметр nsems визначає кількість семафорів в створюваному або вже існуючому масиві. У випадку, якщо масив з вказаним ключем вже є, але його розмір не співпадає з вказаним в параметрі nsems, констатується виникнення помилки.
Параметр semflg прапори грає роль тільки при створенні нового масиву семафорів і визначає права різних користувачів при доступі до масиву, а також необхідність створення нового масиву і поведінка системного виклику при спробі створення. Він є деякою комбінацією (за допомогою операції побітове або "|") наступних приречених значень і вісімкових прав доступу:
IPC_CREAT якщо масиву для вказаного ключа не існує, він повинен бути створений
IPC_EXCL застосовується спільно з прапором IPC_CREAT. При сумісному їх використовуванні і існуванні масиву з вказаним ключем, доступ до масиву не проводиться і констатується помилка, при цьому змінна errno, описана у файлі <errno.h>, прийме значення EEXIST
Знов створені семафори ініціюються нульовим значенням.
Значення, що повертається
Системний виклик повертає значення дескриптора System V IPC для масиву семафорів при нормальному завершенні і значення -1 при виникненні помилки.
Для виконання операцій А, D і Z над семафорами з масиву використовується системний виклик semop(), володіючий досить складною семантикою. Розробники System V IPC явно перенавантажували цей виклик, застосовуючи його не тільки для виконання всіх трьох операцій, але ще і для декількох семафорів в масиві IPC-семафорів одночасно. Для правильного використовування цього виклику необхідно виконати наступні дії:
Системний виклик semop()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops int nsops);
Опис системного виклику
Системний виклик semop призначений для виконання операцій А, D і Z (див. опис операцій над семафорами з масиву IPC семафорів роздягнув "Створення масиву семафорів або доступ до вже існуючого. Системний виклик semget()" цього семінару). Даний опис не є повним описом системного виклику, а обмежується рамками поточного курсу. Для повного опису звертайтеся до UNIX Manual.
Параметр semid є дескриптором System V IPC для набору семафорів, тобто значенням, яке повернув системний виклик semget() при створенні набору семафорів або при його пошуку по ключу.
Кожний з nsops елементів масиву, на який указує параметр sops, визначає операцію, яка повинна бути вчинена над яким-небудь семафором з масиву IPC семафорів, і має тип структури struct sembuf, в яку входять наступні змінні:
Значення елемента структури sem_op визначається таким чином:
Семантика системного виклику має на увазі, що всі операції будуть в реальності виконані над семафорами тільки перед успішним поверненням з системного виклику. Якщо при виконанні операцій D або Z процес перейшов в стан очікування, то він може бути вывеен з цього стану при виникненні наступних форс-мажорних ситуацій:
В цьому випадку відбувається повернення з системного виклику з констатацією помилкової ситуації.
Значення, що повертається
Системний виклик повертає значення 0 при нормальному завершенні і значення -1 при виникненні помилки.
Для ілюстрації сказаного розглянемо найпростіші програми, що синхронізують свої дії за допомогою семафорів
/* Програма 08-1a.c для ілюстрації роботи з семафорами */
/* Ця програма дістає доступ до одного системного семафора чекає, поки його значення не стане більше або рівним 1 після запусків програми 08-1b.c,а потім зменшує його на 1*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
int main()
{
int semid; /* IPC дескриптор для масиву IPC семафорів */
char pathname[] = "08-1a.c"; /* Ім'я файлу що використовується для генерації ключа. Файл з таким ім'ям винен існувати в поточній директорії */
key_t key; /* IPC ключ */
struct sembuf mybuf; /* Структура для завдання операції над семафором */
/* Генеруємо IPC-ключ з імені файлу 08-1a.c в поточній директорії і номери екземпляра масиву семафорів 0 */
if((key = ftok(pathname,0)) < 0){
printf("Can\'t generate key\n");
exit(-1);
}
/* Намагаємося дістати доступ по ключу до масиву семафорів, якщо він існує, або створити його з одного семафора, якщо його ще не існує, з правами доступу read & write для всіх користувачів */
if((semid = semget(key, 1, 0666 | IPC_CREAT)) < 0){
printf("Can\'t get semid\n");
exit(-1);
}
/* Виконаємо операцію D(semid1,1) для нашого масиву семафорів. Для цього спочатку заповнимо нашу структуру. Прапор, як завжди, вважаємо рівним 0. Наш масив семафорів складається з одного семафора з номером 0. Код операції -1.*/
mybuf.sem_op = -1;
mybuf.sem_flg = 0;
mybuf.sem_num = 0;
if(semop(semid &mybuf, 1)< 0){
printf("Can\'t wait for condition\n");
exit(-1);
}
printf("Condition is present\n");
return 0;
}
Лістинг 8.1. Програма 08-1a.c для ілюстрації роботи з семафорами
/* Програма 08-1b.c для ілюстрації роботи з
семафорами */
/* Ця програма дістає доступ до одного системного семафораі збільшує його на 1*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
int main()
{
int semid; /* IPC дескриптор для масиву IPC семафорів */
char pathname[] = "08-1a.c"; /* Ім'я файлу що використовується для генерації ключа. Файл з таким ім'ям винен існувати в поточній директорії */
key_t key; /* IPC ключ */
struct sembuf mybuf; /* Структура для завдання операції над семафором */
/* Генеруємо IPC ключ з імені файлу 08-1a.c в поточній директорії і номери екземпляра масиву семафорів 0 */
if((key = ftok(pathname,0)) < 0){
printf("Can\'t generate key\n");
exit(-1);
}
/* Намагаємося дістати доступ по ключу до масиву семафорів, якщо він існує, або створити його з одного семафора, якщо його ще не існує, з правами доступу read & write для всіх користувачів */
if((semid = semget(key, 1, 0666 | IPC_CREAT)) < 0){
printf("Can\'t get semid\n");
exit(-1);
}
/* Виконаємо операцію А(semid1,1) для нашого масиву семафорів. Для цього спочатку заповнимо нашу структуру. Прапор, як завжди вважаємо рівним 0. Наш масив семафорів складається з одного семафора з номером 0. Код операції 1.*/
mybuf.sem_op = 1;
mybuf.sem_flg = 0;
mybuf.sem_num = 0;
if(semop(semid &mybuf, 1)< 0){
printf("Can\'t wait for condition\n");
exit(-1);
}
printf("Condition is set\n");
return 0;
}
Лістинг 8.1b. Програма 08-1b.c для ілюстрації роботи з семафорами
Перша програма виконує над семафором S операцію D(S,1), друга програма виконує над тим же семафором операцію А(S,1). Якщо семафора в системі не існує, будь-яка програма створює його перед виконанням операції. Оскільки при створенні семафор завжди ініціюється 0, то програма 1 може працювати без блокування тільки після запуску програми 2. Наберіть програми, збережіть під іменами 08-1а.с і 08-1b.c відповідно, відкомпілюйте і перевірте правильність їх поведінки.
Як ми бачили в прикладах, масив семафорів може продовжувати існувати в системі і після завершення процесів, що використали його, а семафори зберігатимуть своє значення. Це може привести до некоректної поведінки програм, що припускають, що семафори були тільки що створені і, отже, мають нульове значення. Необхідно видаляти семафори з системи перед запуском таких програм або перед їх завершенням. Для видалення семафорів можна скористатися командами ipcs і ipcrm, розглянутими в матеріалах попереднього семінару. Команда ipcrm в цьому випадку повинна мати вигляд
ipcrm sem <IPC ідентифікатор>
Для цієї ж мети ми можемо застосовувати системний виклик semctl(), який уміє виконувати і інші операції над масивом семафорів, але їх розгляд виходить за рамки нашого курсу.
Системний виклик semctl()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd union semun arg);
Опис системного виклику
Системний виклик semctl призначений для отримання інформації про масив IPC семафорів, зміни його атрибутів і видалення його з системи. Даний опис не є повним описом системного виклику, а обмежується рамками поточного курсу. Для вивчення повного опису звертайтеся до UNIX Manual.
В нашому курсі ми застосовуватимемо системний виклик semctl тільки для видалення масиву семафорів з системи. Параметр semid є дескриптором System V IPC для масиву семафорів, тобто значенням, яке повернув системний виклик semget() при створенні масиву або при його пошуку по ключу.
Як параметр cmd в рамках нашого курсу ми завжди передаватимемо значення IPC_RMID команду для видалення сегменту пам'яті, що розділяється, із заданим ідентифікатором. Параметри semnum і arg для цієї команди не використовуються, тому ми завжди підставлятимемо замість них значення 0.
Якщо які-небудь процеси знаходилися в змозі очікування для семафорів з масиву, що видаляється, при виконанні системного виклику semop(), то вони будуть розблоковані і повернуться з виклику semop() з індикацією помилки.
Значення, що повертається
Системний виклик повертає значення 0 при нормальному завершенні і значення -1 при виникненні помилки.
В стандарті POSIX вводяться інші семафори, повністю аналогічні семафорам Дейкстри. Для ініціалізації значення таких семафорів застосовується функція sem_init(), аналогом операції P служить функція sem_wait(), а аналогом операції V функція sem_post(). На жаль, в Linux такі семафори реалізовані тільки для ниток виконання одного процесу, і тому детально ми на них зупинятися не будемо.
Питання до захисту роботи