CSC4508 - Systèmes d'exploitation - François Trahay & Gaël thomas 2020 - Télécom SudParis

La page est créée Sabrina Charrier
 
CONTINUER À LIRE
CSC4508 - Systèmes d'exploitation - François Trahay & Gaël thomas 2020 - Télécom SudParis
CSC4508 – Systèmes d’exploitation

       François Trahay & Gaël thomas

                                       2020
CSC4508 - Systèmes d'exploitation - François Trahay & Gaël thomas 2020 - Télécom SudParis
CSC4508 - Systèmes d'exploitation - François Trahay & Gaël thomas 2020 - Télécom SudParis
CSC4508 – Systèmes d’exploitation                                                                                                                                                                      CSC4508 – Systèmes d’exploitation

                                                                         Contents                                                                                                                         4 Patterns de synchronisation classiques                                                                                                                                        31
                                                                                                                                                                                                             4.1 Exclusion mutuelle . . . . . . . . . . . . . . . . . . . . .                             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   31
Licence                                                                                                                                                                                          vii         4.2 Cohorte . . . . . . . . . . . . . . . . . . . . . . . . . . .                            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   32
                                                                                                                                                                                                             4.3 Producteur/Consommateur . . . . . . . . . . . . . . . .                                  .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   33
Présentation du module                                                                                                                                                                            1
                                                                                                                                                                                                                  4.3.1 Implémentation d’un Producteur/Consommateur                                       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   33
                                                                                                                                                                                                             4.4 Lecteur/Rédacteur . . . . . . . . . . . . . . . . . . . . .                              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   33
   1 Présentation du module                                                                                                                                                                       2
      1.1 Organisation . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    2               4.4.1 Implémentation d’un Lecteur/Rédacteur . . . . .                                   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   34
      1.2 Séances kernel: XV6 .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    2
                                                                                                                                                                                                       Mécanismes de synchronisation                                                                                                                                                      37
      1.3 Évaluation . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    3
      1.4 Évaluation du module       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    3       Plan du document                                                                                                                                                                38
Threads                                                                                                                                                                                           5       1 Introduction                                                                                                                                                                  38
   1 Rôles d’un système d’exploitation                                                                                                                                                            6       2 Opérations atomiques                                                                                                                                                          38
      1.1 Pile logicielle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                 6          2.1 Motivation . . . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   39
      1.2 Test du retour des appels système et des fonctions . . . . . . . . . . . . . . . . . . . . . . .                                                                                        6          2.2 Opérations atomiques . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   39
                                                                                                                                                                                                             2.3 Test and set . . . . . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   40
   2 Utilisation de la pile                                                                                                                                                                       9
                                                                                                                                                                                                             2.4 À quoi sert volatile ? . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   41
      2.1 Contenu d’un stack frame           . .     . . . . . . .               . . . . .           . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    9
                                                                                                                                                                                                             2.5 Compare And Swap (CAS) . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   43
      2.2 Buffer overflow . . . . . .        . .     . . . . . . .               . . . . .           . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   10
                                                                                                                                                                                                             2.6 Fetch and Add . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   45
           2.2.1 Stack overflow . .          . .     . . . . . . .               . . . . .           . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   12
                                                                                                                                                                                                             2.7 Barrière mémoire / Memory Fence          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   46
           2.2.2 Comment prévenir            les     buffer/stack                overflow            ?.      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   13
                                                                                                                                                                                                             2.8 Modèle mémoire C11 . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   46
   3 Contexte d’exécution d’un processus                                                                                                                                                         13               2.8.1 Introduction . . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   47
      3.1 Fil d’exécution . . . . . . . . . . . . .                      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   14               2.8.2 Relaxed . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   48
      3.2 Processus multithread . . . . . . . . .                        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   14               2.8.3 Release Acquire . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   48
      3.3 Création d’un Pthread . . . . . . . . .                        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   15               2.8.4 Release-Consume . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   49
      3.4 Autres fonctions Pthread . . . . . . .                         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   16               2.8.5 Sequentially Consistent . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   49

   4 Partage de données                                                                                                                                                                          16       3 Primitives de synchronisation                                                                                                                                                 51
      4.1 Thread-safe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                  16          3.1 Synchronisation par attente active . . . . . . .                     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   51
      4.2 Réentrant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                  17          3.2 Futex . . . . . . . . . . . . . . . . . . . . . . .                  .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   52
      4.3 TLS – Thread-Local Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                       19          3.3 Construction d’un mutex à l’aide d’un futex . .                      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   53
                                                                                                                                                                                                             3.4 Construction d’un moniteur à l’aide d’un futex                       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   54
   5 Synchronisation                                                                                                                                                                             20
      5.1 Mutex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                  21       4 De l’utilisation de la synchronisation                                                                                                                                        57
      5.2 Opérations atomiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                     23          4.1 Interblocage / Deadlock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                      57
                                                                                                                                                                                                             4.2 Granularité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                    58
   Bibliographie du chapitre                                                                                                                                                                     24          4.3 Scalabilité d’un système parallèle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                     58

Programmation concurrente                                                                                                                                                                        25       Bibliographie du chapitre                                                                                                                                                       59

   1 Introduction                                                                                                                                                                                26    Appels système                                                                                                                                                                     61

   2 Mécanismes de synchronisation inter processus                                                                                                                                               26       1 Le Système d’exploitation                                                                                                                                                     62
     2.1 Tubes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                   26          1.1 Le système d’exploitation (2/2) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                      62
     2.2 Mémoire partagée . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                      27
     2.3 Sémaphore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                     27       2 L’interface user/system                                                                                                                                                       62
                                                                                                                                                                                                             2.1 L’interface user/system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                      63
   3 Mécanismes de synchronisation intra-processus                                                                                                                                               28          2.2 L’interface user/system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                      63
     3.1 Mutex . . . . . . . . . . . . . . . . . . . . . . . .                                   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   28
     3.2 Moniteur . . . . . . . . . . . . . . . . . . . . . .                                    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   29    Interruptions et communication                                                                                                                                                     65
     3.3 Barrière . . . . . . . . . . . . . . . . . . . . . . .                                  .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   30
          3.3.1 Read-Write lock . . . . . . . . . . . . . .                                      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   30       Plan du document                                                                                                                                                                66

Télécom SudParis — François Trahay & Gaël thomas — 2020 —                                                                                                                                         i    Télécom SudParis — François Trahay & Gaël thomas — 2020 —                                                                                                                          ii
CSC4508 - Systèmes d'exploitation - François Trahay & Gaël thomas 2020 - Télécom SudParis
CSC4508 – Systèmes d’exploitation                                                                                                                                                          CSC4508 – Systèmes d’exploitation

   1 Les bus de communication                                                                                                                                                        66          3.5 Instructions vectorielles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                93
      1.1 Les bus de communication . . . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   66
      1.2 Le bus mémoire . . . . . . . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   67       4 Parallel Processing                                                                                                                                                                       94
           1.2.1 DMA: Direct Memory Access               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   67          4.1 Hyperthreading / SMT        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    95
           1.2.2 MMIO: Memory-Mapped IO                  .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   68          4.2 Processeurs multi-cœurs     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    95
      1.3 Le bus d’entrées/sorties . . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   68          4.3 Architectures SMP . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    96
      1.4 Le bus d’interruptions – principe . .          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   69          4.4 Architectures NUMA .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    96

   2 Interruptions                                                                                                                                                                   69       5 Hiérarchie mémoire                                                                                                                                                                        97
      2.1 Réception d’une interruption . . . . . .               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   70          5.1 Enjeux . . . . . . . . . . . . . . . . .                .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    97
      2.2 Réception d’une interruption : exemple                 .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   70          5.2 Caches . . . . . . . . . . . . . . . . .                .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    98
      2.3 Réception d’une interruption (suite) . .               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   71          5.3 Memory Management Unit (MMU)                            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    99
      2.4 Interruptions et multicœurs . . . . . . .              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   71               5.3.1 Fully-associative caches . . .                   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   100
      2.5 MSI: Message Signaling Interrupt . . . .               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   72               5.3.2 Direct-mapped caches . . . .                     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   100
      2.6 Communication inter cœur . . . . . . .                 .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   72               5.3.3 Set-associative caches . . . .                   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   101
      2.7 La table IDT . . . . . . . . . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   73               5.3.4 Cohérence de cache . . . . . .                   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   101
      2.8 Gestion du temps : deux sources . . . .                .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   73
                                                                                                                                                                                              Bibliographie du chapitre                                                                                                                                                                  102
