9. týždeň

Semafory

Ciele

  1. Spoznať semafory.
  2. Porozumieť funckii semget().
  3. Porozumieť funkcii semctl().
  4. 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.

Aký chybový výstup bude mať služba semget(), ak použijeme príznaky IPC_CREAT a IPC_EXCL, a sada semaforov pre daný kľúč už existuje

Správna odpoveď: EEXIST
Nesprávna odpoveď

[_______________] vie prístupovať k semafórom prostredníctvom kľúča. (nominatív singuláru)

Správna odpoveď: Proces
Nesprávna odpoveď

Ú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

Služba semctl() umožňuje:

Odoslať odpoveď
Správna odpoveď: Služba semctl() umožňuje inicializáciu sady semafórov a odstránenie posledného semaforu zo sady.
Nesprávna odpoveď

Prvý parameter sem_id vieme získať pomocou:

Odoslať odpoveď
Správna odpoveď: semget()
Nesprávna odpoveď

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.

Sem_undo štruktúry procesu sú dedené?

Odoslať odpoveď
Pri systémovom volaní execve()
Nesprávna odpoveď

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);
}

Čo by sa vypísalo na štandardný výstup, ak by sa sada semafórov odstráňovala v obidvoch programoch? [2 slová s medzerou medzi nimi]

Správna odpoveď: delete semaphore
Nesprávna odpoveď

V prípade, že by bol použitý príznak IPC_EXCL, na akú hodnotu sa nastaví "errno"?

Správna odpoveď: EEXIST
Nesprávna odpoveď

Čo zabezpečuje v službe semctl, že sa vytvorená sada semafórov odstráni?

Správna odpoveď: IPC_RMID
Nesprávna odpoveď

Ako sa nazývajú operácie, ktoré reprezentujú funkcie semaphore_p() a semaphore_v()? [Služby oddeľte čiarkou a medzerou, príklad: ABC, EFG]

Správna odpoveď: prolaag, verhoog
Nesprávna odpoveď