Семафори як спосіб синхронізації взаємодіючих процесів

Для синхронізації взаємодії процесів з|із| ресурсом, що розділяється, часто використовують семафори. Семафори виконують функцію відповідну своїй назві: замикають доступ до ресурсу одним процесом (з|із| тих, що взаємодіють) і вирішують доступ до іншого.

 

SERVER CLIENT

Читання Запис

Hello,World

Для роботи з|із| семафором необхідно забезпечити виконання наступних|слідуючих| умов:

1. Семафори повинні бути «видно» всім взаємодіючим процесам;

2. Фрагменти коду по перемиканню станів семафора повинні бути безперервними, тобто потрібно забезпечити атомарність критичних операцій.

З|із| цього виходить, що семафори повинні бути системними ресурсами. Єдино можливим способом гарантування атомарності виконання критичних ділянок операцій є|з'являється,являється| їх виконання в режимі ядра. Семафор є групою окремих «семафорчиків»,| об'єднаних|з'єднаних| загальними|спільними| ознаками (ідентифікатор, дескриптор). Кожний з семафорів може приймати будь-яке не негативне|заперечне| число, дозволене в даній ОС. Якщо семафор приймає значення тільки|лише| 0 або 1, його називають бінарним.

Розглянемо|розгледимо| завдання|задачу|, в якому:

- Взаємодіють два самостійні процеси, створені програмами Server і Client;

- Server виділяє пам'ять, що розділяється, створює семафор і читає з пам'яті;

- Client підключає пам'ять, що розділяється, вже готовий семафор, записує|занотовує| в пам'ять, що розділяється, рядок, відключає і звільняє|визволяє| пам'ять, звільняє|визволяє| семафор.

Для створення|створіння| семафора використовується функція:

int semget (key_ tKey, int nsems, int semflag); - створює семафор і повертає його ID.

Тут:

- Key - унікальний ключ|джерело|, який використовуватимуть всі процеси, що звертаються|обертаються| до цього семафора;

- nsems - кількість семафорів;

- semflag - прапори (аналогічні прапорам функції shmget()).

Після того, як семафор створений, з|із| ним можна працювати, для цього необхідно:

- Визначити у файлі заголовка операції для семафора;

- Виконати ці операції в програмі.

Для виконання операції над семафором використовується:

semop (int semid, struct sembuf *semop, size_t nops);

Тут:

- semid - ідентифікатор семафора над яким потрібно виконати дію;

- nops - річ у тому, що|справа в тому , що,дело в том | для виконання дії може бути потрібно декілька операцій, це указується|вказується| даним параметром;

- struct sembuf *semop - власне операція над семафором, задається структурою semop, покажчик на яку передається другим параметром.

Розглянемо|розгледимо| допустимі значення операції над семафорами:

Позитивне число - поточне значення семафора збільшується на це значення

Рівне «0» - процес чекає поки семафор не обнулиться

Негативне|заперечне| число - процес чекає поки значення семафора не стане рівним модулю цього числа, а потім віднімає модуль цієї величини, тобто|цебто| результат «0»

Із сказаного виходить:

- Перше значення тільки|лише| змінює|зраджує| значення семафора;

- Друге тільки|лише| перевіряє чи не обнулився семафор;

- Третє перевіряє, а потім змінює|зраджує| значення семафора.

Розглянемо|розгледимо| два приклади|зразки| в яких:

1. задається операція над семафором;

2. записується|занотовується| функція, що виконує цю операцію.

Перший приклад|зразок|:

Умовимося вважати|лічити|, що семафор бінарний (його значення можуть приймати або "0" або "1"), а також, що значення "0" - вирішує доступ до ресурсу, а "1" - блокує ресурс.

Визначимо операції:

Static struct sembuf sop_ lock[]={0,0,0},{0,1,0}

Запишемо функцію, виконуючу цю операцію:

Semop (semid,&sop_lock[0],2).

Запишемо операцію:

Static struct sembuf sop_ unlock[]={0-1,0};

Визначимо функцію:

Semop (semid,&sop_unlock[0],1);

Другий приклад|зразок|:

У ньому рахуємо значення семафора рівне "0" - замикає ресурс; "1" - вирішує (розблоковує ресурс).

Складемо операцію для замикання ресурсу:

Static struct sembuf sop_ lock[1]={0-1,0}

Semop (semid,&sop_lock[0],1);

Static struct sembuf sop_unlock[1]={0,1,0};

Semop (semid,&sop_unlock[0],1);

 

Server.c

int main()

{

printf("Server start\n");

Message *msgptr;

key_t key;

int shmid, semid;

key=ftok("server",1);

shmid=shmget(key,sizeof(Message),PERM | IPC_CREAT);

msgptr = (Message*)shmat(shmid,0,0);

semid = semget(key,2,PERM | IPC_CREAT);

semop(semid,&proc_wait[0],1);

semop(semid,&mem_lock[0],2);

printf("Client send message: %s",msgptr->buff);

semop(semid,&mem_unlock[0],1);

shmdt(msgptr);

printf("Server exit\n");

exit(0);

}

Client.c

int main()

{

printf("Client start\n");

Message *msgptr;

key_t key;

int shmid,semid;

key=ftok("server",1);

shmid=shmget(key,sizeof(Message),0);

msgptr=(Message*)shmat(shmid,0,0);

semid=semget(key,2,PERM);

semop(semid,&mem_lock[0],2);

semop(semid,&proc_start[0],1);

sprintf(msgptr->buff,"Hello world!\n");

printf("Client send message 'Hello world'\n");

semop(semid,&mem_unlock[0],1);

semop(semid,&mem_lock[0],2);

shmdt(msgptr);

shmctl(shmid,IPC_RMID,0);

semctl(semid,0,IPC_RMID);

printf("Client exit\n");

exit(0);

}

Shmem.h

#define MAXBUFF 80

#define PERM 0666

typedef struct mem_msg

{

int segment;

char buff[MAXBUFF];

}Message;

struct sembuf proc_wait[1]={1,-1,0};

struct sembuf proc_start[1]={1,1,0};

struct sembuf mem_lock[2]={0,0,0,0,1,0};

struct sembuf mem_unlock[1]={0,-1,0};