Acest lab trateaza implementarea semafoarelor in Unix System V. Desigur este nevoie de un mecanism de sincronizare atunci cind mai multe procese concureaza pentru resursele sistemului (printre aceste resurse sunt si  regiunile critice ale programelor). Bazele lucrului cu semafoare au fost puse 
   de olandezul Dijkstra. Mai intii sa vedem care sunt premisele teoretice ale 
   problemei. Apoi vom vedea cum sunt implementate ele in Unix System V.  h8j20jk
  O situatie tipica este urmatoarea: doua procese vor sa acceseze  acelasi segment de memorie. Ele nu pot accesa memoria direct, ci trebuie sa 
   folosesca un semafor (sau variabila de sincronizare), pentru a vedea daca in 
   momentul respectiv este permis accesul la segmentul de memorie respectiv.
  Un semafor poate fi privit ca o variabila care ia numai valori intregi  si pozitive. Pentru a se putea realiza sincronizarea, este nevoie sa fie  implementate doua operatii de baza, blocarea (p(sem)) si eliberarea (v(sem)).
  Aceste doua operatii trebuie sa fie atomice, cu alte cuvinte nimeni nu are  voie sa aiba o prioritate atit de mare incit sa le intrerupa. Din acest motiv 
   cele doua operatii trebuie sa fie implementate ca apeluri ale nucleului.
  Operatia p arata in felul urmator:
 p(sem) =  daca(sem este diferit de 0) decrementeaza sem altfel asteapta pina cind sem devine diferit de 0;
 Iar schema pentru operatia v este urmatoarea:
 v(sem) = daca(exista procese care asteapta ca sem sa devina diferit de 0) activeaza primul proces care asteapta  altfel incrementeaza sem;
 Sub Unix System V nu se lucreaza cu un singur semafor, ci apelurile  sistem trateaza grupuri de semafoare. Aceste grupuri (tablouri) de semafoare 
   sunt accesate de procese printr-o cheie globala de identificare. Aceasta  generalizare a problemei duce la o usoara complicare a utilizarii semafoarelor.
  Dar baza ultima ramine aceeasi si anume ca toate operatiile pe grupuri de  semafoare trebuie sa fie atomice. Aceasta presupunere usureaza mult munca  celor care dezvolta de exemplu baze de date.
 
 Iata care sunt variabilele si structurile implicate in descrierea  apelurilor pentru utilizarea semafoarelor in Unix System V:
#include <sys/types.h>
  #include <sys/ipc.h>
  #include <sys/sem.h>
key_t cheie; int semid, nsem, permisii,comanda; int retur,semnr;
union semunA int val; struct semid_ds *stat; ushort *array;
  Sctl_arg;
 Pentru crearea unui grup de semafoare se foloseste apelul semget:
 semid=semget(cheie, nsem, permisii);
 Prin acest apel se creeaza un grup de semafoare care va fi recunoscut  de catre orice proces prin cheia de identificare cheie. Spre deosebire de  cheie, care este globala, identificatorul semid intors de catre apel este  local pentru proces. Va fi folosit pentru referinta la semafor de catre  apelurile urmatoare. Parametrul nsem indica numarul de semafoare care vor  forma noul grup. Ca si la memoria partajata sau la mesaje, parametrul permisii 
   va contine drepturile de acces la semafor si atributele necesare apelului.
  Fiecarui semafor ii apartin citeva valori si anume:
 -semval: valoarea semaforului. Aceasta este intotdeauna un intreg pozitiv.
  Aceasta valoare se poate seta numai prin intermediul apelurilor sistem, accesul 
   direct nefiind posibil 
 -sempid: identificatorul ultimului proces care a efectuat o operatie asupra 
   semaforului
 -semncnt: numarul de procese care asteapta ca semaforul sa primeasca o valoare 
   mai mare decit cea curenta.
 -semzcnt: numarul de procese care asteapta ca semaforul sa aiba valoarea zero.
 Setarea acestor parametri se face dupa crearea semaforului. Pentru aceasa 
   se foloseste apelul semctl:
 retur=semctl(semid, semnr, comanda, ctl_arg);
 Apelul semctl este semnificativ mai complicat decit apelul msgctl.
  Parametrul semid este identificatorul local al grupului de mesaje. Parametrul 
   semnr specifica al citelea semafor din grup va fi afectat de apel. Prin  parametrul comanda alegem actiunea dorita. Intr-o prima grupa de actiuni sunt 
   actiuni obisnuite IPC, cum ar fi: 
