12 | | |
13 | | (à compléter) |
| 12 | - Plus généralement, la propriété d'une ressource c'est le droit de la modifier, c'est à dire le droit de faire des écritures aux adresses où est mappée la ressource dans l'espace d'adressage. |
| 13 | |
| 14 | - **Le mécanisme de base est le verrou** |
| 15 | - Les mécanismes de synchronisation s'appuient un mécanisme élémentaire, le verrou binaire, un objet à deux états : `busy`/`free`. `busy` signifie que le verrou est fermé que l'on ne peut pas avancer au delà dans le programme. `free`signifie que le verrou est ouvert et que l'on peut passer. |
| 16 | - Il y a deux opérations de base: `lock`/`unlock`. L'opération `lock` signifie le souhait de fermer le verrou, donc de le passer à busy, ce qui n'est possible que s'il est `free`. `lock` est donc bloquant si verrou est déjà busy et c'est une attente active (une boucle jusqu'à réussite). `unlock` signifie ouvrir le verrou. Ce n'est pas bloquant. |
| 17 | - Le mécanisme de base des verrous utilise la possibilité de réaliser une séquence atomique d'instructions read-modify-write qui permet de lire l'état du verrou, de le tester et de le modifier s'il est libre avec la garantie que si on y parvient on est le seul. |
| 18 | - Il existe plusieurs solutions matérielles pour implémenter les verrous avec des instructions permettant de créer des séquences atomiques d'instructions : mémoire de verrous, instructions CAS, TAS ou couples d'instruction LL/SC. |
| 19 | - Il existe plusieurs type de verrous : simples, à tickets, avec timeout, etc. |
| 20 | |
| 21 | - **Les threads ont désormais 5 états** |
| 22 | - `READY` : état d'attente du processeur |
| 23 | - `RUNNING` : état en exécution |
| 24 | - `WAIT` : état d'attente d'une ressource |
| 25 | - `ZOMBIE` : état terminé mais valeur de retour non lue (donc non effaçable) |
| 26 | - `DEAD` : état terminé et valeur de retour lue (donc effaçable) |
| 27 | |
| 28 | - **Les ressources partagées gèrent des listes d'attente de threads** |
| 29 | - Quand une ressource est partagée, il existe une API permettant de ''prendre'' la ressource (ici ''prendre'' est très général, le nom de la fonction dépend du type de ressource. |
| 30 | - Si la ressource n'est pas disponible, le thread demandeur est mis en attente dans une liste d'attente de thread dans l'état `WAIT`. Cette liste est propre à la ressource. Il sort de la liste lorsque le propriétaire actuel rend la ressource et le choisit parmi tous ceux en train d'attendre. Il repasse à l'état `READY` donc éligible par l'ordonnanceur. |
| 31 | |
| 32 | - **Les deux nouvelles fonctions de transition d'états pour les threads** |
| 33 | - `thread_wait()` : qui passe le thread courant de `RUNNING` à `WAIT` |
| 34 | - `thread_notify()` : qui passe le thread en argument de `WAIT` à `READY` |
| 35 | - Ces deux fonctions sont utilisées par les fonctions du noyau qui gère l'attribution des ressources partagée. |
| 36 | - Quand un thread veut prendre une ressource indisponible, il s'accroche à la liste d'attente de la la ressource et il appelle `thread_wait()`. |
| 37 | - Quand un thread libère une ressource et que celle-ci est attendue, le ''libérateur'' choisit un thread de la liste d'attente, il le décroche et il appelle `thread_notify()`. |
| 38 | |
| 39 | - **"''Race Condition''" ou "Condition de Compétition"** |
| 40 | - On est en présence d'une ''race condition'' lorsque deux séquences d'instructions s'exécutant en même temps, donnent un résultat différent suivant l'ordre relatif des instructions. |
| 41 | - Par exemple `thread_wait()` et `thread_notify()` peuvent être appelées en même temps, `wait` par le thread demandeur d'une ressource occupée et `notify` par le thread libérateur de la ressource. L'issue doit être que le thread demandeur soit en possession de la ressource et dans l'état `READY`. Mais, il y a un risque qu'il soit en possession de la ressource dans l'état `WAIT`. |
| 42 | - La résolution des ''race condition'' passent par l'usage des **verrous** afin de garantir des séquences atomiques d'instruction (le test d'une condition et la décision ne doivent pas être séparés). |
| 43 | |
| 44 | - **Le Mutex est un verrou avec liste d'attente utilisable par l'application** |
| 45 | - l'API... |
| 46 | |
| 47 | - **`thread_join()` permet à un thread d'attendre le `thread_join()` d'un autre |
| 48 | - Un thread rend un pointeur générique `void *` qui ne peut être récupérer que par un appel à la fonction `thread_join(thread_t th, void **retval)` avec comme arguments: le thread attendu `th` et un pointeur sur une variable pouvant contenir un pointeur générique `void *` (`void **retval`). |
| 49 | - ... |
| 50 | |
| 51 | - **Les codes d'erreur des appels systèmes sont dans la variable globale `errno` |
| 52 | - La variable `errno` est globale parce qu'elle est connue et utilisable par toutes les fonctions de l'application, mais chaque thread dispose de sa propre valeur. |
| 53 | - Les variables globales mais locales aux threads font partie du ''thread local storage'' |
| 54 | - ... |
| 55 | |
| 56 | (pas fini) |