Documente Academic
Documente Profesional
Documente Cultură
Laborator nr. 9
Atunci cind mai multe procese concureaza pentru resursele sistemului este nevoie
de un mecanism de sincronizare (printre aceste resurse sunt si regiunile critice ale
programelor). Bazele lucrului cu semafoare au fost puse de Dijkstra.
Un proces poate avea acces la un set de semafoare daca indeplineste una din
conditiile urmatoare:
procesul este al superuser-ului;
ID utilizatorului, uid, coincide cu sem_perm.cuid sau cu sem_perm.uid si este
prevazut in sem_perm.mode dreptul de acces dorit;
utilizatorul are ID de grup, gid, identic cu sem_perm.cgid sau cu
sem_perm.gid si este prevazut in sem_perm.mode dreptul de acces dorit;
utilizatorul face parte din categoria "alti utilizatori" si este prevazut in
sem_perm.mode dreptul de acces dorit.
Structura de date asociata unui semafor este:
struct sem {
ushort semval;
pid_t sempid;
ushort semncnt;
ushort semzcnt;
};
unde:
semval - este valoarea semaforului, o valoare intreaga mai mare sau egala cu zero;
sempid - reprezinta ID ultimului proces care a operat asupra semaforului;
Sisteme de Operare Laborator nr. 9
semncnt - este numarul proceselor care asteapta ca valoarea semaforului semval sa
devina mai mare decat valoarea sa actuala;
semzcnt - este numarul proceselor care asteapta ca valoarea semaforului semval sa
devina zero.
int semid;
semid=semget( semkey, 1, SEMPERM | IPC_CREAT);
if ( semctl( semid, 0, SETVAL, 1) < 0)
err_sys("Eroare semctl");
return semid;
}
4.2. Acelasi program, dar functiile P si V sunt implementate fara a folosi o functie
suplimentara.
/* 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)
{
int status, semid;
semid=semget(semkey, 1, SEMPERM | IPC_CREAT);
status=semctl(semid, 0, SETVAL, 1);
return semid;
}
p(int semid)
{
struc sembuf pbuf;
pbuf.sem_num=0;
pbuf.sem_op=1;
pbuf.sem_flg=SEM_UNDO;
semop(semid, &pbuf, 1);
return 0;
}
v(int semid)
{
struct sembuf vbuf;
vbuf.sem_num = 0;
vbuf.sem_op=1;
vbuf.sem_flg=SEM_UNDO;
semop(semid, &vbuf, 1);
return 0;
}
handlesem(int semid)
{
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);
}
Sisteme de Operare Laborator nr. 9
main()
{
key_t semkey=0x200;
int semid;
semid=initsem(semkey);
if(fork() == 0)
handlesem(semid);
if(fork() == 0)
handlesem(semid);
if(fork() == 0)
handlesem(semid);
}
4.3. Un exemplu care combina doua mecanisme de comunicare intre procese este
implementarea problemei producator-consumator. In implementarea de fata se considera
ca procesele producator sunt identice intre ele. Un proces producator are ca scop
"producerea" pe rind a caracterelor din intervalul [a,z]. Fiecare proces producator va
genera 26 de caractere. Bufferul este circular si este partajat de procesele producator si
consumator. Procesele consumator sunt si ele identice intre ele si au ca scop
"consumarea" unui caracter din buffer. Dimensiunea bufferului se considera a fi fixa. Daca
bufferul este plin, procesele producatoare sunt puse in asteptare. In mod analog, daca
bufferul este gol, procesele consumatoare sunt puse in asteptare. O operatie asupra
bufferului este o operatie atomica, care se desfasoara intr-o regiune critica.
Pentru implementarea problemei s-a considerat ca bufferul este implementat intr-o
zona de memorie comuna. Zona respectiva contine numarul proceselor producator, doi
indecsi ce permit localizarea primului loc liber si primului loc ocupat din buffer si bufferul
propriu-zis. Structura este definita in fisierul pcsem.c. Sunt necesare trei semafoare:
spacesleft - semafor ce indica spatiul liber din buffer;
itemsready - semafor ce indica numarul de articole produse;
mutex - semafor binar care asigura excluderea mutuala
Primitivele P si V care opereaza asupra semafoarelor au fost implementate folosind apelul
sistem semop.
/******************** PCSEM.h ***********************
Problema producatorconsumator
*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <stdio.h>
#include "hdr.h"
#define SEMPERM 0600
#define N 3
#define BUFFMAX 4
key_t semkey=0x700;
key_t shmkey=0x800;
struct databuf {
int nr_prod;
int nextin;
int nextout;
char d_buff[BUFFMAX];
};
typedef enum { spacesleft, itemsready, mutex} semaphore;
int P( int semid, int name)
{
struct sembuf pbuf;
Sisteme de Operare Laborator nr. 9
pbuf.sem_num=name;
pbuf.sem_op = 1;
pbuf.sem_flg=0;
semop( semid, &pbuf, 1);
return 0;
}
int V( int semid, int name)
{
struct sembuf pbuf;
pbuf.sem_num=name;
pbuf.sem_op = 1;
pbuf.sem_flg=0;
semop( semid, &pbuf, 1);
return 0;
}
void afis( char *p, int l1, int l2)
{
int i;
for ( i=l1; i<l2+1; (i++) % (BUFFMAX+1))
putchar(p[i]);
putchar('\n');
}
Pentru a vizualiza continutul bufferului la fiecare adaugare a unui articol in buffer a
fost implementata functia afis. Programul pentru un proces producator este urmatorul:
/******************** PROD.C *********************/
/*
A se compila cu: gcc o prod prod.c err.o
*/
#include "pcsem.h"
main( void)
{
producer();
}
void put( char ch)
{
int shmid;
struct databuf *pbuf;
char *pd_buf;
shmid=shmget( shmkey, 0, SEMPERM);
pbuf=( struct databuf *) shmat( shmid, 0, 0);
pd_buf=pbuf>d_buff;
pd_buf[pbuf>nextin] = ch;
pbuf>nextin = (pbuf>nextin + 1) % (BUFFMAX + 1);
/* afisarea completa a bufferului circular */
afis( pd_buf, 0, 4);
}
producer( void)
{
char local;
int semid, shmid;
struct databuf *pbuf;
shmid=shmget( shmkey, 0, SEMPERM);
pbuf=( struct databuf *) shmat( shmid, 0, 0);
pbuf>nr_prod++;
semid=semget( semkey, 0, SEMPERM);
for ( local= 'a'; local <= 'z'; local++) {
P( semid, spacesleft);
P( semid, mutex);
Sisteme de Operare Laborator nr. 9
put( local);
V( semid, mutex);
V( semid, itemsready);
}
pbuf>nr_prod;
exit(0);
}
Se poate observa pe acest cod ca procesul producator este blocat la accesul
bufferului in situatia in care bufferul este plin sau daca exista un alt proces care executa
vreo operatie asupra bufferului. Campul nr_prod din structura bufferului esste folosit la
terminarea proceselor consumator. Codul unui program consumator este:
/*********************** CONS.C ********************/
/*
A se compila cu: gcc o cons cons.c err.o
*/
#include "pcsem.h"
#include <signal.h>
main()
{
consumer();
}
char take()
{
int shmid;
struct databuf *pbuf;
char cch, *pd_buf;
shmid=shmget( shmkey, 0, SEMPERM);
pbuf=( struct databuf *) shmat( shmid, 0, 0);
pd_buf=pbuf>d_buff;
cch = pd_buf[pbuf>nextout];
pd_buf[pbuf>nextout]=' ';
pbuf>nextout = (pbuf>nextout + 1) % (BUFFMAX + 1);
return cch;
}
int E_prod_buf()
{
int shmid;
struct databuf *pbuf;
char *pd_buf;
shmid=shmget( shmkey, 0, SEMPERM);
pbuf=( struct databuf *) shmat( shmid, 0, 0);
pd_buf=pbuf>d_buff;
/* Consumatorul se termina cand nu mai sunt producatori */
if ( pbuf>nr_prod==0 && pd_buf[pbuf>nextout] == ' ') {
/* poate apare vreun producator in 10 sec ? */
sleep( 10);
if ( pbuf>nr_prod==0 && pd_buf[pbuf>nextout] == ' ') {
kill(getpid(),SIGTERM);
return 0;
}
else /* A aparut cel putin un producator */
return 1;
} else {
/* Consumatorul a luat caracterul inainte ca producatorul sa
consemneze ca a terminat.*/
if ( pd_buf[pbuf>nextout] != ' ' || pbuf>nr_prod > 1)
return 1;
sleep( 10);
if ( pbuf>nr_prod==0) {
kill( getpid(),SIGTERM);
Sisteme de Operare Laborator nr. 9
return 0;
} else
return 1;
}
}
consumer( void)
{
char local;
int semid;
semid=semget( semkey, 0, SEMPERM);
while ( E_prod_buf()) {
P( semid, itemsready);
P( semid, mutex);
local=take();
printf("Consumat %d: %c\n", getpid(), local);
V( semid, mutex);
V( semid, spacesleft);
}
printf("Consumator %d terminat.\n", getpid());
exit(0);
}
Analog, procesul consumator este blocat la accesul bufferulului daca bufferul este
gol sau daca alt proces executa vreo operatie asupra acestuia. Conditia de terminare a
unui proces consumator este stabilita de functia E_prod_buf, care verifica daca mai sunt
procese producator si daca toate caracterele din buffer au fost consumate. Daca timp de
10 secunde bufferul este gol, un proces consumator poate conchide ca acesta se poate
termina. Acest lucru este realizat prin transmiterea semnalului SIGTERM la propriul
proces.
Inainte de a testa acest program este necesar un program de initializare care
creeaza setul de semafoare si memoria partajata si initializeaza primele trei campuri ale
structurii databuf. Codul acestuia este:
/********************** INIT.C ******************
A se compila cu: gcc o init init.c err.o
*/
#include "pcsem.h"
#include <signal.h>
int shmid;
void initsem( int semid, int semnr, int val)
{
semctl( semid, semnr, SETVAL, val);
printf("Initializarea este: semnr=%d, val=%d\n", semnr, val);
}
main( void)
{
int semid, i;
struct databuf *pbuf;
if ( (semid=semget( semkey, N, SEMPERM | IPC_CREAT)) < 0)
err_sys("Eroare semget");
if ( (shmid=shmget( shmkey, sizeof( struct databuf), IPC_CREAT | IPC_EXCL
| 0600 )) < 0)
err_sys("Eroare shmget");
initsem( semid, spacesleft, BUFFMAX + 1);
initsem( semid, itemsready, 0);
initsem( semid, mutex, 1);
pbuf=( struct databuf *) shmat( shmid, 0, 0);
pbuf>nextin=0; pbuf>nextout=0; pbuf>nr_prod=0;
}
Functia initsem permite initializarea semaforului precizat de argumentul al doilea
Sisteme de Operare Laborator nr. 9
la valoarea precizata de argumentul al treilea.Pentru a sterge zona de memorie comuna
dupa executia programelor producator si consumator, se foloseste programul:
/******************* DEL.C *****************
A se compila cu: gcc o del del.c err.o
*/
#include "pcsem.h"
main()
{
int shmid, semid;
if ( (shmid=shmget( shmkey, 0, 0)) < 0)
err_sys("Eroare Nu exista zona de memorie ");
if ( shmctl( shmid, IPC_RMID, 0) < 0)
err_sys("Eroare shmctl");
if ( (semid=semget( semkey, 0, 0)) < 0)
err_sys("Eroare Nu exista setul de semafoare ");
if ( semctl( semid, 0, IPC_RMID, 0) < 0)
err_sys("Eroare semctl");
printf("OK.\n");
exit(0);
}