Vous êtes sur la page 1sur 7

Sémaphores et exclusions mutuelles

1 Introduction
Dans ce deuxième TP nous allons aborder les outils fournis avec la librarie pthread pour assurer les
accès aux ressources partagées. Ces outils sont les sémaphores et les mutex (mutal exclusions). Pour
illustrer leurs utilisations, nous allons nous servir de l’algorithme du producteur/consommateur.

2 Un buffer circulaire
Un unique producteur (aussi appelé écrivain) et un unique consommateur (aussi appelé client ou
lecteur) partage un buffer circulaire stockant des valeurs entières.

Mise en œuvre des sémaphores :


– sema t désigne le type sémaphore
– sema init( &sem, count, NULL, NULL ) initialise le sémaphore sem à la valeur count
– sema wait( &sem ) ≡ down sur le sémaphore sem
– sema post( &sem ) ≡ up sur le sémaphore sem

#include <stdio.h>
#include <pthread.h>

#define BUFFER_SIZE 16

// buffer circulaire d’entiers


struct prodcons {
int buffer[BUFFER_SIZE]; // la zone de stockage
int readpos, writepos; // les positions des lecteurs et des écrivains
sema_t sem_read; // nombre d’éléments accessible à la lecture
sema_t sem_write; // nombre de place libre pour l’écriture
};

// Initialise le buffer
void init(struct prodcons * b)
{
// sémaphore équivalent au empty
sema_init(&b->sem_write, BUFFER_SIZE - 1, NULL, NULL );

// sémaphore équivalent au full


sema_init(&b->sem_read, 0, NULL, NULL );

b->readpos = 0;
b->writepos = 0;
}

1
// écriture d’un entier dans le buffer
void put(struct prodcons * b, int data)
{
// attendre que le buffer ne soit plus plein
sema_wait( &b->sem_write ); // down(&empty)

// ecrire la donnee et avancer le pointeur d’ecriture


b->buffer[b->writepos] = data;
b->writepos++;
if (b->writepos >= BUFFER_SIZE) b->writepos = 0;

// signaler que le buffer contient un element de plus


sema_post( &b->sem_read ); // up(&full)
}

// lecture et suppression d’un entier dans le buffer


int get(struct prodcons * b)
{
int data;

// attendre que le buffer ne soit plus vide


sema_wait( &b->sem_read ); // down(&full)

// lire la donnee et avancer le pointeur de lecture


data = b->buffer[b->readpos];
b->readpos++;
if (b->readpos >= BUFFER_SIZE) b->readpos = 0;

// signaler qu’il y a une place libre de plus


sema_post( &b->sem_write ); // up(&empty)

return data;
}

#define OVER (-1)

struct prodcons buffer;

void * producer(void * data)


{
int n;

for (n = 0; n < 10000; n++) {


printf("%d --->\n", n);
put(&buffer, n);
}

put( &buffer, OVER );

2
return NULL;
}

void * consumer(void * data)


{
int d;

while (1) {
d = get(&buffer);
if (d == OVER) break;
printf("---> %d\n", d);
}

return NULL;
}

int main(void)
{
pthread_t th_a, th_b;
void * retval;

init(&buffer);

// creation des threads


pthread_create(&th_a, NULL, producer, 0);
pthread_create(&th_b, NULL, consumer, 0);

// attendre la teminaison du producteur et du consommateur


pthread_join(th_a, &retval);
pthread_join(th_b, &retval);

return 0;
}

3 Une mémoire partagée


Plusieurs producteurs et plusieurs consommateurs partagent une mémoire : un enregistrement
contenant l’information et le numéro du client concerné.

Mise en œuvre des mutex :


– pthread mutex t désigne le type mutex
– pthread mutex init( &mutex, NULL ) initliase l’exclusion mutuelle mutex
– pthread mutex lock( &mutex ) ≡ down sur le sémaphore binaire mutex
– pthread mutex unlock( &mutex ) ≡ up sur le sémaphore binaire mutex

#include <stdio.h>
#include <pthread.h>

// Les constantes
#define OVER (-1)

3
#define EMPTY (-2)
#define NBITEM 5
#define NBCL 3 // nbre de clients
#define NBPD 3 // nbre de producteurs

// la strucuture partagée
typedef struct prodcons {
int word ; // la mémoire partagée
int consid ; // numero du client (>= 0)
pthread_mutex_t exclu; // la variable de mutex
} str_prodcons;

// la variable partagée
static str_prodcons buffer;

// trace des entiers lus par les clients


int val[NBCL,NBITEM] ;