IPC_STAT:informatiile de stare vor fi in ctl_arg.stat,
  IPC_SET:drepturile de acces vor fi tot in ctl_arg.stat,
  IPC_RMID:distrugerea grupului de semafoare.
 A doua grupa de actiuni le contine pe cele care se refera la un singur  semafor din grup (specificat prin semnr):
GETVAL:intoarce valoarea semaforului,
  SETVAL:seteaza valoarea semaforului data prin ctl_arg.val,
  GETPID:intoarce valoarea din sempid,
  GETNCNT:intoarce valoarea din semncnt,
  GETZCNT:intoarce valoarea din semzcnt.
 Tot in acest al doilea grup se includ si citeva actiuni asupra tuturor  semafoarelor din grup:
GETALL:pune toate valorile semafoarelor in tabloul ctl_arg.array,
  SETALL:seteaza toate valorile semafoarelor cu valorile din tabloul ctl_arg.array.
 Ultimul parametru al apelului semctl este de fapt un union cu trei  tipuri diferite. Fiecare actiune trateaza aceasta valoare dupa cum are nevoie.
  Dupa ce semaforul a fost initializat, pot incepe de fapt operatiile cu el.
  Pentru aceste operatii exista apelul semop, care este atomic. Acest lucru  inseamna ca semop asteapta pina in momentul in care toate operatiile  oplist din apel se pot executa deodata. Astfel, ori sunt terminate toate  operatiile, ori niciuna. Realizarea doar a unei parti a operatiilor nu se  intimpla niciodata.Iata apelul lui semop:
 veche=semop(semid,oplist,n);
 Prin aceasta toate operatiile din lista oplist se vor executa asupra  grupului de semafoare semid. veche va contine valoarea ultimului semafor  modificat. In oplist va fi prezenta o lista de n operatii de executat. Lista 
   de operatii este de fapt un tablou. Elementele acestui tablou sint de tipul 
   sembuf. Aceasta structura contine trei valori de tipul short. Acestea sunt  sem_num, sem_op si sem_flag. sem_num specifica numarul semaforului afectat in 
   cadrul grupului specificat de semid. sem_op este putin mai complicat. Vom  deosebi trei cazuri in functie de valoarea acestui element:
  
  -numar pozitiv intreg(+n): aceasta valoare va fi adunata la valoarea curenta a semaforului. Prin aceasta vor fi activate toate procesele care asteapta aceasta 
   valoare.
  -zero: la aceasta valoare se va astepta pina cind semaforul dorit din grup va 
   deveni zero in cazul in care nu s-a specificat comutatorul IPC_NOWAIT in sem_flag 
   caz in care se semnaleaza eroare.
  -numar negativ intreg (-n): in acest caz se impune o noua impartire:daca  valoarea curenta a semaforului este mai mare decit (n), valoarea va fi redusa 
   cu n. Prin aceasta se asigura ca valoarea nu va deveni mai mica decit zero. 
  
  Daca se nimereste ca noua valoare sa fie chiar zero atunci toate procesele  care asteapta vor fi activate. In cazul in care valoarea absoluta a lui n este 
   mai mare decit valoarea semaforului, reducerea valorii semaforului nu va avea 
   succes. In acest caz ar aparea o valoare negativa care nu este permisa. Este 
   posibila doar urmatoarea strategie: asteptarea pina valoarea curenta a  semaforului devine mai mare. Comutatorii din sem_flag specifica diverse  moduri de a trata actiunea. Daca este data valoarea IPC_NOWAIT, din nou  asteptarea va fi intrerupta si veche va fi valoarea -1. Aceasta in Unix inseamna caz de eroare, iar variabila globala errno va fi setata pe valoarea 
  
  EAGAIN.
  Daca se foloseste comutatorul SEM_UNDO atunci la terminarea prin exit  a unui proces, operatii pe semafor cu semop vor fi facute in sens invers. Prin 
   aceasta se poate evita un eventual punct mort (deadlock). Este ca si cum un 
   proces blocheaza un semafor, apoi primeste un semnal neasteptat si se termina 
   fara sa deblocheze semaforul.
  In exemplu sunt implementate operatiile p si v. Pentru aceasta s-a  scris mai intii o functie de initializare a semaforului, initsemkey care  creaza un semafor si ii da valoarea initiala 1.
  Functiile p si v sunt implementate cu ajutorul apelului semop. Se  lanseaza trei procese distincte, cu apelul fork, care se sincronizeaza cu  ajutorul semaforului si a operatiilor p si v. 
 