Mémoire virtuelle                                                                                                                                                                    75
                                                                                                                                                                                           Entrées/sorties                                                                                                                                                                               103
   1 Introduction                                                                                                                                                                    76
                                                                                                                                                                                              Plan du document                                                                                                                                                                           104
   2 Pagination                                                                                                                                                                      76
      2.1 Généralités . . . . . . . . . . . . . . .          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   77       1 IO bufferisée / non bufferisée                                                                                                                                                           105
      2.2 État des pages mémoire . . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   77
      2.3 Adresse logique (ou virtuelle) . . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   78       2 Primitives Unix d’entrée-sortie                                                                                                                                                          105
      2.4 Table des pages . . . . . . . . . . . . .          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   79          2.1 Ouverture/fermeture de fichier              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   105
      2.5 Mise en œuvre sur un pentium 64 bits               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   79          2.2 Lecture sur descripteur . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   106
      2.6 Translation Lookaside Buffer (TLB) .               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   79          2.3 Écriture sur descripteur . . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   107
                                                                                                                                                                                                 2.4 Duplication de descripteur . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   109
   3 Le point de vue utilisateur                                                                                                                                                     80
      3.1 Espace mémoire d’un processus      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   80       3 Entrées-sorties et concurrence                                                                             109
      3.2 Mapping mémoire . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   81          3.1 Verrouillage de fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
      3.3 Allocation mémoire . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   81          3.2 Manipulation de l’offset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
      3.4 Le point de vue libc . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   83
                                                                                                                                                                                              4 Améliorer les performances des entrées-sorties                                                                                                                                           112
   4 Politiques d’allocation mémoire                                                                                                                                                 83          4.1 Conseil au noyau pour les lectures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                  112
      4.1 Non-Uniform Memory Access . .          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   84          4.2 IO asynchrones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                113
      4.2 Politique d’allocation First touch     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   84          4.3 mmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                                113
      4.3 Politique d’allocation Interleaved     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   85
                                                                                                                                                                                           Systèmes de fichier                                                                                                                                                                           115
      4.4 mbind . . . . . . . . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   85
                                                                                                                                                                                              Plan du document                                                                                                                                                                           116
Architecture                                                                                                                                                                         87
                                                                                                                                                                                              1 Périphérique et pilote de périphérique                                                                                                                                                   116
   Plan du document                                                                                                                                                                  88
                                                                                                                                                                                                 1.1 Périphérique et pilote de périphérique .                        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   116
   1 Introduction                                                                                                                                                                    88          1.2 Les périphériques dans les UNIX . . . .                         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   117
      1.1 Loi de Moore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                       88          1.3 2 types de périphériques . . . . . . . . .                      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   117
                                                                                                                                                                                                 1.4 Périphériques de type bloc dans xv6 . .                         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   118
   2 Processeur séquentiel                                                                                                                                                           89          1.5 Principe de l’algorithme de iderw . . .                         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   118

   3 Pipeline                                                                                                                                                                        89       2 Le cache d’entrées/sorties                                                                                                                                                               119
      3.1 Micro architecture d’un pipeline . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   90          2.1 Le cache d’entrée/sortie . . . . . . . .                    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   119
      3.2 Processeurs superscalaires . . . . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   90          2.2 Principe d’un cache d’entrée/sortie . .                     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   120
           3.2.1 Processeurs superscalaires . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   91          2.3 Le buffer cache de xv6 . . . . . . . . .                    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   120
           3.2.2 Dépendance entre instructions               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   91          2.4 Fonctionnement du buffer cache (1/3)                        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   121
      3.3 Gestion des branchements . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   92          2.5 Fonctionnement du buffer cache (2/3)                        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   121
      3.4 Prédiction de branchement . . . . . .              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   92          2.6 Fonctionnement du buffer cache (3/3)                        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   122

Télécom SudParis — François Trahay & Gaël thomas — 2020 —                                                                                                                            iii   Télécom SudParis — François Trahay & Gaël thomas — 2020 —                                                                                                                                      iv
CSC4508 – Systèmes d’exploitation                                                                                                                                                          CSC4508 – Systèmes d’exploitation

   3 Le journal                                                                                                                                                                      122
      3.1 Opération versus écriture sur disque           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   123
      3.2 Problèmes de cohérence . . . . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   123
      3.3 Mauvaises solutions . . . . . . . . .          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   124
      3.4 Première idée : les transactions . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   124
      3.5 Deuxième idée : le journal . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   125
      3.6 Troisième idée : le journal parallèle .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   125
      3.7 Structure du journal . . . . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   126
      3.8 Principe d’algorithme du journal . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   126
      3.9 Utilisation du journal . . . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   127
      3.10 Mise en œuvre dans xv6 (1/3) . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   127
      3.11 Mise en œuvre dans xv6 (2/3) . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   128
      3.12 Mise en œuvre dans xv6 (3/3) . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   128

   4 Partitions et systèmes de fichiers                                                                                                                                              129
      4.1 Système de fichiers . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   129
      4.2 Principe d’un système de fichiers      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   130
      4.3 Les partitions . . . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   130
      4.4 L’image disque . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   131

   5 Le système de fichiers UFS/xv6                                                                                                                                                  131
      5.1 Structure globale du système de fichiers               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   132
      5.2 Le dinode . . . . . . . . . . . . . . . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   132
      5.3 Les blocs de données d’un fichier . . . .              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   133
      5.4 Ajout d’un bloc à un fichier . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   133
      5.5 Les répertoires . . . . . . . . . . . . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   134
      5.6 Du chemin à l’inode . . . . . . . . . . .              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   134
      5.7 Création et suppression de fichier . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   135

   6 La pile d’entrée/sortie de xv6                                                                                                                                                  135
      6.1 L’inode . . . . . . . . . . . . .   . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   136
      6.2 Principales fonctions des inodes    (1/3)          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   136
      6.3 Principales fonctions des inodes    (2/3)          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   137
      6.4 Principales fonctions des inodes    (3/3)          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   137
      6.5 Le fichier ouvert . . . . . . . .   . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   138
      6.6 Le descripteur de fichier . . . .   . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   138

   7 Ce qu’il faut retenir                                                                                                                                                           139

Références                                                                                                                                                                           141

Index                                                                                                                                                                                143

