/* * Se realizeaza o interfata simpla cu apelurile sistem ale * semafoarelor, la UNIX System V. Sunt disponibile 7 rutine: * * id = sem_create(key, initval); # creaza cu val. init. sau * deschide * * id = sem_open(key); # deschide, presupunand ca exista deja * * sem_wait(id); # operatia P: scade cu 1 * * sem_signal(id); # operatia V creste cu 1 * * sem_op(id, * cantitate); # wait daca cantitate < 0 # signal daca * cantitate > 0 * * sem_close(id); # inchide * * sem_rm(id); # sterge semaforul * * Vom crea si folosi un set de trei membri pentru fiecare semafor * cerut. * * Primul membru, [0], este valoarea actuala a semaforului. * * Al doilea membru, [1], este un numarator folosit sa stie cand * toate procesele au trecut de semafor. El este initializat cu * un numar foarte mare, decrementat la fiecare creare sau * deschidere si incrementat la fiecare inchidere. In acest mod * se foloseste facilitatea de "ajustare" oferita de SV pentru * situatiile de exit accidental sau intentionat si nepotrivit. * * Al treilea membru, [2], este folosit pentru a bloca variabile si * a evita conditiile rele de la sem_create() si sem_close(). */ #include #include #include #include extern int errno; #define BIGCOUNT 10000 /* Valoarea initiala a * contorului de procese */ /* * Defineste operatiile cu semafoare apelate prin semop() */ static struct sembuf op_lock[2] = { 2, 0, 0, /* asteapta ca semaforul [2] sa * devina zero */ 2, 1, SEM_UNDO /* apoi incrementeaza-l pe [2] * cu 1. UNDO va elibera * blocarea daca procesul exista * inainte de deblocarea * efectiva */ }; static struct sembuf op_endcreate[2] = { 1, -1, SEM_UNDO, /* Decrementeaza [1] la iesire. * UNDO ajusteaza contorul de * procese daca procesul exista * inaintea apelului explicit * sem_close() */ 2, -1, SEM_UNDO /* apoi decrementeaza-l pe [2] * cu 1. */ }; static struct sembuf op_open[2] = { 1, -1, SEM_UNDO, /* Decrementeaza [1] la iesire. */ }; static struct sembuf op_close[3] = { 2, 0, 0, /* Asteapta pentru [2] sa devina * = 0 */ 2, 1, SEM_UNDO, /* apoi incrementeaza [2] cu 1 * ptr blocare */ 1, 1, SEM_UNDO /* apoi incrementeaza [1] cu 1 * (contor de procese) */ }; static struct sembuf op_unlock[1] = { 2, -1, SEM_UNDO, /* Decrementeaza [2] inapoi la 0 */ }; static struct sembuf op_op[1] = { 0, 99, SEM_UNDO, /* Decrementeaza sau * incrementeaza [0] cu undo si * exit. 99 este cantitatea * actuala de adaugat sau scazut * (pozitiva sau negativa) */ }; union { int val; struct semid_ds *buf; ushort *array; } semctl_arg; /* * ********************************************************* * Creaza un semafor cu o valoare initiala. Daca exista, atunci nu * se va mai initializa. Intoarce ID al semaforului, sau -1 la * esec */ int sem_create(key, initval) key_t key; int initval; { register int id, semval; if (key == IPC_PRIVATE) return (-1); else if (key == (key_t) - 1) return (-1); iarasi: if ((id = semget(key, 3, 0666 | IPC_CREAT)) < 0) return (-1); /* * Cand se creaza semaforul, se stie ca toate valorile sunt 0. * Se face o blocare a semaforului, asteptand ca [2] sa devina * 0, apoi il incrementeaza. * * Apare o problema: intre semget de mai sus si semop de mai jos * poate interveni un alt proces. De aceea vom relua */ if (semop(id, &op_lock[0], 2) < 0) { if (errno == EINVAL) goto iarasi; err_sys("Nu se poate bloca"); } /* * Intoarce valoarea semaforului */ if ((semval = semctl(id, 1, GETVAL, semctl_arg)) < 0) err_sys("Nu pot face GETVAL"); if (semval == 0) { /* In loc de a face SETALL, care * va sterge si valoarea de * ajustare, vom initializa [0] * si [1] */ semctl_arg.val = initval; if (semctl(id, 0, SETVAL, semctl_arg) < 0) err_sys("Nu pot face SETVAL[0]"); semctl_arg.val = BIGCOUNT; if (semctl(id, 1, SETVAL, semctl_arg) < 0) err_sys("Nu pot face SETVAL[1]"); } /* * Decrementeaza contorul de procese si elibereaza blocarea */ if (semop(id, &op_endcreate[0], 2) < 0) err_sys("Nu pot endcreate"); return (id); } /* * ********************************************************* * Deschide un semafor care exista deja. Functia trebuie folosita * in locul lui sem_create daca utilizatorul stie ca semaforul * exista deja. De exemplu, un client poate face asa ceva daca * stie ca sarcina crearii revine serverului */ int sem_open(key) key_t key; { register int id; if (key == IPC_PRIVATE) return (-1); else if (key == (key_t) - 1) return (-1); if ((id == semget(key, 3, 0)) < 0) return (-1); /* * Decrementeaza numarul de procese. Aceasta operatie nu * trebuie sa se faca blocat */ if (semop(id, &op_open[0], 1) < 0) err_sys("Nu pot open"); return (id); } /* * ********************************************************* * Sterge un semafor. Aceasta sarcina revine de regula serverului, * si trebuie sa fie precedata de close de catre toate procesele * care-l folosesc */ sem_rm(id) int id; { semctl_arg.val = 0; if (semctl(id, 0, IPC_RMID, semctl_arg) < 0) err_sys("Nu pot IPC_RMID"); } /* * Inchide un semafor.Se va decrementa contorul de procese. Daca * este ultimul proces, atunci se va sterge semaforul. */ sem_close(id) int id; { register int semval; /* * Mai intai semop blocheaza semaforul, apoi incrementeaza * contorul de procese */ if (semop(id, &op_close[0], 3) < 0) err_sys("Nu pot semop"); /* * Dupa blocare, citeste valoarea contorului de procese si * vede daca este ultima referire la el. Daca da, atunci ? */ semctl_arg.val = 0; if ((semval = semctl(id, 1, GETVAL, semctl_arg)) < 0) err_sys("Nu pot GETVAL"); if (semval > BIGCOUNT) err_sys("sem[1]>BIGCOUNT"); else if (semval == BIGCOUNT) sem_rm(id); else if (semop(id, &op_unlock[0], 1) < 0) err_sys("Nu pot unlock"); } /* * ********************************************************* * Asteapta pana cand valoarea semaforului este > 0, apoi * decrementeaza cu 1. Este vorba de operatia P a lui Dijkstra * sau DOWN a lui Tanenbaum */ sem_wait(id) int id; { sem_op(id, -1); } /* * ********************************************************* * Incrementeaza semaforul cu 1. Este vorba de operatia V a lui * Dijkstra sau UP a lui Tanenbaum */ sem_signal(id) int id; { sem_op(id, 1); } /* * ********************************************************* * Operatii generale cu semaforul. Incrementeaza sau decrementeaza * cu o cantitate specificata, care trebuie sa fie diferita de * zero */ sem_op(id, cantitate) int id, cantitate; { if ((op_op[0].sem_op = cantitate) == 0) err_sys("Cantitate egala cu zero"); if (semop(id, &op_op[0], 1), 0) err_sys("Eroare sem_op"); }