Documente Academic
Documente Profesional
Documente Cultură
1. Programmation concurrente
1
Problmes inhrents la programmation concurrente (1)
R1 R2
T1 R1 R1
R2 R1
R2
T2
Interblocage
2
2. Gnralits sur la manipulation de tches
z Processus vs tche
- Un processus possde son propre espace dadressage. Il sexcute sur sa propre
machine virtuelle.
- Lee syst
systme
e dexploitation
d e p o tat o met
et e
en p
place
ace les
es mcanismes
ca s es de p
protection
otect o
inter processus.
- Le partage de mmoire entre processus est soit impossible, soit fait de manire
explicite par des appels systme.
- Une tche partage son espace dadressage avec dautres tches (mais ce nest
pas obligatoire).
- Le programmeur doit mettre en place manuellement les mcanismes de
protection inter tches.
z Tches indpendantes
- Deux tches sont indpendantes si lexcution de lune ninflue pas sur lautre.
z Tches en coopration
- Deux tches sont en coopration si elles mnent un travail commun ncessitant
synchronisation et/ou communication.
z Tches en comptition
- Deux tches sont en comptition si accdent des ressources partages en exclusion.
3
Etats dune tche
Inexistante
Cre Termine
Prte Active
Bloque
z Quatre possibilits
z Remarque
- Lorsquon veut crire des tches qui constituent une mme application, il faut
viter (si possible) dutiliser fork et join. Il faut utiliser les primitives de cration et
de manipulation de threads.
4
Exemple de tche avec Fork et Join
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define N 50
int main(void)
{ p
pid
d_t
t tab_p
tab pid[N];
d[ ]; // p
pid
d des fils
s
int status, i;
for (i=0; i<N; i++){
if ( ! (tab_pid[i] = fork()) ){
// le code du fils; ici un simple affichage et un repos de une seconde
printf("hello\n");
sleep(1);
exit();
}
}
// Seul le pre arrive ici car les 50 fils ont termin par exit ;
// Il attend leur terminaison
for (i=0; i<N; i++){
waitpid( tab_pid[i], &status, 0);
}
return 0;
}
t k body
task b d Genre_De_Tache
G D T h i is -- Corps du type
begin
loop
Put(Param);
end loop;
end Un_Genre_De_Tache;
5
3. Programmation concurrente en C avec la norme Posix
(cration et terminaison de thread)
z La norme POSIX 1003.c (1995) dfinit une API pour manipuler des tches appeles
threads (pthreads) avec le langage C.
z Les dfinitions lies aux threads se trouvent dans le fichier <pthread.h>
p
z Chaque thread a ses registres et sa pile dexcution. Mais tous les threads dun mme
processus partagent le mme espace virtuel, les mmes actions vnementielles, les
mmes descripteurs de fichiers et dautres ressources du processus.
Fichiers
Registre Registre Registre
Registre Pile
Pile Pile Pile
Fil
dexcution
z Un thread (tche) est dfini, du point de vue programmeur, par son identifiant systme de
type pthread_t
z Une tche POSIX nest pas dfinie mais cre par un appel de la primitive pthread_create.
Lappel de pthread_create cre une nouvelle tche qui sexcute en parallle avec
celle
ll quii la
l cre.
int pthread_create ( pthread_t *thread, const pthread_attr_t *attr,
void *(*start_fnc)(void *), void *arg)
6
z Le type pthread_attr_t est dfini par les attributs de thread suivants :
typedef struct
{ int __detachstate; // indique si ce thread peut se synchroniser (par join)
// avec un autre
int __schedpolicy; // Politique dordonnancement du thread (FIFO, RR, Other)
struct __sched_param __schedparam; // Paramtres dordo du thread
// en gnral, il y a un seul paramtre : la priorit
int __inheritsched; // Attribut dhritage de la politique dordo du pre
int __scope; // indique la porte de la priorit du thread (en gnral,
// l
la priorit
i it estt globale
l b l au systme)
t )
size_t __guardsize; // Infos sur la pile dexcution
int __stackaddr_set; //
void *__stackaddr; // Adresse en mmoire de la pile dexcution du thread
size_t _stacksize; // Taille de la pile dexcution du thread
} pthread_attr_t;
z Terminaison de tche
- Une tche POSIX se termine soit en appelant explicitement la fonction pthread_exit ou
implicitement lorsque la fonction start-routine sachve
- Si le programme principal termine normalement, les tches Posix cres par celui-ci sont
termines par un appel implicite la fonction systme exit.
- Si lon veut terminer proprement un programme crant des tches Posix, il est ncessaire
dattendre la terminaison de celles-ci en utilisant la primitive pthread_join
int pthread_join(pthread_t th_a_attendre, void **tache_return);
7
Exemple de programme multi-tches
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *affichCar(void *arg){
char c;
int k;
c = * (char *)arg;
for (k=1; k<50; k++){
putchar( c);
}
}
int main(void)
{ char *leCar; int i;
pthread_t tache_Posix_B;
pthread_t tache_Posix_C;
leCar = (char*) malloc(1*sizeof(char)); *leCar = 'B';
pthread_create( &tache_Posix_B, NULL, affCar, (void*) leCar);
leCar = (char*) malloc(1*sizeof(char)); *leCar = 'C';
pthread_create( &tache_Posix_C, NULL, affCar, (void*) leCar);
for (i=0; i<100; i++){
putchar(Z');
}
pthread_j
p join (tache_Posix_B, NULL);
pthread_join (tache_Posix_C, NULL);
}
Si on ne met pas ces deux instructions, les deux tches B et C seront termines
peine cres (elles nauront pas le temps de sexcuter)
8
4. Synchronisation et communication inter-tches
- Signaux
Si (
(ex. signaux
i Unix)
U i )
- Autres : verrous
9
Mutex de Posix pour lexclusion mutuelle
- Un mutex ne peut tre partag que par des threads dun mme processus
10
z Verrouillage dun mutex
- Un Mutex peut tre verrouill par la primitive pthread_mutex_lock
init pthread_mutex_lock(pthread_mutex_t *mutex);
11
Exemple dutilisation de mutex
.../..
pthread_mutex_t monMutex = PTHREAD_MUTEX_INITIALIZER;
void LitRegistre(ValeurRegistre R){
int i; for (i=0; i<TAILLE_REGISTRE; i++) R[i] = LeRegsitre[i];
}
void EcritRegistre(ValeurRegistre R){
int i; for (i=0; i<TAILLE_REGISTRE; i++) LeRegsitre[i] = R[i];
}
void f(void){
int i, j; ValeurRegistre RLocal;
for(j=0; j < 1000000; j++){
pthread_mutex_lock( &monMutex );
g ( RLocal )
LitRegistre( ); Section
for(i=0; i<TAILLE_REGISTRE; i++){ RLocal[i] ++; } critique
EcritRegistre( RLocal );
pthread_mutex_unlock( &monMutex );
}
}
Smaphores de Posix
z Notion de smaphore Posix
- Un smaphore Posix est un smaphore compte pouvant tre partag par plusieurs
threads de plusieurs processus (selon les implmentations)
- Le compteur associ un smaphore peut donc prendre des valeurs plus grandes que 1
(
(contrairement
t i t un mutex)
t )
- La prise d'un smaphore dont le compteur est ngatif ou nul bloque l'appelant
- Les dfinitions ncessaires la manipulation de smaphore se trouvent dans le fichier entte
<semaphore.h>
12
z Prise de contrle et libration de smaphore Posix
- Les deux oprations P et V sont implantes par :
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
- Posix 1003.b de 1999 dfinit une version de P temporise (ce qui limite le blocage infini) :
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
z Objectifs
- Les variables conditionnelles servent mettre un thread en attente de la satisfaction dune
condition (dpassement dun seuil de temprature par exemple) qui dpend de ltat dune
variable p
partage.
g
- Les variables conditionnelles sont utilises conjointement avec les mutex. Chaque variable
conditionnelle est lie un mutex qui la protge.
- Avant de tester une variable conditionnelle, le thread doit verrouiller laccs la variable par
le mutex associ cette variable.
- Quand un thread se bloque, par pthread_cond_wait, en attente de la satisfaction dune
condition, le mutex associ la variable conditionnelle est automatiquement libr par le
systme pour permettre un autre thread de rendre la condition vraievraie.
- Quand un thread est rveill, suite un pthread_cond_signal ou pthread_cond_broadcast,
le mutex est toujours en position verrouill (le thread rveill doit donc le dverrouiller).
- Les variables conditionnelles sont utilises pour la signalisation pas pour lexclusion mutuelle.
13
z Dclaration, initialisation et manipulation de variable conditionnelle
- Type de variable conditionnelle : pthread_cond_t
- On peut dfinir une variable conditionnelle de manire statique avec des attributs par dfaut :
pthread_con_t NomCond = PTHREAD_COND_INITIALIZER;
- Une variable conditionnelle p
peut aussi tre initialise de manire dynamique
y q par
p
int pthread_cond_init(pthread_cond_t *cond,
const pthread_condattr_t *attr = NULL)
struct pthread_condattr_t { int __dummy; } pthread_condattr_t;
- Une variable conditionnelle est dtruite par
int pthread_cond_destroy (pthread_cond_t *cond)
- Un thread se bloque en attente quune variable conditionnelle devienne vraie
int p
pthread_cond_wait p
pthread_cond_t *cond,
, p
pthread_mutex_t *mutex)
)
- Un thread peut signaler une variable conditionnelle par
int pthread_cond_signal (pthread_cond_t *cond)
(si aucun thread est en attente de la variable, le signal est perdu contrairement V(sem))
- Un thread peut signaler une variable conditionnelle tous les threads en attente par
int pthread_cond_braodcast (pthread_cond_t *cond)
- Posix 1000.3 de 1999 dfinit aussi lattente temporise sur une condition pour viter le
blocage infini
int pthread_cond_timedwait(pthread_cond_t *cond,
pthread_mutex_t *mutex, const struct timespec *abstime)
14
Exemple 1 dapplication avec variable conditionnelle
z Dans lexemple suivant un thread incrmente un compteur, les autres attendent quil
franchisse le seuil de 100.
z Description de lexemple
- Un robot doit envoyer rgulirement des informations de temprature
- Il dispose pour cela dun capteur de temprature
- On dcide dattribuer une tche le travail de lecture p
priodique
q du capteur
p (toutes
(
les secondes) et de transmettre une autre tche la valeur moyenne sur 10 mesures
- La seconde tche fait un pr-traitement de cette moyenne et envoie son rythme ces
informations vers un central
Analyse de la
Capteur de
temprature moyenne
temprature
Valeur de Tampon
temprature dchange
15
z Solution avec mutex et variable conditionnelle
#include <stdio.h>
#include <pthread.h>
typedef unsigned char Registre_8_Bits;
Registre_8_Bits *capteurTemperature; // a mapper sur adresse registre
unsigned int periodeLecture = 1000*1000; // 1 seconde = 106 micro secondes
unsigned int nombreDonnees = 10; // 10 lectures avant de transmettre
pthread_mutex_t mutexTampon = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t attenteDonnee; // variable conditionnelle, pour la gestion du
// tampon dchange, initialise dans le
// programme principal avant de lancer les tches
16
// le code associ la tche de lecture rgulire du registre
// (cest une version simplifie pour la gestion du temps)
void codeLectureReguliere(void){
int somme, CPT = 0;
while(1){
usleep(periodeLecture);
somme += *capteurTemperature;
CPT ++;
if (CPT == nombreDonnees){
somme /= nombreDonnees;
Tampon_metAJour(somme);
CPT = somme = 0;
}
}
}
17
int main(){
pthread_t tacheCalcul;
pthread_t tacheLecture;
Signaux de Posix
z Principes
z Remarques
1) Attention
Att ti lutilisation
l tili ti des
d signaux
i avec les
l threads
th d peutt conduire
d i des
d erreurs difficiles
diffi il
dtecter au moment de lcriture du code. Ceci est d au fait que les threads dun process
partagent les mmes infos sur les signaux.
2) Lutilisation des signaux doit tre conforme lutilisation traditionnelle des signaux sous UNIX,
En particulier pour la dfinition des actions vnementielles
18
z Liste des signaux POSIX
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGEMT 8) SIGFPE
9) SIGKILL 10) SIGBUS 11) SIGSEGV 12) SIGSYS
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGUSR1
17) SIGUSR2 18) SIGCHLD 19) SIGPWR 20) SIGWINCH
21) SIGURG 22) SIGIO 23) SIGSTOP 24) SIGTSTP
25) SIGCONT 26) SIGTTIN 27) SIGTTOU 28) SIGVTALRM
29) SIGPROF 300) SIGXCPU 31) SIGXFSZ
- Masquage de signaux
int p
pthread_sigmask
g (int how,
, const sigset
g _t *newmask,
, sigset
g _t *oldmask)
19
Exemple avec signaux et smaphores
On notera que le smaphore est initialis 0 (par sem_init) pour bloquer tout processus
qui excute un sem_wait.
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <time.h>
#include "errors.h"
semt semaphore;
semt_
/* Fonction de capture de signal
void signal_catcher (int sig)
{ if (semp_post(&semaphore)==-1) erno_abort ("post sempahore"); }
20
int main (int argc, char *argv[])
{ int thread_CPT, status;
struct sigevent sig_event;
struct sigaction sig_action;
sigset_t sig_mask;
timer_t timer_id;
struct itimerspec timer val;
pthread_t sem_waiters[5];
21
sigemptyset (&sig_mask);
sigaddset (&sig_mask, SIGRTMIN);
sig_action.sa_handler = signal_catcher;
sig_action.sa_flags = 0;
if (sigaction(SIGRTMIN, &sig_action, NULL) == -1)
errno_abort(erreur dinitialisation daction de signal);
timer_val.it_interval.tv_sec = 2;
timer_val.it_interval.tv_nsec = 0;
timer_val.it_value.tv_sec = 2;
timer_val.it_value.tv_nsec = 0.
if (timer_settime(timer_id, 0, &timer_val, NULL) == -1)
errno_abort(Erreur darmement du timer);
- int clock_gettime (clockid_t clock_id, struct timespec *tp) : lit la valeur de lhorloge
- int clock_getres (clockid_t clock_id, struct timespec *res) : renvoie la rsolution de
lhorloge
- int timer_gettime ( timer_t timerid, struct itimerspec *value ) : lit le temps restant
un timer avant dexpirer
22
z Primitives de lAPI (suite)
- int timer_getoverrun ( timer_t timerid ) : lit le temps de dpassement dun timer
- int nanosleep( const struct timespec *rqtp, struct timespec *rmtp ) :
suspension
p du thread appelant
pp pendant
p une dure spcifie
p par
p rqtp.
q p
rmtp est gnralement gal NULL.
- Posix 1003.b de 1999 dfinit la fonction suivante qui permet dobtenir lId dune horloge :
int clock_getcpuclockid (pid_t pid, clockid_t *clock_id);
z Utilisation de lAPI
- LLutilisation
utilisation des primitives relatives au temps ncessite le fichier entte <time.h>
<time h>
z Primitives de lAPI
- mqd-t mq_open ( const char *name, int oflag, ... ) : cre une file de messages
et la lie un processus. oflag spcifie les droits sur la file (criture, lecture ou les deux).
23
z Primitives de lAPI (suite)
http://www.opengroup.org/onlinepubs/007908799/xshix.html
24
Etude de cas
Soit un atelier de production compos dun convoyeur, un robot dalimentation de pices usiner, M
machines dusinage et un robot de retrait de pices usines. Lensemble est reli par un rseau de
communication (voir figure ci-dessous).
Superviseur
Oprateur
Convoyeur
Robot_retrait
Robot_alimentation
Le superviseur reoit ses ordres de son environnement reprsent ici par un oprateur. On suppose que le
convoyeur tourne en permanence et que sa vitesse est choisie de manire adquate pour que les robots et
machines puissent manipuler les pices sans problme.
Quand loprateur signale au superviseur larrive dune nouvelle pice usiner (on suppose que chaque
pice est accompagne dun code qui spcifie lopration dusinage lui appliquer), le superviseur
dtermine la machine qui va lusiner et envoie un ordre au robot dalimentation pour dposer la pice sur le
y
convoyeur et la machine dusinage
g concerne pour
p se prparer.
p p Le robot dalimentation effectue
lopration de placement sur le convoyeur au bout de 20 secondes au maximum, sil ny arrive pas il gnre
un signal danomalie vers le superviseur qui fait passer le systme dans un tat de dfaillance.
Lorsquune machine dusinage retire la pice quelle doit usiner, elle la dpose sur sa table et envoie un
signal de libration du convoyeur au superviseur. Si la machine na pas retir la pice au bout de 50
secondes, le systme passe dans un tat de dfaillance. Chaque machine dusinage possde sa propre table
dusinage sur laquelle elle dpose la pice durant lopration dusinage. Lorsquune machine dusinage
termine son travail, elle envoie un compte rendu au superviseur et attend de celui-ci un ordre pour dposer
la pice sur le convoyeur. Si une machine na pas fini son travail au bout de 10 minutes, le superviseur
avertit loprateur en dclarant la machine en panne, mais narrte pas le systme. Si une pice arrive et
quelle ncessite une opration
q p sur une machine djj dclare en panne,
p , le superviseur
p passe
p dans un tat
de dfaillance. Lorsquun compte rendu de fin dusinage est reu, le superviseur attend que le convoyeur
soit libre, ensuite il envoie un ordre la machine dusinage concerne pour dposer la pice sur le
convoyeur et un ordre au robot de retrait pour retirer la pice afin de lenvoyer au dpt. Quand la pice est
retire un compte rendu est renvoy au superviseur par le robot de retrait. Si le robot de retrait na pas fini
son travail au bout de 30 secondes, le superviseur fait passer le systme dans un tat de dfaillance.
25
Comme il ny a quun seul convoyeur, le superviseur doit assurer sa bonne utilisation : une seule pice la
fois sur le convoyeur.
On suppose que tous les quipements (robots et machines dusinage) sont munis de capteurs pour dtecter
larrive de pices et leur dpart du convoyeur et que les robots et machines peuvent manipuler les pices
alors que le convoyeur est en mouvement.
Question
Modliser et programmer le problme dcrit prcdemment laide de thread Posix.
Bien rflchir au(x) mcanisme(s) de synchronisation utiliser.
26