/* pv.h */
  #include <sys/types.h>
  #include <sys/ipc.h>
  #include <sys/sem.h> 
  #define SEMPERM 0600 
/* oplist.c */
  #include "pv.h"
initsem(Key_t semkey)
  A  int status, semid; semid=semget(semkey, 1, SEMPERM | IPC_CREAT); status=semctl(semid, 0, SETVAL, 1); return semid;
  S p(int semid)
  A struc sembuf pbuf; pbuf.sem_num=0; pbuf.sem_op=-1; pbuf.sem_flg=SEM_UNDO; semop(semid, &pbuf, 1); return 0;
  S
v(int semid)
  A struct sembuf vbuf; vbuf.sem_num = 0; vbuf.sem_op=1; vbuf.sem_flg=SEM_UNDO; semop(semid, &vbuf, 1); return 0;
  S
handlesem(int semid)
  A int pid=getpid(); printf("Procesul %d inainte de regiunea critica\n",pid); p(semid); printf("Procesul %d in regiunea critica\n", pid); sleep(10); printf("Procesul %d paraseste regiunea critica\n",pid); v(semid); printf("Procesul %d terminat\n",pid); exit(0);
  S
main()
  A key_t semkey=0x200; int semid; semid=initsem(semkey); if(fork() == 0) handlesem(semid); if(fork() == 0) handlesem(semid); if(fork() == 0) handlesem(semid);
  S
  /* reader-writer.c */ 
  #include <stdio.h>
  #include <sys/types.h>
  #include <sys/ipc.h>
  #include <sys/sem.h> 
  #include <sys/shm.h>
#define SEMKEY1 (key_t)0x10
  #define SEMKEY2 (key_t)0x15
  #define SEMKEY (key_t)0x20
  #define SIZ 5*BUFSIZ
struct data_bufA int d_nread; char d_bufaSIZi;
  S;
main()
  A static int semid, shmid1, shmid2; int pid; struct databuf *buf1, *buf2; semid=semget(SEMKEY, 2, 0600 | IPC_CREAT |IPC_EXCL); semctl(semid, 0, SETVAL, 0); semctl(semid, 1, SETVAL, 0); shmid1=shmget(SHMKEY1, sizeof(struct databuf), 0600 | IPC_CREAT |IPC_EXCL); shmid2=shmget(SHMKEY2, sizeof(struct databuf), 0600 | IPC_CREAT |IPC_EXCL); buf1=(struct databuf *)shmat(shmid1, 0, 0); buf2=(struct databuf *)shmat(shmid2, 0, 0); if((pid=fork()) == 0)
  A
  /* in procesul fiu */ reader(semid, buf1, buf2); shmctl(shmid1, IPC_RMID, 0); shmctl(shmid2, IPC_RMID, 0); semctl(semid, 0, IPC_ RMID, 0); exit(0);
  S
 /* in procesul parinte *
  writer(semid, buf1, buf2);
  S
struct sembuf p1=A0, -1, 0S; struct sembuf p2=A1, -1, 0S; struct sembuf v1=A0, +1, 0S; struct sembuf v2=A1, +1, 0S;
writer(int semid, struct databuf *buf1, struct databuf *buf2)
  A  for(;;)A buf1->d_nread=read(0, buf1->d_buf, SIZ); semop(semid, &v1, 1); semop(semid, &p2, 1); if(buf1->d_nread <= 0) return 0; buf2->d_nread=read(0, buf1->d_buf, SIZ); semop(semid, &v2, 1); semop(semid, &p1, 1); if(buf2->d_nread <= 0) return 0;
  S
 reader(int semid, struct databuf *buf1, struct databuf *buf2)
  A  sleep(5); for(;;)A semop(semid, &p1, 1); semop(semid, &v2, 1); if(buf1->d_nread <= 0) return 0;
  write(1, buf1->d_buf, buf1->d_nread); semop(semid, &p2, 1); semop(semid, &v1, 1); if(buf2->d_nread <= 0) return 0;
  write(1, buf2->d_buf, buf2->d_nread);
  S