Reword and syntax corrections
This commit is contained in:
parent
b95fe8380b
commit
5c682b618a
1 changed files with 38 additions and 33 deletions
|
@ -42,17 +42,17 @@ void exit_sc ()
|
|||
```
|
||||
|
||||
Nous avons donc besoin d'une solution non seulement valable pour un nombre
|
||||
indéfinis de fils d'exécutions, mais aussi plus efficace.
|
||||
indéfini de fils d'exécutions, mais aussi plus efficace.
|
||||
|
||||
## Une solution matérielle
|
||||
|
||||
Vu du processeur, une opération atomique ne peut être interompue par une autre.
|
||||
Les processeurs modernes en comportent un certain nombres. La solution viens
|
||||
Les processeurs modernes en comportent un certain nombre. La solution vient
|
||||
donc des fabricant de processeurs.
|
||||
|
||||
Pour forcer l'exclusion mutuelle, nous en avons besoin d'une seule:
|
||||
`Test_and_Set`. C'est donc une instruction matérielle, voici une pseudo
|
||||
implémentation en C:
|
||||
implémentation en C :
|
||||
|
||||
```c
|
||||
int test_and_set (int *verrou){
|
||||
|
@ -104,12 +104,13 @@ threads en attente. Car seul le noyau peut endormir les processus.
|
|||
|
||||
## Les sémaphores
|
||||
|
||||
On en a déjà parlé [en lpro]({{< ref"../../progsys/8_IPC">}} "Les IPC"). Ce sont
|
||||
On en a déjà parlé [en lpro]({{<ref"../../progsys/8_IPC">}} "Les IPC"). Ce sont
|
||||
des outils de haut-niveau, implémentés dans le noyau. Il se compose d'une
|
||||
structures contenant un entier positif et une liste de processus en attente et
|
||||
de deux méthodes `P()` et `V()`. Il ont été inventés en 1962 par E. Dijsktra.
|
||||
structures contenant un entier positif, d'une liste de processus en attente et
|
||||
de deux méthodes `P()` et `V()`. Il ont été inventés en 1962 par E. Dijsktra.
|
||||
|
||||
Les méthodes permettent :
|
||||
|
||||
* `P()`: attendre un jeton, le prendre lorqu'il devient disponible et continuer
|
||||
son exécution.
|
||||
* `V()`: remettre un jeton
|
||||
|
@ -146,7 +147,7 @@ void barrier (int i)
|
|||
count[i] = 0;
|
||||
V(mutex[i]);
|
||||
|
||||
// point
|
||||
// point 2
|
||||
for (int k=0; k < N-1; k++)
|
||||
V(wait[i]);
|
||||
}
|
||||
|
@ -154,6 +155,7 @@ void barrier (int i)
|
|||
```
|
||||
|
||||
Explication de code :
|
||||
|
||||
* **point 1**: Utiliser des tableaux de 2 éléments permet de réutiliser notre
|
||||
fonction `barrier()` pour fixer plusieurs rendez-vous. Il suffit alors
|
||||
d'apeller à tous de rôle `barrier(0);` et `barrier(1);`. Sans ça un
|
||||
|
@ -178,24 +180,24 @@ Un producteur appelle la fonction `put(element)` et un consommateurs
|
|||
|
||||
```c
|
||||
#define MAX 8
|
||||
semaphore cons(0), prod(MAX)
|
||||
semaphore conso(0), prod(MAX)
|
||||
|
||||
// Pour le consommateur
|
||||
P(cons);
|
||||
P(conso);
|
||||
elemet = get();
|
||||
V(prod);
|
||||
|
||||
// Pour le producteur
|
||||
P(prod);
|
||||
put(element);
|
||||
V(cons);
|
||||
V(conso);
|
||||
```
|
||||
|
||||
Le fonctionnement est ici assez simple :
|
||||
|
||||
* **Pour le consomateur**:
|
||||
1. il prend un jeton sur le sémaphore `cons` s'il est disponible (ou attends
|
||||
qu'il le soit)
|
||||
1. il prend un jeton sur le sémaphore `conso` s'il est disponible (ou
|
||||
attend qu'il le soit)
|
||||
2. consomme l'élement
|
||||
3. on relache celui sur `prod`. Cette dernière action permet de relancer
|
||||
la production si la file était pleine (`prod` en attente).
|
||||
|
@ -204,8 +206,8 @@ Le fonctionnement est ici assez simple :
|
|||
action permet d'arrêter la production s'il n'y a plus de jeton
|
||||
disponible.
|
||||
2. On produit ensuite l'élément
|
||||
3. puis on relache le sémaphore `cons`. On réveille ansi notre consommateur
|
||||
s'il dormait en attendant la disponibilité d'un élement
|
||||
3. puis on relache le sémaphore `conso`. On réveille ansi notre
|
||||
consommateur s'il dormait en attendant la disponibilité d'un élement
|
||||
|
||||
#### Multiple producteurs et consomateur
|
||||
|
||||
|
@ -213,15 +215,15 @@ Dans la vraie vie, il y souvent plusieurs consomateurs / producteurs. Le
|
|||
problème devient alors plus complexe...
|
||||
|
||||
Le code ressemble à celui ci-dessus, saut qu'il faut faire attention à ce qu'il
|
||||
y ai un seul consmateir à la fois sur `get()` et un seul producteur sur
|
||||
y ai un seul consomateur à la fois sur `get()` et un seul producteur sur
|
||||
`put(element)`. nous allons donc rajouter deux *"mutex"* :
|
||||
|
||||
```c {linenos=table,hl_lines=[6,8,13,15]}
|
||||
#define MAX 8
|
||||
semaphore cons(0), prod(MAX), mutex_c(1), mutex_p(1);
|
||||
semaphore conso(0), prod(MAX), mutex_c(1), mutex_p(1);
|
||||
|
||||
// Pour le consomateir
|
||||
P(cons)
|
||||
// Pour le consommateur
|
||||
P(conso)
|
||||
P(mutex_v);
|
||||
element = get();
|
||||
V(mutex_v);
|
||||
|
@ -232,7 +234,7 @@ P(prod);
|
|||
P(mutex_c);
|
||||
put(element);
|
||||
V(mutex_p);
|
||||
V(cons)
|
||||
V(conso)
|
||||
```
|
||||
|
||||
### Problème des lecteurs rédacteurs
|
||||
|
@ -273,16 +275,18 @@ if(--nbr == 0) // last to leave
|
|||
V(write_token);
|
||||
V(mutex_r);
|
||||
```
|
||||
Pour les lecteur, il faut envoyer **un éclaireur** afin de savoir si un lecteur
|
||||
|
||||
Pour les lecteurs, il faut envoyer **un éclaireur** afin de savoir si un lecteur
|
||||
est actif. Le problème est similaire à celui des producteur consommateurs sauf
|
||||
qu'il faut savoir si notre lecteur est le premier *(ligne 12)*
|
||||
|
||||
De plus. afin que notre algorithme ne privilegie pas les lecteurs au
|
||||
détriment des rédacteur; ils peuvent même ne jamais rendre la main aux
|
||||
rédacteurs. Pour pallier au problème, nous devons mettre en place une **salle
|
||||
d'attente** *(ligne 8)*.
|
||||
De plus, afin que notre algorithme ne privilegie pas les lecteurs au
|
||||
détriment des rédacteur; ils pourraient même ne jamais rendre la main aux
|
||||
rédacteurs, nous devons mettre en place une **salle d'attente** *(ligne 8)*.
|
||||
|
||||
```c {linenos=table,hl_lines=[8,12]}
|
||||
Voici le code pour les rédacteurs:
|
||||
|
||||
```c {linenos=table}
|
||||
P(wait_room);
|
||||
P(write_token);
|
||||
V(wait_room);
|
||||
|
@ -296,7 +300,7 @@ Ce code est plutôt clair et ne comporte rien de particulier.
|
|||
|
||||
Les moniteurs sont des primitives de synchronisation initialement proposées dans
|
||||
les langages objet. Il est utilisé actuellemt dans des langages tel que ADA ou
|
||||
Java et implemente au sein de systèmes d'exploitation.
|
||||
Java et implementé au sein de systèmes d'exploitation.
|
||||
|
||||
Le moniteur se positionne sur une classe et les mutexes sur ses methodes et sont
|
||||
basés sur des variables condition. elle sont forcement privée et inaccessible à
|
||||
|
@ -317,7 +321,7 @@ public method g() {
|
|||
}
|
||||
```
|
||||
|
||||
Il sont différent des *sémaphores*: il n'y a pas de jeton à prendre. Leur
|
||||
Il sont différents des *sémaphores*, il n'y a pas de jeton à prendre. Leur
|
||||
implémentation dans les systèmes d'exploitation est différente, elle se fait par
|
||||
les les types `mutex_t` et `cond_t`:
|
||||
|
||||
|
@ -400,9 +404,10 @@ tout comme les barrières, le code est ici bien plus simple. Et bien entendu il
|
|||
n'y a pas d'attente active...
|
||||
|
||||
Dans les codes ci-dessous, il est préféfable d'utiliser `while (nbe ...)` plutôt
|
||||
qu'un `if (nbe ...)`. (Mais je ne sais plus pourquoi...)
|
||||
qu'un `if (nbe ...)`. Si le processus se reveille, il faut s'arrurer que les
|
||||
autres n'on pas déjà pris toutes les places disponibles.
|
||||
|
||||
Voici les variables nécessaires aux producteurs et au consomate
|
||||
Voici les variables nécessaires aux producteurs et au consommateurs.
|
||||
|
||||
```c
|
||||
#define MAX 8
|
||||
|
@ -538,7 +543,7 @@ rwl_writeunlock(mylock);
|
|||
|
||||
## conclusion
|
||||
|
||||
Les sémaphores et moniyeurs sont implémentés au niveau du système d'exploitation
|
||||
Les sémaphores et moniteurs sont implémentés au niveau du système d'exploitation
|
||||
et utilient le matériel sous-jacent (`test_and_set`). Ces appels sont
|
||||
**coûteux**, nécessitent des **changements de contexte**. Dans les systèmes
|
||||
modernes, on leur préfèrera un **mix de primitive de synchronisation et de
|
||||
|
@ -548,11 +553,11 @@ pendant quelques cycles et se bloquera si elle n'est pas disponible.
|
|||
Les moniteurs de Hoare sont en général préférés par les programmeurs cas comme
|
||||
on l'a vu, ils sont plus simple à implémenter.
|
||||
|
||||
Il eest intéressant aussi de parler des **FUTEX**, apparus en 2003 sur Linux et
|
||||
plus tard sous Windows 8 (sous l'appelation `wait_on_address` et brevete en
|
||||
Il est intéressant aussi de parler des **FUTEX**, apparus en 2003 sur Linux et
|
||||
plus tard sous Windows 8 (sous l'appelation `wait_on_address` et breveté en
|
||||
2013).
|
||||
|
||||
Ils utilisent des opétations atomiques sur des variables entières de 32bits *en
|
||||
Ils utilisent des opérations atomiques sur des variables entières de 32bits *en
|
||||
espace utilisateur* et deux operation bloquante si nécessaires:
|
||||
|
||||
```c
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue