Changes between Version 32 and Version 33 of AS6-TME-B7
- Timestamp:
- Apr 2, 2024, 6:17:56 PM (14 months ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
AS6-TME-B7
v32 v33 60 60 - **Le mécanisme de base est le verrou** 61 61 - 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. 62 - 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.62 - Il y a deux opérations de base: `lock`/`unlock`. L'opération `lock` signifie le souhait de fermer le verrou (pour interdire aux autres de passer, on dit aussi prendre le verrou), donc de passer l'état à 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 ou lâcher le verrou. Ce n'est pas bloquant. 63 63 - 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. 64 64 - 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. … … 74 74 - **Les ressources partagées gèrent des listes d'attente de threads** 75 75 - 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. 76 - 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. 76 - 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 à chaque ressource. 77 - Un thread sort de la liste d'attente 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. 77 78 78 79 - **Les deux nouvelles fonctions de transition d'états pour les threads** … … 82 83 - Quand un thread veut prendre une ressource indisponible, il s'accroche à la liste d'attente de la la ressource et il appelle `thread_wait()`. 83 84 - 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()`. 85 - Le choix du thread parmi tous ceux en attente est une opération d'ordonnancement, ici c'est l'ordre premier-arrivé-premier-servi. 84 86 85 87 - **"''Race Condition''" ou "Condition de Compétition"** … … 89 91 90 92 - **Le Mutex est un verrou avec liste d'attente utilisable par l'application** 91 - l'API... 93 - On peut utiliser des `spinlocks` en mode utilisateur, mais ce sont des attentes actives ce qui signifie qu'un thread peut passer tout son quantum de temps processeur à attendre. 94 - Les mutex permettent d'éviter cette attente inutile en mettant explicitement les threads demandeur dans une liste d'attente, dans un état inéligible par l'ordonnanceur. 95 - Les threads sortent de la liste d'attente dès que le mutex est libéré. 96 - Les opérations de bases sont `lock`/`unlock` comme pour le `spinlock`. 97 - Seul le thread ayant verrouillé `lock` un mutex a le droit de le déverrouiller `unlock`. 92 98 93 99 - **`thread_join()` permet à un thread d'attendre le `thread_join()` d'un autre 94 - 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`). 95 - ... 100 - Un thread rend un pointeur générique `void *` lors de sa terminaison. Ce pointeur `void *` 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`). 101 - `thread_join()` permet donc de se mettre en attente de la terminaison d'un thread. 102 - `thread_join()` est une fonction bloquante pour le thread qui fait le join, sauf si le thread attendu est déjà terminé. 103 - C'est `thread_join()` qui fait passer l'état du thread attendu de `ZOMBIE` à `DEAD`. 96 104 97 105 - **Les codes d'erreur des appels systèmes sont dans la variable globale `errno` 98 106 - 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. 99 107 - Les variables globales mais locales aux threads font partie du ''thread local storage'' 100 - ... 101 102 (pas fini) 108 - Pour ko6, cette variable globale est placé en haut de chaque pile utilisateur, il y a donc autant de valeurs possibles de cette variable qu'il y a de threads. 109 - On obtient l'adresse de la variable par un syscall. 103 110 104 111