220 likes | 433 Views
Comunicarea intre procese prin semafoare UNIX. Marinescu Ionela-Anisoara.
E N D
Comunicarea intre procese prin semafoare UNIX Marinescu Ionela-Anisoara
Noţiunea de semafor a fost introdusă de Dijkstra şi Scholten. Implementările unor primitive de sincronizare a proceselor, cum ar fi wait_signal, au fost făcute de Brinch Hansen, altele, de tipul P_V, de Dijkstra, Patil, Saal, Ridle etc. Semafoarele sunt resurse IPC folosite pentru sincronizarea intre procese (e.g. pentru controlul accesului la resurse). Operatiile asupra unui semafor pot fi de setare sau verificare a valorii (care poate fi mai mare sau egala cu 0) sau de test and set. Un semafor poate fi privit ca un contor ce poate fi incrementat si decrementat, dar a carui valoare nu poate scadea sub 0. Un identificator id permite accesul la o multime de nsops semafoare - un apel semget ofera accesul nu la un singur semafor, ci la un set/multime de semafoare (eventual continand un singur semafor). Operatiile de citire, incrementare sau decrementare a semafoarelor dintr-o multime sunt executate printr-un apel semop(), care proceseaza nsops operatii la un moment dat, operatii aplicate numai in cazul in care toate se termina cu succes; fiecare operatie e specificata printr-o structura sembuf. Operatiile asupra semafoarelor pot avea un flag SEM_UNDO, care comanda ca la terminarea procesului rezultatul operatiei sa fie undone.
Daca se incearca o operatie de decrementare si aceasta nu se poate produce (valoarea semaforului nu poate scadea sub 0), procesul va fi pus in asteptare intr-o coada pana la incrementarea valorii semaforului - semval (daca nu a fost specificat IPC_NOWAIT). O operatie de citire poate duce, la fel, la punerea procesului in asteptare intr-o coada, pana cand semval devine 0. Sunt prezentate în continuare cele mai cunoscute primitive de sincronizare P şi V. P(v) { e(v) = e(v) - 1; if ( e(v) < 0) { stare_proces = blocat; procesul este trecut în coada f(v); } }
V(v) { e(v) = e(v) + 1; if ( e(v) <= 0) { selecteaza un proces din coada f(v); stare_proces_selectat = activ; } } Valoarea curentă a semaforului este: e(v) = e0(v) + nV(v) - nP(v) unde nV(v) reprezintă numărul de primitive V executate asupra semaforului v, iar nP(v) reprezintă numărul de primitive P executate asupra semaforului v. Pentru realizarea unei regiuni (secţiuni) critice se va proceda astfel: valoarea iniţială a semaforului este 1, iar regiunea critică este delimitată de primitiva P şi V astfel: P(v); regiune critică; V(v); rest proces;
Se subliniază faptul că primitivele P şi V, şi în general toate primitivele de sincronizare, sunt indivizibile (atomice). În UNIX System V, noţiunea de semafor a fost generalizată. Astfel, asupra unui semafor pot fi date mai multe operaţii simultan (primitivele P şi V erau apelate la momente de timp diferite într-un proces), iar valoarea de decrementare sau incrementare nu trebuie să fie neapărat 1. Toate operaţiile executate de funcţiile de sistem aferente sunt indivizibile. Dacă nucleul nu poate executa toate operaţiile cerute într-un proces, nu se executa nici una, procesul fiind trecut în stare de aşteptare până când vor putea fi executate toate operaţiile. Un proces poate crea un ansamblu de semafoare. Ansamblul are o intrare în tabela semafoarelor, care constituie antetul setului. Nucleul asociază procesului o tabelă cu atâtea elemente, câte semafoare sunt în set. Fiecare element păstrează valoarea asociată semaforului corespondent. Gestionarea semafoarelor este asemănătoare cu gestionarea memoriei comune şi a cozilor de mesaje. Astfel, un proces poate avea acces la structurile legate de semafoare numai dacă cunoaşte cheia asociată. Intern setul de semafoare este identificat printr-un număr întreg, numit identificatorul setului.
Prin intermediul identificatorului setului, un proces poate opera asupra unui anumit semafor. Un set de semafoare are asociate: un identificator, care este un număr întreg pozitiv, similar cu descriptorul de fişier şi o structură de date de tipul semid_ds. struct semid_ds { struct ipc_perm sem_perm; struct sem *sem_base; int sem_nsens; time_t sem_otime; time_t sem_ctime; }; Câmpurile structurii au următoarele semnificaţii: • sem_perm -este o structură de tipul ipc_perm prezentată deja. • Sem_base -este un pointer la primul semafor din set. Nu este util unui proces utilizator, deoarece referă o adresă în nucleu • .Sem_nsens -indică numărul semafoarelor din set. Fiecare semafor din ansamblu are un număr de identificare între 0 şi numărul semafoarelor minus 1, numit sem_num;
Sem_otime-indică timpul la care a avut loc ultima operaţie asupra ansamblului de semafoare prin operaţia semop; • Sem_ctime-indică timpul la care a avut loc ultima modificare a structurii de date. Un proces poate avea acces la un set de semafoare dacă îndeplineşte una din condiţiile următoare: • Procesul este al superuser-ului; • ID utilizatorului, uid, coincide cu sem_perm.cuid sau cu sem_perm.uid şi este prevăzut în sem_perm.mode dreptul de acces dorit; • utilizatorul are ID de grup, gid, identic cu sem_perm.cgid sau cu sem_perm.gid şi este prevăzut în sem_perm.mode dreptul de acces dorit; • utilizatorul face parte din categoria "alţi utilizatori" şi este prevăzut în sem_perm.mode dreptul de acces dorit.
Structura de date asociată unui semafor este: struct sem { ushort semval; pid_t sempid; ushort semncnt; ushort semzcnt; }; unde, • semval este valoarea semaforului, o valoare întreagă mai mare sau egală cu zero; • sempid reprezintă ID ultimului proces care a operat asupra semaforului; • semncnt este numărul proceselor care aşteaptă ca valoarea semaforului semval să devină mai mare decât valoarea sa actuală; • semzcnt este numărul proceselor care aşteaptă ca valoarea semaforului semval să devină zero.
Functia de sistem semget O multime de semafoare e alocata printr-un apel de sistem semget() : int semid = semget(key_t key, int nsems, int semflg); Parametri : • key - un intreg obtinut de obicei prin ftok() sau IPC_PRIVATE • nsems - numarul de semafoare in multime (0 <= nsems <= SEMMSL; 0 inseamna don't care) semflg • IPC_CREAT - creeaza o resursa daca ea nu exista deja • IPC_CREAT | IPC_EXCL - daca resursa exista deja, apelul esueaza • rwxrwxrwx - permisiuni de acces specificate ca in cazul fisierelor • Funcţia de sistem semget permite obţinerea ID unui set de semafoare. Dacă setul de semafoare nu există anterior, acesta este creat. Indiferent dacă setul există sau nu, procesul trebuie să cunoască cheia asociată semaforului. Interfaţa funcţiei semget este: #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget( key_t cheie, int nrsem, int ind);
Returnează ID semaforului în caz de succes, (-1) în caz de eroare. unde, • cheie reprezintă cheia ataşată setului de semafoare; • nrsem este numărul semafoarelor din set. Dacă un nou set este creat (de regula în server), trebuie precizată valoarea nrsem. Dacă se refera un set care exista, valoarea nrsem este 0. • ind indică acţiunea de creare şi drepturile de acces la setul de semafoare. Pentru a crea un set de semafoare, trebuie precizate toate cele trei argumente, iar indicatorul IPC_CREAT trebuie poziţionat. Este recomandată şi poziţionarea indicatorului IPC_EXCL cu rolul de a semnala existentă unui set de semafoare având ataşată aceeaşi cheie. Argumentul ind este specificat sub forma: drepturi_de_acces | IPC_CREAT | IPC_EXCL Dacă nu este setat indicatorul IPC_EXCL, atunci în cazul existentei setului creat anterior asociat la cheia indicată, nu se semnalează eroare, ci se returnează ID setului.
Un exemplu de creare a unui set de semafoare: #define KEY 1995 semid=semget( (key_t) KEY, 5, 0666 | IPC_CREAT); Dacă se doreşte numai obţinerea ID unui set de semafoare, apelul funcţiei semget se face precizând valoarea cheii şi zero pentru ceilalţi parametri. La crearea unui set de semafoare, structura de date asociată este completată cu următoarele informaţii: • sem_perm.cuid, sem_perm.uid conţin ID utilizatorului ataşat procesului care a lansat apelul semget; • sem_perm.cgid, sem_perm.uid conţin ID grupului de utilizatori ataşat procesului care a lansat apelul semget; • sem_perm.mode conţine drepturile de acces prevăzute în apelul funcţiei semget de argumentul ind; • sem_nsems conţine numărul de semafoare indicat în apelul funcţiei semget; • sem_ctime este completat cu valoarea curentă a timpului. • sem_otime are valoarea0.
Un set de semafoare poate să nu aibă cheie de acces ataşată. În acest caz, la creare setului, apelul funcţiei semget se face precizând drept valoare pentru cheie constantă simbolică IPC_PRIVATE. Funcţia de sistem semctl Funcţia de sistem semctl are rolul de a efectua o serie de operaţii asupra structurii asociate unui set de semafoare: citirea şi modificarea informaţiilor din structura de date asociată setului de semafoare sau din cea asociată unui semafor sau ştergerea setului. vector (0 < nsops < SEMOPM) Interfaţă funcţiei semctl este: #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semctl( int semid, int nr_sem, int cmd, union semun arg);
Returnează valorile aferente pentru toate comenzile GET, excepţie făcând comanda GETALL. În rest 0. unde, • semid reprezintă ID setului de semafoare obţinut prin apelul funcţiei semget; • nr_sem este numărul semaforului din set asupra căruia se executa comanda cmd indicată în funcţia semctl (numerotarea începe cu 0); • cmd este operaţia dorită, având următoarele valori: • GETVAL-Funcţia returnează valoarea semval a semaforului cu numărul nr_sem; • SETVAL-Actualizează valoarea semval a semaforului cu numărul nr_sem la valoarea specificată de câmpul arg.val. • GETPID-Funcţia returnează valoarea sempid pentru semaforul nr_sem; • GETNCNT-Funcţia returnează valoarea semncnt pentru semaforul nr_sem; • GETZCNT-Funcţia returnează valoarea semzcnt pentru semaforul nr_sem; • GETALL-Valorile semval ale tuturor semafoarelor din set sunt memorate în tabloul referit de arg.array;
SETALL-Valorile semval ale tuturor semafoarelor din set sunt actualizate la valorile corespondente din tabloul arg.array. • IPC_STAT-Elementele structurii semid_ds sunt salvate în structura referita de arg.buf; • IPC_SET- Valorile pentru câmpurile sem_perm.uid, sem_perm.gid şi sem_perm.mode sunt actualizate conform valorilor din arg.buf. Modificarea poate fi facută de un proces superuser sau de catre un proces al carui ID utilizator efectiv este egal cu sem_perm.uid sau sem_perm.cuid. • IPC_RMID-Ştergerea setului de semafoare. Ştergerea este imediată, astfel încât orice alt proces ce foloseşte semaforul va recepţiona o eroare la operaţii asupra semaforului ( EIDRM). Comanda poate fi executată numai de un proces superuser sau de un proces al cărui ID utilizator efectiv egal cu sem_perm.uid sau sem_perm.cuid. arg este un tip variabil pentru specificarea argumentelor operaţiei cmd: union semun { int val; /* pentru SETVAL */ struct semid_ds *buf; /* pentru IPC_STAT şi IPC_SET */ ushort *array; /* pentru GETALL şi SETALL */ };
Funcţia de sistem semop Operatiile pe semafoare sunt executate de apeluri semop() : int semop(int semid, struct sembuf *sops, unsigned nsops); Parametri : • semid - ID-ul obtinut printr-un apel semget • sops - vectorul de operatii pe semafoare • nsops - numarul de operatii in vector (0 < nsops < SEMOPM) Intoarce semval pentru ultima operatie efectuata, sau -1 in caz de eroare. Funcţia de sistem semop are rolul de a efectua operaţii de adunare şi scădere asupra unor semafoare din set. Interfaţa funcţiei este: #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop( int semid, struct sembuf oper[], size_t nr_oper);
Returnează 0 în caz de succes, (-1) în caz de eroare. unde, • semid reprezintă ID setului de semafoare obtinut prin apelul functiei semget; • oper este un pointer la un set de nr_oper structuri de tipul sembuf. Structura sembuf este definită astfel: struct sembuf { ushort sem_num; short sem_op; short sem_flg; /* IPC_NOWAIT, SEM_UNDO */ }; unde, • sem_num indică numărul semaforului din set identificat prin semid. • sem_op indică operaţia de efectuat după convenţiile date de valoarea lui sem_flg. • Operaţia pe fiecare membru al setului este specificată de valoarea aferenta sem_op.
Dacă: sem_op < 0 a) Dacă semval >= |sem_op|, atunci semval = semval - |sem_op|. Acest lucru asigură că valoarea rezultată pentru semafor este >= 0. Dacă indicatorul SEM_UNDO este poziţionat, atunci |sem_op| este adunat la semadj corespondent semaforului în tabela asociată în proces pentru setul de semafoare; b) Dacă (semval < |sem_op|) & (sem_flg & IPC_NOWAIT) = true, funcţia returnează un cod de eroare; c) Dacă (semval < |sem_op) şi indicatorul IPC_NOWAIT nu este specificat, procesul este blocat până când se îndeplineşte una dintre condiţiile: semval >= |sem_op| (alt proces a eliberat vreo resursa); semaforul este şters din sistem. Funcţia returnează în acest caz (-1) şi errno=ERMID. dacă un semnal este tratat de proces şi se revine din rutina de tratare. În acest caz valoarea semncnt pentru acest semafor este decrementată şi funcţia returnează (-1) cu errno=EINTR. sem_op > 0 atunci semval=semval + sem_op. Dacă indicatorul SEM_UNDO este pozitionat, atunci în tabela asociată în proces pentru setul de semafoare semadj = semadj - semop pentru semaforul asupra căruia se execută operaţia.
sem_op == 0 a) Dacă semval == 0, atunci funcţia se termină imediat; b) Dacă (sem_val <> 0) şi indicatorul IPC_NOWAIT este poziţionat, funcţia returnează (-1) şi errno=EAGAIN; c) Dacă (sem_val <> 0) şi indicatorul IPC_NOWAIT nu este poziţionat, procesul este blocat până când sem_val devine zero, sau setul de semafoare este distrus sau procesul primeşte un semnal. Indicatorul undo pentru un semafor este de fapt indicatorul corespunzător bitului SEM_UNDO din sem_flg. Bibliografie : [1].http://users.utcluj.ro/~somodi/so2/lab1314.html [2].http://www.cs.pub.ro/~so/index.php?section=Laboratoare&file=05.%20IPC%20(1)#Semafoare