// initialisation de la variable partagee


void init_mem( ) {
buffer.word = EMPTY ;
buffer.consid = EMPTY ;
pthread_mutex_init( &( buffer.exclu ), NULL ) ;
}

// stocke un mot en mémoire partagé... si possible


int put( int data,
int clientid /* consumer’s identifier */ ) {

int modif = 0 ; // pour savoir si il y a eu ecriture ou pas...

// attendre que l’accès soit libre


pthread_mutex_lock( &( buffer.exclu ) ) ;

// écrire le mot et le numero client.. si buffer vide


if( buffer.word == EMPTY ) {
buffer.word = data ;
buffer.consid = clientid ;
modif = 1 ; // noter que l’ecriture s’est faite...

// liberer la variable partagee


pthread_mutex_unlock( &( buffer.exclu ) ) ;

} else { // le buffer est plein


// liberer la variable partagee
pthread_mutex_unlock( &( buffer.exclu ) ) ;
// sleep( 10 ); // une pause si necessaire
}

return modif ;

4
}

// lecture et supprission du mot... si c’est le bon client


int get( int clientid /* consumer’s identifier */ ) {

int data ;

// attendre que l’accès soit libre


pthread_mutex_lock( &( buffer.exclu ) ) ;

// lire la donnee et effacer la memoire...


// si c’est pour le client clientid...
if( ( buffer.consid == clientid ) && ( buffer.word != EMPTY ) )
{
data = buffer.word ; // lire buffer
buffer.word = EMPTY ; // effacer buffer

// liberer l’acces à la memoire


pthread_mutex_unlock( &( buffer.exclu ) ) ;

} else { // la donnee n’est pas pour le client

// liberer la variable partagee


pthread_mutex_unlock( &( buffer.exclu ) ) ;
data = EMPTY ; // on a rien lu...
// sleep( 10 ) ; // une pause
}

return data ;
}

void *producer( void * arg ) {


int word,
prodid = ( int ) *arg, // numero du producteur
clientid = OVER; // numero des clients

for ( word=0; word<NBITEM ; word++ ) {


clientid = ( clientid + 1 ) % NBCL ;
while( !put( word+10*prodid, clientid ) ) ;
}

// signaler la fin de communication aux clients


for( clientid=0; clientid<NBCL; clientid++ )
while(!put(OVER, clientid)) ;

return NULL ;
}

5
void *consumer( void * data ) {
int word,
i=0,
num = (int) data,
cb = 0 ; // compteur d’OVER
char c ;

printf("le client %d vient de naitre\n",num);


while (1) {
word = get( num ) ;
if( word == EMPTY ) continue ;
if( word == OVER )
{
cb++ ;
if( cb >= NBPD ) break ;
}
val[num,i++]=word ;
// sleep(1); // une pause
}

return NULL;
}

int main(void) {

pthread_t th_a[NBPD], th_b[NBCL];


pthread_attr_t attr ;
int i, clientid, prodid ;

// initialise les attributs des threads


pthread_attr_init( &attr ) ;
pthread_attr_setschedpolicy( &attr, SCHED_RR ) ;

// initialise word et mutex


init_mem( ) ;

for( clientid=0; clientid<NBCL; clientid++ )


for( i=0; i<NBITEM; i++ )
val[clientid,i] = 0 ;

// creation des threads clients et producteurs


for( clientid=0; clientid<NBCL; clientid++ )
pthread_create( th_b+clientid, &attr, consumer, (void *) clientid ) ;
for( prodid=0; prodid<NBPD; prodid++ )
pthread_create( &th_a[ptodid], &attr, producer, ( void * ) prodid ) ;

// attendre que les producteurs et les consommateurs finissent


for( prodid=0; prodid<NBPD; prodid++ )

6
pthread_join( th_a[prodid], NULL ) ;
for( clientid=0; clientid<NBCL; clientid++ )
pthread_join( th_b[clientid], NULL) ;

// affiche les valeurs recues par les clients


for( i=0 ; i<NBITEM ; i++ ) {
for( clientid=0; clientid<NBCL; clientid++ )
printf( "%d ", val[clientid,i] ) ;
printf( "\n" ) ;
}

return 0;
}

4 Exercices
1. Plusieurs producteurs et plusieurs consommateurs partagent un buffer circulaire
2. Écrire la solution des lapins et des renards. Les threads concernent uniquement les fonctions
lapin(i) et renard(i). Les algorithmes Parent Renard(r), Parent Lapins(l) et Grand Parent(r,l)
restent sous forme de processus.