Télécom SudParis — François Trahay & Gaël thomas — 2020 —                                                                                                                             v    Télécom SudParis — François Trahay & Gaël thomas — 2020 —   vi
CSC4508 – Systèmes d’exploitation                                                                                  CSC4508 – Systèmes d’exploitation
        '                                                                                                    $
                                                       Licence

           Ce document est une documentation libre, placée sous la Licence de Documentation Libre GNU (GNU
           Free Documentation License).
           Copyright (c) 2020 François Trahay & Gaël Thomas
           Permission est accordée de copier, distribuer et/ou modifier ce document selon les
           termes de la Licence de Documentation Libre GNU (GNU Free Documentation License),
           version 1.2 ou toute version ultérieure publiée par la Free Software Foundation; avec
 #1        les Sections Invariables qui sont ‘Licence’ ; avec les Textes de Première de Couverture
           qui sont ‘CSC4508 – Systèmes d’exploitation’
           et avec les Textes de Quatrième de Couverture qui sont ‘Help’.
           Une copie de la présente Licence peut être trouvée à l’adresse suivante :
           http://www.gnu.org/copyleft/fdl.html.

           Remarque : La licence comporte notamment les sections suivantes : 2. COPIES VERBATIM, 3.
           COPIES EN QUANTITÉ, 4. MODIFICATIONS, 5. MÉLANGE DE DOCUMENTS, 6. RECUEILS DE
           DOCUMENTS, 7. AGRÉGATION AVEC DES TRAVAUX INDÉPENDANTS et 8. TRADUCTION.

         &                                                                                                   %
   Ce document est préparé avec des logiciels libres :
  • LATEX : les textes sources sont écrits en LATEX (http://www.latex-project.org/, le site
    du Groupe francophone des Utilisateurs de TEX/LATEX est http://www.gutenberg.eu.org).
    Une nouvelle classe et une nouvelle feuille de style basées sur la classe seminar ont
    été tout spécialement dévéloppées:   newslide et slideint (projet fusionforge slideint,
    https://fusionforge.int-evry.fr/www/slideint/);
  • emacs: tous les textes sont édités avec l’éditeur GNU emacs (http://www.gnu.org/software/emacs);
  • dvips: les versions PostScript (PostScript est une marque déposée de la société Adobe Systems In-
    corporated) des transparents et des polycopiés à destination des étudiants ou des enseignants sont
    obtenues à partir des fichiers DVI (« DeVice Independent ») générés à partir de LaTeX par l’utilitaire
    dvips (http://www.ctan.org/tex-archive/dviware/dvips);
  • ps2pdf et dvipdfmx: les versions PDF (PDF est une marque déposée de la société Adobe Sys-
    tems Incorporated) sont obtenues à partir des fichiers Postscript par l’utilitaire ps2pdf (ps2pdf
    étant un shell-script lançant Ghostscript, voyez le site de GNU Ghostscript http://www.gnu.org/-
    software/ghostscript/) ou à partir des fichiers DVI par l’utilitaire dvipfmx;
  • makeindex:   les index et glossaire sont générés à l’aide de l’utilitaire Unix makeindex
    (http://www.ctan.org/tex-archive/indexing/makeindex);
  • TeX4ht: les pages HTML sont générées à partir de LaTeX par TeX4ht (http://www.cis.ohio-
    -state.edu/~gurari/TeX4ht/mn.html);
  • Xfig: les figures sont dessinées dans l’utilitaire X11 de Fig xfig (http://www.xfig.org);
  • fig2dev: les figures sont exportées dans les formats EPS (« Encapsulated PostScript ») et PNG
    (« Portable Network Graphics ») grâce à l’utilitaire fig2dev (http://www.xfig.org/userman/-
    installation.html);
  • convert: certaines figures sont converties d’un format vers un autre par l’utilitaire convert
    (http://www.imagemagick.org/www/utilities.html) de ImageMagick Studio;
  • HTML TIDY: les sources HTML générés par TeX4ht sont « beautifiés » à l’aide de HTML TIDY
    (http://tidy.sourceforge.net) ; vous pouvez donc les lire dans le source.
Nous espérons que vous regardez cette page avec un navigateur libre: Firefox par exemple. Comme l’indique
le choix de la licence GNU/FDL, tous les éléments permettant d’obtenir ces supports sont libres.

Télécom SudParis — François Trahay & Gaël thomas — 2020 —                                                    vii   Télécom SudParis — François Trahay & Gaël thomas — 2020 —   viii
Présentation du module                                                          1 Présentation du module
                                                    '                                                                                       $
                                                                                          1 Présentation du module

                                                     Objectifs du module :
                                                       Comprendre le fonctionnement interne d’un système d’exploitation
                                                       Savoir interagir avec l’OS depuis un programme
                                            #2
                                                     Structure du module :
                                                     [U] des séances orientées “userland”
                                                     [K] des séances orientées “kernel”

  Présentation du module                             [G] des séances “plus générales”

                                                   &                                                                                      %

          François Trahay
                                                   '                                                                                      $
                                                                                                   1.1 Organisation
                                                       1. Processus
                                                         CI1 [U] Threads

CSC4508 – Systèmes d’exploitation                        CI2 [U] Programmation concurrente
                                                         CI3 [G] Mécanismes de synchronisation
                                                         CI4 [K] Appels systèmes
                                                         CI5 [K] Interruption et ordonnancement
                                                         CI6 [K] Sprint : finalisation de l’ordonnanceur

                                                       2. Mémoire
                                            #3
                                                         CI7 [U] Mémoire virtuelle
                                                         CI8 [K] Memory Management Unit
                                                         CI9 [G] Architecture
                                                        CI10 [K] Sprint

                                                       3. Entrées/Sorties
                                                        CI11 [U] Entrées/sorties
                                                        CI12 [U] Synthèse : mini-projet
                                                        CI13 [K] Systèmes de fichier
                                                        CI14 [K] Sprint

                                                   &                                                                                      %
                                                     CI15 TP noté

                               2019–2020

                                       1   Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                    2
Présentation du module                                                    1 Présentation du module   Présentation du module                                                 1 Présentation du module
         '                                                                                 $                  '                                                                              $
                                       1.2 Séances kernel : XV6                                                                            1.4 Évaluation du module

          Lors des séances [K], vous allez développer un OS
            basé sur l’OS xv6
 #4                                                                                                   #6
            développement de certains mécanismes de l’OS                                                        À la fin du module, les étudiants évaluent le module.

            séances sprint :                                                                                    Objectif : améliorer le module
               finalisation du développement
               évaluation par les enseignants

        &                                                                                %                   &                                                                             %

        '                                                                                $
                                             1.3 Évaluation

          Évaluation :
            20% - Contrôle continu lors des sprints :
               “comment vous avez implémenté ce mécanisme de l’OS ?”
 #5
               “que se passe-t-il si X ?”
            80% - TP noté avec plusieurs parties :
               question(s) de cours
               expliquer comment vous avez implémenté un mécanisme de l’OS
               développer une application

        &                                                                                %

Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation              3    Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation           4
Threads                                                                                1 Rôles d’un système d’exploitation
                                                     '                                                                                                      $
                                                                                      1 Rôles d’un système d’exploitation

                                                         Abstraction : fournir une interface unique pour des matériels divers
                                                            exemple : accès à un disque dur, disque SSD, disque NVMe, etc.
                                             #2
                                                         Gestion des ressources : assurer l’utilisation efficiente des ressources
                                                            ordonnancement des processus sur les CPUs, allocation mémoire, etc.
                                                         Protection : contrôler l’utilisation des ressources et gérer les erreurs
                                                            séparation de l’espace mémoire des processus, droits d’accès aux ressources, etc.

           Threads
                                                     &                                                                                                               %

                                                     '                                                                                                               $
                                                                                                1.1 Pile logicielle

          François Trahay
                                                                                                                  main
                                                                            application
                                                                                                                  store_result
                                                            user space

                                                                                                                  fprintf                         high level API

                                             #3
CSC4508 – Systèmes d’exploitation

                                                                                                                                           libc
                                                                              libraries                                                           syscall wrappers
                                                                                                                  write

                                                                                                                   write
                                                                                                                   filesystem           network
                                                            kernel space

                                                                                              ipc     scheduler
                                                                           operating system                        buffer cache
                                                                                              memory       sd_setup_read_write_cmnd
                                                                                              management          block char netw
                                                                                                                    device driver

                                                                              hardware              CPU    Mem      Disk         ...

                                                     &                                                                                                               %

                                               Le système d’exploitation est chargé d’exploiter des matériels divers. Il intègre donc des pilotes (drivers)
                                           capables de dialoguer avec un matériel particulier. Les différents pilotes pour un même type de périphérique
                                           offrent une même interface, ce qui permet aux couches supérieures de l’OS d’utiliser indifféremment le
                                           matériel.
                                               Le passage de l’espace utilisateur à l’espace noyau se fait via un appel système (syscall). Le noyau traite
                                           la demande de l’application et retourne un entier positif ou nul en cas de succès, et -1 en cas d’échec.
                                              Du point de vue d’une application, les appels système sont exposés sous la forme de fonctions (définies
                               2019–2020   dans la libc) chargées d’exécuter l’appel système.

                                       5   Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                                        6
Threads                                                                   1 Rôles d’un système d’exploitation      Threads                                                                1 Rôles d’un système d’exploitation
          '                                                                                         $
                                                                                                                       fprintf(stderr, "Error while accessing file ’%s’: %s\n", file, strerror());
                     1.2 Test du retour des appels système et des fonctions
                                                                                                                       // -> message "Error while accessing file ’plop’: No such file or directory"
                                                                                                                       exit(EXIT_FAILURE);
                                                                                                                   }

                                                                                                                       ou

                                                                                                                   struct stat buf;
              Il faut toujours tester la valeur de retour d’un appel système et traiter les erreurs               int rc = stat(file, &buf);
                 Évite la propagation d’erreurs (la découverte de l’erreur peut avoir lieux                       if(rc < 0) {
  #4              beaucoup plus tard)
                                                                                                                     perror("Error while accessing file");
                                                                                                                     // -> message affiché: "Error while accessing file: No such file or directory"
                  I voir l’approche fail-fast présentée en CSC4102                                                   exit(EXIT_FAILURE);
              errno : variable externe indiquant la cause de la dernière erreur                                   }

                 La section ERRORS du manuel d’une fonction décrit les causes d’erreur possibles
                                                                                                                   Traitement d’erreur générique.           Il est possible de définir une macro affichant un message d’erreur et
                                                                                                                   indiquant où a eu lieu l’erreur. Par exemple :

                                                                                                                   #define FATAL(errnum, ...) do { \

          &                                                                                            %
                                                                                                                       fprintf(stderr, "Error in %s:%d:\n", __FILE__, __LINE__); \
                                                                                                                       fprintf(stderr, __VA_ARGS__); \
                                                                                                                       fprintf(stderr, ": %s\n", strerror(errnum)); \
   Témoignage d’un ancien ASR : « Sans insistance de [l’équipe pédagogique de CSC4508], cela ne nous
                                                                                                                       abort(); \
aurait pas sauté si vite aux yeaux que les problèmes (en début de la coupe de robotique) venait d’un manque
                                                                                                                     } while(0)
de gestion des erreurs sur un code qui n’avait pas été relu par suffisamment de monde ».
                                                                                                                   int main(int argc, char**argv) {
Comment vérifier le code de retour d’une fonction et traiter les problèmes ?                  La macro               char *file = argv[1];
void assert(scalar expression) teste l’expression passée en paramètre et, si celle-ci est fausse, affiche            struct stat buf;
un message d’erreur et termine le programme (avec la fonction abort()) :                                             int rc = stat(file, &buf);
                                                                                                                     if(rc < 0) {
  struct stat buf;
                                                                                                                       FATAL(errno, "Cannot access file ’%s’", file);
  int rc = stat(file, &buf);
                                                                                                                     }
  assert(rc>=0);
                                                                                                                     return EXIT_SUCCESS;
  // -> en cas d’erreur, affiche:
                                                                                                                   }
  //   appli: appli.c:12: main: Assertion ‘rc>=0’ failed.
                                                                                                                   // affiche:
  //   Abandon
                                                                                                                   // Error in fatal.c:21:
   Toutefois, la macro est à utiliser avec précaution car elle est désactivée lorsque le programme est compilé     // Cannot access file ’plop’: No such file or directory
en mode optimisé (avec gcc -O3 par exemple).                                                                       // Abandon
   Il est donc préférable de tester le code de retour, afficher un message décrivant l’erreur, et éventuellement
terminer le processus.                                                                                             Debugger.       Lorsqu’un programme appelle la fonction abort() afin de terminer le processus. Un fichier
                                                                                                                   core dump décrivant le processus lors de l’erreur peut alors être généré afin de débugger le programme avec
struct stat buf;                                                                                                   gdb.
int rc = stat(file, &buf);                                                                                            Pour activer la génération d’un core dump, lancer la commande ulimit -c unlimited. Dès lors, la
if(rc < 0) {                                                                                                       fonction abort() génère un core dump qui peut être fourni à gdb :
  fprintf(stderr, "Error\n");
  exit(EXIT_FAILURE); // ou abort();                                                                               $ ./fatal plop
}                                                                                                                  Error in fatal.c:21:
                                                                                                                   Cannot access file ’plop’: No such file or directory
Afficher la cause d’une erreur.          Le fichier errno.h listes les erreurs standard. Le manuel de chaque       Abandon (core dumped)
appel système (voir man 2 fonction, ou man 3 fonction) et de chaque fonction indique, dans la section
ERRORS, les différents codes d’erreurs qui peuvent être renvoyés.                                                  $ gdb ./fatal core
   Le message d’erreur associé à une valeur de errno peut être obtenu avec strerror() ou perror() :                GNU gdb (Debian 8.1-4+b1) 8.1
                                                                                                                   [...]
struct stat buf;                                                                                                   Reading symbols from ./fatal...(no debugging symbols found)...done.
int rc = stat(file, &buf);                                                                                         [New LWP 11589]
if(rc < 0) {                                                                                                       Core was generated by ‘./fatal plop’.

Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                            7    Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                          8
Threads                                                                                  2 Utilisation de la pile   Threads                                                                                                      2 Utilisation de la pile

Program terminated with signal SIGABRT, Aborted.                                                                    x86 32 bits.        Sur les architectures x86 32 bits, les arguments sont placés sur la pile de façon à ce que le
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50                                            premier argument soit à l’adresse ebp+8, le deuxième à l’adresse ebp+12 (si le premier argument est stocké
50      ../sysdeps/unix/sysv/linux/raise.c: Aucun fichier ou dossier de ce type.                                    sur 4 octets), etc.
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50                                                L’adresse de retour (c’est-à-dire l’adresse de l’instruction à exécuter après la fonction) est stockée sur la
#1 0x00007ffff7dfb535 in __GI_abort () at abort.c:79                                                                pile à l’adresse ebp+4.
#2 0x0000555555555232 in main (argc=2, argv=0x7fffffffdcd8) at fatal.c:21

          '                                                                                             $                                                                      ebp + 16        arg3      3rd argument
                                         2 Utilisation de la pile                                                                                                              ebp + 12        arg2      2nd argument

                                                                                                                                                                                   ebp + 8     arg1      1st argument

                                                                                                                                                                                   ebp + 4   foo+17      return adress

                                                                                                                                                                                             ebpfoo      old ebp value
                                                                                                                                                 ebp   (base pointer)
                                                                                                                                                                                   ebp - 4     var1      first local variable

                                                                                                                                                 esp   (stack pointer)
             Chaque appel de fonction crée un stack frame sur la pile                                                                                                             ebp - 8     var2      second local variable

             Un stack frame contient
 #5
                les variables locales                                                                                                           Figure 1 : Stack frame sur architecture x86 32 bits
                une sauvegarde des registres modifiés
                les arguments de la fonction (spécifique aux architectures x86 32 bits)
                l’adresse de retour de la fonction (spécifique aux architectures x86)

                                                                                                                    x86 64 bits.       Sur les architectures x86 64 bits, les arguments sont passés via les registres rdi, rsi, rdx,
          &                                                                                             %
                                                                                                                    rcx, r8 et r9. S’il y a plus de 6 arguments, les arguments suivants sont placés sur la pile.

          '                                                                                             $
                                     2.1 Contenu d’un stack frame                                                                                r9     arg5

                                                                                                                                                 r8      arg4

                                                                                                                                                 rdx arg3
             Un stack frame est défini par                                                                                                      rsi arg2                rbp + 8       foo+17         return adress

                une adresse de base qui indique où le frame commence (le registre rbp sur x86)                                                  rdi arg1                              rbpfoo         old rbp value
                l’adresse du sommet de la pile (le registre rsp sur x86)                                                                        rbp                     rbp - 4        var1          first local variable

 #6          Entrée de fonction :                                                                                                               rsp                     rbp - 8        var2          second local variable

                sauvegarder rbp (avec push rbp)
                réinitialiser rbp (avec mov rbp, rsp)
             Sortie de fonction :                                                                                                               Figure 2 : Stack frame sur architecture x86 64 bits

                restoration de l’ancien rbp (pop rbp)
                saut à l’adresse de retour (ret)

          &                                                                                             %

Convention d’appel de fonction.           En fonction de l’architecture CPU (et parfois du compilateur), la         Arm.      Sur les architectures Arm, les arguments sont passés via les registres (x0 à x7 sur Arm 64 bits).
façon de faire un appel de fonction peut varier.                                                                    L’adresse de retour est également stockée dans un registre.

Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                              9   Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                                                 10
Threads                                                                                                 2 Utilisation de la pile   2 Utilisation de la pile                                                                                                                        2.2 Buffer overflow

                                    x4 arg5                                                                                        Exemple.       Ici, le bug vient de la boucle sensée remplir le tableau qui effectue une itération de trop (à
                                                                                                                                   cause du “
# include < stdlib .h >                                                                                                            Exemple.           Voici un exemple de stack overflow :
int main ( int argc , char ** argv ) {                                                                                                                                                                     stack_overflow.c
    int N = 4;                                                                                                                     # include < stdio .h >
    char tab [ N ];                                                                                                                # include < stdlib .h >
    int a = 17;                                                                                                                    # include < string .h >

    for ( int i =0; i
Threads                                                                                                          Threads                                                                                                                             3 Contexte d’exécution d’un processus
                                                                                                                           '                                                                                                                                                      $
}
    return 0;
                                                                                                                                                         3 Contexte d’exécution d’un processus
   Ici, la fonction foo ne vérifie pas que new_str est suffisament grand pour contenir str. Donc, si str est
trop long, strcpy déborde et peut écraser l’adresse de retour de foo.                                                         Contexte : contexte d’exécution + contexte du noyau
   Voici un exemple d’exécution menant à un stack overflow :
                                                                                                                              Espace d’adressage : code, données et pile
  $ gdb ./stack_overflow
  (gdb) r coucouAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA                                                                                                                                                        0x0000008000000000
The program being debugged has been started already.                                                                                                                                                    Stack
                                                                                                                                                                                                                              Functions context

Start it from the beginning? (y or n) y                                                                                          Process context                                                                                                                   SP   (stack pointer)

                                                                                                                  # 10
Starting program: stack_overflow coucouAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                                                                                                                                 Execution context
new_str = coucouAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA                                Data registers                                                                              Shared libraries
                                                                                                                                                                                                        Libs
                                                                                                                                   Status register

                                                                                                                                                                                            userspace
Program received signal SIGSEGV, Segmentation fault.                                                                               Stack pointer SP)
0x000055555555518e in foo (str=0x7fffffffe03e "coucou", ’A’ ) at stack_overflow.c:9                              Program counter (PC)

9       }                                                                                                                        Kernel context
(gdb) bt                                                                                                                           Virt. Mem. structures                                                Heap               Dynamic allocation
                                                                                                                                                                                                                               (malloc, ...)
                                                                                                                                                                                                                                                                   brk
                                                                                                                                   Descriptor table
#0 0x000055555555518e in foo (str=0x7fffffffe03e "coucou", ’A’ ) at stack_overflow.c:9                           brk pointer
                                                                                                                                                                                                        .bss               Unitialized variables

#1 0x4141414141414141 in ?? ()                                                                                                                                                                          .data              Initialized variables                   PC   (program counter)

#2 0x4141414141414141 in ?? ()                                                                                                                                                                          .text                         Instructions
                                                                                                                                                                                                                                                           0x0000000000000000

                                                                                                                           &                                                                                                                                                                %
#3 0x4141414141414141 in ?? ()
#4 0x4141414141414141 in ?? ()
#5 0x4141414141414141 in ?? ()
#6 0x4141414141414141 in ?? ()
#7 0x4141414141414141 in ?? ()
#8 0x4141414141414141 in ?? ()
#9 0x0000555555550041 in ?? ()
#10 0x0000000000000000 in ?? ()
(gdb)

                                                                                                                           '                                                                                                                                                                $
    Ici, on observe qu’en sortant de la fonction foo, le programme tente d’exécuter l’instruction située à
l’adresse 0x4141414141414141 (0x41 est la valeur hexadécimale de ’A’), ce qui génère une erreur.
    On pourrait exploiter le bug en insérant dans argv[1] l’adresse de la fonction void bar(int a, int b)                                                                            3.1 Fil d’exécution
ainsi que ses paramètres [?].
           '                                                                                           $
                          2.2.2 Comment prévenir les buffer/stack overflow ?                                                  Fil d’exécution != Ressources
                 Vérifier les bornes lors de l’accès à un tableau                                                               Fil d’exécution (ou thread) : contexte d’exécution + pile
                    fait de manière automatique en Java                                                                         Ressources : code, données, contexte du noyau
                    n’est pas fait en C/C++ car trop coûteux
                                                                                                                                                 Thread
                 Ne pas utiliser les fonctions “dangereuses” (strcpy, gets ...)                                  # 11                           Stack
                                                                                                                                                          Functions context
                                                                                                                                                                                                                                      0x0000008000000000

                    Utiliser à la place les fonctions sûres (strncpy, fgets ...)                                                                             SP   (stack pointer)

                                                                                                                                              Execution context

                 Pile non-exécutable (activé par défaut par Linux)                                                                             Data registers
                                                                                                                                                Status register
                                                                                                                                                                                                      Libs
                                                                                                                                                                                                                Shared libraries
                                                                                                                                                                                          userspace

    #9                                                                                                                                          Stack pointer SP)
                    évite l’exécution de code arbitraire                                                                                       Program counter (PC)

                 Stack canaries                                                                                                                 Kernel context
                                                                                                                                                                                                      Heap
                                                                                                                                                                                                      .bss
                                                                                                                                                                                                              Dynamic allocation
                                                                                                                                                                                                                  (malloc, ...)

                                                                                                                                                                                                              Unitialized variables
                                                                                                                                                                                                                                           brk

                    Un canari (une valeur spécifique) est positionné sur la pile à l’entrée de la                                                 Virt. Mem. structures
                                                                                                                                                   Descriptor table
                                                                                                                                                                                                      .data
                                                                                                                                                                                                      .text
                                                                                                                                                                                                              Initialized variables

                                                                                                                                                                                                                     Instructions
                                                                                                                                                                                                                                          PC   (program counter)

                     fonction
                                                                                                                                                                                                                                       0x0000000000000000
                                                                                                                                                   brk pointer

                    Si en sortant de la fonction, le canari a été modifié, il y a eu stack overflow
                    option -fstack-protector-all de gcc                                                                   &                                                                                                                                                                %
                 Address space layout randomization (ASLR) (activé par défaut par Linux)
             charge le code de l’application à une adresse aléatoire
           &                                                                                           %

Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                      13       Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                                                                                         14
Threads                                                                                                                                                      3 Contexte d’exécution d’un processus   Threads                                                                                 4 Partage de données
          '                                                                                                                                                                               $
                                                                                                                                                                                                     processus), il n’y a pas de hiérarchie entre les threads.
                                                             3.2 Processus multithread

                                                                                                                                                                                                               '                                                                                         $
                                                                                                                                                                                                                                             3.4 Autres fonctions Pthread
              Plusieurs fils d’exécution
              Ressources partagées

                              Thread 1                                                                                    Thread 2

# 12                                    Functions context                      Stack 1                                                   Functions context
                              Stack 1                                                                                      Stack 2
                                            SP   (stack pointer)                                                           SP   (stack pointer)
                                                                               Stack 2
                             Execution context 1                                                                         Execution context 2
                               Data registers                                                                              Data registers
                               Status register                                                                             Status register
                                                                                                                                                                                                                   int pthread_exit(void* retval);
                                                                   userspace

                                                                                           Shared libraries
                               Stack pointer SP)                               Libs                                        Stack pointer SP)
                               Program counter (PC)                                                                        Program counter (PC)
                                                                                                                                                                                                     # 14
                                                                                                                                                                                                                      Termine le thread courant avec la valeur de retour retval
                                                                               Heap      Dynamic allocation         brk

                                                                                                                                                                                                                   int pthread_join(pthread_t tid, void **retval);
                                                                                             (malloc, ...)
                              Kernel context
                                                                               .bss      Unitialized variables
                                Virt. Mem. structures
                                Descriptor table                               .data     Initialized variables      PC   (program counter)

                                brk pointer                                    .text            Instructions
                                                                                                                 0x0000000000000000                                                                                   Attend la terminaison du thread tid et récupère sa valeur de retour

          &                                                                                                                                                                              %

    Dans un processus multi-thread, chaque thread possède un contexte (registres + pile). Le reste de la
mémoire (code, données, etc.) et les ressources (fichiers ouverts, etc.) sont partagés entre les threads.
    Les piles des différents threads sont espacées en mémoire de manière à ce qu’elles puissent grossir. Tou-                                                                                                  &                                                                                         %
tefois, si la pile d’un thread grossit trop, elle risque de déborder sur la pile d’un autre thread. Pour éviter ce
problème, la taille des piles est limitée (la commande ulimit -s donne la taille de pile maximum). Cette taille
limite peut être modifiée en ligne de commande (par exemple ulimit -s 32768), ou depuis un programme
(en utilisant la fonction setrlimit).
          '                                                                                                                                                                              $                     '                                                                                         $
                                                             3.3 Création d’un Pthread                                                                                                                                                           4 Partage de données

            Création d’un pthread                                                                                                                                                                               L’espace mémoire est partagé entre les threads, notamment

              int pthread_create(pthread_t *thread, const pthread_attr_t *attr,                                                                                                                                   les variables globales
# 13           void *(*start_routine) (void *), void *arg);                                                                                                                                          # 15
                                                                                                                                                                                                                   les variables locales statiques
                 attr (in) : attributs du thread à créer
                                                                                                                                                                                                                   le contexte du noyau (flux, signaux, etc.)
                 start_routine (in) : fonction à exécuter une fois le thread créé
                                                                                                                                                                                                                Ne sont pas partagées :
                 arg (in) : paramètre à passer à la fonction
                 thread (out) : identifiant du thread créé                                                                                                                                                        les variables locales

          &                                                                                                                                                                              %                     &                                                                                         %

   Nous présentons ici l’API Pthread (POSIX thread) qui est la plus utilisée en C. La norme C11 définit une
autre interface pour la manipulation de threads. Toutefois, rares sont les implémentations de cette interface.
Le standard de facto reste donc Pthread.                                                                                                                                                                Techniquement, tout l’espace mémoire est partagé entre les threads. Il est donc possible de partager
   Contrairement à la création de processus qui génère une hiérarchie (ie. un processus parent d’un autre                                                                                            toutes les variables, y compris les variables locales.

Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                                                                                                             15    Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                        16
Threads                                                                                 4 Partage de données   Threads                                                                                         4 Partage de données
           '                                                                                        $
                                                4.1 Thread-safe                                                    for ( char * token = strtok ( string , " : " ) ;
                                                                                                                         token ;
                                                                                                                         token = strtok ( NULL , " : " ) ){
                                                                                                                     printf ( " \ t % s \ n " , token );
                                                                                                                   }
                                                                                                               }

                                                                                                               int main ( int argc , char ** argv ) {
                                                                                                                 extract_path ();
                                                                                                                 return 0;
                                                                                                               }

             Code thread-safe : donne un résultat correct lorsqu’exécuté simultanément par plusieurs                   Voici un exemple de résultat obtenu :
# 16         threads :                                                                                         Parsing ’/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games’
                                                                                                                        /usr/local/bin
                Pas d’appel à du code non thread-safe                                                                  /usr/bin
                                                                                                                        /bin
                                                                                                                        /usr/local/games
                Protéger l’accès aux données partagées                                                                 /usr/games

                                                                                                                   La fonction strtok n’est pas réentrante car elle se base sur un état précédent (un pointeur sur le dernier
                                                                                                               caractère testé dans la chaîne). Ainsi, dans cet exemple, le traitement appliqué à chaque token ne peut pas
                                                                                                               utiliser strtok. Par exemple :

                                                                                                                                                                       strtok_example_bug.c
           &                                                                                           %       # include < stdlib .h >
                                                                                                               # include < stdio .h >

           '                                                                                           $
                                                                                                               # include < string .h >

                                                                                                               void extract_path () {
                                                  4.2 Réentrant                                                  char * string = getenv ( " PATH " );
                                                                                                                 printf ( " Parsing ’% s ’\ n " , string );
                                                                                                                 // string should contain a list of d i r e c t o r i e s s e p a r a t e d with :
                                                                                                                 // eg . / usr / local / bin :/ usr / bin :/ bin :/ usr / local / games :/ usr / games

                                                                                                                   // Extract the d i r e c t o r i e s
                                                                                                                   // eg . / usr / local / bin , / usr / bin , / bin , / usr / local / games , / usr / games
                                                                                                                   for ( char * token = strtok ( string , " : " ) ;
                                                                                                                         token ;
                                                                                                                         token = strtok ( NULL , " : " ) ){
                                                                                                                     // token co n t a in s a d i r e c t o r y ( eg . / usr / local / bin )
                                                                                                                     printf ( " \ t % s contains : " , token );

                                                                                                                       // Extract the s u b d i r e c t o r i e s
# 17         Code réentrant : code dont le résultat ne dépend pas d’un état précédent                                  // eg . usr , local , bin
                                                                                                                       for ( char * word = strtok ( token , " / " ) ;
                Ne pas maintenir d’état persistant entre les appels                                                         word ;
                                                                                                                             word = strtok ( NULL , " / " ) ){
                                                                                                                         printf ( " % s " , word );
                exemple de fonction non réentrante : fread dépend de la position du curseur du flux                   }
                                                                                                                       printf ( " \ n " );
                                                                                                                   }
                                                                                                               }

                                                                                                               int main ( int argc , char ** argv ) {
                                                                                                                 extract_path ();
                                                                                                                 return 0;
                                                                                                               }

           &                                                                                           %
                                                                                                                       Aura pour résultat :
                                                                                                               Parsing ’/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games’
Exemple : strtok.       Un autre exemple de fonction non-réentrante est la fonction char* strtok(char*                  /usr/local/bin contains: usr local bin

str, char *delim). Cette fonction extrait des sous-chaînes d’une chaîne.
   Par exemple, le code suivant affiche les différents répertoires de la variable PATH :                           Ici, le premier token (/usr/local/bin) est découpé en mots (usr, local, bin) par des appels suc-
                                                                                                               cessif à strtok qui modifient l’état précédent de strtok, ce qui empêche les appels suivants à token =
                                                strtok_example.c                                               strtok(NULL, ":") de parcourir la chaîne string.
# include < stdlib .h >
# include < stdio .h >
# include < string .h >                                                                                        Rendre une fonction réentrante.         Il est possible de rendre réentrante une fonction non-réentrante en
void extract_path () {                                                                                         ajoutant un paramètre correspondant à l’état de la fonction. Par exemple, la version réentrante de char*
  char * string = getenv ( " PATH " );                                                                         strtok(char *str, const char *delim); est char* strtok_r(char *str, const char *delim, char
  printf ( " Parsing ’% s ’\ n " , string );
                                                                                                               **saveptr );
                                                                                                                  Ainsi, le programme précédent peut être corrigé en :

Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                       17    Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                                18
Threads                                                                            4 Partage de données     Threads

                                                   strtok_example_fixed.c                                     • destruction :
# include < stdlib .h >
# include < stdio .h >                                                                                                – int pthread_key_delete(pthread_key_t *key););
# include < string .h >
                                                                                                              • utilisation :
void extract_path () {
  char * string = getenv ( " PATH " );
  char * saveptr = NULL ;                                                                                             – void *pthread_getspecific(pthread_key_t key);
  printf ( " Parsing ’% s ’\ n " , string );
                                                                                                                      – int pthread_setspecific(pthread_key_t key, const void *value);
    for ( char * token = strtok_r ( string , " : " , & saveptr ) ;                                            • initialisation :
          token ;
          token = strtok_r ( NULL , " : " , & saveptr ) ){
      printf ( " \ t % s contains : " , token );                                                                      – int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
        char * saveptr_word = NULL ;
        for ( char * word = strtok_r ( token , " / " , & saveptr_word ) ;
              word ;
              word = strtok_r ( NULL , " / " , & saveptr_word ) ){
          printf ( " % s " , word );
        }
        printf ( " \ n " );
    }
}

int main ( int argc , char ** argv ) {
  extract_path ();
  return 0;
}

Qui aura pour résultat :
Parsing ’/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games’
         /usr/local/bin contains: usr local bin
         /usr/bin contains: usr bin
         /bin contains: bin
         /usr/local/games contains: usr local games
         /usr/games contains: usr games

              '                                                                                $
                                           4.3 TLS – Thread-Local Storage

                                                                                                                       '                                                                                        $
                                                                                                                                                         5 Synchronisation

                  Variable globale (ou locale statique) propre à chaque thread
                  Exemple : errno
# 18                                                                                                                      Garantir la cohérence des données
                  Déclaration :                                                                                             Accès simultanés à une variable partagée en lecture/écriture
                      en C11 : _Thread_local int variable = 0;                                                                 I x++ n’est pas atomique (constitué de load, update, store)
                      en C99 avec gcc : __thread int variable = 0;                                         # 19             Accès simultanés à un ensemble de variables partagées
                      en C99 avec Visual studio : __declspec(thread) int variable = 0;                                         I exemple : une fonction swap(a, b){ tmp=a; a=b; b=tmp; }
                                                                                                                          Plusieurs mécanismes de synchronisation
                                                                                                                             Mutex
                                                                                                                             Instructions atomiques
              &                                                                                %                             Conditions, sémaphores, etc. (cf CI3)

pthread_key.       Une autre manière (plus portable, mais beaucoup plus pénible à écrire) de déclarer une
variable TLS est d’utiliser un pthread_key :
                                                                                                                       &                                                                                        %
    • création :
                                                                                                                Le programme suivant illustre le problème des accès simultanés à des variables partagées. Ici, deux threads
            – int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));                        incrémentent chacun 1 000 000 000 fois la même variable :

Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                    19    Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                         20
Threads                                                                               5 Synchronisation      Threads                                                                               5 Synchronisation
                                                                                                                             '                                                                              $
                                                       compteurBOOM.c
                                                                                                                                                                                       5.1 Mutex
/*
 * compteurBOOM .c
 *
 * Probleme de s y n c h r o n i s a t i o n
 *                                                                                                                                 Type : pthread_mutex_t
 *
 */
                                                                                                                                   Initialisation :
# include    < error .h >
# include    < unistd .h >                                                                                                               pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
# include    < stdlib .h >
# include    < stdio .h >                                                                                                                int pthread_mutex_init(ptread_mutex_t *m, const
# include    < pthread .h >
                                                                                                                                          pthread_mutexattr_t *attr);
                                                                                                             # 20
/* INT_MAX / 2 */
# define NBITER 1000000000                                                                                                         Utilisation :
int compteur = 0;                                                                                                                        int pthread_mutex_lock(pthread_mutex_t *mutex));
void * start_routine ( void * arg ) {                                                                                                    int pthread_mutex_trylock(pthread_mutex_t *mutex);
  int i ;

    for ( i = 0; i < NBITER ; i ++) {
                                                                                                                                         int pthread_mutex_unlock(pthread_mutex_t *mutex);
        /* OOPS : FAUX acces a v a r ia b l e p a r tag ee non p rot eg ee */
        compteur ++;                                                                                                               Destruction :
      }
    pthread_exit ( NULL );                                                                                                               int pthread_mutex_destroy(pthread_mutex_t *mutex);
}

int main ( int argc , char * argv []) {
  int rc ;
                                                                                                                             &                                                                             %
  pthread_t thread1 , thread2 ;
                                                                                                                 En utilisant un mutex, on peut corriger le programme compteurBOOM en assurant que les incrémenta-
    rc = pthread_creat e (& thread1 , NULL , start_routine , NULL );                                         tions du compteur se font en exclusion mutuelle :
    if ( rc )
      error ( EXIT_FAILURE , rc , " p thread_c reate " );
                                                                                                                                                                                compteur_mutex.c
    rc = pthread_creat e (& thread2 , NULL , start_routine , NULL );
                                                                                                             /*
    if ( rc )
                                                                                                              * compteurBOOM .c
      error ( EXIT_FAILURE , rc , " p thread_c reate " );
                                                                                                              *
                                                                                                              * P rob le me de s y n c h r o n i s a t i o n
    rc = pthread_join ( thread1 , NULL );
                                                                                                              *
    if ( rc )
                                                                                                              *
      error ( EXIT_FAILURE , rc , " pthread_join " );
                                                                                                              */
    rc = pthread_join ( thread2 , NULL );
    if ( rc )
                                                                                                             # include       < error .h >
      error ( EXIT_FAILURE , rc , " pthread_join " );
                                                                                                             # include       < unistd .h >
                                                                                                             # include       < stdlib .h >
    if ( compteur != 2 * NBITER )
                                                                                                             # include       < stdio .h >
      printf ( " BOOM ! compteur = % d \ n " , compteur );
                                                                                                             # include       < pthread .h >
    else
      printf ( " OK compteur = % d \ n " , compteur );
                                                                                                             /* INT_MAX / 2 */
                                                                                                             # define NBITER 1000000000
    exit ( EXIT_SUCCESS );
}
                                                                                                             int compteur = 0;
                                                                                                             p thread_mu te x_t mutex = P T H R E A D _ M U T E X _ I N I T I A L I Z E R ;
   Alors que le compteur devrait valoir 2*1 000 000 000 = 2 000 000 000, l’exécution de ce programme donne
un autre résultat, par exemple :                                                                             void * start_routine ( void * arg ) {
                                                                                                               int i ;
$ ./compteurBOOM
BOOM! compteur = 1076588402                                                                                      for ( i = 0; i < NBITER ; i ++) {
                                                                                                                   p th r ea d _ m u t e x _ l o c k (& mutex );
                                                                                                                   compteur ++;
                                                                                                                   p t h r e a d _ m u t e x _ u n l o c k (& mutex );
                                                                                                                 }
                                                                                                                 pthread_exit ( NULL );
                                                                                                             }

                                                                                                             int main ( int argc , char * argv []) {
                                                                                                               int rc ;
                                                                                                               pthread_t thread1 , thread2 ;

                                                                                                                 rc = pthread_creat e (& thread1 , NULL , start_routine , NULL );
                                                                                                                 if ( rc )
                                                                                                                   error ( EXIT_FAILURE , rc , " p thread_c reate " );

                                                                                                                 rc = pthread_creat e (& thread2 , NULL , start_routine , NULL );
                                                                                                                 if ( rc )
                                                                                                                   error ( EXIT_FAILURE , rc , " p thread_c reate " );

                                                                                                                 rc = pthread_join ( thread1 , NULL );
                                                                                                                 if ( rc )
                                                                                                                   error ( EXIT_FAILURE , rc , " pthread_join " );

Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                    21     Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                  22
Threads                                                                                      5 Synchronisation   Threads

    rc = pthread_join ( thread2 , NULL );                                                                                                                              compteur_atomic.c
    if ( rc )
      error ( EXIT_FAILURE , rc , " pthread_join " );                                                            /*
                                                                                                                  * compteurBOOM .c
    if ( compteur != 2 * NBITER )                                                                                 *
      printf ( " BOOM ! compteur = % d \ n " , compteur );                                                        * P rob le me de s y n c h r o n i s a t i o n
    else                                                                                                          *
      printf ( " OK compteur = % d \ n " , compteur );                                                            *
                                                                                                                  */
    exit ( EXIT_SUCCESS );
}                                                                                                                # include     < error .h >
                                                                                                                 # include     < unistd .h >
                                                                                                                 # include     < stdlib .h >
   Si le résultat obtenu est correct, l’utilisation d’un mutex ralentit considérablement le programme (144s      # include     < stdio .h >
avec mutex, contre 4.1s sans mutex)                                                                              # include     < pthread .h >

                                                                                                                 /* INT_MAX / 2 */
                                                                                                                 # define NBITER 1000000000

                                                                                                                 _Atomic int compteur = 0;

                                                                                                                 void * start_routine ( void * arg ) {
                                                                                                                   int i ;

                                                                                                                     for ( i = 0; i < NBITER ; i ++) {
                                                                                                                       compteur ++;
                                                                                                                     }
                                                                                                                     pthread_exit ( NULL );
                                                                                                                 }

                                                                                                                 int main ( int argc , char * argv []) {
                                                                                                                   int rc ;
                                                                                                                   pthread_t thread1 , thread2 ;

                                                                                                                     rc = pthread_creat e (& thread1 , NULL , start_routine , NULL );
                                                                                                                     if ( rc )
                                                                                                                       error ( EXIT_FAILURE , rc , " p thread_c reate " );

                                                                                                                     rc = pthread_creat e (& thread2 , NULL , start_routine , NULL );
                                                                                                                     if ( rc )
                                                                                                                       error ( EXIT_FAILURE , rc , " p thread_c reate " );

                                                                                                                     rc = pthread_join ( thread1 , NULL );
                                                                                                                     if ( rc )
                                                                                                                       error ( EXIT_FAILURE , rc , " pthread_join " );
                                                                                                                     rc = pthread_join ( thread2 , NULL );

             '                                                                                        $
                                                                                                                     if ( rc )
                                                                                                                       error ( EXIT_FAILURE , rc , " pthread_join " );
                                                5.2 Opérations atomiques                                             if ( compteur != 2 * NBITER )
                                                                                                                       printf ( " BOOM ! compteur = % d \ n " , compteur );
                                                                                                                     else
                                                                                                                       printf ( " OK compteur = % d \ n " , compteur );

                                                                                                                     exit ( EXIT_SUCCESS );
                                                                                                                 }

                 Opération s’exécutant de manière atomique                                                           Ici, le résultat est correct et le programme nettement plus rapide qu’en utilisant un mutex :

                 C11 définit un ensemble de fonctions effectuant des opérations atomiques                           • sans synchronisation : 4.1s

# 21                 C atomic_fetch_add(volatile A *object, M operand);                                             • avec mutex : 144s
                     _Bool atomic_flag_test_and_set(volatile atomic_flag *object);                                  • avec opération atomique : 35s
                 C11 définit des types atomiques
                     les opérations sur ces types sont atomiques
                     déclaration : _Atomic int var; ou _Atomic(int) var;

                                                                                                                                                                   Bibliographie du chapitre
             &                                                                                        %
   On peut corriger le programme compteurBOOM en utilisant des opérations atomiques. Pour cela, il suffit de
déclarer le compteur comme _Atomic int. L’incrémentation du compteur utilise alors l’opération atomique
atomic_fetch_add.

Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                          23   Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                   24
Programmation concurrente                                 2 Mécanismes de synchronisation inter processus
                                                   '                                                                                        $
                                                                                         1 Introduction

                                                     Contenu de cette séance :

                                            #2         découverte des mécanismes de synchronisation existants
                                                          mécanismes inter processus
                                                          mécanismes intra processus
                                                       étude des patterns de synchronisation les plus classiques

Programmation concurrente
                                                   &                                                                                       %

          François Trahay
                                                   '                                                                                       $
                                                                  2 Mécanismes de synchronisation inter processus

CSC4508 – Systèmes d’exploitation

                                                       IPC : Inter Process Communication
                                            #3
                                                          basés sur des objets IPC dans l’OS
                                                          utilisation : souvent via une entrée dans le système de fichier
                                                           I fournit une persistance des données

                                                   &                                                                                       %

                               2019–2020

                                      25   Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                    26
Programmation concurrente                                  2 Mécanismes de synchronisation inter processus       Programmation concurrente                                3 Mécanismes de synchronisation intra-processus
        '                                                                                         $                      '                                                                                       $
                                                  2.1 Tubes                                                                                                   2.3 Sémaphore
                                                                                                                             Objet constitué d’une valeur et d’une file d’attente
             Fichiers spéciaux gérés en FIFO                                                                                Création :
             Tubes anonymes                                                                                                    sémaphore nommé : sem_t *sem_open(const char *name, int oflag,
                                                                                                                                 mode_t mode, unsigned int value);
                 int pipe(int pipefd[2]);
                                                                                                                                I name est une clé de la forme “/cle”
                  I crée un tube accessible par le processus courant
                  I accessible également aux futurs processus enfants                                                           sémaphore anonyme : int sem_init(sem_t *sem, int pshared, unsigned
 #4               I pipefd[0] pour lecture, pipefd[1] pour écriture                                               #6             int value);
                                                                                                                                I si pshared != 0, utilisable depuis plusieurs processus (via une mémoire
             Tubes nommés                                                                                                         partagée)
                 int mkfifo(const char *pathname, mode_t mode);
                                                                                                                             Utilisation :
                 crée une entrée dans le système de fichier accessible par n’importe quel processus                            int sem_wait(sem_t *sem);
             Utilisation (presque) comme un fichier “normal”                                                                   int sem_trywait(sem_t *sem);
                 lecture bloquante                                                                                             int sem_timedwait(sem_t *sem, const struct timespec
                 pas de lseek                                                                                                   *abs_timeout);

         &                                                                                             %                 &                                                                                      %
                                                                                                                           int sem_post(sem_t *sem);

   Vous avez déja manipulé des tubes sans forcément vous en apercevoir : en bash, l’enchaînement de
commandes reliées par des pipes se fait via des tubes anonymes créés par le processus bash.
    Ainsi, lorsqu’on exécute cmd1 | cmd2 | cmd3, bash crée 2 tubes anonymes et 3 processus, puis redirige
(grâce à l’appel système dup2, cf le Cours Intégré 11) les entrées et sorties standards des processus vers les
différents tubes.
                                                                                                                         '                                                                                      $
         '                                                                                             $                                3 Mécanismes de synchronisation intra-processus
                                          2.2 Mémoire partagée

             Permet de partager certaines pages mémoire entre plusieurs processus
             Création d’un segment de mémoire partagée de taille nulle :
                 int shm_open(const char *name, int oflag, mode_t mode);
                  I name est une clé de la forme “/cle”                                                           #7
                                                                                                                             Basés sur des objets partagés en mémoire
 #5
             Changement de la taille du segment :
                                                                                                                             Utilisation possible d’IPC
                 int ftruncate(int fd, off_t length);
             Projeter en mémoire le segment :
                 void *mmap(void *addr, size_t length, int prot, int flags, int
                  fd, off_t offset);
                 flags doit contenir MAP_SHARED

                                                                                                                         &                                                                                      %
         &                                                                                             %

   Nous reverrons plus tard (lors du CI 11 sur les entrées/sorties) une autre utilisation de mmap.

Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                        27     Télécom SudParis — François Trahay — 2019–2020 — CSC4508 – Systèmes d’exploitation                   28
Vous pouvez aussi lire