МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ «ЛЬВІВСЬКА ПОЛІТЕХНІКА»
Кафедра ЕОМ
Лабораторна робота №5
З дисципліни: «Організація обчислювальних процесів в паралельних системах»
На тему: «Семафори в ОС UNIX»
Мета: Засвоїти механізм синхронізації процесів через семафори.
Синтаксис та призначення системних викликів.
1. Системний виклик semget.
int semget (key, nsems, semflg)
key_t key;
int nsems;
int semflg;
Системний виклик semget повертає ідентифікатор множини семафорів, який асоційований із ключем key.
Ідентифікатор і асоційовані з ним структура даних і множина з nsems семафорів створюються для ключа key у наступних випадках:
- Значення аргументу key дорівнює ІPC_PRІVATE.
- Ключ key ще не має асоційованого з ним ідентифікатора множини семафорів і вираз (semflg & ІPC_CREAT) істинний.
Використання параметру semflg таке саме, як і у системному виклику msgget(2) (лабораторна робота №4).
При успішному завершенні системного виклику повертається ідентифікатор множини семафорів. У випадку помилки повертається -1, а змінній errno присвоюється код помилки.
2. Системний виклик semop.
int semop (semid, sops, nsops);
int semid;
struct sembuf** sops;
unsigned nsops;
Системний виклик semop використовується для виконання набору операцій над множиною семафорів, який асоційований з ідентифікатором semіd. Аргумент sops (масив структур) визначає, над якими семафорами будуть виконуватися операції і які саме. Структура, що описує операцію над одним семафором, визначається в такий спосіб:
#include <sys/sem.h>
struct sembuf
{
// Номер семафора
short sem_num;
// Операція над семафором
short sem_op;
// Прапорці операції
short sem_flg;
};
Номер семафора задає конкретний семафор у множині, над яким повинна бути виконана операція.
Виконувана операція визначається в такий спосіб:
- Позитивне значення поля sem_op наказує збільшити значення семафора на величину sem_op.
- Негативне значення поля sem_op наказує зменшити значення семафора на абсолютну величину sem_op. Операція не може бути успішно виконана, якщо в результаті вийде негативне число.
- Нульове значення поля sem_op наказує порівняти значення семафора з нулем. Операція не може бути успішно виконана, якщо значення семафора відмінне від нуля.
Допустимі значення прапорців операцій (поле sem_flg):
- ІPC_NOWAІT
Якщо яка-небудь операція, для якої заданий прапорець ІPC_NOWAІT, не може бути успішно виконана, системний виклик завершується невдачею, причому значення жодного із семафорів не буде змінено.
- SEM_UNDO
Даний прапорець задає перевірочний режим виконання операції; він наказує анулювати її результат навіть у випадку успішного завершення системного виклику semop(2). Іншими словами, блокування всіх операцій (у тому числі і тих, для яких заданий прапор SEM_UNDO) виконується звичайним чином, але коли нарешті всі операції можуть бути успішно виконані, операції з прапором SEM_UNDO ігноруються.
Аргумент nsops специфікує кількість структур у масиві. Максимально припустимий розмір масиву визначається системним параметром SEMOPM, тобто в кожному системному виклику semop(2) можна виконати не більш SEMOPM операцій.
3. Системний виклик semctl.
int semctl (semid, semnum, cmd, arg)
int semid, cmd;
int semnum;
union semun
{
int val;
struct semid_ds *buf;
ushort *array;
} arg;
Системний виклик semctl дозволяє виконувати операції керування семафорами. Семафори задаються аргументами semіd і semnum. Операція визначається значенням аргументуcmd. Розглянемо деякі значення аргументу cmd:
GETVAL
Одержати значення семафора semval.
SETVAL
Встановити значення семафора semval рівним arg.val.
GETPІ
Одержати значення sempіd.
GETNCNT
Одержати значення semncnt.
GETZCNT
Одержати значення semzcnt.
GETALL
Прочитати значення семафорів у масив, на який вказує arg.array.
SETALL
Встановити значення семафорів рівними значенням елементів масиву, на який вказує arg.array.
IPC_RMID
Видалити із системи ідентифікатор semіd, ліквідувати множину семафорів і асоційовану з ними структуру даних.
Текст програми (parent.c)
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <stdio.h>
#define SLEEP_TIME 5
#define ITERATION_NUM 7
void main(void)
{
int i;
int errFlag;
int status;
int sem1Id;
int sem2Id;
struct sembuf sem_command;
sem1Id = semget(IPC_PRIVATE, 1, 0xfff);
if(sem1Id == -1)
{
fprintf(stderr, "Can't create first semaphore.\n");
exit(1);
}
sem2Id = semget(IPC_PRIVATE, 1, 0xfff);
if(sem2Id == -1)
{
fprintf(stderr, "Can't create second semaphore.\n");
exit(1);
}
sem_command.sem_num = 0;
sem_command.sem_op = 2;
sem_command.sem_flg = SEM_UNDO;
semop(sem1Id, &sem_command, 1);
sem_command.sem_op = 1;
semop(sem2Id, &sem_command, 1);
int mPid;
int pPid;
int ecPid;
char arg1[5];
char arg2[5];
int icPid = fork();
switch(icPid)
{
case 1:
fprintf(stderr, "Can't fork for internal child.\n");
exit(1);
case 0:
ecPid = fork();
switch(ecPid)
{
case 1:
fprintf(stderr, "Can't fork for external child.\n");
exit(1);
case 0:
sprintf(arg1, "%d", sem1Id);
sprintf(arg2, "%d", sem2Id);
errFlag = execl("./echild", "echild", arg1, arg2, 0);
if(errFlag == -1)
{
fprintf(stderr, "Can't execute the external child.\n");
exit(1);
}
default:
mPid = getpid();
pPid = getppid();
printf("IChild: My PID = %d, "
"My parent's PID = %d, "
"My child's PID = %d.\n",
mPid, pPid, ecPid);
sem_command.sem_op = -1; semop(sem1Id, &sem_command, 1);
sem_command.sem_op = 0;
semop(sem2Id, &sem_command, 1);
for(i = 0; i < ITERATION_NUM; i++)
{
printf("INTERNAL CHILD now is working.\n");
sleep(SLEEP_TIME);
//for(j=0;j<1000000;j++);
}
wait(&status);
printf("\nIChild: EChild exit code is %d.\n",
(status & 0xff00) >> 8);
exit(1);
}
default:
mPid = getpid();
pPid = getppid();
printf("Parent: My PID = %d, "
"My parent's PID = %d, "
"My child's PID = %d.\n",
mPid, pPid, icPid);
sem_command.sem_op = 0; // waits for semval==0
semop(sem1Id, &sem_command, 1);
printf("Parent: Press the <Enter> key to continue...\n");
getchar();
sem_command.sem_op = -1;
semop(sem2Id, &sem_command, 1);
for(i = 0; i < ITERATION_NUM; i++)
{
printf("PARENT now is working.\n");
sleep(SLEEP_TIME);
//for(j=0;j<1000000;j++);
}
wait(&status);
printf("Parent: IChild exit code is %d.\n",(status & 0xff00) >> 8);
exit(0);
}
}
Текст програми (child.c)
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#define SLEEP_TIME 5
#define ITERATION_NUM 7
void main(int argc, char *argv[])
{
int i;
struct sembuf sem_command;
if(argc != 3)
{
fprintf(stderr, "Missing arguments were detected.\n");
exit(1);
}
sem_command.sem_num = 0;
sem_command.sem_flg = SEM_UNDO;
int sem1Id = atoi(argv[1]);
int sem2Id = atoi(argv[2]);
int mPid = getpid();
int pPid = getppid();
printf("EChild: My PID = %d, My parent's PID = %d.\n", mPid, pPid);
sem_command.sem_op = -1;
semop(sem1Id, &sem_command, 1);
sem_command.sem_op = 0;
semop(sem2Id, &sem_command, 1);
for(i = 0; i < ITERATION_NUM; i++)
{
printf("EXTERNAL CHILD now is working.\n");
sleep(SLEEP_TIME);
//for(j=0;j<1000000;j++);
}
exit(0);
}
Результат роботи:
Висновок: на даній лабораторній роботі я засвоїв механізм синхронізації процесів через семафори.