Changes between Version 21 and Version 22 of AS6-TME-B7


Ignore:
Timestamp:
Apr 13, 2022, 10:27:53 AM (2 years ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • AS6-TME-B7

    v21 v22  
    1010- **Il est nécessaire d'avoir des mécanismes de synchronisation**
    1111  - Dès lors que plusieurs threads se partagent le processeur et que ces threads peuvent utiliser les mêmes ressources, il est nécessaire d'avoir des mécanismes de synchronisation afin de décider de la propriété des ressources.
    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)
    1457
    1558
     
    132175Les exercices sont classés par niveau de difficultés supposées (on est jamais à l'abri de surprise)
    133176
    134 En préalable de tous les exercices, quelques questions sur le code. Dans le répertoire `s7` vous trouvez le répertoire `01_synchro` qui contient le code complet des nouveaux mécanismes de synchro. Cela a un impact sur `hcpua.S` pour les spinlocks, `kthread.[ch]` pour la gestion des deux nouveaux états `WAIT` et `ZOMBIE`, en particulier les fonctions `thread_wait` et `thread_notify`, et évidemment `thread.[ch]` pour l'ajout des appels systèmes.
     177Dans le répertoire `s7` vous trouvez le répertoire `01_synchro` qui contient le code complet des nouveaux mécanismes de synchro. Cela a un impact sur `hcpua.S` pour les spinlocks, `kthread.[ch]` pour la gestion des deux nouveaux états `WAIT` et `ZOMBIE`, en particulier les fonctions `thread_wait` et `thread_notify`, et évidemment `thread.[ch]` pour l'ajout des appels systèmes.
     178
     179Il y a trois répertoires d'application : `app0`, `app1` et `app2`. Quand vous exécutez `make exec`, c'est l'application `app0` qui est utilisée, c'est celle par défaut. Pour les autres, vous devez utilisez la commande `make APP=1 exec` ou `make APP=2 exec`. Vous pouvez changer l'application par défaut si vous changez la valeur par défaut de la variable `APP` dans le makefile.
    135180{{{
    13618101_synchro
     
    162207│   ├── kthread.c
    163208│   └── kthread.h
    164 ├── uapp
     209├── uapp[012]            // Il y a 3 répertoires d'application
    165210│   ├── Makefile
    166211│   └── main.c
     
    178223
    179224
    180 == B.1.
    181 
    182 
    183 == B.2.
    184 
    185 
    186 == B.3. Mécanisme des barrières
     225== B.1. `app0`
     226
     227
     228== B.2. `app1`
     229
     230
     231== B.3. `app2` : Mécanisme des barrières
    187232
    188233
     
    224269 *          the last arrived thread doesn't wait and notifies the other threads which becomes READY
    225270 * \param   barrier a pointer referencing a barrier
    226  * \return  SUCCESS or FEALURE
     271 * \return  SUCCESS or FAiLURE
    227272 */
    228273extern int thread_barrier_wait (thread_barrier_t * barrier);
     
    297342}}}
    298343
    299 Maintenant, vous n'allez pas avoir à écrire le code des barrières mais voue allez devoir répondre à quelques questions
     344Maintenant, vous n'allez pas avoir à écrire le code des barrières mais je vous demande de commenter la fonction `barrier_wait()` avec vos propres mots. Commencez sans regarder le code et après ouvrez le code et lisez les commentaires.
     345
    300346{{{#!c
    301347#define MAGIC_BARRIER   0xDEADBABA
     
    343389int thread_barrier_wait (thread_barrier_t * barrier)
    344390{
    345     thread_barrier_t b = *barrier;                          // get the barrier pointer
    346 
    347     if (b == NULL) return EINVAL;                           // b is not created
    348     if (b && (b->magic != MAGIC_BARRIER)) return EINVAL;    // check that it is barrier (MAGIC)
    349 
    350     spin_lock (&b->lock);                                   // get the ownership
    351     b->waiting++;                                           // the current thread is the newcomer
     391    thread_barrier_t b = *barrier;                          //
     392
     393    if (b == NULL) return EINVAL;                           //
     394    if (b && (b->magic != MAGIC_BARRIER)) return EINVAL;    //
     395
     396    spin_lock (&b->lock);                                   //
     397    b->waiting++;                                           //
    352398    if (b->waiting == b->expected) {                        //
    353399        list_foreach (&b->wait, waiting_item) {             //
    354400            list_unlink (waiting_item);                     //
    355             thread_t t = thread_item (waiting_item);        // get the pointer of a waiting thread
     401            thread_t t = thread_item (waiting_item);        //
    356402            thread_notify (t);                              //
    357403        }
     
    359405        spin_unlock (&b->lock);                             //
    360406    } else {
    361         thread_addlast (&b->wait, ThreadCurrent);           // the current thread in the wait. list
     407        thread_addlast (&b->wait, ThreadCurrent);           //
    362408        spin_unlock (&b->lock);                             //
    363409        thread_wait ();                                     //
    364410
    365     return SUCCESS;                                         // it's fine
     411    return SUCCESS;                                         //
    366412}
    367413
    368414
     415
    369416== B.4. Ajout de l'API des sémaphores