Ciele
- Spoznať semafory.
- Porozumieť funckii semget().
- Porozumieť funkcii semctl().
- Porozumieť fukncii semop().
Postup
Krok 1: Úvod
Príprava
Pre prácu na cvičení si stiahnite súbor k cvičeniu.
Obsah súboru:
.
└── 09
├──u1_1.c
├──u2_1.c
├──u2_2.c
├──u3_1.c
├──u4_1.c
├──u4_2.c
├──Makefile
└──tests
├──u1_1_test.sh
├──u2_1_test.sh
└──u4_1_test.sh
Poznámka
Programy je možné kompilovať pomocou make nazov_suboru.c
Napríklad:
make u1_1.c
Krok 2: Semafory
Úvod
Semafor je dátová štruktúra, ktorá pomáha vláknam spolupracovať bez toho aby sa navzájom obmedzovali. Nadobúda povolené hodnoty od 0 po max(integer). Je to pasívny synchronizačný nástroj. Synchronizáciu zabezpečujú dve neprerušiteľné operácie P a V (buď sa vykoná celá naraz, alebo sa nevykoná vôbec).
Poznámka
Pre lepšie pochopenie je potrebne navštíviť manuál man 2 sem_overview.
Krok 3: Vytvorenie sady semaforov
Služba jadra semget()
Služba jadra semget() vytvorí novú sadu semaforov a vráti ich identifikátor. Semaforová sada je určitý počet semaforov, ktorý je identifikovaný unikátnym identifikátorom.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget (key_t key, int num_sems, int sem_flags);
Parametre | |
---|---|
key_t key | celočíselná hodnota, ktorá umožňuje nezávislým procesom prístupovať k rovnakej sade semaforov |
int num_sems | počet semaforov v sade |
int sem_flags | špecifikuje prístupové práva k sade semaforov, ktoré fungujú ako prístupové práva k súboru |
Návratová hodnota | |
---|---|
V prípade úspešného vykonania vráti identifikátor vytvorenej sady semaforov. | |
V prípade neúspešného vykonania vráti -1. |
Príznaky | |
---|---|
IPC_CREAT | Zaistí vytvorenie novej sady semaforov, ak ešte neexistuje. |
IPC_EXCL | Zobrazí chybové hlásanie ak sada už existuje. |
IPC_PRIVATE | Zaistí vytvorenie novej sady semaforov , ku ktorej môže pristupovať iba proces ktorý ju vytvoril. |
Poznámka
Pre podrobnejšie vysvetlenie služby navštívte manuál man 2 semget
Príklad použitia:
#include <stdio.h>
#include <sys/sem.h>
int main(void) {
int sem1 , sem2, sem3;
key_t ipc_key;
//Získanie kľúča, ktorý bude využitý pre vytvorenie sady semaforov.
ipc_key = ftok(".", 'S');
//Vytvorenie sady semaforov pre príslušný kľúč:
if (( sem1 = semget (ipc_key, 3, IPC_CREAT | 0666))==-1){
perror("semget: IPC_CREAT | 0666");
}
printf("sem1 identifikator: %d\n", sem1);
//Vytvorenie sady semaforov no v prípade existujúcej sady je vrátená chyba:
if ((sem2 = semget(ipc_key, 3, IPC_CREAT| IPC_EXCL| 0666)) ==-1){
perror("semget: IPC_CREAT | IPC_EXCL | 0666");
}
printf("sem2 identifikator: %d\n", sem2);
//Vytvorenie jedinečnej sady semaforov len pre proces, ktorý ju vytvoril:
if ((sem3 = semget(IPC_PRIVATE, 3, 0666)) == -1){
perror("semget: IPC_PRIVATE");
}
printf("sem3 identifikator: %d\n", sem3);
return 0;
}
Upozornenie
Pre správne skompilovanie predošlého kódu zvoľte nasledovný kompilačný riadok:
gcc -std=c11 -Wall -Werror -D_SVID_SOURCE -D_GNU_SOURCE semget.c -lm -o semget
Poznámka
Vyšie spomenutá funkcia ftok(const char* pathanme, int proj_id), vracia systémovo unikátny identifikátor pre každú kombináciu pathname a proj_id. Pri parametri proj_id nemusí byť použité len číslo ale môže byť použitý aj znak ale berie sa jeho hodnota z ASCII tabuľky.
Úloha 3.1
Vytvorte sadu semafórov. Ako kľúč použite hodnotu získanú prostredníctvom služby ftok s použitím parametramov "." a 'D'. Prístupové práva k sade semafórov nastavte na 0666. Program nech vytvorí sadu semafórov, ak ešte nebola vytvorená. V prípade, že sada semafórov už existuje tak dôjde ku chybe.
Poznámka
Zdrojové súbory k úlohe sa nachádzajú v priečinku 09 pod názvom u1_1.c.
Pred testovaním je potrebné skompilovať program u1_1.c.
Úlohu je možné testovať zadaním následovného príkazu do terminálu:
./tests/u1_1_test.sh
Krok 4: Inicializovanie a odstránenie saady semaforov
Služba jadra semctl()
Služba jadra semctl inicializuje, alebo prečíta hodnoty semaforov zo sady semaforov alebo prípadne sadu semaforov odstráni zo systému.
Poznámka
Pre podrobnejšie vysvetlenie služby navštívte manuál man 2 semctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl (int sem_id, int sem_num, int command, ...);
Parametre | |
---|---|
int sem_id | identifikátor sady semaforov, s ktorou sadou semaforov sa má pracovať |
int sem_num | počet semaforov v sade |
int command | špecifikuje akciu, ktorá sa má vykonať |
Návratová hodnota | |
---|---|
V prípade úspešného vykonania vracia 0. | |
V prípade neúspešného vykonania vráti -1. |
Parameter command môže v službe semctl() nadobúdať rôzne hodnoty.
Hodnoty parametra command | |
---|---|
SETVAL | Slúži k inicializácii semafora určitou hodnotou, semafor je potrebné nastaviť ešte pred prvým použitím. |
GETVAL | Slúži na zistenie nastavenej hodnoty semaforu. |
IPC_RMID | Slúži na zmazanie sady semaforov, keď už nie je potrebná. |
Poznámka
V prípade ak máme štyri parametre tak štvrtým parametrom je štruktrúra semun
Príklad použitia:
#include <stdio.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int sem_id, sem_value, i;
key_t ipc_key;
struct semid_ds sem_buf;
static ushort sem_array[] = {3,1,4};
ipc_key = ftok(".", 'S');
//Vytvorenie sady semaforov:
if (( sem_id = semget (ipc_key, 3, IPC_CREAT | 0666))==-1){
perror("semget: IPC_CREAT | 0666");
exit (1);
}
printf("Semafor ID: %d\n", sem_id);
//Poskytnutie informácií o vytvorenej sade semaforov:
if (semctl(sem_id, 0, IPC_STAT, &sem_buf) == -1){
perror("semctl:IPC_STAT");
exit (2);
}
printf("Vytvoreny %s", ctime(&sem_buf.sem_ctime));
//Inicializacia sady semaforov:
if (semctl(sem_id, 0, SETALL, sem_array) == -1){
perror("semctl: SETALL");
exit (3);
}
//Výpis hodnôt semaforov:
for (i = 0; i < 3; ++i){
if ((sem_value = semctl(sem_id, i, GETVAL)) == -1){
perror("semctl: GETVAL");
exit (4);
}
printf("Semafor %d ma hodnotu %d\n", i, sem_value);
}
//Odstránenie sady semaforov:
if (semctl(sem_id, 0, IPC_RMID) == -1){
perror("semctl: IPC_RMID");
exit (5);
}
return 0;
}
Poznámka
Pre správne skompilovanie predošlého kódu zvoľte nasledovný kompilačný riadok:
gcc -std=c11 -Wall -Werror -D_SVID_SOURCE -D_GNU_SOURCE semctl.c -lm -o semctl
Upozornenie
Pre prácu na nasledujúcej úlohe je potrebné vložiť vaše riešenie zo súboru u1_1.c do pripraveného súboru s názvom u1_2.c.
Úloha 4.1
Odstráňte sadu semafórov, ktorú ste vytvorili v predchadzajúcej úlohe, zo systému.
Poznámka
Zdrojové súbory k úlohe sa nachádzajú v priečinku 09 pod názvom u1_2.c.
Pred testovaním je potrebné skompilovať program u1_2.c.
Úlohu je možné testovať zadaním následovného príkazu do terminálu:
./tests/u1_2_test.sh
Krok 5: Vykonávanie operácii
Služba jadra semop()
Služba semop vykonáva operácie na vybraných semaforoch v danom sete označeným semid
.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop (int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
Parametre | |
---|---|
int sem_id | identifikátor sady semaforov, s ktorou sadou semaforov sa má pracovať |
struct sembuf *sem_ops | počet semaforov v sade |
size_t num_sem_ops | je počet prvkov štruktúry sembuf (semaforov v poole), nad ktorými sa vykonávaju operácie P a V. |
Poznámka k parametru sembuf:
struct sembuf {
short sem_num;
short sem_op;
short sem_flg;
}
Premenné štruktúry | |
---|---|
short sem_num | je číslo semaforu, nad ktorým sa má zo sady semaforov urobiť daná operácia |
short sem_op | je hodnota alebo číslo (určujúce typ operácie), na ktorú má byť semafor zmenený(hodnotu môže zmeniť o viac než 1), často používame hodnoty P a V |
short sem_flg | obsahuje jeden z dvoch príznakov SEM_UNDO alebo IPC_NOWAIT |
Poznámka
Pre podrobnejšie vysvetlenie príznakov SEM_UNDO a IPC_NOWAIT navštívte manuál man 2 semop
Operácie | |
---|---|
P | zníženie hodnoty semaforu o 1 |
V | zvýšenie hodnoty semaforu o 1 |
Návratová hodnota | |
---|---|
V prípade úspešného vykonania vracia 0. | |
V prípade neúspešného vykonania vráti -1. |
Aplikácia služieb v programe:
Upozornenie
Nasledujúci príklad použitia spúšťajte prostredníctvom prvého programu:
./sem1
sem1.c
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
union semun{
int val, arg1, arg2;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
bool semaphore_p(void);
bool semaphore_v(void);
int sem_id;
int main(int argc, char *argv[]){
if(fork()==0){
execve("sem2",(char *[]){NULL}, (char *[]){NULL});
exit(0);
}
int i;
char op_char = '1';
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
if(sem_id==-1){
perror("semget()");
exit(1);
}
printf("sem1.c\nsemafor: %d\n",sem_id);
union semun sem_union;
sem_union.val = 1;
if (semctl(sem_id, 0, SETVAL, sem_union) == -1){
perror("initialize semaphore");
exit(1);
}
sleep(2);
for(i = 0; i < 5; i++) {
if (!semaphore_p()){
exit(1);
}
printf("%c", op_char);
fflush(stdout);
sleep(1);
printf("%c\n", op_char);
fflush(stdout);
if (!semaphore_v()){
exit(1);
}
sleep(1);
}
sleep(1);
printf("Proces %d sa ukoncil.\n", getpid());
if (semctl(sem_id, 0, IPC_RMID) == -1){
perror("delete semaphore");
}
exit(0);
}
bool semaphore_p(void){
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
perror("p operation");
return(false);
}
return(true);
}
bool semaphore_v(void){
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
perror("v operation");
return(false);
}
return(true);
}
sem2.c
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
bool semaphore_p(void);
bool semaphore_v(void);
int sem_id;
int main(int argc, char *argv[]){
int i;
char op_char = '2';
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
if(sem_id==-1){
perror("semget()");
exit(1);
}
printf("sem2.c\nsemafor: %d\n",sem_id);
sleep(1);
for(i = 0; i < 5; i++) {
if (!semaphore_p()){
exit(1);
}
printf("%c", op_char);
fflush(stdout);
sleep(1);
printf("%c\n", op_char);
fflush(stdout);
if (!semaphore_v()){
exit(1);
}
sleep(1);
}
sleep(2);
printf("Proces %d sa ukoncil.\n", getpid());
exit(0);
}
bool semaphore_p(void){
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
perror("p operation");
return(false);
}
return(true);
}
bool semaphore_v(void){
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
perror("v operation");
return(false);
}
return(true);
}