Vous êtes sur la page 1sur 115

Cours de Systèmes d’Exploitation

Yann Morère

Septembre 2001
Table des matières

Introduction 1

1 Historique des Systèmes Informatiques 3


1.1 Définition d’un système informatique . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Les Premiers Systèmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1 Les Automates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.2 Le zéro et la notation positionnelle (chiffres arabes) . . . . . . . . . . . . 4
1.2.3 Les machines à calculer . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 La machine Analytique (1887) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3.1 Les systèmes mécanographiques . . . . . . . . . . . . . . . . . . . . . . . 5
1.3.2 Théorie des systèmes et cybernétique . . . . . . . . . . . . . . . . . . . . 5
1.3.3 Les mathématiques de la calculabilité . . . . . . . . . . . . . . . . . . . . 5
1.3.3.1 Logique Mathématique . . . . . . . . . . . . . . . . . . . . . . . 5
1.3.3.2 Théorie des nombres . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3.3.3 Théorie des ensembles . . . . . . . . . . . . . . . . . . . . . . . 5
1.3.3.4 La machine de Turing . . . . . . . . . . . . . . . . . . . . . . . 6
1.3.4 Le Seconde Guerre Mondiale . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3.5 Première Génération : 1944-1954 . . . . . . . . . . . . . . . . . . . . . . 6
1.3.5.1 Entrée dans l’ère commerciale . . . . . . . . . . . . . . . . . . . 7
1.3.6 Seconde Génération : 1955-1965 . . . . . . . . . . . . . . . . . . . . . . . 7
1.3.6.1 Invention des langages de programmation . . . . . . . . . . . . 7
1.3.6.2 Progrès Technologique . . . . . . . . . . . . . . . . . . . . . . . 7
1.3.6.3 Éléments de l’informatique actuelle . . . . . . . . . . . . . . . . 7
1.3.7 Troisième Génération :1965-1980 . . . . . . . . . . . . . . . . . . . . . . 9
1.3.8 Quatrième Génération : 1980-1990 . . . . . . . . . . . . . . . . . . . . . . 9

2 Structure de systèmes informatiques 11


2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2 Organisation matérielle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3 Architecture générale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4 Architecture du processeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.5 Les entrées/sorties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.5.1 Les entrées/sorties programmées . . . . . . . . . . . . . . . . . . . . . . . 14
2.5.2 Les entrées/sorties par accès direct à la mémoire (DMA) . . . . . . . . . 14
2.5.3 Les entrées/sorties par processeur spécialisé . . . . . . . . . . . . . . . . 15
2.6 Les interruptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.7 Notion d’appels système . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.7.1 Mode maı̂tre-esclave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

i
2.8 Fonctionnement d’un système informatique . . . . . . . . . . . . . . . . . . . . . 17
2.9 Structure d’entrées/sorties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.9.1 Comment cela fonctionne . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.10 Structure de Stockage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.11 Protection du système informatique . . . . . . . . . . . . . . . . . . . . . . . . . 20

3 Structures des systèmes d’exploitation 21


3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2 Le modèle processus-ressources . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.3 Composants et fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.3.1 Gestion de l’UCT : Gestion de processus . . . . . . . . . . . . . . . . . . 23
3.3.2 Gestion des fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.3.3 Gestion de la mémoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.3.4 Autres composants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.3.5 Services d’un système d’exploitation . . . . . . . . . . . . . . . . . . . . 24
3.3.6 Accès aux services du système d’exploitation . . . . . . . . . . . . . . . . 24
3.4 Structure d’un système d’exploitation . . . . . . . . . . . . . . . . . . . . . . . . 25
3.4.1 Structure simple (systèmes monolithiques) . . . . . . . . . . . . . . . . . 25
3.4.2 Structure en couches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.4.3 Structure à noyau (kernel ) . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.5 Exemple de systèmes d’exploitation . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.5.1 Unix classique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.5.2 Le Mac OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

4 Les Processus 31
4.1 Concepts élémentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.1.1 Le processus et son espace adresse . . . . . . . . . . . . . . . . . . . . . . 31
4.1.2 Le kernel (noyau) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.1.3 Mode, espace et contexte . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.1.4 États d’un processus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.1.5 Mécanismes de gestion de processus . . . . . . . . . . . . . . . . . . . . . 33
4.2 Ordonnancement (Scheduling) de processus . . . . . . . . . . . . . . . . . . . . . 35
4.2.1 Rôles des ordonnanceurs (répartiteurs, Schedulers) . . . . . . . . . . . . 35
4.2.2 Files d’ordonnancement . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.2.2.1 Traitements par lots (batch) . . . . . . . . . . . . . . . . . . . . 36
4.2.2.2 Multiprogrammation sans temps partagé . . . . . . . . . . . . . 36
4.2.2.3 Répartiteur pour le temps partagé . . . . . . . . . . . . . . . . 36
4.2.2.4 Temps partagé avec gestion de la mémoire virtuelle . . . . . . . 38
4.2.3 Algorithmes d’ordonnancement . . . . . . . . . . . . . . . . . . . . . . . 38
4.2.3.1 Premier Arrivé, Premier Servi ou PAPS . . . . . . . . . . . . . 38
4.2.3.2 Plus court d’abord . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.2.3.3 Priorité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.2.3.4 Le tourniquet (Round Robin) . . . . . . . . . . . . . . . . . . . 41
4.2.3.5 Ordonnancement à listes multiples . . . . . . . . . . . . . . . . 42
4.2.3.6 Ordonnancement temps réel . . . . . . . . . . . . . . . . . . . . 42
4.2.4 Évaluation des algorithmes d’ordonnancement . . . . . . . . . . . . . . . 42
4.2.4.1 Essais sur le système en fonctionnement réel . . . . . . . . . . . 42
4.2.4.2 Analyse déterministe . . . . . . . . . . . . . . . . . . . . . . . . 43
4.2.4.3 Modèles de files d’attente . . . . . . . . . . . . . . . . . . . . . 43
4.2.4.4 Simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

ii
4.3 Création de processus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.4 Exemple du système Unix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.4.1 Fonctionnement de fork() . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.4.2 La notion de ressources . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.5 Synchronisation de processus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.5.1 Conditions de course (race condition) . . . . . . . . . . . . . . . . . . . . 46
4.5.2 Exclusion mutuelle avec attente active (busy wait) . . . . . . . . . . . . . 46
4.5.2.1 Variable de blocage (lock variable) . . . . . . . . . . . . . . 46
4.5.2.2 Désactivation des interruptions . . . . . . . . . . . . . . . . . . 47
4.5.2.3 Solution matérielle : TSL (Test and Set Lock ) . . . . . . . . . . 47
4.5.2.4 Alternance stricte . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.5.3 Problèmes classiques de synchronisation de processus . . . . . . . . . . . 49
4.5.3.1 Le problème des producteurs et des consommateurs (le buffer
limité) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
4.5.3.2 Le problème des philosophes dineurs . . . . . . . . . . . . . . . 49
4.5.3.3 Le problème du barbier endormi (sleeping barber ) . . . . . . . . 50
4.5.4 Les Sémaphores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.5.4.1 Définition d’un sémaphore . . . . . . . . . . . . . . . . . . . . . 50
4.5.4.2 Exemple d’implantation de sémaphores . . . . . . . . . . . . . . 50
4.5.4.3 Une solution au problème des producteurs et consommateurs . 51
4.5.4.4 Une solution au problème des philosophes dineurs . . . . . . . . 52
4.5.4.5 Une solution au problème du barbier endormi . . . . . . . . . . 53
4.6 Processus poids-legers et threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.6.2 Notion de thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.6.3 Abstraction fondamentales . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.6.3.1 Le point de vue du kernel . . . . . . . . . . . . . . . . . . . . . 55
4.6.3.2 Thread du kernel . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.6.3.3 Processus poids-léger (lightweight process ou LWP) . . . . . . . 57
4.6.3.4 Threads au niveau de l’usager . . . . . . . . . . . . . . . . . . . 57

5 Gestion de la mémoire 61
5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
5.2 Monoprogrammation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
5.2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
5.2.2 La liaison entre noms et adresses (address binding) . . . . . . . . . . . . 61
5.2.3 Espace adresse logique et espace adresse physique . . . . . . . . . . . . . 62
5.3 Multiprogrammation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
5.3.1 Multiprogrammation et utilisation de l’UCT et de la mémoire . . . . . . 63
5.3.2 Multiprogrammation à partitions fixes . . . . . . . . . . . . . . . . . . . 63
5.3.3 Multiprogrammation à partitions variables . . . . . . . . . . . . . . . . . 64
5.3.4 Allocation d’un trou libre . . . . . . . . . . . . . . . . . . . . . . . . . . 65
5.3.5 Représentation de l’espace libre . . . . . . . . . . . . . . . . . . . . . . . 65
5.3.5.1 Bitmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
5.3.5.2 Liste chaı̂née . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
5.3.5.3 Listes de trous de taille spécifiques . . . . . . . . . . . . . . . . 66
5.3.6 Limitations de ces techniques . . . . . . . . . . . . . . . . . . . . . . . . 66
5.4 La mémoire virtuelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.4.1 Pallier le manque de mémoire . . . . . . . . . . . . . . . . . . . . . . . . 67
5.4.1.1 Structures de chevauchement (overlays) . . . . . . . . . . . . . 67

iii
5.4.1.2 Segmentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.4.1.3 Pagination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
5.4.2 Généralisation à plusieurs processus . . . . . . . . . . . . . . . . . . . . . 73
5.4.3 Le va-et-vient (swap inswap out) . . . . . . . . . . . . . . . . . . . . . . 73
5.4.4 La demande de page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.4.5 Performance de la mémoire virtuelle . . . . . . . . . . . . . . . . . . . . 74
5.4.6 Caractéristiques principales dun algorithme de demande de page . . . . . 75
5.4.7 Algorithmes statiques de remplacement de pages . . . . . . . . . . . . . . 75
5.4.7.1 PAPS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.4.7.2 Remplacement aléatoire . . . . . . . . . . . . . . . . . . . . . . 76
5.4.7.3 LRU (Least Recently Used) . . . . . . . . . . . . . . . . . . . . 76
5.4.7.4 LFU (Least Frequently Used) . . . . . . . . . . . . . . . . . . . 76
5.4.7.5 L’anomalie de Belady et lalgorithme de remplacement optimal 77
5.4.8 Une approximations de LRU : l’algorithme de la seconde chance . . . . . 78
5.4.8.1 L’algorithme de la seconde chance . . . . . . . . . . . . . . . . 79

6 Interblocage 81
6.1 Introduction : définition et caractérisation . . . . . . . . . . . . . . . . . . . . . 81
6.1.1 Définition de l’interblocage (deadlock) . . . . . . . . . . . . . . . . . . . 82
6.1.2 Caractérisation de l’interblocage . . . . . . . . . . . . . . . . . . . . . . 82
6.1.3 Graphe dallocation des ressources . . . . . . . . . . . . . . . . . . . . . 82
6.1.4 Remarques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6.1.5 Comment traiter le problème de linterblocage ? . . . . . . . . . . . . . . 86
6.2 Vagabondage théorique sur linterblocage . . . . . . . . . . . . . . . . . . . . . . 87
6.2.1 Préambule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.2.2 Bibliographie réduite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.2.3 Mécanisme de demande de ressource . . . . . . . . . . . . . . . . . . . . 88
6.2.4 Interblocage et cycles dattente . . . . . . . . . . . . . . . . . . . . . . . 88
6.2.5 Mauvaises nouvelles (1ère partie) : problèmes non décidables . . . . . . 89
6.2.5.1 Introduction plus formelle à la théorie de la calculabilité . . . . 90
6.2.6 Une version plus faible du problème de linterblocage . . . . . . . . . . . 92
6.2.7 Mauvaises nouvelles (2e partie) : problèmes NP-complets . . . . . . . . . 92
6.2.8 Que faire ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
6.2.9 Le modèle qui en résulte . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
6.2.10 Les matrices dallocation de ressources . . . . . . . . . . . . . . . . . . . 95
6.3 Solutions au problème de l’interblocage . . . . . . . . . . . . . . . . . . . . . . 96
6.3.1 Évitement de l’interblocage . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.3.1.1 États sûrs, risqués (unsafe) et interblocage . . . . . . . . . . . 96
6.3.1.2 L’algorithme du banquier . . . . . . . . . . . . . . . . . . . . . 98
6.3.2 La prévention de l’interblocage . . . . . . . . . . . . . . . . . . . . . . . 98
6.3.2.1 Condition d’exclusion mutuelle . . . . . . . . . . . . . . . . . . 99
6.3.2.2 Condition d’attente circulaire . . . . . . . . . . . . . . . . . . . 99
6.3.2.3 Condition de non réquisition . . . . . . . . . . . . . . . . . . . 99
6.3.3 Détection et résolution de l’interblocage . . . . . . . . . . . . . . . . . . 99
6.3.3.1 Résolution de l’interblocage . . . . . . . . . . . . . . . . . . . . 100
6.3.3.2 Résoudre par rollback. . . . . . . . . . . . . . . . . . . . . . . 100
6.3.4 La stratégie de l’autruche (ne rien faire) . . . . . . . . . . . . . . . . . . 101

iv
Table des figures

1.1 Vue des composants d’un système informatique . . . . . . . . . . . . . . . . . . 4


1.2 Schéma d’un moniteur résident . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3 Paquets de cartes pour le traitements par lots . . . . . . . . . . . . . . . . . . . 8
1.4 Spooling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.1 Structure de l’ordinateur IAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11


2.2 Architecture générale d’un ordinateur . . . . . . . . . . . . . . . . . . . . . . . . 12
2.3 Architecture générale d’un processeur . . . . . . . . . . . . . . . . . . . . . . . . 13
2.4 Protocole élémentaire d’entrées/sorties . . . . . . . . . . . . . . . . . . . . . . . 14
2.5 Mécanisme d’accès direct à la mémoire . . . . . . . . . . . . . . . . . . . . . . . 15
2.6 Déroulement d’un sous-programme d’interruption . . . . . . . . . . . . . . . . . 16
2.7 Structure d’un Système Informatique . . . . . . . . . . . . . . . . . . . . . . . . 17
2.8 Mécanisne d’un disque dur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.9 Hiérarchie de dispositifs de stockage . . . . . . . . . . . . . . . . . . . . . . . . . 19

3.1 Le SE comme intermédiaire entre usagers et ressources systèmes . . . . . . . . . 22


3.2 Modèle révisé : Processus et Ressources . . . . . . . . . . . . . . . . . . . . . . . 22
3.3 Structure d’un système d’exploitation MS-DOS . . . . . . . . . . . . . . . . . . 25
3.4 Organisation d’un SE avec un kernel (noyau) . . . . . . . . . . . . . . . . . . . . 27
3.5 Organisation en kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.6 L’architecture en couche Unix . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4.1 Modes et contextes d’opération . . . . . . . . . . . . . . . . . . . . . . . . . . . 33


4.2 Diagramme d’états de processus . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.3 Déroulement d’un changement de contexte . . . . . . . . . . . . . . . . . . . . . 34
4.4 Ordonnancement pour le traitement par lot (batch) . . . . . . . . . . . . . . . . 37
4.5 Ordonnancement pour la multiprogrammation sans temps partagé . . . . . . . . 37
4.6 Ordonnancement avec temps partagé . . . . . . . . . . . . . . . . . . . . . . . . 37
4.7 Ordonnancement avec gestion de la mémoire virtuelle . . . . . . . . . . . . . . . 38
4.8 Fonctionnement d’un répartiteur PAPS . . . . . . . . . . . . . . . . . . . . . . . 39
4.9 Exécution des processus par un répartiteur PAPS . . . . . . . . . . . . . . . . . 40
4.10 Exécution de la séquence par un répartiteur tourniquet . . . . . . . . . . . . . . 41
4.11 Ordonnancement à listes multiples . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.12 Évaluation du temps d’attente moyen sous PAPS . . . . . . . . . . . . . . . . . 43
4.13 Évaluation du temps d’attente moyen sous SJF . . . . . . . . . . . . . . . . . . 43
4.14 Valeur retournée par fork() pour le père et le fils . . . . . . . . . . . . . . . . . 44
4.15 Exemple de hiérarchie de processus Unix . . . . . . . . . . . . . . . . . . . . . . 45
4.16 Exemple avec deux processus 0 et 1 . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.17 Inversion de priorités . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

v
4.18 Le problème des philosophes dineurs . . . . . . . . . . . . . . . . . . . . . . . . 49
4.19 Le modèle classique tâche = processus . . . . . . . . . . . . . . . . . . . . . . . 55
4.20 Nouveau modèle : un processus peut comprendre plusieurs chaı̂nes de contrôle,
ou threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4.21 Une des chaı̂nes de contrôle (threads) d’un processus peut bloquer sans que cela
affecte les autres threads de ce processus . . . . . . . . . . . . . . . . . . . . . . 56
4.22 Les processus poids-léger (lightweight process) sont vus par le kernel et en parti-
culier par le répartiteur qui leur offre l’accès au(x) processeur(s) disponible(s) . . 58
4.23 Cas des threads implantés par des librairies comme C-threads ou pthreads : le
répartiteur ne voit que les processus . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.24 Structure complexe combinant les deux types de threads usager : processus poids
léger et threads non supportés par le kernel . . . . . . . . . . . . . . . . . . . . . 59

5.1 Organisation de la mémoire avec un SE et un programme usager . . . . . . . . . 62


5.2 transformation de l’adresse physique en adresse logique . . . . . . . . . . . . . . 62
5.3 Taux d’utilisation de l’UCT pour divers taux d’E/S et nombre de processus . . . 63
5.4 Deux types de répartiteurs de haut niveau : répartiteurs à files multiples (une
par partition) et répartiteur à file unique . . . . . . . . . . . . . . . . . . . . . . 64
5.5 Occupation de la mémoire pour la séquence d’exécution des processus donnée
dans le cas d’une gestion de la mémoire à partitions variables . . . . . . . . . . . 64
5.6 Représentation de l’occupation mémoire par une carte de bits . . . . . . . . . . 66
5.7 Représentation de l’occupation de la mémoire par une liste chaı̂née . . . . . . . 66
5.8 Accès à un élément d’un segment grâce à son déplacement . . . . . . . . . . . . 68
5.9 Transformation d’une adresse logique en adresse physique dans le cas de la mé-
moire segmentée . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
5.10 Pagination : décomposition de l’espace d’adresse d’un processus en pages de taille
fixe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
5.11 Implantation de la mémoire paginée à l’aide d’une table de pages . . . . . . . . 70
5.12 Pagination à l’aide des registres assocoatifs . . . . . . . . . . . . . . . . . . . . . 71
5.13 Pagination à l’aide des registres assocoatifs . . . . . . . . . . . . . . . . . . . . . 71
5.14 Implémentation de la pagination à deux niveaux . . . . . . . . . . . . . . . . . . 72

6.1 Interblocage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
6.2 Processus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
6.3 Ressources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6.4 Ressources allouées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6.5 Ressources demandées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6.6 État dinterblocage après que P3 a demandé R B. . . . . . . . . . . . . . . . . . 84
6.7 État impossible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6.8 Graphe d’allocation de ressources (une ressource de chaque type). . . . . . . . . 85
6.9 Graphe d’attente correspondant . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
6.10 Graphe d’allocation de ressources (plusieurs de chaque type). . . . . . . . . . . 86
6.11 Graphe d’attente correspondant (plusieurs de chaque type). . . . . . . . . . . . . 86
6.12 Un autre graphe dallocation de ressources . . . . . . . . . . . . . . . . . . . . . 88
6.13 Graphe dattente correspondant à létat représenté à la figure précédente . . . . . 89
6.14 Module de prédiction de chronologie des besoins en ressources. . . . . . . . . . . 90
6.15 On suppose que lon dispose dune chronologie des demandes de ressources . . . 92
6.16 Exemple de fonction dordre O(n) . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.17 Temps dexécution approximatif en fonction de la complexité algorithmique et
de la taille n de lentrée (tiré du livre de Garey & Johnson cité plus haut). On
suppose ici quune instruction élémentaire sexécute en 1 µs. . . . . . . . . . . . 93

vi
6.18 Effet de progrès technologiques sur la taille du plus grand problème traitable en
1 heure de calcul. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
6.19 Matrices dallocation de ressources : Requises = Max Détenues . . . . . . . . . . 95
6.20 Exemple de séquences dallocation/libération de deux ressources de type impri-
mante, I, et table traçante, T, par deux processus. Les instants tij correspondent
à des points dans le code des processus. . . . . . . . . . . . . . . . . . . . . . . 96
6.21 Séquencements possibles des allocations et libérations de ressources pour les deux
processsus. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

vii
Liste des tableaux

3.1 Structure en couche de THE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

4.1 Bloc de contrôle de processus générique pour Unix . . . . . . . . . . . . . . . . . 35


4.2 Contraintes d’opérations des répartiteurs de bas et haut niveau . . . . . . . . . . 36
4.3 Exemple où la variable verrou ne protège pas l’accès à la ressource partagée . . . 47

ix
1 / 103

Introduction

Dans l’informatique d’aujourd’hui, les divisions bien distinctes entre la matériel informatique
(hardware) et les différentes formes de logiciel (software) : système d’exploitation (operating
system), langages de programmation et applications.
On peut par exemple installer différentes variantes d’UNIX (SunOs, Solaris, AIX, IRIX, Li-
nux, QNX, FreeBSD), sur des ordinateurs utilisant différentes générations de microprocesseurs
Digital Alpha, Sun Sparc, Intel x86, MIPS, Motorola 68K et PowerPc. De même des applica-
tions comme les traitements de textes, traitements d’images numériques, sont utilisables sur
différrentes plate-formes, Wintel, Macintosh ou Unix.
Ces divisions et distinctions actuelles ne se sont pas imposées d’emblée. Elles sont le résultat
(avec avantages et défauts) d’une longue évolution qui a plusieurs origines : mathématique,
philosophique, électronique, cybernétique, autimatique, économique et marketing.
3 / 103

Chapitre 1
Historique des Systèmes Informatiques

1.1 Définition d’un système informatique


Ce dernier se décompose généralement en 4 composants : le matériel, le système d’exploita-
tion, les programmes applications et les utilisateurs.

Le matériel : composé de l’unité centrale (UC), la mémoire et les périphériques d’entrée/sortie,


fournit les ressources informatiques de bases.
Les programmes d’applications : comme les compilateurs et les systèmes de base de don-
nées, les jeux, les programmes bureautiques définissent les manières d’utiliser le matériel
afin de résoudre les problèmes informatiques des utilisateurs.
Le système d’exploitation : qui contrôle et coordonne l’utilisation du matériel parmi les
divers programmes d’applications et les utilisateurs.
Les utilisateurs : qui utilisent les programmes d’applications afin de réaliser les tâches vou-
lues.

Le système d’exploitation peut alors être envisager comme un programme d’allocation de res-
sources. En effet le système informatique possède différentes ressources qui peuvent servir à
résoudre un problème : temps processeur, espace mémoire, espace de stockage des fichiers, pé-
riphériques d’entrée/sortie. Le SE est alors le gestionnaire de ces ressources, et les alloue aux
programmes et aux utilisateurs quand cela est nécessaire.
Bien sur il peut exister des requêtes imcompatibles au niveau des ressources, il faut alors
que le processeur choisisse la manière efficace est équitable pour les exploiter.
Afin de bien comprendre ce que font les systèmes d’exploitation, il est important de suivre
leur évolution durant les 30 dernières années.

1.2 Les Premiers Systèmes


1.2.1 Les Automates
Les premiers furent les mécaniciens chinois, dès le 2ème millénaire avant J-C avec les moteurs
à eau, à sable, les horloges. Puis les mécaniciens grecs : Archimède, Philon de Bysance, Héron
d’Alexandrie avec les systèmes à base de poulies, vis sans fin, engrenages, plans inclinnés. Puis
les Mécaniciens Arabes avec la conception de systèmes mécaniques et d’automates séquentiels
à arbre à came.
4 / 103 Chapitre 1 : Historique des Systèmes Informatiques

paqr:s<r<tIuqIvpwmx yaz{:|<{<}I~zIy€5 ‚aƒ„:<„<†I‡ƒIˆ‚‰‹Š ŒaŽ:<Ž<I‘I’Œ“‹”

?A@#BCD:E:FGIHJK LNM/M/OPRQ S<OT UWVX Y$ZI[\ ]^X[_Z[`aZ[3b c#d3ef&g h#i ij,g<kdmld


n jNe dldlh#i io3d3e
( )*,+-)/.,001325467.,( ( 8:9<;.=9<*#>2

   !#"$&%" !#'


 

Fig. 1.1 – Vue des composants d’un système informatique

1.2.2 Le zéro et la notation positionnelle (chiffres arabes)


Deux inventions fondamentales des mathématiques vont permettre de concevoir les ma-
chines à calculer. L’apparition du zéro et de la notation positionnelle en Inde, va permettre aux
mathématiciens européens de développer des systèmes de numérotation en bases non décimales
et en particulier la base 2 : code binaire au lettre F. Bacon (1623), théorie de la divisibilité en
base m ≥ 2 par Blaise Pascal (1654), traité de la numérotation binaire de Leibnitz (1679).

1.2.3 Les machines à calculer


¤ 1623 la machine à calculer de Wilhelm Schickhard (1623) est capable de faire quatre
opérations avec l’intervention d’un opérateur,
¤ La Pascaline de Blaise Pascal (1642) basée sur un système de roues dentées est capable
de d’additionner et de soustraire,
¤ La Machine de Leibnitz est capable de multiplier (1675).
L’invention du clavier numérique (1714-1850) et des dispositifs d’impression vont permettre la
ication en série de ces machines à calculer.
¤ Machine de Burroughs (1885),
¤ Caisses enregistreuses (National Cash Register : NCR Corporation) vers 1884-1890,
¤ Électrification des machines à écrire (Eddison 1880),
¤ Électrification des machines à calculer (1900).

1.3 La machine Analytique (1887)


Deux grands précurseurs, Charles Babbage et Adelaide (ADA) Lovelace inventent la machine
analytique qui possédait les caractéristiques suivantes :
¤ programmable avec des cartes perforées,
¤ composée de 1000 registres de 50 chiffres chacun,
¤ dispositif d’entrée/sortie,

Yann MORÈRE
1.3 La machine Analytique (1887) 5 / 103

¤ un organe de commande,
¤ un magasin (store) permettant de stocker les résultats des calculs intermédiaires,
¤ un moulin (mill) qui constituait l’unité arithmétique,
¤ et un mécanisme d’impression.
Il s’agit de la première machine de calcul programmable généraliste. Le même matériel pourra
être utilisé pour résoudre des problèmes différents. Il apparait alors la notion d’algorithme.

1.3.1 Les systèmes mécanographiques


En 1728 Falcon invente les cartes perforées pour l’opération des machines. Ces travaux
sont repris par J.M. Jacquard qui améliore ce système en ajoutant un tambour et un lecteur
à aiguilles mobile qui permet de lire des rouleaux perforés en continu. De là, naı̂t le premier
métier à tisser automatique en 1804 à Vaucanson.
H. Hollerith invente les machines à statistiques à cartes perforées en 1889. Il crèe la Tabu-
lating Machine Corporation qui deviendra par la suite IBM.
Le norvégien F.R. Bull invente une machine similaire en 1919. Ses brevets seront rachetés
par une compagnie française en 1931 qui devient la Compagnie des Machines Bull.

1.3.2 Théorie des systèmes et cybernétique


En 1938 les concepts intuitifs comme celui de la quantité d’information sont formalisés par
la théorie de l’information de C. Shannon en 1938.
La théorie générale des systèmes (H. Simon, L. Von Bertalanfy, N. Wiener...) vise à dé-
velopper des modèles de systèmes complexes dont on connait les entrées et sorties (réacteur
chimique, plante...).

1.3.3 Les mathématiques de la calculabilité


Simultanément, de gros progrès ont été fait dans le domaine des mathématiques. Ces avan-
cées seront décisives pour le développement de l’informatique.

1.3.3.1 Logique Mathématique

Au 19ème siècle, G. Boole et A. de Morgan, posent les fondations de la logique des mathé-
matiques modernes. Les notions de systèmes logiques, de calcul de propositions et de théorie de
la quantification sont développées par T. Skolem en 1919, S.C. Kleene 1936, W.V. Quine 1937,
K. Gödel 1930-1938.

1.3.3.2 Théorie des nombres

Des mathématiciens s’attaquent aux bases axiomatiques de la théorie des nombres : G.


Peano 1891, A. Church 1936, A. Turing 1936, K. Gödel, S.C. Kleene.

1.3.3.3 Théorie des ensembles

La notion intuitive d’ensemble est formalisée par G. Cantor 1873 et A. Tarki 1923-1938 pour
éliminer de nombreux paradoxes.

Cours de Systèmes d’Exploitation


6 / 103 Chapitre 1 : Historique des Systèmes Informatiques

1.3.3.4 La machine de Turing

En 1936, A. Turing présente un modèle formel d’une machine calculatoire. Ce modèle ma-
thématique détache complètement le processus de calcul des détails de l’implantation. Donc
pratiquement, tous les systèmes informatiques peuvent être modélisés comme une machine de
Turing.
Ces travaux permettent d’intégrer la notion de calculabilité : Existe-t-il un solution algo-
rithmique à un problème donné (même si on n’en a pas encore trouvé une).

1.3.4 Le Seconde Guerre Mondiale


Lorsque le seconde guerre mondiale survient, la technologie nécessaire à la création d’ordi-
nateur existe depuis 20 ans. La guerre va donner d’autres sources de motivation :
¤ motivation politique (donc financement) à la prise de risque technologique,
¤ assimilation des travaux des grands théoriciens des années 30,
¤ collaboration de certains de ces théoriciens avec des ingénieurs de grand talent pour
construire des systèmes opérationnels (Von Neumann, Turing et Mauchly).
Le projet le plus célèbre de calculateur développé durant la seconde guerre mondiale est le
Colossus (opérationnel en 1943), qui visait à décoder les messages de la marine allemande
encodés par la machine Enigma.

1.3.5 Première Génération : 1944-1954


Après la fin de la guerre, les efforts de développement de calculateur se maintiennent. Ils pro-
viendront surtout de l’armée (contrôle des réseaux radars, météorologie...) dans les laboratoires
nationaux.
L’armée favorise aussi ce développement de manière indirecte par le financement de centres
de recherche universitaires : Angleterre, Cambridge (MARK I, 1949), Pennsylvanie (ENIAC,
1944-1946), au MIT (WHIRLWIND, 1945-1952) qui était dédié à des problèmes de contrôle et
de temps réel, Illinois (ORDVAC et ILLIAC, 1949-1952).
Quelques compagnies commencent à faire des recherches et à developper des machines. IBM
embauche H.H. Aiken, de l’université d’Harvard, pour développer le MARK I (qui sera donné
par IBM à Harvard).
De leurs cotés, P. Eckert et J.W. Mauchly, qui étaient à la base du développement de
l’ENIAC, fondent leur propre compagnie et créent l’UNIVAC I, et seront rachetés par Remington
Rand.
Toutes ces machines avaient à peu près les mêmes caractéristiques :
¤ Système à base de composants mécaniques et d’éléctronique à lampes,
¤ Machines très complexes et très volumineuses,
¤ Concepteur = constructeur = programmeur = opérateur
¤ Programmation en language machine absolu et pas en langage d’assemblage (cablage de
carte),
¤ Opération de l’ordinateur : réservation de la machine, cablage des cartes, élimination des
bugs,
¤ Problèmes traités : calculs numériques (tables de fonctions).
Une des innovations technologiques les plus importantes à cette époque est l’introduction des
cartes perforées au début des années 50. (faire un petit truc sur les cartes parforées)

Yann MORÈRE
1.3 La machine Analytique (1887) 7 / 103

1.3.5.1 Entrée dans l’ère commerciale


Ce nouveau domaine est investit par les compagnies des industries des machines mécano-
graphiques et des caisses enregistreuses. Plus particulièrement IBM, développe le modèle 701,
composé de plusieurs boı̂tes interconnectées : 701, l’unité électronique et analytique de contrôle,
706, l’unité de stockage électrostatique, 711, un lecteur de cartes perforées, 716, une imprimante
alphabétique, 721, un enregistreur de carte perforées, 726, des lecteurs enregistreurs de bandes
magnétiques, 731, le lecteur enregistreur de tambour magnétique ainsi que l’unité d’alimenta-
tion électrique.
IBM hésite pour la commercialisation de ses ordinateurs et saute finalement le pas sous la
pression de la concurrence (UNIVAC I pour le recensement de la population des E.U.).
En 1953, le premier 701 est livré au centre national scientifique de Los Alamos, et cette
même année IBM annonce la sortie du modèle 650. A la fin de l’année 1956, IBM a rattrappé
Remington-Rand et prend la tête des fabriquants d’ordinateurs. La compagnie conservera cette
position jusqu’à l’apparition des clones d’IBM PC.

1.3.6 Seconde Génération : 1955-1965


1.3.6.1 Invention des langages de programmation
¤ Assembleur
¤ FORTRAN (FORmula TRANslator), John Backus 1954, est un langage compilé particu-
lièrement adapté aux calculs scientifiques. Il est toujours utilisé de nos jours, après avoir
subi de nombreuses modifications.
¤ COBOL (COmmon Business Oriented Language), G. Hopper 1959, est un langage com-
pilé spécialisé pour le traitement d’information de gestion et de comptabilité.

1.3.6.2 Progrès Technologique


¤ Introduction du transistor en 1947 par J. Bardeen, H.W. Brattain et W. Shockley qui
remplace alors les lampes.
¤ Apparition des bandes magnétiques, qui permettent de conserver les programmes impor-
tants (assembleurs, compilateurs, etc...) qui doivent être montés en mémoire.

1.3.6.3 Éléments de l’informatique actuelle


¤ l’ordinateur devient réellement un produit commercial,
¤ séparation des rôles : programmeur 6= opérateur,
¤ notion de Job (programme ou ensembles de programmes) que le programmeur saisit sur
des cartes perforées et que l’opérateur exécute sur l’ordinateur,
¤ regroupement des programmes à exécuter, ou tâches (jobs), pour le traitement par lots
(batch processing). On assiste même à l’introduction de la notion d’ordonnancement.
Dans quel ordre faut il lancer les programmes afin de gagner du temps. Le temps machine
est alors une valeur essentielle, et il est important d’optimiser l’utilisation de la machine.
Afin de diminuer ces temps d’inactivité de la machine, l’enchaı̂nement automatique des travaux
a été développé. On observe alors les vrais débuts des systèmes d’exploitation avec l’apparition
d’un moniteur résident. Il s’agit d’un petit programme, toujours résident en mémoire, qui permet
de donner le contrôle de travail à un autre. Afin de permettre à ce petit programme de savoir quel
programme exécuter, les cartes de contrôle ont été inventées. Ces cartes de contrôle contiennent
des directives pour le programme résident. Il permet l’enchaı̂nement automatique des travaux,
le chargement et le déchargement en mémoire, la gestion des entrées/sorties et la définition d’un
langage de commande.

Cours de Systèmes d’Exploitation


8 / 103 Chapitre 1 : Historique des Systèmes Informatiques

 

  
 
PQ RSUT,VWX  !#"  $&%
')(*,+-/.-102*+3-4 +5
67 -*,+584 + 6#9 (*-/: ;<+

=> ? @BAC
DE >F E!GHIH @
CKJL)MNL<O G J,@ C E

Fig. 1.2 – Schéma d’un moniteur résident

$END

       


$RUN

$LOAD

!#" $&%& 
$FNT

$JOB

Fig. 1.3 – Paquets de cartes pour le traitements par lots

Yann MORÈRE
1.3 La machine Analytique (1887) 9 / 103

1.3.7 Troisième Génération :1965-1980


L’apparition des circuits intégrés et des disques magnétiques donne une nouvelle fois une
accélération à l’industrie de l’informatique. On voit alors apparaitre des familles d’ordinateurs
qui sont basés sur la même architecture. Théoriquement les mêmes programmes doivent tourner
sur la famille d’une même machine.
Il y a aussi l’apparition de la multiprogrammation. L’UCT (unité de calcul et de traitement)
peut travailler sur un autre job, si un job effectue ses E/S.
La notion de spooling (Simultaneous Peripheral Operation On Line) apparait. Les pro-
grammes (jobs) sont lus directement des cartes vers un disque afin de réduire les temps de
chargement en mémoire. Le disque est utilisé comme un très large tampon (buffer ) afin d’at-
tendre la complétion des tâches.





 
!#" $%&" ')(+*,"-$ ./(0%&"1 2#354)627398/:;&<
2?>@:A<B4C8/6 2>@:A<
= =

Fig. 1.4 – Spooling

Lorsque plusieurs travaux sont sur un disque, il devient possible de faire de l’ordonnancement
(scheduling) de travaux. Le système d’exploitation maintient plusieurs travaux en mémoire à
la fois. Lorsque l’UC devient inactive, le SE commute sur un autre travail et l’exécute.
Les systèmes interactifs font leur apparition, l’utilisateur peut entrer directement des com-
mandes à l’ordinateur et obtenir les réponses (clavier, terminal à impression, terminal à écran
cathodique).
Dans ce cas l’utilisateur actif bloque la machine pour tous les autres. La solution est donc
la temps partagé, introduit par les systèmes MULTICS (MIT Bell Labset General Electric)
MULTiplexed Information and Computing Service et UNIX (K. Thompson, D. Ritchie, Bell
Labs).

1.3.8 Quatrième Génération : 1980-1990


On observe l’apparition des micro-ordinateurs grand public, comme le PET de commodore,
l’Apple II, le TRS 80, le Sinclair ZX80, Thomson MOXX. Puis, arrive l’IBM PC et son insépa-
rable MS-DOS.
Mais le grand-public a besoin d’ordinateurs plus simple. On observe l’apparition d’interface
graphique avec le XEROX Star, L’Apple Lisa puis l’Apple Macintosh.
Apparition du langage Postscript pour les imprimantes et des éditeurs WYSIWYG.
La fin des années 90 verra le boum du multimédia et des réseaux informatiques (internet).
Dans un cadre plus sérieux de nouvelles technologies apparaissent :
¤ Les systèmes parallèles (multiprocesseurs), les processeurs partagent les mêmes ressources
(bus, mémoire, horloge), on parle alors de systèmes fortement couplés. La capacité de

Cours de Systèmes d’Exploitation


10 / 103 Chapitre 1 : Historique des Systèmes Informatiques

traitement est largement augmentée, même si le doublement du nombre de processeurs


ne divise pas le temps de traitement par 2. Il sont aussi plus fiables, puisque si l’un des
processeurs tombe en panne les autres peuvent prendre le relais.
¤ les systèmes distribués (répartis), les processeurs ont leurs ressources propres et ils com-
muniquent entre eux par des lignes de communication comme des bus ou encore des
lignes téléphoniques. On parle de systèmes répartis ou faiblement couplés. Chaque unité
de traitement (processeur) est nommé nœud, site, ordinateur. Ces systèmes permettent
des partager les ressources entre les différents sites (partage de fichier), d’accélérer des
calculs, en subdivisant ces derniers qui s’effectuent en concurrence sur différents sites, de
fiabiliser le système, par dispersion des fonctions dans son architecture, de communiquer
entre les différents sites et les programmes se déroulant sur les différents nœuds.
¤ informatique temps réel (industrie), ou système temps réel est une forme de système
d’exploitation spécialisé. Il est utilisé lorsqu’il existe des exigences impérieuses de temps
de réponse pour le fonctionnement d’un processeur. Un système d’exploitation temps réel
possède des contraintes de temps fixes et bien définies. Le traitement doit être effectué
dans les contraintes définies, sinon le système tombe en panne.

Yann MORÈRE
11 / 103

Chapitre 2
Structure de systèmes informatiques

2.1 Introduction
Le but de cette partie est d’avoir une connaissance générale du fonctionnement de base
d’un système informatique. Ceci permettra ensuite de rentrer dans les arcannes du système
d’exploitation et d’en comprendre les différentes parties.

2.2 Organisation matérielle


La plupart des systèmes informatiques d’aujourd’hui sont toujours organisés selon les prin-
cipes de l’ordinateur IAS, conçu par John Von Neuman et ses collègues du Princeton Institute for
Advanced Studies (1946-1952). L’implatation la plus commune de l’IAS utilise une architecture

  
 !"#  !$% 
 '&(*)+$,  ACB AEDGHJF IKH
    LNM HJO+D P HQ HA!R
   ATS*PUD!VHJA

- ./ 0241 352


6 7 .+0 879 :2;3 2
<8 7*= 8!>*?@? 2

Fig. 2.1 – Structure de l’ordinateur IAS

de bus pour établir les connections entre les différents modules du système. Les caractéristiques
importantes du bus sont, sa largeur (en bits), la fréquence à laquelle il fonctionnne (en Mhz)
et le type de transfert autorisé. Il existe plusieurs standard, comme le bus ISA, PCI, EISA,
Microchannel...

2.3 Architecture générale


En première approche, un ordinateur est constitué d’un processeur qui effectue des traite-
ments, d’une mémoire centrale où le processeur range les données et les résulats de ces traite-
ments et enfin de périphériques qui permettent l’échange d’informations vers l’extérieur. Tous
ces composants sont reliés entre eux par l’intermédiaire d’un bus, qui est l’artère centrale et leur
12 / 103 Chapitre 2 : Structure de systèmes informatiques

permet de s’échanger des données. Patriquement tous les ordinateurs actuels ont cette archi-
tecture (Cf. figure 2.2). Les différences entre gros systèmes industriels et ordinateurs personnels
résident dans les performances des composants.

  
$%&'()*,+
- %& & .+ /
0 - (+//+   ! 
" ! #
13254

[@\ \ ]^`_ a b<c\ d yz{|}~~}  z


6,789:<;>=@?9
efeg a b<c }€z‚}~„ƒ ~{@z<†}~

ACBDEA F BD<EGIH J h ijklmno p l


C‘’ “ ‘<’”I• –

KCLMNK O LM<NPIQ R SCTUVS W TU<VXIY Z qCrstq u rs<tvIw x ‡Cˆ‰Š‡ ‹ ˆ‰<ŠŒI Ž

Fig. 2.2 – Architecture générale d’un ordinateur

La mémoire est un organe passif qui répond à des à des ordres indiqués par les files de
contrôle du bus. En répons à un ordre d’écriture, elle range la valeur réprésentée par les fils
de données du bus dans un emplacement défini par les fils d’adresse du bus. En réponse à un
ordre de lecture, elle fournit sur les fils de données du bus la valeur mémorisée à l’emplacement
défini par les fils d’adresse. Le nombre de fils de données du bus définit les nombres de bits
des emplacements mémoire. C’est une caractéristique importante pour les performances de
l’ordinateur, puisqu’elle détermine le nombre de bits pouvant être lus ou écrits en mémoire par
une seul opération. Le nombre de fils d’adresse du bus définit la taille maximale de la mémoire
centrale. Le bus est géré par un contrôleur, parfois intégré au processeur, qui empêcheson
utilisation simultanée par plusieurs organes.

2.4 Architecture du processeur


Le processeur est l’organe qui effectue les traitements suivant un algorithme défini par
le programmeur. Il est constitué essentiellement de trois partie :
¤ l’Unité Arithmétique et Logique (UAL) est capable d’effectuer les oprérations élémen-
taires habituelles sur les valeurs binaires, telles que l’addition, la soustraction, le OU
logique, les décalages ...
¤ Les registres permettent de mémoriser des résultats intermédaires ou des états particuliers
du processeur. Ils sont en général en petit nombre, mais d’accès très rapide. Certains ont
un rôle particulier, comme l’accumulateur, le compteur ordinal ou le registre d’instruction.
¤ Le décodeur-séquenceur contrôle l’exécution des différentes phases d’instructions.
Le mécanisme de fonctionnement est assez simple. Le décodeur séquenceur répète indéfiniment
la séquence d’opérations suivantes :
1. lecture mémoire à l’adresse indiquée par le compteur ordinal, et rangement du résultat
dans le registre d’instruction,
2. décodage de cette instruction pour en exécuter les différents phases.
Les instructions sont en général assez rudimentaires. Ce sont essentiellement des opérations
de transfert de données entre les registreset l’extérieur du processeur (mémoire et périphérique),

Yann MORÈRE
2.5 Les entrées/sorties 13 / 103



 !"# 455678694:;6<

 

$%&'($)*+
&',.-+/0) =?>@ABDCFE@AGHIB"A@J3KL
%1 23*),&)"*+ LMAFNPOQ@J.KL


... ...

Fig. 2.3 – Architecture générale d’un processeur

ou des opérations arithmétiques et logiques avec un ou deux opérandes. Pour ces dernières opé-
rations, un registre particulier, l’accumulateur, est souvent utilisé implicitement comme l’un des
opérandes et comme résultat. En général le déroulement de l’instruction entraı̂ne l’incrémenta-
tion du compteur ordinal et donc l’exécution de l’instruction qui suit. Notons que le transfert
d’une valeur dans le compteur ordinal entraı̂ne un branchement à l’adresse correspondant à
cette valeur. La tendance naturelle a été de contruire des processeurs avec un jeu d’instruction
de plus en plus large. On pensait que le programmeur utilisarait les instructions ainsi disponibles
pour améliorer l’efficacité de ses programmes. Ceci a conduit à l’architecture CISC (Complex
Instruction Set Computer ). Ce pendant on a constaté que les programmes contenaient toujours
les mêmes instructions, laissant une partie du jeu d’instruction très peu utilisé. Une nouvelles
famille de processeur a vu le jour avec une nouvelle architecture : l’architecture RISC (Reduced
Instruction Set Computer ), qui offre un jeu très réduit d’instructions simples mais très rapides.

2.5 Les entrées/sorties

Le principe elémentaire mis en œuvre pour l’échange de données entre deux constituants
physiques est donné en figure 2.4. En dehors des donnée proprement dites, deux liaisons sup-
plémentaires sont nécessaires pour permettre d’une part à l’émetteur de la donnée de signaler
la présence effective de cette donnée sur les fils correspondants, et d’autre part au récepteur de
signaler qu’il a lu la donnée.

Cours de Systèmes d’Exploitation


14 / 103 Chapitre 2 : Structure de systèmes informatiques

   


  !"$#% &  #' (  )#'*&+"$#
,- . ,/!01$23 4 . 2'. 5- - 62'4273 2

Fig. 2.4 – Protocole élémentaire d’entrées/sorties

2.5.1 Les entrées/sorties programmées


La manière la plus simple d’assurer la liaison entre le bus et un périphérique, est de faire une
simple adaptation des signaux évoqués ci-dessus. On parle alors d’une interface. Le processeur
adresse directement le périphérique, soit par les instructions habituelles d’accès à la mémoire
centrale, l’interface jouant alors le rôle d’un (ou plusieurs) emplacement de mémoire, soit par
des instructions spécialisées qui assurent le transfert d’une donnée élémentaire avec un registre
ou un emplacement mémoire. Dans tous les cas, le programmeur doit assurer le protocole
élémentaire d’échange de la figure 2.4, on parle alors d’entrées/sorties programmées.
tant que il_y_a_des_donnees faire
tant que donnee_suivante_non_prete faire fait ; (at-
tente de la donnée)
lire_la_donnee ;
traitement_de_la_donnee ;
fait ;
L’algorithme ci-dessus donne le schéma d’un tel programme de lecture. On constate une boucle
d’attente de donnée. De plus si le périphérique est lent, le processeur est monopolisé pendant
toute la durée de l’échange.

2.5.2 Les entrées/sorties par accès direct à la mémoire (DMA)


Afin d’accroı̂tre le débit des entrées/sorties et de diminuer la monopolisation du processeur,
la première solution a été de déporter un peu de fonctionnalité dans le dispositif qui relier le
périphérique au bus, de manière à lui permettre de ranger directement les données provenant
du périphérique en mémoire dans le cas d’une lecture, ou d’extraire directement ces données de
la mémoire dans le cas d’une écriture. Cest ce que l’on appel l’accès directe à la mémoire, ou
encore le vol de cycle.
La figure 2.5 schématise le mécanisme d’accès direct à la mémoire. L’exécution d’un transfert
se déroule de la manière suivante :
¤ le processeur informe le dispositif d’accès direct à la mémoire de l’adresse en mémoire
où commence le tampon (buffer ) qui recevra les données s’il s’agit d’une lecture, ou qui
contient les données s’il s’agit d’une écriture. Il informe également le dispositif du nombre
d’octet à transférer.
¤ le processeur informe le contrôleur des paramètres de l’opération proprement dite.
¤ pour chaque donnée élémentaire échangée avec le contrôleur, le dispositif demande au
processeur le contrôle du bus, effectue la lecture ou l’écriture mémoire à l’adresse contenue
dans son registre et libère le bus. Il incrémente ensuite cette adresse et décrémente le
compteur.
¤ lorsque le compteur atteint zéro, le dispositif informe le processeur de la fin du transfert
par une ligne d’interruption.

Yann MORÈRE
2.6 Les interruptions 15 / 103

  

2o p)qmrLso2p
  ! t$u)vbwxEy4z){gz)|Xy "$#%'&)('('& *,+.-0/2143)526 728.99:);)<
}E~X4€)''‚ƒ 4}@„.~ =2>@?'ACB.?'>ED4>GFH=I JLK)K)M,?N=>@O'P)K,DRQ0S)Q0B.>EO'P
i2j)kmlLni2j ^$_)`bacEd4e)fge)hXd
T,U.VXW4Y'Z.[@\,]Y
Fig. 2.5 – Mécanisme d’accès direct à la mémoire

Pendant toute la durée du transfert, le processeur est libre d’effectuer un traitement quel-
conque. La seule contrainte est une contrainte de ses propres accès mémoire pendant toute
cette même durée, car il doit parfois retarder certains de ses accès pour permettre au dispositif
d’accès direct à la mémoire d’effectuer les siens (d’ou le terme vol de cycle).
La limitation du débit inhérent au mécanismeest en général lié au débit potentiel de la
mémoire.
Dans le cas de périphériques rapides (autre qu’un clavier), si on utilise le système d’inter-
ruption simplement, l’UCT ne fait plus que gérer des interruptions, et n’a plus de ressources
pour les calculs. Le DMA permet de remédier à cela. Avec cet Accès Direct à la Mé-
moire, le contrôleur de périphérique transfert des blocs entiers de données, dans la mémoire
sans l’intervention de l’UCT.
On a alors une seul interruption par bloc à la place d’une interruption par octet.

2.5.3 Les entrées/sorties par processeur spécialisé


La troisième façon de relier un périphérique avec la mémoire est d’utiliser un processeur
spécialisé d’entrées/sorties, c’est à dire de déléguer plus d’automatisme à ce niveau. Dans le
mécanisme précédent c’est le processeur principal qui avait la charge de la préparation de
tous les dispositifs, accès direct à la mémoire, contrôleur, périphérique..., pour la réalisation de
l’opération. L’utilisation d’un processeur spécialisé a pour but de reporter dans ce dernier la
prise en charge de toute cette préparation, mais aussi des opérations plus complexes comme le
contrôle et la reprise d’erreurs. L’intérêt est de faire exécuter des tâches de bas niveau par un
processeur moins performant et donc moins coûteux à réaliser, afin de préserver le processeur
principal pour des taches plus nobles.

2.6 Les interruptions


Ce mécanisme a été imaginé pour permettre à un dispositif exécuteur d’interrompre le dérou-
lement normal du processeur pour lui faire exécuter un traitement spécifique. Un périphérique

Cours de Systèmes d’Exploitation


16 / 103 Chapitre 2 : Structure de systèmes informatiques

peut signaler la fin d’une opération d’entrées/sorties précédemment demandées, ce qui permet
au système de reprendre l’exécution du programme interrompu.
Le mécanisme est obtenu en modifiant légèrement le fonctionnement du décodeur-séquenceurdu
processeur par l’introduction d’une troisième phase :

1. lecture mémoire à l’adresse indiquée par le compteur ordinal, et rangement du résultat


dans le registre instruction,
2. décodage de cette instruction pour en exécuter les différentes phases,
3. s’il y a une demande d’interruption, alors la prendre en compte.

La prise en compte de l’interruption peut se faire de différentes manières. Puisqu’il s’agit d’in-
terrompre le déroulement normal des instructions, il suffit de modifier le compteur ordinal.
Mais comme l’on désire reprendre ultérieurement le programme interrompu, il faut aussi sau-
vegarder la valeur de ce compteur. En général, on utilise un PSW Program Status Word,
ou mot d’état du programme, qui contient en particulier, le compteur ordinal ainsi que certains
indicateurs sur l’état courant du processeur. La prise en compte de l’interruption consiste à ran-
ger en mémoire ce PSW dans un emplacement déterminé ou repéré par un registre particulier
comme le pointeur de pile et à charger à nouveau ce PSW.
Afin d’éviter de ranger inutilement en mémoire des registres, le processeur ne range que le
minimum lors de la prise en compte de l’interruption. Si l’on désire reprendre ultérieurement
le programme interrompu, les instructions qui constituent le traitement de l’interruption, sous-
programme d’interruption, doit ranger en mémoire le contenu de l’ensemble des registres du
processeur qui ne l’ont pas été lors de la prise en compte de l’interruption, pour permettre cette
reprise ultérieure. On nomme cela la sauvegarde du contexte. Lorsque le contexte est sauvegardé,
le sous-programme analyse la cause de l’interruption et effectue le traitement approprié. Il
peut ensuite restituer le contexte du programme interrompu. Il se termine par une instruction
spéciale qui demande au processeur de restituer le PSW du programme interrrompu, entraı̂nant
sa reprise effective. Il y a des difficultés si un nouvelle interruption arrive avant la fon du sous-

     <>=?&@BAC D=FEDGHIHJLK MNJDD?&C&NK =M


O&PQ>RS#TPUV&SLV Q5W#XYZS([&ZS
!#""$&%&(' )*!(+&!"&! \^]_(`&]a_(`&]L_#bc&d]
e.fgh ij kj linm&j:oqp h lijffr&s&i(h tl
u^v#wxyz&{yFx| }~5 z5€#}~xv(>xv
,.-/ 0121 354#678&91:/ 391--;&8&9/ 63
‚ƒ(„& † ‡ˆ ‰ˆ Š‡‹Œ † Š‡ˆ#&Ž&‡(† Š
ˆ#‡(† ‡>‡(† Š5‹ ’‘”“>•

Fig. 2.6 – Déroulement d’un sous-programme d’interruption

programme d’interruption. Si aucune précaution n’est prise, le sous-programme d’interruption


est de nouveau appellé avant sa fin, entraı̂nant la sauvegarde du contexte du sous-programme
d’interruption et ainsi la perte du contexte du programme interrompu. Ce ptroblème est résolu
par le masquage des interruptions qui interdit au processeur la prise en compte d’interruption
si elle est masquée. Cela n’annulle pas la demande d’interruption proprement dite, qui n’est
pas considérée par le processeur. Lorsque cette instruction sera exécutée par le processeur pour
démasquer cette interruption, ce dernier pourra alors la prendre en compte.

Yann MORÈRE
2.7 Notion d’appels système 17 / 103

2.7 Notion d’appels système


2.7.1 Mode maı̂tre-esclave
En général, il est nécessaire d’empêcher le programme d’un utilisateur de perturber le fonc-
tionnement global du système ou les programmes des autres utilisateurs. Cela implique qu’il
n’aie pas l’accès à l’ensemble des ressources de la machine mais seulement celles qui lui ont éré
allouées. La solution la plus couramment adoptée est de distinguer deux modes de fonction-
nement du processeur, le mode maı̂tre et le mode esclave. Dans le mode maı̂tre le processeur
a accès à toutes les ressources de la machine. Dans le mode esclave, certaines instructions lui
sont interdites. Le fonctionnement du décodeur-séquenceur est modifié en ajoutant une phase
intermédiaire.
1. lecture mémoire à l’adresse indiquée par le compteur ordinal, et rangement du résultat
dans le registre instruction,
2. si le processeur est en mode esclave, vérification que l’instruction est autorisée,
3. décodage de cette instruction pour en exécuter les différentes phases,
4. s’il y a une demande d’interruption, alors la prendre en compte.
L’indicateur de mode de fonctionnement maı̂tre/esclave fait partie du mot d’état programme.

2.8 Fonctionnement d’un système informatique


Le système est composé de l’UCT (unité de calcul et de traitement) et d’un certains nombres
de périphériques (disques, etc...) connectées sur un bus commun, fournissant l’accès à la mémoire
partagée. On peut noter qu’un contrôleur s’occupe d’un type de périphérique particulier.

[\^]_[!\`abc
MN!O#PRQSUTV!W#XRYZ

:;<=>;? @AB>
%&'()&* +,-) CED B<F =AG H
 !#" $  .0/21!354 ) 1!376 '(,98 C AJILK< C A9H

dfeghiej k l9mionlJprl9q pesi#l

   

Fig. 2.7 – Structure d’un Système Informatique

Cours de Systèmes d’Exploitation


18 / 103 Chapitre 2 : Structure de systèmes informatiques

Lorsque l’ordinateur est mis sous tension, un programme initial s’exécute (boot code, pro-
gramme d’amorçage). Son travail est d’initialiser tous les composants de l’ordinateur en partant
des registres de l’UCT jusqu’aux périphériques.
Ensuite il doit pouvoir charger le système d’exploitation et commencer à l’exécuter. Le SE
démarre alors le premier processus et attend un évènement. Le plus souvent il s’agit d’une
interruption matérielle ou logicielle. Le matériel interrompt l’UCT en lui envoyant un signal sur
le bus système. Dans le cas d’une interrruption logicielle il s’agit d’un appel système. L’UCT
arrête son travail actuel, et transfère l’éxécution à l’adresse de l’interruption. Là une routine
d’interruption s’exécute, et lorsqu’elle a terminé, l’UCT reprend son calcul.
Les interruptions sont très importantes, car c’est par elle que les programmes peuvent accé-
der au matériel. Ces interruptions sont en nombre fini, et il est possible d’accéder aux routines
d’interruption par l’intermédiaire d’une table stockée dans la mémoire basse. On parle alors de
vecteur d’interruptions. Ce tableau d’adresse est utilisé pour indexer un périphérique unique.
Mais pendant que nous exécutons notre routine d’interruption, il ne faut pas perdre le travail
que l’on était en train de faire. Donc le plus souvent l’adresse de l’instruction interrompue est
stockée dans la pile système (stack ).
Tout le système fonctionne comme cela. Dès que l’on veut atteindre un périphérique il faut
interrompre l’UCT.

2.9 Structure d’entrées/sorties


Comme nous l’avons déjà dit, chaque périphérique est rattachée à un contrôleur spécifique
(IDE, SCSI), Souvent il contient peu de mémoire et des registres spécialisés. Son principal but
est de déplacer les données entre les unités de périphériques qu’il contrôle.

2.9.1 Comment cela fonctionne


Pour demander une opération d’entrées/sorties, l’UCT charge les registres du contrôleur
qui examine alors les les actions à effectuer (lecture, écriture). Une fois l’opération terminée, il
imforme l’UCT. Cettet communication se fait par l’intermédiaire d’un interruption. Il existe 2
types E/S :
¤ E/S synchrone, le contrôle est pris par le processus d’E/S, et le contrôle est rendu au
processus utilisateur à la fin des actions d’E/S,
¤ E/S asynchrone, le contrôle est rendu tout de suite au processus utilisateur, puis le
controleur s’occupe des opérations d’E/S et informe en fin de traitement. Ceci permet
d’augmenter l’efficacité du système.

2.10 Structure de Stockage


La seule grande zone de stockage à laquelle le processeur (UCT) puisse accèder est la mé-
moire principale (RAM), qui est un gigantesque tableau d’octets regroupés en mots et chaque
mots possède son adresse propre.
L’idéal serait que les programmes et les données soient résidents en mémoire (pour une
question de rapidité), mais cela est impossible :
¤ La mémoire principale est souvent trop petite,
¤ c’est un procédé de stockage volatile, et une panne de courant signifie la perte des données.
Il est donc nécessaire d’avoir une mémoire auxiliaire (souvent des disques magnétiques, optiques,
bandes).

Yann MORÈRE
2.10 Structure de Stockage 19 / 103


/1 0 / 12143516 /879 1:<;1 6 9 =>/ 79 1
  

  

  !#"%$

&')(+* ,(.-

Fig. 2.8 – Mécanisne d’un disque dur

Il est alors possible de montrer une hiérarchie de stockage en partant des mémoires les plus
proche du processeur et donc du plus rapide, aux plus éloignées et plus lentes.

 



    ! #"$%

&'( )+*,.0-, /%,0132 4 567')+*,

89: ;+<=?>A@CBDF3E= GH9;+<=

IJK L+MN?OPQHJL+MRN

SUTV7WX0Y[ZATC\V^`]X _Hab+cXY

Fig. 2.9 – Hiérarchie de dispositifs de stockage

Il est important de remarquer la très grande différence de vitesse de traitement entre les
mémoires volatiles et les disques. Donc afin de ne pas trop pénaliser la vitesse globale du système
et de pallier cette différence, les mémoires cache ont été ajoutées.
Elle sont chargées de stocker les données qui sont utilisées très souvent, afin que le système
n’aie pas à les recharger à partir d’un périphérique lent. Il est à noter que ce stockage n’est que
temporaire.
Mais ces mémoires rapides sont d’une taille limitée, donc la gestion du cache est très im-
portante. En effet un cache bien dimmensionné couplé à un programme de gestion astucieux,
peux éviter de nombreux accès à un périphérique lent comme les disques.

Cours de Systèmes d’Exploitation


20 / 103 Chapitre 2 : Structure de systèmes informatiques

2.11 Protection du système informatique


Le passage à des systèmes multitâches, multi-utilisateurs et à temps partagé, à apporter de
nouveaux problèmes. Les plus important sont les suivants :
¤ une erreur sur un programme peut se répercuter sur les autres et même sur le SE,
¤ la sécurité des données.
Il est donc nécessaire de protéger toutes les composantes du système informatique.
Il faut dans un premier temps protéger le système d’exploitation et les données d’un pro-
gramme qui fonctionne mal et qui risque de tout détruire. Pour cela, on a introduit deux modes
d’exécution :
¤ le mode utilisateur
¤ le mode superviseur, système, moniteur ou encore provilégié.
Il s’agit alors de ne pas donner tous les droits à un programme utilisateur. Celui ci s’exécutera
en mode utilisateur, et les fonctions qui pourraient être dangeureuses pour le système seront en
mode superviseur. Et, bien sur, un mécanisme de contrôle sera effectuer pour passer d’un mode
à l’autre.
Il s’agit typiquement de la différence entre un Unix et un système basé sur MS-DOS.
De même afin d’éviter des instructions illégales d’E/S, ces dernières seront définies comme
des instructions privilégiées. L’utilisateur ne pourra donc pas émettre directement ses instruc-
tions. Il devra passer par le SE qui va contrôler ces appels au matériel.
Il est alors aussi nécessaire de protéger le vecteur d’interruption, car c’est lui qui permet
d’accéder aux périphériques. On protègera aussi les routines de traitement d’interruption, d’ou
au final la protection de la mémoire, puisque tout ceci est placé en mémoire. Cette dernière
protection va permettre en même temps de protéger les programmes des utilisateurs entre eux.
La dernière chose dont il est nécessaire de s’assurer, c’est que le SE à toujours le contrôle.
Il faut à tout pris eviter les blocages. Pour cela on utilise une horloge, qui va générer des
interruptions sur l’UCT, et le SE peut alors reprendre le contrôle. Cette horloge est aussi
utilisée dans les systèmes à temps partagés pour faire commuter les tâches.

Yann MORÈRE
21 / 103

Chapitre 3
Structures des systèmes d’exploitation

3.1 Introduction
Il s’agit d’une partie très importante de tout système informatique.
Un système d’exploitation (SE ou OS) est un programme qui permet de faire la liaison entre
l’utilisateur et le matériel de l’ordinateur. Sans celui-ci les applications telles que les SGBD, les
outils PAO, les traitements de texte ne pourraient pas fonctionner. De plus, il doit permettre
à l’utilisateur d’exécuter ses programmes de manière pratique et efficace (avec par exemple les
API (de programmation) Application Program Interface et la gestion simultanée de plusieurs
programmes).
Ce système doit assurer le fonctionnement correct du système informatique. Il fournit aussi
certains services aux progammes et aux utilisateurs (faciliter la programmation par exemple).
Ils sont souvent différents d’un système à l’autre, mais on retrouve des services communs.
Le système d’exploitation peut aussi être vu comme un programme de contrôle qui vérifie
l’exécution des programmes utilisateurs afin d’éviter les erreurs et les mauvaises utilisations.
Pour cela il s’occupe principalement du contrôle des périphériques.
Une définition plus commune consiste à expliquer que le SE est le seul programme tournant
à tout moment dans l’ordinateur (on parle aussi de noyau ou kernel ).
Pour l’utilisateur final, le but principal est la commodité du système d’exploitation. L’ex-
ploitation efficace des ressources est alors un problème secondaire. Mais dans le cadre d’un
système partagé multi-utilisateurs, cet objectif est des plus important car ces systèmes coûtent
très cher et il est important de les rendre les plus efficaces possible.
Ces deux objectifs, commodité et efficacité, sont souvent contradictoires. Mais par le passé
la notion d’efficacité était la plus importante. c’est pour cela que la plupart des théories se
concentrent sur l’utilisation optimale des ressources du système.

3.2 Le modèle processus-ressources


Le systèmes d’exploitation est un intermédiaire offrant un ou plusieurs niveaux d’abstraction
entre les usagers et un ensemble de ressources : UCT, mémoire, disque, périphériques. L’usager
présente des demandes d’accès aux ressources suivantes :
¤ l’UCT pour effectuer des opérations arithmétiques et logiques,
¤ la mémoire, pour conserver temporairement l’information,
¤ des disques, ou autres systèmes de mémoires secondaires, pour conserver les informations
de manière durable,
¤ claviers, moniteurs, souris, imprimantes, et autres périphériques d’E/S pour introduire
des données dans le système informatique et visualiser les résultats.
22 / 103 Chapitre 3 : Structures des systèmes d’exploitation

! " #
$$%'& (*))+', -/.103254768.94;:03<=<=>@?BA'0C:1?@D5E=.

   

OPKQ F HIKJ LMKN G f/g@h8ikj=l YCZ/[1\8]7^_`5a


b@cd \ b@c Y3e5[1]7^

RT S RVU5W@X T

Fig. 3.1 – Le SE comme intermédiaire entre usagers et ressources systèmes

Un autre modèle du système d’exploitation consiste en un intermédiaire présentant des niveaux


d’abstraction entre un ensemble de processus et les ressources du système que des processus
utilisent. Le système d’exploitation offre un ensemble de services aux processus, ce qui nécessite

' ( ) * + , -

   

" ! # $ % &

Fig. 3.2 – Modèle révisé : Processus et Ressources

la résolution de nombreux problèmes de partage de ressources.

3.3 Composants et fonctions


Pour créer un système aussi grand et complexe qu’un système d’exploitation, il est nécessaire
de le décomposer en pièces plus petites. Chacune d’elles sera un fonction bien délimitée du
système. Bien que les systèmes d’exploitation n’aient pas tous la même structure, ils partagent
le même but : supporter et exploiter les composants du système informatique (ressources) décrits

Yann MORÈRE
3.3 Composants et fonctions 23 / 103

précédemment.

3.3.1 Gestion de l’UCT : Gestion de processus


L’UCT (Unité de Calcul et de Traitement) est une ressource du système informatique, dont
le SE doit optimiser l’utilisation.
On peut considérer un processus comme un programme en cours d’exécution. Mais ce n’est
pas la seul définition possible, car un travail de traitement par lot est un processus. On considère
parfois le processus comme un programme en mouvement.
Mais attention, un programme en lui-même n’est pas un processus, car il s’agit d’une entité
passive, alors que le processus, lui, est une entité active.
On peut dire que le processus est l’unité de travail d’un système.
Un des buts du système d’exploitation est la gestion des processus :
¤ Activer un processus,
¤ Suspendre, réactiver un processus,
¤ Tuer un processus
¤ Surveiller l’opération d’un processus,
¤ Synchronisation de processus
de manière à ce que l’UCT soit utilisée de manière optimale.

3.3.2 Gestion des fichiers


C’est une des composantes les plus visibles d’un système d’exploitation. Le stockage d’infor-
mation peut se faire sur différents types de support. Afin de permettre une utilisation pratique,
le système d’exploitation propose une vue logique uniforme de toutes ces ressources de stockage
en faisant abstraction des propriétés physiques et définit une unité de stockage : le fichier.
C’est un ensemble d’informations définies par son créateur. Habituellement les fichiers re-
présente des programmes et des données (numériques, alphanumériques, alphabétiques).
Physiquement le fichier se compose d’une séquence de bits, octets, lignes ou enregistrements.
Le système d’exploitation à la responsabilité des activités suivantes concernant les fichiers :
¤ Création et suppression de fichiers,
¤ Création et suppression de répertoires,
¤ Primitives pour manipuler des fichiers et des répertoires,
¤ Correspondances entre les fichiers et la mémoire auxiliaire,
¤ Sauvegarde des fichiers sur des supports d’information stables (non volatiles).

3.3.3 Gestion de la mémoire


Pour être exécuter un programme doit être transférer à des adresses absolues de la mémoire
centrale. Pendant son exécution, L’UCT accède aux instructions et aux données contenues dans
la mémoire, et à la fin de l’exécution, cet espace mémoire est déclaré disponible et le programme
suivant peut être chargé.
Afin d’améliorer l’utilisation de l’UCT diverses approches de la gestion mémoire existent.
Le système d’exploitation est responsable des activités suivantes :
¤ Connaitre en permanence les parties de mémoire en cours d’exécution, et par quel pro-
gramme,
¤ Décider quels processus doivent être chargés en mémoire quand on dispose de l’espace
mémoire,
¤ Affecter et désaffecter l’espace mémoire.

Cours de Systèmes d’Exploitation


24 / 103 Chapitre 3 : Structures des systèmes d’exploitation

3.3.4 Autres composants


¤ La gestion des entrées/sorties,
¤ Système de protection (mémoire, fichier, UCT, E/S...),
¤ Connection à un réseau dans la cadre de la communication dans un système distribué,
¤ Interpréteur de commande (de l’usager) :
. interface entre l’usager et le système d’exploitation,
. choix : ligne de commande (shell), ou interface graphique.

3.3.5 Services d’un système d’exploitation


Services fournis :
¤ pour les programmes,
¤ pour les utilisateurs de ces programmes.
1. Exécution de programmes,
2. Opération d’entrées/sorties,
3. Manipulation du système de fichiers,
4. Communication entre processus,
(a) par mémoire partagée (processus sur le même ordinateur),
(b) par passage de message (ordinateurs différents),
5. Détection et gestion d’erreurs
Services fournis au système d’exploitation lui-même :
1. Allocation de ressources,
2. Suivi et gestion des comptes,
3. Protection.

3.3.6 Accès aux services du système d’exploitation


L’usager peut accéder au services du système d’exploitation :
¤ en lançant des appels systèmes, à l’intérieur d’un programme,
. Gestion de processus
– charger et exécuter
– créer et terminer un processus
– activer ou suspendre des processus
– obtenir et fixer les attributs d’un processus
– attendre pendant un certain temps
– attendre un signal, un évènement
– allouer et libérer de la mémoire
. Manipulation de fichier
– créer et détruire des fichiers
– ouvrir et fermer un fichier
– lire et écrire un fichier
– obtenir et fixer les attributs d’un fichier
. Manipulation de périphériques
– requérir et relâcher un périphérique
– lire, écrire
– obtenir et fixer les attributs d’un périphérique

Yann MORÈRE
3.4 Structure d’un système d’exploitation 25 / 103

– attacher ou détacher logiquement un périphérique


. Maintenance de l’information
– obtenir et fixer la date, l’heure
– obtenir et fixer les données du système
. Communication
– créer et détruire des connections pour la communication
– envoyer et recevoir des messages
– transférer de l’information d’état
– attacher ou détacher des périphériques

3.4 Structure d’un système d’exploitation


Quelle que soit la taille du système, il doit être conçu soigneusement, pour fonctionner
correctement et être facilement modifiable.
Une approche courante (linux, Unix) consiste à subdiviser les tâches en composants plus
petits, plutôt que d’avoir un gros système monolithique. Chacun de ces modules constitue une
portion bien définie du système.

3.4.1 Structure simple (systèmes monolithiques)


¤ en fait il n’y a pas de structure,
¤ le SE est un ensemble de procédures dont chacune peut appeler n’importe quelle autre,
sans restriction.
Il s’agit souvent de SE qui on grandit à partir d’un système original simple :
¤ les premières versions d’Unix,
¤ Mac OS
¤ MS-DOS

       

   "!#$!%'& (*'!) + ,*-.%

/0 1 234'576*4(8:94 ;0 8*<794 ;0 =>4'5


6*4@?BA.C D@E(A

FHG I JKL'MON$L(P:QL RSG PT:QL RSG U.V*L'MOW7XZY([]\ ^OY`_Ba

Fig. 3.3 – Structure d’un système d’exploitation MS-DOS

Cours de Systèmes d’Exploitation


26 / 103 Chapitre 3 : Structures des systèmes d’exploitation

3.4.2 Structure en couches


À l’opposé des structures monolithiques, on trouve les systèmes organisés par couches, et
une communication se crée entre couches adjacentes. Le principal avantage de cette approche
en couche est la modularité. Chaque couche est conçue de manière à n’utiliser que les fonction-
nalités de la couche inférieure.
L’archétype des strustures en couches est le système THE (Technische Hogeschool Eind-
hoven) conçu par Djikstra en 1968. Ce système se décompose en 6 couches comme le montre
la figure 3.1. La difficulté majeure de l’approche en couches consiste à déterminer les diverses

Couche 5 : programmes utilisateurs

Couche 4 : bufferisation pour les périphériques d’entrée/sortie

Couche 3 : driver pour la console de l’opérateur

Couche 2 : gestion mémoire

Couche 1 : scheduling de l’UC

Couche 0 : matériel

Tab. 3.1 – Structure en couche de THE

couches qui vont composer le système de manière adéquate. Une soigneuse planification est
nécessaire. De plus ces implémentations sont souvent moins performantes que d’autres types de
structures. En effet par exemple pour faire un appel E/S (accès au matériel) il est nécessaire
de faire appel à toutes les couches. Chaque couche ajoute une surchage à l’appel initial.
Les systèmes actuels se portent donc sur une structure avec moins de couches et plus de
modularité (par exemple OS2).

3.4.3 Structure à noyau (kernel )


L’architecture en couches est conceptuellement élégante, mais se révèle peu efficace (tout
accès au matériel demande au moins 5 appels de fonctions en cascade). En outre, les systèmes
conçus de cette manière sont souvent peu portables.
L’architecture de noyau (kernel ) est préférée de nos jours. Le kernel regroupe un ensemble
de fonctions du système qui sont indépendantes du matériel. Elles sont donc très facilement
portables d’une plate-forme à une autre. Le kernel gère les aspects d’accès à l’UCT et aux res-
sources ainsi qu’au système de fichier. Il communique avec les pilotes (driver ) de périphériques
pour gérer les appels systèmes en provenance des processus.
Les processus communiquent avec le kernel à travers les API (Application Programming
Interface) qui définissent la syntaxe et la sémantique des appels système. Ils le font aussi à
travers les API d’extensions du système qui ajoutent un ensemble de fonctionnalités (interface
graphique et gestionnaire (manager ) de fenêtres, QuickTime, DirectX, etc...).
Idéalement le kernel devrait comporter un petit nombre de fonctions et devraient être de
taille réduite.
Malheureusement, cette réduction de la taille du kernel se fait le plus souvent au détriment
des performances du système : plutôt que d’écrire un grand nombre de fonctions spécialisées, on

Yann MORÈRE
3.4 Structure d’un système d’exploitation 27 / 103

    

DFEHG

  

IKJML NOPJ

354)6  758)9 :5;)<  => ? @1ACB !" # $&%')(+*


, -/. # , - !01" $2%

Fig. 3.4 – Organisation d’un SE avec un kernel (noyau)

‡'ˆ‰Š)‹Œ,ŒŽŒ

W3X5Y T3U5V

F8GHI&J)KL MJ 6879:&;)<= >;


N,O ?,@
PQ KH&I&R SEI AB <9&:&C DE: 13254
     ! "#
  "$$$%&&!#
'()%*"$ +  #
'(,- !/.$%+*$ &"&#
&%0 0 0

Z\[ ] ^_`
a bc [ d` c+e l mf\n g h ijkn+p w xq\y r s tuvy+{ ‚ ƒ|\„ } ~ €„+†
g ok r zv } 

Fig. 3.5 – Organisation en kernel

Cours de Systèmes d’Exploitation


28 / 103 Chapitre 3 : Structures des systèmes d’exploitation

aura un faible nombre de fonctions élémentaires, mais on augmente du même coup le nombre
d’appels de fonctions pour toutes opérations que doit effectuer le kernel.
On oppose alors souvent les kernels dit monolithiques qui continuent à être indépendant
du matériel, mais ils sont de taille plus importante, aux microkernels qui n’offrent plus que
4 services minimaux :
¤ un mécanisme de communication interprocessus,
¤ un système de gestion mémoire,
¤ un niveau minimal de gestion de processus,
¤ un système de gestion des E/S de bas niveau.
Contrairement à un kernel monolithique, un microkernel n’est plus un système d’exploitation,
mais une base sur laquelle on peut construire un système d’exploitation.
Dans la famille des kernels monolithiques on retrouve la fameux kernel Linux, les WinNT
etc... de l’autre coté, on retrouve les implantations Mac OS, basée sur un microkernel Mach, ou
encore Solaris, la variante Unix de Sun. On peut noter aussi le développement de GNU Hurd,
un micronoyau, basé lui aussi sur Mach.
On peut alors lister les principaux avantages des deux architectures :
¤ Microkernel : portabilité, maintenance facile,
¤ Kernel monolithique : performance.

3.5 Exemple de systèmes d’exploitation


3.5.1 Unix classique
Les caractéristiques principales sont les suivantes :
¤ un système d’exploitation à temps partagé. Système multi-utilisateurs et multi-tâches.
¤ Au départ, conçu par un seul programmeur, il était assez simple pour être compris dans
son ensemble.
¤ En fait c’était un système conçu par des programmeurs pour des programmeurs.
¤ Il est très portable, car il est quasiment complètement écrit en C.
¤ Il possède l’avantage de fournir le même environnement sur des plates-formes très diffé-
rentes.
¤ L’interface avec l’usager est la ligne de commande ou CLI (Command Line Interface).
Le Shell (coquille) est l’interpréteur de commande en Unix. Il porte ce nom, car c’est un
processus qui entoure le noyau du système d’exploitation. Les commandes de l’usager sont
interprétées par Unix par l’intermédiaire du Shell. Il existe différentes sortes de Shell. Les plus
connus sont les suivants :
¤ Bourne Shell (Bourne Again Shell),
¤ C Shell,
¤ TC Shell,
¤ Korn Shell.

3.5.2 Le Mac OS
Il a été introduit en 1984. Ses principales caractéristiques sont les suivantes :
¤ Ce fût le premier ordinateur personnel grand public avec une interface graphique (exclu-
sivement).
¤ Contrairement à Windows, ou à X-Windows, l’environnement graphique n’est pas un
shell du Mac OS, mais fait partie intégrante du SE.
¤ Il s’agit d’un système mono-utilisateur.
¤ Son fonctionnement est multi-tâches coopératifs.

Yann MORÈRE
3.5 Exemple de systèmes d’exploitation 29 / 103

Ä Å Æ'Ç.ÈÉ Ê%Ë3ÇZÌIÍ'Ê%Î+Ç.È
 
Ï Ð Ñ'Ò.ÓÔ Õ%Ö3ÒZ×1Ø ÙÓ'Õ+Ø Ó Ø Ò
  
Ú ÛÜÝ.Þ'ß à+á3ÝUâÝ<ã
àääÝ<å ãÀãæãÜ.Ý.ç è”Ý<ã !#"%$ $'&)+( *, -.) /1032 &54%6798, : ; -<)+/0 2 &)%-4= éëêìíUîIï'ð%ñ+í.ò
>?@AB?A?CED#FBGHIBAH
J5KL5MNPOQRJTSRLUNWVL1XRYZNW[V#\]L^N`_TJ5V1a#b5c%def f f
g#hi+jlk mnlPoZp qsrutUvw#x jyTj xw#z|{W}W~+ óëôõöZ÷ øùú.ö.û ü”ö
€+<‚ƒ „ +†ˆ‡^‰Š'‹3<‚'‚Œ‚.€+<‚ƒ'„ +†ˆ‡UŽ Z.‘  „ Š+‚’‚ƒ<.“ ”Z‡^•‹'–„ .Š.—#˜+™<ƒ'‹%š š š ýö.þÿö
›œTž1¡Ÿ ¢T£¥¤ ¦#§¨©ªU§¨ «<¬
­^®#¯°±³ ² ±”´ µ ¶ ³ °·µ ¸¹ º ³ ¸.°» ³ ¶'±¥µ ¼½º¾R°¿À³ ² ¶'µ ¿Á¶'µ ¹+º ³ ¸.° ³ »'Â+à à Ã

Fig. 3.6 – L’architecture en couche Unix

¤ Il ne possède pas de vraie structure de niveaux. Il n’a pas non plus de kernel, le système
est composé d’un ensemble de managers :
. Event Manager, Manu Manager, Window Manager, Dialog Manager, Control Manager,
Help Manager, ....
. Ressource Manager, Component Manager...
. File Manager, Memory Manager...
. Image Compression Manager.
Contrairement à Unix, les processus Mac OS sont toujours liés à une application. Une appli-
cation est en avant-plan (foreground), et toutes les autres en arrière-plan (background). La
gestion des processus est basée sur la notion d’évènement.
Dans le Mac OS, il existe trois types d’évènements :
¤ événements de bas niveau (internes à une application) : activation ou remise à jour d’une
fenêtre suite à une pression ou un déplacement de la souris, l’insertion d’une disquette...
¤ événements du système d’exploitation, relatifs au changement d’application en avant-
plan.
¤ événements de haut niveau, relatifs aux communications entre applications.

Cours de Systèmes d’Exploitation


31 / 103

Chapitre 4
Les Processus

Les premiers systèmes informatiques permettaient l’exécution d’un seul programme à la


fois. Ce dernier possédait le contrôle total de toutes les ressources du système. De nos jours, les
systèmes informatiques actuels permettent de charger plusieurs programme en mémoire et de les
exécuter en concurrence. Cette évolution a conduit à un contrôle plus ferme des programmes.
Ces besoins ont abouti à la notion de processus, qui est un programme en exécution. Un
processus aura besoin de certaines ressources, comme le temps processeur, la mémoire, les
fichiers et les périphériques d’entrées/sorties.
On peut considérer le processus comme l’unité de travail élémentaire dans les systèmes en
temps partagé. Les processus peuvent être séparés en deux classes : ceux du système d’exploi-
tation exécutant du code système, et ceux des utilisateurs qui exécutent du code utilisateur.
Le système d’exploitation est alors responsable des activités de création, suppression, ordo-
nancement, par rapport à la gestion des processus.

4.1 Concepts élémentaires


4.1.1 Le processus et son espace adresse
Dans une système multiprogrammé, les différents processus doivent se partager l’accès aux
ressources : UCT, mémoire et périphériques. Par exemple, un processus ayant besoin d’une
ressource qui est occupée devra être bloqué en attendant qu’elle se libère.
Dans le cas d’un système à temps partagé, l’exécution des processus doit être en apparence
concurrente : une fraction de temps UCT doit être accordée à chacun des processus.
Un processus exécute une séquence d’instructions dans un espace d’adresse comprenant
l’ensemble des locations en mémoire auxquelles le processus est autorisé à accéder.
Cette espace d’adresse est a priori virtuel (il ne correspond pas forcément à la mémoire
physique et peut être sur un disque dur en attente d’être chargé). Il correspond au texte (code)
et aux données utilisées par le processus.
À chaque processus correspond un ensemble de registres et compteurs, comme le compteur
programme qui pointe sur l’instruction à exécuter (compteur d’instructions).

4.1.2 Le kernel (noyau)


On peut définir le kernel comme un programme spécial qui implante le modèle de processus
et les autres services du système.
Le kernel est chargé à partir du disque, lors du démarrage du système par l’opération de
bootstrapping. Il initialise le système et définit l’environnement pour exécuter des processus. Il
32 / 103 Chapitre 4 : Les Processus

demeure en mémoire jusqu’à l’extinction du système.


Les interactions entre le kernel et les programmes des usagers est définie par son interface
de programmation des applications (Application Programming Interface ou API).
Un processus interagit avec le kernel par des appels système (system calls). Le kernel exécute
des demandes pour le compte des processus appelant.
Le kernel doit aussi intervenir lorsqu’un processus effectue une erreur (une division par
zéro par exemple, ou encore un essai de débordement de pile/stack ) qui cause une exception
matérielle (hardware execption) que le kernel traite aussi pour le compte du processus.

4.1.3 Mode, espace et contexte


Mode : on distingue souvent sous Unix deux modes d’exécution :
¤ le mode système (système mode ou kernel mode), privilégié,
¤ le mode usager (user mode), moins privilégié.
Certains types de processeurs reconnaissent plus de deux modes d’exécution. Par exemple l’ar-
chitecture 80x086 d’Intel autorise quatre anneaux d’exécution (execution rings). Unix n’utilise
que 2 de ces anneaux.
Espace usager et espace système : (user space et kernel space)
une partie de l’espace d’adresse de chaque processus correspond à du code et des structures
de données kernel. Cette portion de code est appelée espace système (system space ou
kernel space) et contient des structures de données globales et des objets relatifs aux
processus.
Il n’existe qu’une instance du kernel dans le système, et donc chaque processus pointe vers un
seul espace système.
L’accès à cette espace système ne peut se faire qu’en mode système : un processus accède à
l’information de l’espace système en effectuant un appel système. Le contrôle est alors transféré
après un changement de mode (mode switch) au kernel qui effectue l’accès pour le compte du
processus. À la fin de l’exécution, le contrôle est alors retourné au processus ; après un autre
changement de mode.

Chaque processus possède deux objets spécifiques particulièrement importants qui sont gérés
par le kernel :
¤ l’espace usager (user area ou u area) : qui contient des informations sur le processus qui
sont utiles au kernel (liste de fichiers ouverts, valeurs des registres lorsque le processus
est interrompu). L’espace usager n’est pas accessible (en écriture) au processus, même si
il est parfois implanté dans son espace d’adresse.
¤ la pile du kernel (kernel stack ) : dans le cas d’un kernel réentrant comme celui d’Unix (c-
à-d autorisant plusieurs processus à être engagés concurrement dans des appels système),
il faut maintenir pour chaque processus la liste des séquences d’appels de fonctions.
Contexte processus et contexte système (process context et system context)
Le kernel peut opérer en contexte processus, c’est à dire pour le compte d’un processus
(en exécutant un appel système ou en traitant une exception). Il peut aussi opérer en
contexte système pour gérer les interruptions en provenance des périphériques.

4.1.4 États d’un processus


Un processus est une instance d’un programme en exécution. Mais pointons le fait qu’un
programme en lui-même n’est pas un processus. c’est une entité passive.

Yann MORÈRE
4.1 Concepts élémentaires 33 / 103

Résumé des modes, espaces et contextes :


     

&(' ) *,+.-/10*"2 N >@HP?BODADODCFQ R ED>JHJG SHJR IK?PCMTVL U cJdedef@g h"h iBh jMfMk lf
fMmDnMfMdej o pKqDh 3(4 5 6,78:9;"6"< =%6
W X YZP[@WYV\ ]B[@W X X@^PXXMWJ^P_ W ` W aVb r s t uPv@rtVwMxBvMr s sJyJsFr@z s {Ds@zPr | }~r
 €  ‚Pƒ@"€ „D€MK† ‡ "€ PˆJ‰  ‡  ŠV
œePž$JŸD ¡P¢ £ žD¥ ¤ ‹ ŒB Ž@  e‘D ‹ ’PŒD“
J•P” – — Ž@“˜D:“ ™B“ MŽMš ›Ž

     "$! #%

Fig. 4.1 – Modes et contextes d’opération

Un processus est une entité active (dynamique), dont on peut étudier l’état au cours du
temps.
Un processus peut être dans 5 états différents (Cf. figure 4.2) :
¤ nouveau, le processus est en cours de création,
¤ prêt, le processus attend d’être affecté à un processeur,
¤ élu, les instructions sont en cours d’exécution,
¤ en attente (bloqué), le processus attend qu’un événement se produise (comme l’achève-
ment d’une entrée/sortie ou la réception d’un signal),
¤ fini, le processus a fini l’exécution.

  %'&)(+* , - ./10"2124357698 :<;)= > 

   

?)@ A1BDC"EGFIH?)J!AGFIH K"?)J)L K"J)M


N1O"P1Q7R S9T'R U1VS!WX Y)S[OIZ \]O"Z S O"Q^O"S_N iji i kl m n n i
VYWX Y9S Oa`]b_c  !"$# $# dfehg "d q e md kl m e r]dIo s_dft e)dfp7dfe
ed

Fig. 4.2 – Diagramme d’états de processus

4.1.5 Mécanismes de gestion de processus


Le cycle de vie d’un processus est constitué d’une alternance de sections durant lesquelles
il est actif, en contrôle de l’UCT ( giclées d’UCT ou CPU bursts), en attente d’E/S comme
montré à la figure 4.3.

Cours de Systèmes d’Exploitation


34 / 103 Chapitre 4 : Les Processus

   "#"! $%#'&)( #+*-,/. 01 +2" 1 03    

4 5"687 9:98;=<>68? @ 5A@ ;CB<=<D7 EF:GHF:6 7 I J7 w x+y8z:{ w |


Þ:Ý ß àsá ÞUâ ÞUÝ ã:à ä Þå } ~+U€ {D‚ƒ  xy8{ {  x { „

K8LM=N O PRQ SUTO VL+VXW=OYZRW>L+[KR\)]_^)Z

`8abc:d e cXf gUhe ibiRjHekmlobn p>bc8i8q cRjHesrutuvRl

Æ Ç+È8É:Ê Æ Ë
Ì Í+ÎUÐ Ï ÊDÑÒ Ð ÇÈ8Ê Ê Ð Ç Ê ÐÓ ç:æ è ésê çUë çUæ ì:é í çî
†"‡8ˆ ‰:‰8Š=‹>‡8Œ  †A ŠCŽ‹=‹Dˆ :‘H:‡ ˆ ’ “ˆ

¸ ¹+º8»:¼ ¸ ½
”8•–=— ˜ ™Rš ›Uœ˜ •+Xž=˜Ÿ/už>•+¡”R¢)£_¤_ ¾ ¿+ÀUÂÁ ¼DÃÄ Â ¹º8¼ ¼  ¹ ¼ ÂÅ

¥8¦§¨:© ª ¨X« ¬U­ª ®§®R¯Hª°-±§² ³>§¨8®8´ ¨R¯Hªsµu¶u·u±

:Õ Ô Ö ×sØ ÕUÙ ÕUÔ :Ú × Û ÕÜ

Fig. 4.3 – Déroulement d’un changement de contexte

Il est important de comprendre que l’exécution de ces transitions consomme du temps de


processeur physique qui n’est pas utilisé par les processus eux-mêmes. C’est ce q’on appelle la
déperdition (overhead) résultant de la gestion des processus. Tous les systèmes visent donc à
réduire celle-ci.
Deux mécanismes de gestion de processus seront vus en détail :
¤ Ordonnancement (Scheduling)
effectué par un ordonnanceur ou répartiteur (Scheduler ) qui décide quel processus
doivent être admis, lequel doit être activé...
¤ Synchronisation
certains processus peuvent avoir à accéder aux mêmes ressources et il faut alors gérer les
droits d’accès.
Le dernier mécanisme concerne la gestion des interruptions/activation à proprement parlé, et le
maintien à jour des tables de processus et des blocs de contrôle de processus (Processus Control
Block ou PCB ).
Ce dernier représente chaque processus dans le système d’exploitation. Ce PCB contient
plusieurs informations concernant un processus spécifique :
¤ L’état du processus : nouveau, prêt, élu, bloqué, terminé,
¤ Le compteur d’instructions : il indique l’adresse de l’instruction suivante à exécuter,
¤ Les registres de L’UCT : les informations des registres doivent être sauvegardées, quand
il se produit une interruption, afin de pouvoir reprendre correctement la suite,
¤ Informations sur le scheduling de l’UCT : elles comprennent la priorité du processus, des
pointeurs sur le file d’attente de scheduling,
¤ Informations sur la gestion mémoire : valeurs des registres de base, limites, les tables de
pages, les tables de segment,
¤ Information de comptabilisation : elles tiennent en compte la quantité de temps proces-
seur, et temps réel utilisé, les limites de temps, les numéros de compte, de travaux, de
processus,

Yann MORÈRE
4.2 Ordonnancement (Scheduling ) de processus 35 / 103

¤ Informations sur l’état des entrées/sorties : la liste des périphériques d’entrées/sorties


alloués à ce processus, liste des fichiers ouverts.
Le tableau suivant montre un bloc de contrôle (PCB) générique dans un système Unix.

Gestion de processus Gestion de la mémoire Gestion des fichiers

registres pointeur sur segment de code masque UMASK

compteur programme pointeur sur segment de données répertoire racine

descripteur d’état du prog. état de sortie répertoire de travail

pointeur de pile id du processus descripteurs de fichiers

état du processus processus parent uid effective

instant de début du processus groupe du processus gid effective

temps UCT utilisé uid réelle paramètres d’appel système

temps UCT des enfants uid effective drapeaux (flags) divers

instant de la prochaine alarme gid réelle etc.

id du processus gid effective

drapeaux (flags) divers drapeaux (flags) divers

etc. etc.

Tab. 4.1 – Bloc de contrôle de processus générique pour Unix

4.2 Ordonnancement (Scheduling ) de processus


Nous allons tout d’abord définir quelques termes :
¤ la multiprogrammation consiste à disposer de quelques programmes en exécution à tout
moment,
¤ le temps partagé consiste à faire commuter l’UCT entre les processus de manière à ce que
les utilisateurs puissent interagir avec les différents programmes en cours d’exécution.

4.2.1 Rôles des ordonnanceurs (répartiteurs, Schedulers)


Les répartiteurs ou ordonnanceurs choisissent les travaux (jobs) ou processus qui vont pou-
voir accéder à l’UCT. On peut considérer qu’il y a deux types de répartiteurs :
¤ Dans un environnement de traitement par lots (batch processing) le répartiteur de haut
niveau (à long terme) sélectionne le prochain travail à charger en mémoire.

Cours de Systèmes d’Exploitation


36 / 103 Chapitre 4 : Les Processus

¤ Dans un environnement multiprogrammé, et en particulier dans un système à temps


partagé, le répartiteur de bas niveau (à court terme) sélectionne à chaque fois que l’UCT
devient inactive, un processus parmi tous ceux présents en mémoire (prêts). Ce processus
devient le nouveau processus élu. Cette intervention du répartiteur de bas niveau se
produit aussi lorsque la fraction de temps de l’UCT allouée au processus est expirée.

Exécution Performance demandée

répartiteur de bas niveau fréquente excellente

répartiteur de haut niveau occasionnelle bonne

Tab. 4.2 – Contraintes d’opérations des répartiteurs de bas et haut niveau

Sur la plupart des systèmes modernes, le rôle du répartiteur de haut niveau se résume surtout
à accepter ou refuser une demande de chargement de processus en mémoire (espace mémoire
occupé, quota de processus atteint, charge courante du système est trop importante).
Dans un environnement à temps partagé et à mémoire virtuelle ; deux répartiteurs doivent
cohabiter : le répartiteur de bas niveau qui gère le temps partagé et un répartiteur de haut
niveau qui gère les transferts entre disque et mémoire (swap in et swap out).

4.2.2 Files d’ordonnancement


Le diagramme de transition de la figure 4.3 ne concernait qu’un seul processus. Il est main-
tenant nécessaire de faire l’étude avec n processus P0 , P1 , . . . , Pn , sachant que :
¤ un seul au plus de ces processus peut être élu par l’UCT,
¤ plusieurs processus pourront être en attente d’E/S,
¤ d’autres seront prêt à occuper l’UCT.
Le mécanisme le plus commun pour traiter ces processus est celui de la file d’attente :
¤ une file d’attente pour tous les processus prêts,
¤ une file d’attente pour chaque périphérique et type d’E/S
Il s’agit maintenant de savoir :
¤ dans quel ordre les processus seront insérés dans la file d’attente,
¤ comment s’effectue le passage entre les files et l’UCT.

4.2.2.1 Traitements par lots (batch)


Ce style de file d’ordonnancement n’a pas de répartiteur de bas niveau : l’UCT reste inactive
pendant que le processus courant traite sa demande d’E/S.

4.2.2.2 Multiprogrammation sans temps partagé


Dans ce cas les processus ne sont suspendus que s’ils font une demandes d’E/S.

4.2.2.3 Répartiteur pour le temps partagé


Dans ce cas, on ajoute la notion de quantum de temps après lequel le processus élu perd le
contrôle de l’UCT et repasse à l’état prêt.

Yann MORÈRE
4.2 Ordonnancement (Scheduling ) de processus 37 / 103

 !#" $%'& \]^ _`\!a#b a ^c%\'d^


( $)" *$ e `f1gb h^`c
+ , -./10!2 3!412#5 i j:k c\Xala ^\ mn^o

FG HIJ K L L HML HIH


     N KO1MP QHKR
6!78:9<;= >= ? @A= ?>CB= >DE 9<= prqts
   SUTVXWY HO O RO TV:H#Z L O#[

unvxw

ynzx{

|n}x~

Fig. 4.4 – Ordonnancement pour le traitement par lot (batch)

 !#" $%'& \]^ _`\!a#b a ^c%\'d^


( $)" *$ e `f1gb h^`c
+ , -./10!2 3!412#5 i j:k c\Xala ^\ mn^o

FG HIJ K L L HML HIH


     N KO1MP QHKR
6!78:9<;= >= ? @A= ?>CB= >DE 9<= prqts
   SUTVXWY HO O RO TV:H#Z L O#[

unvxw € ‚1ƒ„ ‡†‰ˆ

Š‹ Œ1Ž ‡‘‰’


ynzx{

|n}x~ “” •–1—˜ ™‡š‰›

Fig. 4.5 – Ordonnancement pour la multiprogrammation sans temps partagé

89: ;=<8?>@ >$:A8CB':


D <*E+F@ G:*<A
H I6J A8>K>$:8#L:*M

x'y zR{|x'y&x } y6~Cy €ƒ‚„ |    ! "#"$%"#&' †‡ˆ‰Š ‹Œ ‹ ‰ Ž'‹†6‰ $‹6‘ƒ#‹
  ( !*)+%, -*!. NPORQ
   /1023'4 )#)$.') 0265 "$)7

m npo*qKr stn*uvs w

Z[ \]+^ _ `ba c


STVU

de fg+h i jbk l


WXVY

Fig. 4.6 – Ordonnancement avec temps partagé

Cours de Systèmes d’Exploitation


38 / 103 Chapitre 4 : Les Processus

4.2.2.4 Temps partagé avec gestion de la mémoire virtuelle


Ici on rajoute les problèmes relatifs à la gestion des transferts entre la mémoire principale
et la mémoire virtuelle.
‚ƒ „yO‡†Fˆ † ƒ ‰PUŠƒ
‹ AŒ+ˆ ŽƒAO‰
 ‘ ‰†C† ƒ  ’ƒA“

qArstvuPwyx

[F\P]O^_2` ab`^2cd`e[f^2c `gc `


zA{|}~ €
hijkl m m nm+hilFo p m     
JKLMONPJKQJ-R KSUTK V WXFY ZN

<2=?>A@CBDEF=AGHD2I

&' ( )+*-, .0/21


"!

#$"% 34 5 6+7-8 90:2;

Fig. 4.7 – Ordonnancement avec gestion de la mémoire virtuelle

4.2.3 Algorithmes d’ordonnancement


Ils existent plusieurs critères, parfois mutuellement contradictoires, que l’on peut pendre
en compte dans le choix d’un algorithme d’ordonnancement. On peut aussi s’interesser à la
moyenne, variance, minima, maxima de ces critères afin de choisir le répartiteur (ordonnanceur).
Tout d’abord, on distinguera deux grandes classes de répartiteurs :
¤ répartiteurs non préemptif, sans réquisition, (non-preemptive). Quand un processus prend
le contrôle de l’UCT, il ne le relâche que lorsqu’il a terminé son exécution ou lorqu’il
bloque sur une attente d’E/S.
¤ répartiteurs préemptifs, à réquisition (preemptive). Un processus qui a pris le contrôle de
l’UCT, le relâche lorsqu’il a terminé son exécution ; ou lorsqu’il bloque sur une attente
E/S, mais aussi quand le répartiteur le suspend, lorsque le quantum de temps qui lui
avait été aloué est terminé.
On notera par exemple que les répartiteurs d’UNIX, OS/2, Win NT et BeOS sont préemptifs,
et que ceux de MacOS, Windows 3.x/9x/Millenium sont non-préemptifs.
Maintenant il est possible de distinguer les différents répartiteurs, par rapport à la manière
dont ils gèrent la liste des processus prêts.

4.2.3.1 Premier Arrivé, Premier Servi ou PAPS


(First Come, First Served ou FCFS )
Lorsque l’UCT se libère (soit parce que le processus élu a terminé, soit parce qu’il a fait une
demande d’E/S), le processus en tête de la file des processus prêts devient le nouvel élu.
Exemple de PAPS pour un répartiteur de bas niveau : Soit 3 processus, A, B et C qui
sont composés d’une répétition de giclées d’UCT et d’opérations d’E/S de longueur
constante. On suppose ici que les processus n’attendent pas pour les E/S (chacun leur
périphérique). Pour A, nous avons 5 unités de temps d’accès à l’UCT puis 2 temps d’accès
aux E/S. Pour B, 4 UCT, 4 E/S, 4 UCT .... Enfin pour C, 1 UCT, 8 E/S, 1 UCT ....

Yann MORÈRE
4.2 Ordonnancement (Scheduling ) de processus 39 / 103

à maximiser
taux d’utilisation de l’UCT (en %tage de temps
écoulé)

à maximiser
débit (throughput) : le nombre total de processus traités
par unité de temps

à maximiser
prédictabilité : une tâche donnée devrait toujours avoir
les mêmes temps de réponse et de virement

à maximiser
justice : en particulier aucun processus ne devrait souf-
frir de famine (starvation)

à minimiser
temps de virement (turnaround time) : temps écoulé
entre la soumission et la fin de l’exécution

à minimiser
temps d’attente (passé dans la file de bas niveau)

à minimiser
temps de réponse (pour un processus interactif, avant
la première sortie vers l’usager)

>@?BA

$%&'( ) &
     *+ %,( - -.
& -
       !#"  /10 23 617 :; 415 <= 819

b@cBd

efghi jlki j g
CED FG HJIKL \1] ^_ `a
M LNOFK M L MQP RTS U V1W XY Z1[

sts
m1n o p qQr o u vxwTy

„@B†

‹#Œ Ž 1‘!’Ž“ ‰1Š z1{ |} ~1 € ‚1ƒ ‡ˆ


”• Ž1–T— ˜

Fig. 4.8 – Fonctionnement d’un répartiteur PAPS

Cours de Systèmes d’Exploitation


40 / 103 Chapitre 4 : Les Processus

En supposant que A arrive en premier suivi de B, une unité de temps plus tard, puis C une
unité de temps encore après. Comment les 3 processus vont-ils utiliser l’UCT dans les 30 unités
à venir ?

  


  ! " #$%

& ')*,( +

Fig. 4.9 – Exécution des processus par un répartiteur PAPS

4.2.3.2 Plus court d’abord


(Shortest Job First ou SJF )
On fait d’abord passer le processus ayant la plus petite giclée d’UCT. Dans la cas d’ex æquo
l’arbitrage est réglé par PAPS. Le problème est maintenant de connaı̂tre la longueur des giclées
d’UCT des processus. Ceci peut être réalisé de deux manières :
¤ demande à l’usager,
¤ estimation à partir des temps de service passés :

τn+1 = α · tn + (1 − α) · τn
où tn est le temps de service du dernier passage par l’UCT, τn est la prédiction qui avait
été faite avant ce passage, et τn+1 est la prédiction (corrigée) qui est faite pour la durée
de la prochaine giclée d’UCT du processus. Le paramètre α indique le poids que l’on veut
donner à l’historique.

4.2.3.3 Priorité
Il s’agit d’une forme générale du SJF, mais le critère de choix n’est plus simplement le temps.
On associe à chaque processus une priorité, et l’UCT est allouée au processus de plus haute
priorité. De même que pour le FJS, les processus de même priorité sont arbitrés par PAPS. Ce
type d’ordonnancement peut être fait avec ou sans réquisition.
Un des problèmes majeures de ce type de répartiteur est le blocage indéfini ou famine. En
effet, il peut laisser des processus de basses priorités attendre indéfiniment l’UCT. La solution à
ce type de problème est le vieillissement automatiques des processus. Cette technique consiste
à augmenter graduellement la priorité des processus attendant dans le système.

Yann MORÈRE
4.2 Ordonnancement (Scheduling ) de processus 41 / 103

4.2.3.4 Le tourniquet (Round Robin)

C’est un algorithme d’ordonnancement avec réquisition spécialement conçu pour les systèmes
à temps partagé.
Le tourniquet ajoute juste à l’algorithme de PAPS un quantum de temps limitant la durée
des giclées de l’UCT. Avec ce type d’algorithme, un processus peut perdre le contrôle de l’UCT
de deux manières :

1. volontairement (similaire à PAPS sans réquisition) :

(a) parce que son exécution est terminée,


(b) parce qu’il a fait une demande d’E/S.

2. par réquisition :

(a) parce que son quantum de temps a expiré.

Un problème majeur à ce type d’ordonnancement est la choix du quatum de temps. En effet si


le quantum est trop long , on pénalise les processus courts. À la limite, si le quantum est infini,
on se retrouve avec un PAPS standard.
De même si le quantum est trop court, une fraction trop importante du temps UCT est
occupé à réaliser les changements de contexte.
En général, dans un système Unix, le quantum est fixé aux alentours de 100 ms.

Exemple : on reprend l’exemple précéde, mais maintenant en imposant un quantum de 3


unités. Il est à noter que l’on n’attend pas la fin du quantum (temps maximal accordé)

gHhjik lHmjno pHqjrs tHujvw xHyjz{ |H}j~ €Hj‚ƒ

T8U VW XRYQZD[,W \8Z8Y6W ZD]


-/.021 3,465781 96: E=; F <8>@?BA6C8CD> ^ ]@\ W _Q`DaBbD\D]Qc
M <8>HGI8>@JK>@LI ]@_\8ZD]ed=]@\ f
AKN ODHP> I8AQIRHP> N S
  


  ! " #$%

& ')*,( +

Fig. 4.10 – Exécution de la séquence par un répartiteur tourniquet

de C pour activer un nouveau processus. Les mécanismes de changement de contexte


volontaires sont toujours actifs.

Cours de Systèmes d’Exploitation


42 / 103 Chapitre 4 : Les Processus

4.2.3.5 Ordonnancement à listes multiples


Les processus présents sur un système ne partagent pas tous les mêmes caractéristiques
(calculs scientifiques, applications interactives, multimédia, etc...). Ils peuvent avoir des besoins
de répartition différents. On organise alors un ordonnancement de priorité avec réquisition entre
files d’attente. Chaque file d’attente peut avoir son propre algorithme d’ordonnancement.

wx yzyD{| } y~,x yD€x <€‚I| ƒ‚ | „ ~| ‚y


†qy q €‚ƒ‡yq€~ Py‚ ƒ{#„Ny{€‚ yˆk| y‚
‰ y x ƒ{Š‹ŒŠqjƒ,ŽNŽ
     ]&^ _ `a,b c d eP`fg hjdk i bd
        !#"  l b hmnc d g ghagh Q&R SUT,V YZ
mh oqpr`sh o ofo
mhtoP`aau vh d f

35476

8 9 :<;=>?9::@:<A9?9B B9DC8 9
E 9D;>@= => E B9F B =9G?BIH JLK9 :NM@98 >=I:M#@9 $&% *,+ /,0 '&() 12 -.
8 G!C8 9A9 E H J9 G @&OD:P9 = G!JH A9

ž Ÿ <¡¢£¤Ÿ¥<¦Ÿ¤Ÿ§ §ŸD¨ž Ÿ
© D Ÿ ¡£¥¢ ¢£ © §Ÿª § ¢Ÿ«¤§I¬ ­L®Ÿ N¯¥Ÿž £¢I¯#¥Ÿ [&\\ WX “&”• &‘’ ˜&™š –— ›&œ 
ž «t¨ž Ÿ¦Ÿ © ¬ ­Ÿ « ¥±°Ÿ§Œ²tŸ¢ £ © §N­¬ ¦Ÿ

Fig. 4.11 – Ordonnancement à listes multiples

4.2.3.6 Ordonnancement temps réel


Quelles seront les fonctions qui permettront d’assurer un ordonnancement temps réel ?
¤ les systèmes temps réel rigides, qui nécessite de réaliser une tâche critique dans une
quantité de temps garantie. La plupart du temps le processus est soumis avec la quantité
de temps dont il a besoin pour se terminer, ou faire une action d’E/S. Ensuite soit
l’ordonnanceur accepte le processus en garantissant qu’il se terminera à temps, soit il le
rejette. On appelle cela aussi la réservation de ressources. Avec ce type d’ordonnancement,
il est impératif que l’ordonnanceur connaisse exactement les travaux à effectuer.
¤ les systèmes temps réel souples, sont beaucoup moins restrictifs. Dans ce cas l’ajout de
fonctionnalités temps réel peut provoquer une allocation injuste de ressources et produire
des délais plus longs, voir même un état de famine. Dans ce cadre la conception de
l’ordonnanceur demande une attention toute particulière. Il faut en effet concilier les
priorités hautes des processus temps réel, sans pour autant léser les autres processus.

4.2.4 Évaluation des algorithmes d’ordonnancement


La question qui se pose à présent est : quel est le meilleur algorithme pour un environnement
donné ? Comment ajuster ses paramètres (α pour SJF, vitesse de vieillissement pour la priorité,
quantum pour le tourniquet, etc...) ? On peut répondre à cela en 3 points :

4.2.4.1 Essais sur le système en fonctionnement réel


Ceci permet de faire ensuite l’analyse des résultats (statistiques, réactions des usagers, coût,
etc...), pour ensuite modifier les paramètres et obtenir de meilleurs résultats.

Yann MORÈRE
4.3 Création de processus 43 / 103

4.2.4.2 Analyse déterministe


Dans ce cadre, on fixe une charge de travail typique et on compare les résultats des
algorithmes. Le choix de cette charge typique est bien entendu crucial (et subjectif).

4.2.4.3 Modèles de files d’attente


Il est possible de construire des modèles probabilistes de l’arrivée des processus dans une
file d’attente, de durées de giclées d’UCT et de traitements d’E/S, de problème requérant
l’intervention du kernel, etc... Ceci permet de dériver des expressions mathématiques (modéliser)
pour chacun des critères d’évaluation de performance, et de juger de l’influence de chacun des
paramètres.

4.2.4.4 Simulation
À partir des modèles probabilistes déterminés précédement.
Exemple d’évaluation : temps d’attente moyen (TAM)
La file d’attente des processus prêts contient quatre processus, A, B, C et D (arrivés dans
cette ordre), dont les temps d’exécution respectifs sont 16, 2, 8 et 12 unités de temps.
Quel est le TAM pour PAPS ? Pour SJF ?
Cas de PAPS : T AM = [0 + 16 + (16 + 2) + (16 + 2 + 8)]/4 = 15.

  

 

    

Fig. 4.12 – Évaluation du temps d’attente moyen sous PAPS

Cas de SJF : T AM = [0 + 2 + (2 + 8) + (2 + 8 + 12)]/4 = 8, 5. On calcule en général ce

  

 
  

Fig. 4.13 – Évaluation du temps d’attente moyen sous SJF

temps d’attente moyen sur une longue période de temps, en utilisant les temps d’attente
des processus tout au long de leur durée de vie.

4.3 Création de processus


Afin de ne pas gaspiller de l’espace mémoire, les processus sont créés de manière dynamique.
Des opérateurs de création et de destruction de processus sont disponibles sur le système d’ex-
ploitation. L’opération de création doit aussi permettre d’initialiser l’état du nouveau processus.

Cours de Systèmes d’Exploitation


44 / 103 Chapitre 4 : Les Processus

C’est à dire, définir d’une part le programme (ou suite d’instructions) décrivant l’activité du
processus, ainsi que l’état initial de ces données, de ses variables et des registres du processeur.
La plupart des sytèmes permettent cette création dynamique des processus. De plus la rela-
tion entre le processus créateur et le processus créé est importante, ce qui conduit à structurer
l’ensemble des processus sous la forme d’un arbre et à maintenir cette structure.
Lors de la création de processus, le processus créé est relié automatiquement comme fils du
processus créateur (père).
Lors de la fin d’exécution normale d’un processus P , deux solutions sont possibles :
¤ le destruction de P n’est effective que lorque tous ses fils sont eux-mêmes achevés. Ceci
est nécessaire lorsque le contexte initial du fils est inclus dans celui du père P . En effet
la destruction de celui-ci entrainerai la perte d’une partie du contexte du fils et donc un
déroulement anormal s’il pouvait survivre à son père.
¤ la destruction de P entraı̂ne le rattachement de ses fils à l’un de ses ancêtres. Cependant
comme l’ascendance du processus P n’a pas connaissance des actions de ce dernier, on
rattache les fils au processus qui a initialisé le travail (login), soit à la racine qui est un
processus standard et éternel.

4.4 Exemple du système Unix


Dans le système Unix, la création dynamique de processus est simplifiée à l’extrème. En
effet le processus fils créé est une copie exacte du créateur le père. Il n’est donc pas nécessaire
de passer en paramètre le programme, les données, variables et les registres du processeur. Le
système réalise les copies de ces différentes parties.
La seule distinction entre le processus père et le processus fils réside dans la valeur que
retourne la fonction fork() dans les deux processus (le père et le fils). La fonction de création
retourne l’identificateur du processus créé dans le cas d’un processus père, et la valeur 0 dans
le cas d’un processus fils.

+-,.+/    56789 : : ;:5<5= >?A@CB


&&&&&&&&& YZ [ \ MMMMMMMMM
''''''''' NNNNNNNNN
01  "! #%$ DEFGH I"J K%L
((((((((( RSTCU VWX OOOOOOOOO
))))))))) PPPPPPPPP
2-3.24 ********* QQQQQQQQQ

Fig. 4.14 – Valeur retournée par fork() pour le père et le fils

Sous Unix la fonction de création de processus est la fonction fork(). fork() duplique
l’espace d’adresse du processus original (code, variables, contenu des registres).

4.4.1 Fonctionnement de fork()


Un appel à fork() est transmis au kernel qui cherche une place disponible dans la table
des processus. S’il en trouve une, il copie toutes les informations sur le père dans le block de
contrôle du fils. Comme nous l’avons déjà dit plus haut, les seules différences entre ces deux
blocks de contrôle concernent le retour de la fonction fork dans le processus père et le processus
fils. Le processus fils hérite en particulier des uid et gid réels et relatifs de son père.

Yann MORÈRE
4.4 Exemple du système Unix 45 / 103

   


„ „†ˆ‡Š‰‹  ! "$#%$& '()*++%+,&!*.- '*
Œ ŒŒŽˆˆ6‘
’“ “6” • / /0132465 789.:;,<=9.;?>,@ 9A2!9.B ;9
–— ˜—™š
›œ žœ6Ÿ C DC6E FHGI6J K6LM.N OQP=M.ORQS MATS RQU=J KWVM
¡¢ £¤¢6¥ ¦
X YZX[\3]^6_ `6ab.c d,e=b.d?fQg bAh db.i dbA]g cfkjlb.c` bmfc_ en`6a
o p qors3tu6v w6xy.z {,|=y.{?}Q~ yA {y.€ {yAt~ z}Q‚k ƒy }lzv |wx

§ §¨©ˆªŠ«¬ Ê ÊË̈͊Î6Ï à àáâˆãŠäå


­ ­­®¯ˆ°Š±6²
³´ ´6µ ¶

ü üüýþˆÿ
ÐÑ Ñ6Ò Ó
ö öö÷øˆùŠú6û
æç ç6è é
·¸ ¹¸º»ˆ¼Š½        
¾¿ ÀÁ¿6 à ÔÕ Ö ×Õ6Ø Ù êë ì íë6î ï
ÄÅ ÆÇÅ6È É Ú Û Ü ÝÛ6Þ ß
 ð ñ ò óñ6ô õ


Fig. 4.15 – Exemple de hiérarchie de processus Unix

Il est important de comprendre que les zones de données, de variables sont identiques à la
création du nouveau processus. Par contre par la suite les deux processus sont complètement
indépendants et n’ont pas de données communes.
Si le processus père se termine avant le processus fils, le fils est rattaché au processus racine de
manière à conserver la structure d’arbre. Le processus père peut aussi attendre la terminaison
de l’un de ses fils par la fonction id_fils = wait(&status). Cette fonction retourne dans
id_fils, le numéro d’un processus fils qui s’est terminé, et la variable status contient un code
indiquant la manière dont le processus s’est terminé. Si aucun fils ne s’est terminé et qu’il y en
a encore d’actifs, le processus père est mis en attente d’une terminaison de l’un de ses fils.
Par ailleurs le système Unix fourni une fonction exec qui permet à un processus de changer
de programme en cours d’exécution. Elle remplace les anciennes données par celle du nouveau
programme.

4.4.2 La notion de ressources


On appelle ressource, toute entité dont a besoin un processus pour s’exécuter (processeur,
mémoire, périphériques). Il en est aussi des données dont le processus a besoin et qui seraient
momentanément indisponibles. Une des caractéristiques importantes des ressources, est la quan-
tité de processus qui peuvent l’utiliser en même temps.
¤ Il peut y en avoir un nombre quelconque et alors il n’y a pas de contrôle à mettre en
œuvre.
¤ Il peut y en avoir plusieurs mais en nombre limité, il est alors nécessaire de contrôler lors
des allocations que ce nombre n’est pas dépassé.
¤ Il peut y avoir au plus un processus qui utilise la ressource. On dit alors que la ressource
est une ressource critique. On dit alors que les processus sont en exclusion mutuelle pour
l’accès à cette ressource critique (processeur, imprimante...).

Cours de Systèmes d’Exploitation


46 / 103 Chapitre 4 : Les Processus

4.5 Synchronisation de processus


Les processus étant des entités indépendantes et autonomes, ils peuvent se trouver en conflit
pour l’accès à certaines ressources communes. Il est donc nécessaires de mettre en œuvre des
mécanismes dits de synchronisation pour gérer ces conflits.

4.5.1 Conditions de course (race condition)


Il s’agit de la situation ou deux processus ou plus doivent accéder à la même ressource.
Par exemple, deux instances d’un même programme qui utilisent un pointeur commun pour
accéder à la zone de mémoire partagée. Il est alors possible qu’un instance A du programme soit
suspendue au moment ou elle doit accéder à la ressource critique. C’est donc l’autre instance
B qui va utiliser cette ressource, et à la fin de l’exécution de cette instance rendre la main à
A qui pense retrouver la mémoire dans l’état ou elle l’a laissée. Bien sur il y a eu modification
par l’instance B.
Il nous faut donc un mécanisme d’exclusion mutuelle pour éviter que plus d’un processus
lise ou écrive dans la mémoire partagée au même moment.
La section critique est la partie de programme où des accès à la ressource partagée ont lieu
pour un processus et une ressources partagée donnés.
Objectifs d’une bonne solution à un problème de conditions de course :
¤ A tout moment, au plus 1 processus doit être dans sa section critique ;
¤ Aucune hypothèse ne doit être faite sur la vitesse ou le nombre d’UCT ;
¤ Aucun processus exécutant en dehors de sa section critique ne doit pouvoir bloquer
d’autres processus ;
¤ Aucun processus ne devrait avoir à attendre indéfiniment avant de pouvoir entrer en
section critique.
On suppose que les processus ne trichent pas (n’abusent pas de leur droit d’accès, qu’ils
signalent lorsqu’ils entrent dans leur section critique et lorsqu’ils la quittent).

4.5.2 Exclusion mutuelle avec attente active (busy wait)


4.5.2.1 Variable de blocage (lock variable)
La première technique à laquelle on pense, consiste, pour chaque processus, à attendre que la
ressource dont il a besoin soit disponible, en vérifiant le contenu d’une variable verrou associée
à la ressource qui possède deux états libre ou occupé.
...
code generique
tant que (etat_verrou == occupe) ; //test de la variable verrou
//(busy wait)
etat_verrou = occupe ; //entree en section critique
(... acces a la ressource partagee ...) //section critique
etat_verrou = libre ; //sortie de la section critique
...
Problème 1 : Cette solution ne résoud pas le problème de la condition de course : elle ne fait
que le reporter sur le verrou, qui devient une autre ressource partagée.
Problème 2 : Si le répartiteur est à réquisition, le processus peut être interrompu entre le test
du verrou et l’accè à la ressource partagée.

Yann MORÈRE
4.5 Synchronisation de processus 47 / 103

       ! "  # %$& %!   ')(! *+ + , " #-! 
./01 2,3 452 06 6 7 8:9;=< 1 7 4?>5;-@ 2 ;8BA0C2 08:91D0E93 6 FHG
IJK LM-K LN-OQP R N?L STU+VW X R NQY ZR [?LY Y LC\+] \HX W,TW T
 X N5W LK K R ^:MVL%_&X Y\ UU+`OLZY \%K L+P P R VK ULCM-\HK W \Ha)TL
   

bcedfQg:hjikh,f:lnmehpo,q%f%l&h&rQl&h

s tuv {| } w xyz




€¤ ¥,¦§ ¨,© ª5¨ ¦« « ¬ ­:®¯=° § ¬ ª?±5¯-² ¨ ¯­B³¦C¨ ¦­:®§´¦µ®© « ¶H·


Ž€jH‘‘+’“”•– — ”+˜ ˜ ™ š-— ‘”•– — ”+˜ ˜ ™ š-— ‘”C›-H— œ H)ž”:Ÿ ˜ ”+‘œ ™ ¡Q‘— œ ¢5š”H£
~€, ‚ƒ  ‚ „ † ‡ ˆ ‰„ ‚%Š&„ %‡ ‚ƒ ƒ ‹)Œ‡ +‚ ‚+ƒ ,„ ˆ ‰-‡ ‚

Tab. 4.3 – Exemple où la variable verrou ne protège pas l’accès à la ressource partagée

Sur cette exemple, on remarque que ce problème pourrait être éviter si la commande qui permet
de tester la ressource et de la réserver se faisait en une seul fois. Il s’agit de la notion de
commande atomique.

4.5.2.2 Désactivation des interruptions


Afin d’empêcher qu’un processus soit interrompu à n’importe quel point de son exécution,
une solution serait de désactiver les interruptions quand on se prépare à entrer en section
critique et de les réactiver en sortant.
Problème : généralement il est très dangereux d’autoriser les utilisateurs (programmes utilisa-
teurs) à désactiver les interruptions. En cas d’erreur un processus pourrait complètement
bloquer l’ordinateur.

4.5.2.3 Solution matérielle : TSL (Test and Set Lock )


Certains processeurs disposent d’instructions permettant d’effectuer directement le test de
la valeur d’un registre ou le contenu d’une location en mémoire et d’assigner une nouvelle valeur
si la valeur courante était nulle.
De cette manière un processus ne peut plus être interrompu entre le test de la variable
verrou et le blocage du verrou.

4.5.2.4 Alternance stricte


Exemple entre deux processus 0 et 1.
Problème avec l’alternance stricte
¤ le nombre de processus qui partagent la ressource apparaı̂t directement dans le code,
¤ à chacun son tour n’est pas nécessairement la manière la plus efficace et la plus
équitable de partager une ressource,

Cours de Systèmes d’Exploitation


48 / 103 Chapitre 4 : Les Processus

      


  "!# $&%(' HIJK L M"NO P&Q(R
) S
*+ -,./0+.* *.21(320547698;:5'*'=< H*I+JKL M-TUVW+JU*K L*U2X(Y2W5Z7[9\E]R*R=^
> *? 123@&.*? 4(A1 /0(BA'=< _ L*` XJ2Ya&U*` Z(JAXJ VW(LBMAR=^
,*. /02.5 .1&320&4C8ED< T*U VWJ2U5K LUX&Y2W&ZC\;bc^
> *? 123@&.2@(32@&.5?4(1+2/0& F'=< _ L*` XJ2Ya&U2a(Y2a&U5`Z(JX+J2VW&L MFR=^
G d

Fig. 4.16 – Exemple avec deux processus 0 et 1

¤ de plus un processus qui a une très longue section non critique peut en bloquer un autre
qui attend que sont tour vienne. En effet l’algorithme requiert une alternance stricte de
processus dans l’exécution de leurs sections critiques. Par exemple si le processus P1 est
prêt à entrer dans sa section critique après une première exécution de son programme
(le tour est donc au processus P0 ), donc a_qui_le_tour vaut 0, et ce dernier n’a
pas encore fini sa section non critique, il bloque donc le processus P0 en section non
critique.
Problème généraux avec l’alternance stricte
¤ L’attente active gaspille du temps UCT. Il faut réactiver régulièrement tous les pro-
cessus qui veulent entrer dans leur section critique afin qu’ils puissent vérifier si leur
tour est enfin arrivé.
¤ En règle générale, l’alternance stricte ne peut pas garantir qu’un processus n’attende
indéfiniment, ou qu’un processus bloque d’autres processus en dehors de sa section
critique.
Exemple : inversion de priorités
Soit deux processus, H (haute priorité) et B (basse priorité). La règle à suivre est la
suivante : si H est prêt, alors il doit être activé (élu). Il faut donc trouver une solution

            !"$# "$#%'&("$ )!"
* %!,+- . /0!#%!1) !324 .5"2'/  4 * %!

MN9O P5Q R P N9S S TAUV W=N<P


X1N<O P%Y[Z<P Q \0]

6798 : ; 7$7<8=; 7<> > ?A@; B97$B<; C : C D @ 7

E
I$J K4L FG H

Fig. 4.17 – Inversion de priorités

qui combine la notion de commande atomique (commande très courte qui ne peut pas
être interrompue) avec un mécanisme de blocage/réactivation qui ne gaspille pas l’UCT
comme le fait l’attente active.

Yann MORÈRE
4.5 Synchronisation de processus 49 / 103

Une des solutions est l’utilisation des sémaphores.

4.5.3 Problèmes classiques de synchronisation de processus


Les problèmes suivants sont des représentations théoriques de problèmes réels d’accès à une
ou plusieurs ressources partagées. Tout nouvel algorithme (méthode, solution) de synchronisa-
tion doit être confronté à ces problèmes.

4.5.3.1 Le problème des producteurs et des consommateurs (le buffer limité)


La ressource partagée est constituée d’un buffer de taille limitée N .
Les processus sont séparés en 2 types :
¤ un ou plusieurs producteurs qui produisent des items. Chaque producteur ayant fini de
produire (calculer) un item vient l’ajouter au buffer si celui-ci n’est pas plein. S’il est
plein, il s’endort (il attend que le buffer ne soit plus plein pour pouvoir ajouter sont
item).
¤ un ou plusieurs consommateurs qui viennent retirer des items du buffer. Si un consom-
mateur venant retirer un item du buffer, trouve ce dernier vide, il s’endort en attendant
qu’un item soit ajouté au buffer.
Le problème est de régler la condition de course sur l’accè au buffer partagé.

4.5.3.2 Le problème des philosophes dineurs


N philosophes passent leurs temps soit à manger, soit à penser. Afin de manger, un phi-
losophe doit utiliser 2 fourchettes. Malheureusement, il y a exactement autant de fourchettes
que de philisophes. Dès qu’un philosophe à fini de manger, il repose ses fourchettes et pense.

Fig. 4.18 – Le problème des philosophes dineurs

Le problème est de synchroniser les actions des philosophes de manière à ce qu’ils parviennent
à manger et à penser.

Cours de Systèmes d’Exploitation


50 / 103 Chapitre 4 : Les Processus

4.5.3.3 Le problème du barbier endormi (sleeping barber )


Dans ce cas la ressource est un barbier (pouvant modéliser l’UCT d’un système multipro-
grammé) et N chaises d’attente. Des clients viennent et demandent à être servis ; ils peuvent
donc modéliser des processus cherchant à accéder à l’UCT.
Tâche du barbier :
¤ Si au moins un client est présent, il en prend un et lui coupe les cheveux ;
¤ Si aucun client n’est présent, il s’assied dans son fauteuil et s’endort.
Tâche d’un client :
¤ Si le barbier est endormi, le client le reveille et se fait servir ;
¤ Si le barbier est occupé et qu’il reste au moins une chaise disponible, le client s’assied et
attend son tour ;
¤ S’il n’y a aucune chaise disponible, le client s’en va.
Le problème est donc de faire en sorte que le barbier puisse traiter de manière juste les
clients qui se présentent.

4.5.4 Les Sémaphores


Un sémaphores est un mécanisme proposé par E.W. Dijkstra en 1965 plus général que le
verrou (variable de blocage). Il se représente comme un distributeur de jetons, mais le nombre
de jeton est fixe et non renouvelable : les processus doivent restituer leur jeton après utilisation.
Le but de cette nouvelle approche est d’éviter l’attente active engendrée par l’utilisation du
verrou.

4.5.4.1 Définition d’un sémaphore


Afin de régler les problèmes de condition de courses, il est nécessaire de rendre les fonctions
d’appel aux sémaphores atomiques. Une commande atomique ne peut pas être interrompue,
même si le quantum de temps du processus appelant a expiré. Par conséquent :
¤ Son temps d’exécution doit être très court.
¤ Comme les interruptions sont bloquées pendant l’exécution de la commande, celle ci doit
se faire en mode système.
En résumé, un sémaphore est une variable qui contrôle l’accès à une ressource partagée et
indique le nombre d’éléments de la ressource qui sont disponibles et maintient une liste des
processus bloqués en attente de cette ressource (s’il y en a).
Quand un sémaphore ne peut pas prendre de valeur plus grande que 1, on parle de sémaphore
binaire.

4.5.4.2 Exemple d’implantation de sémaphores


type semaphore =
record
valeur : int ;
L : list of process ID ;
end
où L est une liste de processus qui va être utilisée pour conserver les processus bloqués en
attente de la ressource partagée.
Quand le sémaphore est ≥ 0, il indique un nombre d’unités de la ressource qui sont dispo-
nibles. Quand il est < 0, il indique généralement le nombre de processus qui sont bloqués en
attente de la ressource.

Yann MORÈRE
4.5 Synchronisation de processus 51 / 103

Remarque : Il existe d’autres manières d’implémenter un sémaphore. En particulier certaines


implantations ne permettent pas à la valeur du compteur d’être négative.
Il faut maintenant définir les deux opérations atomiques qui vont permettre de modifier l’état
(la valeur d’un sémaphore) S :
¤ DOWN(S) (ou Wait(S)) décrémente le sémaphore, puis vérifie s’il est toujours ≥ 0,
. dans ce cas tout va bien (la ressource était disponible),
. sinon, on bloque le processus : SLEEP(), sera reveillé quand la ressource sera libre.
¤ UP(S) (ou Signal(S)) incrémente le sémaphore. Si celui ci était < 0, il réveille le pro-
cessus endormi WAKEUP(proc).

4.5.4.3 Une solution au problème des producteurs et consommateurs


Cette solution utilise 3 sémaphores :
¤ un sémaphore binaire initialisé à 1, mutex, qui contrôle l’entrée et la sortie des processus
de la section critique,
¤ un sémaphore full initialisé à 0 avec une liste vide, qui contrôle le nombre d’items
contenus dans le buffer, et donc les processus consommateurs,
¤ un sémaphore empty initialisé à N (dimension du buffer) avec une liste vide qui contrôle
le nombre de cases vides demeurant dans le buffer, et donc les processus producteurs.
Cette initialisation des sémaphores ne peut pas être effectuée par les processus producteurs et
consommateurs eux-mêmes puisqu’elle est commune à tous. De plus on ne peut pas laisser un
processus usager manipuler directement le contenu d’un sémaphore.
L’initialisation est effectuée avant la création des processus producteurs et comsommateurs
par leur processus parent qui crée et initialise les sémaphores par des appels système.
Code Producteur
void producteur(void)
{
objet item ;
while(TRUE)
{
produire_item(&item) ;
down(&empty) ;
down(&mutex) ;
ajouter_item(&item) ; //section critique
up(&mutex) ;
up(&full) ;
}
}
Code Consommateur
void consommateur(void)
{
objet item ;
while(TRUE)
{
down(&full) ;
down(&mutex) ;
retirer_item(&item) ; //section critique
up(&mutex) ;
up(&empty) ;

Cours de Systèmes d’Exploitation


52 / 103 Chapitre 4 : Les Processus

consommer_item(&item) ;
}
}

4.5.4.4 Une solution au problème des philosophes dineurs


Cette solution est écrite pour N philosophes et utilise :
¤ un sémaphore binaire initialisé à 1, mutex, qui contrôle l’entrée et la sortie des processus
de la section critique,
¤ un tableau de sémaphores S[N] initialisés à 0 avec une liste vide, qui contrôle l’accès de
de chaque philosophe à ses fourchettes,
¤ un tableau de sémaphores etat[N] permettant de représenter l’état de nos philosophes.
Les états décrits par le problème sont pense et mange. La solution décrite ici doit leur
ajouter un troisième état intermédiaire faim. Tous les philosophes sont initialement dans
l’état pense.
On peut noter que l’on ne représente pas l’état de chaque fourchette explicitement. L’accent est
porté sur l’état de chaque philosophe. Si un philosophe désire prendre sa fourchette gauche, il
ne vérifie pas si elle est libre, mais si son voisin de gauche est dans l’état mange. Le philosophe
se bloque alors en attente de cette fourchette en état faim.
Ici encore l’initialisation est effectuée avant la création des processus représentant chaque
philosophes et elle est effectuée par des appels système.
On numérote alors les philosophes de 0 à N et le ième philosophe est donc une instance de
la fonction philosophe(int i). On définit aussi les macros suivantes :
¤ #define GAUCHE (i-1)%N
¤ #define DROITE (i+1)%N
Voici le codage :
void philosophe(int i)
{
while(TRUE)
{
pense() ;
prendre_fourchettes(i) ; //accès aux ressources partagées
manger() ; ;
poser_fourchettes(i) ; //accès aux ressources partagées
}
}
void prendre_fourchettes(int i)
{
down(&mutex) ;
etat[i]=faim ;
test(i) ; //si les f. sont libres, s[i].valeur incrémentée à 1...
up(&mutex) ;
down(s+i) ; //... et est ra-
mené ici à 0. Si les f. n’étaient pas libres
//s[i]<0 et on bloque sur l’appel à down()
}
}
void poser_fourchettes(int i)
{
down(&mutex) ; //entrée en section critique

Yann MORÈRE
4.5 Synchronisation de processus 53 / 103

etat[i]=pense ;
test(GAUCHE) ; //si les phil. gauche attendait sa f.d. on le re-
veille
test(DROITE) ; //si les phil. droit attendait sa f.g. on le re-
veille
up(&mutex) ;
}
}
void test(int i) //on verifie que les 2 fourchettes sont libres
{
if (etat[i] == faim && etat[GAUCHE] != mange && etat[DROITE] != mange)
{
etat[i]=mange ;
up(s+i) ; //permet de reveiller le voisin
}
}

4.5.4.5 Une solution au problème du barbier endormi


Cette solution utilise trois sémaphores :
1. un sémaphore à n jetons pour les clients (qui compte les clients qui attendent),
2. un sémaphore binaire pour le barbier (endormi 0 ou actif 1),
3. et un sémaphore d’exclusion mutuelle.
Lorsque le barbier arrive au travail, sa procédure est exécutée et se bloque sur le sémaphore des
clients jusqu’à ce qu’un client arrive. Quand un client arrive, sa procédure est exécutée et il fait
l’acquisition de mutex afin d’entrer en section critique. Les clients suivants doivent attendre que
celui-ci aie rendu le jeton de mutex. After avoir fait l’acquisition de mutex, le client regarde si le
nombre de personne en attente est inférieur au nombre de chaise. Si ce n’est pas le cas, le jeton
de mutex est restitué et le client s’en va sans être servi. S’il y a une place assise, le compteur
de personne en attente est incrémenté, la barbier est reveillé, et le client restitue le jeton du
mutex. La barbier prend alors le jeton du mutex et commence la coupe. Lorsque le client a les
cheveux coupés, il s’en va. La barbier verifie alors s’il n’y a pas un autre client, sinon il s’endort.

#define CHAIRS 5

typedef int semaphore ;

semaphore customers = 0 ;
semaphore barbers=0 ;
semaphore mutex=1 ;
int waiting=0 ;

void barber(void)
{
while (TRUE)
{
DOWN(customers) ; /*go to sleep if no customers*/
DOWN(mutex) ;
waiting=waiting-1 ;

Cours de Systèmes d’Exploitation


54 / 103 Chapitre 4 : Les Processus

UP(barbers) ;
UP(mutex) ;
cut_hair() ;
}
}

void customer(void)
{
DOWN(mutex) ;
if (waiting lessthan CHAIRS)
{
waiting=waiting+1 ;
UP(customers) ;
UP(mutex) ;
DOWN(barbers) ;
get_haircut() ;
}
else
{
UP(mutex) ;
}
}
Même en utilisant des sémaphores, il est possible que se posent d’autres problèmes de synchro-
nisation :
¤ difficulté de programmation,
¤ interblocage

4.6 Processus poids-legers et threads


4.6.1 Introduction
Selon le modèle classique, les notions d’application, de tâche, de job, et de processus sont à
peu près équivalentes. Une application en cours d’exécution est modélisée, du point de vue du
SE, par un processus. Chaque processus a son espace d’adresse propre. Le système d’exploitation
a connaissance de l’existence des processus et le répartiteur accorde une fraction de temps
de l’UCT à chacun d’entre eux.
Mais ce modèles possède des inconvénients :
¤ très souvent certaines parties d’une application pourraient être exécutées de manière
concurrente, en parallèle , mais elles ne peuvent être exécutées que de manière serielle,
¤ le modèle de processus classique n’exploite pas de manière satisfaisante les architectures
multi-processeurs.
On peut rappeller les définitions de concurrence et parallélisme :
¤ le parallélisme d’une application multiprocesseurs est son degré réel d’exécution parallèle,
et il est donc limité par le nombre de processeurs,
¤ le degré de concurrence (concurrency) de cette application est le parallélisme maximal
qu’elle pourrait atteindre avec un nombre illimité de processeurs.
Premier essai de modification de modèle de processus il pourrait paraı̂tre intéressant
de s’attaque à l’équivalence application-processus en autorisant une application à être
composée de plusieurs processus, mais ce modèle ainsi modifié pose des problèmes :

Yann MORÈRE
4.6 Processus poids-legers et threads 55 / 103

  


    !#"

Fig. 4.19 – Le modèle classique tâche = processus

¤ chaque processus ayant toujours son espace d’adresse propre, on multiplie le nombre
d’espaces d’adresse à charger en mémoire. En particulier, si l’application fait appel à
plusieurs instances d’un même processus, on retrouvera plusieurs copies quasi-identiques
du même espace d’adresse (le code en particulier) en mémoire.
¤ l’exécution de l’application impose la résolution d’un complexe problème de communica-
tion entre processus.
L’idée est de conserver la notion de plusieurs chaı̂nes d’exécution à l’intérieur d’une même
application, tout en limitant les problèmes de communications.

4.6.2 Notion de thread


Selon ce nouveau modèle, une application correspond toujours à un seul processus, mais ce
processus est composé de plusieurs chaı̂nes de contrôle, ou threads partageant le même espace
d’adresse (et en particulier les mêmes variables globales).
Chaque thread s’exécute de manière séquentielle (sérielle) et dispose pour cela de son propre
compteur programme, de ses registres et de sa propre pile.
Les threads d’un même processus partagent le même espace d’adresse, ce qui signifie qu’il
n’y a pas de protection entre threads. Un thread peut donc par exemple complètement détruire
la pile d’un autre thread du même processus.
Selon ce nouveau modèle, on voit que les différents threads d’un même processus peuvent
progresser à des vitesses différentes. Un thread peut être dans l’un des états suivants :
¤ nouveau, quand il vient d’être créé,
¤ élu ou actif, quand il a accès à un processeur,
¤ bloqué ou en attente (d’entrée/sortie),
¤ terminé.
Le modèle de thread permet d’exploiter de manière bien plus efficace les architectures multi-
processeurs.

4.6.3 Abstraction fondamentales


4.6.3.1 Le point de vue du kernel
La concurence peut être offerte par les threads à deux niveaux ; au niveau du système ou
au niveau de l’application.

Cours de Systèmes d’Exploitation


56 / 103 Chapitre 4 : Les Processus



!"$#%'&

      

Fig. 4.20 – Nouveau modèle : un processus peut comprendre plusieurs chaı̂nes de contrôle, ou
threads

#$%

6.708 93:;<.6 !"



&'(

,.-0/ 13245., )*+
     

Fig. 4.21 – Une des chaı̂nes de contrôle (threads) d’un processus peut bloquer sans que cela
affecte les autres threads de ce processus

Yann MORÈRE
4.6 Processus poids-legers et threads 57 / 103

Concurrence système Le kernel offre la possibilité de concurrence système (system concur-


rency) en reconnaissant des chaı̂nes de contrôles multiples à l’intérieur d’un processus, et
en ordonnançant ces chaı̂nes de contrôle (souvent appelées hot threads) indépendamment.
Dans le cas d’un système multiprocesseurs, plusieurs threads peuvent être simultanément
activés : le répartiteur les multiplexe sur les différentes UCT disponibles.
Même un système monoprocesseur peut bénéficier de la concurrence système ; car si l’un
des threads bloque, d’autres peuvent poursuivre leur exécution.
Concurrence usager Il est aussi possible d’offrir la possibilité de concurrence par le biais
de librairies de threads qui peuvent être appelées par l’application. Ce type de threads,
communément appelés cold threads ou coroutines, ne sont pas reconnues par le kernel. Le
répartiteur continue donc à partager le temps de calcul diponible entre des processus, et
non entre leurs threads.
Chaque application est donc en charge des ses propres threads, et en particulier leur alloue
leur temps d’accès à l’UCT à l’intérieur du segment de temps qui lui a été alloué par le
répartiteur.
C’est de cette manière que les versions classiques d’Unix et MacOS implantent le concept
de thread.

4.6.3.2 Thread du kernel


Un thread du kernel n’est pas nécessairement associé à un processus usager. Il est créé et
détruit, selon les besoins, de manière interne par le kernel et est chargé de l’exécution d’une
fonction particulière. Il partage le texte et les données globales du kernel, mais il a sa propre
pile. Il peut être ordonnancé indépendamment et utilise les mécanismes de synchronisation
standards du kernel, tels que SLEEP() ou WAKEUP().
Les threads du kernel sont utilisés par exemple pour des opérations d’E/S asynchrones.
La requète est gérée de manière synchrone à l’intérieur de la chaı̂ne de contrôle, mais paraı̂t
asynchrone au reste du kernel.
Les threads du kernel sont peu coûteux à créer et à utiliser. En particulier, les changements
de contexte entre threads du kernel sont rapides.

4.6.3.3 Processus poids-léger (lightweight process ou LWP)


Un processus poids-léger est un thread usager supporté par le kernel. C’est une abstraction
de haut niveau basée sur la notion de thread du kernel. Les PPL (processus poids-léger) sont
ordonnancés indépendamment et partage l’espace d’adresse et les ressources du processus. Ils
peuvent faire des appels système et bloquer en attente d’E/S, ou, plus généralement, d’une
ressource.
Un PPL doit maintenir une pile kernel et le contexte des registres relatifs au kernel. mais
ils doivent aussi conserver l’état usager (en particulier le contexte des registres).
Pratiquement toutes les opérations sur les PPL (création, destruction, synchronisation) de-
mandent l’exécution d’appels système, c’est à dire à chaque fois deux changement de mode :
usager à kernel, puis kernel à usager, avec à chaque fois le passage d’une frontière de protection
et la copie d’information entre l’espace kernel et l’espace usager.

4.6.3.4 Threads au niveau de l’usager


Il est aussi possible de présenter l’abstraction de thread entièrement au niveau de l’usa-
ger, sans que le kernel aie connaissance de leur existence. Ceci est accompli par des librairies
telles que C-threads de Mach et pthreads de POSIX. Dans ce cas, les interactions entre threads
n’impliquent pas le kernel et sont donc par conséquent très rapides.

Cours de Systèmes d’Exploitation


58 / 103 Chapitre 4 : Les Processus

* +-,./&021131
 

4 5-678&92::;:
< (7 = >:@?@A BCD926FEG5H5HIKJ

    L MONP&QR(SUTWVYXZQP[\Q]

^-_`aDbcFaDdec__c

 

  "!#$&%('()

qhrjs nhojp fhgji khljm thujv

Fig. 4.22 – Les processus poids-léger (lightweight process) sont vus par le kernel et en particulier
par le répartiteur qui leur offre l’accès au(x) processeur(s) disponible(s)

temps de création (en mi- temps de synchronisation


crosecondes) utilisant des sémaphore (en
microsecondes)

Thread usager 52 66

PPL 350 390

Processus classique 1700 200

L’inconvénient majeur de ce type de thread est qu’ils ne permettent pas de tirer parti des
architectures multi-processeurs : quel que soit le nombre d’UCT, au plus un thread de chaque
processus peut être élu à un instant donné. Si on utilise ensemble les deux types de threads
usager (supportés par le kernel ou pas), on obtient des structures de threads plus sophistiquées.

Yann MORÈRE
4.6 Processus poids-legers et threads 59 / 103

 
H I J L M O P Q

G !#"%$&')(#*+

K N
,-./1023/1452--2

 

A8B:C >8?:@ 687:9 ;8<:= D8E:F

Fig. 4.23 – Cas des threads implantés par des librairies comme C-threads ou pthreads : le
répartiteur ne voit que les processus

 
U V W X Y Z [ \ 
   ! "#$&%'(()+*

T ,.-/0124356879:

_ ` ^ ]
;<=>$?@&>$AB@<<@

  

NEOGP KELGM CEDGF HEIGJ QERGS

Fig. 4.24 – Structure complexe combinant les deux types de threads usager : processus poids
léger et threads non supportés par le kernel

Cours de Systèmes d’Exploitation


61 / 103

Chapitre 5
Gestion de la mémoire

5.1 Introduction
Il existe dans un système informatique plusieurs supports de conservation de l’information.
Ces supports ou types de mémoire sont organisés selon une hiérarchie en couche comme on l’a
vu dans le chapitre 2. Au sommet se trouve les registres (petite capacité, grande vitesse, coût
elevé). En bas de la pyramide on retrouve les supports magnétiques et optiques de mémoire
secondaire (grande capacité, faible coût, lents).
Afin d’obtenir une bonne performance du système informatique, il est important de tirer le
meilleur parti des types de mémoires les plus rapides pour pallier les limitations des plus lents
et à l’inverse d’exploiter au maximum les types de mémoire les plus abondants pour simuler les
types plus rapides disponibles en quantités limitées.
Nous nous concentrerons sur deux points :
¤ l’utilisation de la mémoire secondaire pour émuler la mémoire réelle, que l’on appelle
aussi les techniques de mémoire virtuelle,
¤ l’utilisation de la mémoire principale (RAM) pour tirer une meilleure performance des
systèmes d’entrées/sorties appellée aussi technique de cache disque.

5.2 Monoprogrammation
5.2.1 Introduction
Nous nous interesserons dans un premier temps au cas d’un seul processus chargé en mé-
moire. La plupart des concepts que nous allons voir s’appliquent au cas de la multi-programmation.

5.2.2 La liaison entre noms et adresses (address binding )


Un programme écrit dans un langage de haut niveau, ou même en langage d’assemblage,
fait appel à des noms, ou codes ou mnémoniques, pour accéder aux données qu’il doit mani-
puler. Le programme en langage machine qui est effectivement exécuté n’utilise plus ces noms
symboliques, mais des adresses par lesquelles il accède aux données.
Problème : À quel moment se fait ce passage des adresses symboliques aux adresses système ?
1. Lors de la compilation ou de l’assemblage du programme (code absolu)
C’est une solution qui est surtout appliquable pour les systèmes très spécialisés et le plus
souvent monoprogrammés, en particulier les systèmes temps réels.
62 / 103 Chapitre 5 : Gestion de la mémoire

678848:99 ;
 <=>?@ A Y Z&[&\ ]^_M`"^

BC A "D E$F&GH(> I*>H&GJ acbed Z a$f$bd Z&g4h$^_
 
AJ-KMLON ^i-jMkOl
mnopnq r r s
PQRSQT U U V tuq psn
WXT SVQ
  vwxyz{ |
  "!$#&%'( )*'&%+ }~ | "€$&‚ƒ(y „*yƒ&‚
+-,/.10 23"2242 |-†/‡1ˆ
55

Fig. 5.1 – Organisation de la mémoire avec un SE et un programme usager

2. Lorsque le programme est chargé en mémoire (code relogeable)


S’il n’est pas possible de savoir lors de la compilation à quelle adresse le programme sera
chargé, alors il est nécessaire de générer du code machine relogeable. Les adresses sont
toutes relatives à l’adresse de début de la partition.
3. À l’exécution du programme
Par exemple, dans le cas de la figure précédente, si le SE est partiellement résident en
mémoire, la frontière entre l’espace du SE et l’espace Usager peut varier. La liaison à
l’exécution est alors nécessaire.

5.2.3 Espace adresse logique et espace adresse physique


Dans le cas d’un lien effectué au chargement ou à l’exécution, l’UCT émet des adresses
logiques qui doivent être converties en adresses physiques. correspondant à une location en
mémoire. Ceci est fait à l’aide de deux registres :
¤ le registre de base, ou registre de relocation, contient l’adresse du début de la partition
mémoire du programme usager,
¤ le registre limite correspond à la taille de la partition mémoire du programme usager
et permet de vérifier si la demande d’accès en mémoire est permise ou non (protection
mémoire par registre).


 !"#  $#% &'(#)$ %+* %
$#% ,-/.102)'-3
  ?@ A B C C B
  NO P D GE F C HIJKB
 4 576/598;:=< >
L ML
QR ST TVUWXQ RSYQU ZU\[GQ^] `U _ba U R R U c Rd];a S] R U e e SfU

Fig. 5.2 – transformation de l’adresse physique en adresse logique

Yann MORÈRE
5.3 Multiprogrammation 63 / 103

5.3 Multiprogrammation
5.3.1 Multiprogrammation et utilisation de l’UCT et de la mémoire
Un des objectifs de la multiprogrammation est une meilleure utilisation de l’UCT. Si l’on
considère le modèle simpliste d’un système sur lequel n processus s’exécutent en concurrence, n
est alors appelé le degrés de multiprogrammation du système. Si l’on suppose que les n processus
passe tous une fraction p de leur temps à affectuer des E/S, on voit que le taux d’utilisation de
l’UCT est :
T UU CT = 1 − pn .

Taux d’utilisation de l’UCT


100

90

80

70
Taux d’utilisation de l’UCT

60

50

40

30

20

10 20% E/S
50% E/S
80% E/S
0
0 1 2 3 4 5 6 7 8 9 10
Degrés de multiprogrammation

Fig. 5.3 – Taux d’utilisation de l’UCT pour divers taux d’E/S et nombre de processus

5.3.2 Multiprogrammation à partitions fixes


Une manière simple d’autoriser la multiprogrammation est de décomposer lors de l’initia-
lisation du système) l’espace mémoire usager en plusieurs partitions fixes. Le répartiteur de
haut niveau décide de l’attribution des partitions libres au processus qui en font la demande.
La partition mémoire demandée se fait sur la base de la taille du code du programme et d’une
estimation de la taille de la pile et du monceau requis pour l’exécution du programme.
La multiprogrammation à partitions fixes était surtout utilisé sur les ordinateurs main-
frame d’IBM.
Idéalement on cherchera à avoir des partitions de tailles différentes de manière à pouvoir
accommoder différents types de programme. Il sera alors possible d’avoir un file d’attente par
type de partition : une file pour les petites partitions, une pour les partitions moyennes etc...
De même on cherchera à éviter ou à réduire les problèmes suivants :
¤ fragmentation interne : une partition de grande taille est gaspillée par un petit
processus (cas d’une file d’attente unique),

Cours de Systèmes d’Exploitation


64 / 103 Chapitre 5 : Gestion de la mémoire

¤ fragmentation externe : un processus attend indéfiniment qu’une partition correspondant


à ses besoins mémoire se libère, alors que l’espace libre est décomposé en plusieurs petites
partitions,
¤ un processus attend indefiniment qu’une partition correspondant à ses besoins en mé-
moire se libère, alors qu’une partition de grande taille est disponible (dans le cas des files
d’attente multiples).

    P QCQCQCR STUVW UX Y Z [ [ UT[ U ’ “C“C“C”


     \] ^_^ STU

       "! `a b c d c d e f"g

LCMNCNCO ŽCCC‘
#$ % & ' & ' ( )+* hi j k l k l m n+o
HIJCJCK Š‹ŒCŒC

,- . / 0 / 0 1 2+3 pq r s t s t u v+w

EFCFCFCG ‡ˆCˆCˆC‰

45 6 7 8 7 8 9 :<; xy z { | { | } ~<


ABCB D „C †

=> ? @ € ‚ ƒ

Fig. 5.4 – Deux types de répartiteurs de haut niveau : répartiteurs à files multiples (une par
partition) et répartiteur à file unique

5.3.3 Multiprogrammation à partitions variables


La principale différence entre les multiprogrammations à partitions fixes et à partitions
variables est que, dans le cas de cette dernière, le nombre, la taille et la location des partitions
changent au cours du temps.
En multiprogrammation à partitions variables, un processus ne peut pas avoir de garantie
sur la location de sa partition avant d’être chargé en mémoire. En particulier un processus ne
peut pas requérir un région particulière de la mémoire principale, vu que celle-ci pourrait ne
jamais se libérer. Par conséquent, opter pour une stratégie de multiprogrammation à partition
variable impose que le code exécutable des programmes soit au minimum relogeable.
Exemple : d’évolution possible des partitions correspondant aux évènements suivants : (a) le
processus A est chargé en mémoire, (b) B est chargé en mémoire, (c) C est chargé en
mémoire, (d) A termine (et libère la mémoire), (e) D est chargé en mémoire, (f) B se
termine, (g) E est chargé en mémoire, (h) D termine, (i) E termine, (j) F est chargé en
mémoire.

     

   


 




  "! #$&% '(*) +-,/. 0-132 45&6 78 9 : ;/<

Fig. 5.5 – Occupation de la mémoire pour la séquence d’exécution des processus donnée dans
le cas d’une gestion de la mémoire à partitions variables

Yann MORÈRE
5.3 Multiprogrammation 65 / 103

Observations
1. L’utilisation de partitions variables ne résoud pas le problème de la fragmentation externe :
si le processus F se présente avant que E ne se termine, il devra attendre faute de trou
mémoire assez grand.
2. Afin d’éviter un fragmentation externe trop importante, on peut avoir à ré-introduire le
problème de la fragmentation interne : par exemple lors du passage de (f) en (g), plutôt
que de laisser un trou de très petite taille entre C et D, on peut préférer donner tout
l’espace libre à D.
Solutions pour 1 et 2
¤ Lorsque le niveau de fragmentation externe de la mémoire devient trop important, ou
lorsqu’une demande d’allocation de mémoire ne peut être satisfaite, on effectue une com-
paction de la mémoire, de manière à regrouper les trous isolés en trous plus importants :
. un trou à une extrémité de la mémoire usager,
. un trou au centre de la mémoire usager,
. un trou juste assez grand pour la demande reçue.
¤ Le SE peut aussi effectuer une opération de ramasse-miettes (garbage collection) à
chaque fois que de la mémoire est libérée dans le monceau (heap).

5.3.4 Allocation d’un trou libre


Il existe trois stratégies communément employées :

1. Le premier trou disponible (First Fit). On effectue une recherche parmi les trous
disponibles. Le premier trou de taille suffisante qui est rencontré est choisi. Cette
technique est la plus rapide.
2. Le plus petit trou disponible (Best Fit). Le plus petit trou de taille suffisante est
sélectionné. Il faut donc tester tous les trous disponibles avant d’en choisir un.
3. Le plus grand trou disponible (Worst Fit). Sauf s’il existe un trou ayant exactement
layant exactement la taille la taille requise (auquel cas on le selectionne), le plus grand
trou disponible est sélectionné. La raison est la suivante : on veut que le trou restant, un
fois que l’espace demandé a été alloué, soit le plus grand possible.

Quelle que soit la srtatégie choisie, il faudra une bonne représentation de l’ensemble des trous
disponibles.

5.3.5 Représentation de l’espace libre


Nous présentons ici les représentations de l’espace libre utilisant des images de bits (bitmaps)
ou des listes chaı̂nées.

5.3.5.1 Bitmap

L’espace mémoire adressable est décomposé en blocs de taille 2n fixe. La partition allouée à
un processus est composée d’un nombre entier de blocs.
On définit une table (conservée en mémoire) dont chaque bit définit l’état (libre=0, oc-
cuppé=1) d’un bloc en mémoire.
Une fraction 1/2n+3 de la mémoire totale est donc occupée par la table.

Cours de Systèmes d’Exploitation


66 / 103 Chapitre 5 : Gestion de la mémoire

   

        

1 1 1 0 0 0 0 1
1 1 1 1 1 1 0 0
0 0 0 1 1 1 1 1
0 0 1 1 1 0 0 1
1 1 1 0 0 0 0 1
0 0 0 0 0 0 0 1

Fig. 5.6 – Représentation de l’occupation mémoire par une carte de bits

5.3.5.2 Liste chaı̂née


Une autre technique consiste à maintenir une liste chaı̂née des segments de mémoire allouée
et de mémoire libre, chaque segment correspondant soit à un processus, soit à un trou entre deux
partitions de processus. Contrairement à la technique bitmap, la liste chaı̂née ne se contente

   

     
   !#"$  &%!'  (*)+ , "$%!-. &/!01- 2 -32 4
5 607 8 8 9;:<9*=?> @ 5 9$5 AB>4C?9*=?D!8 >@$A E+F 9,8 6#G!60H537 537 >4=
IKJ4L MN3OP QSR3P QUT V#I!VWQN3L N L J4MXJ4PT OYN3QJ4P?R3PZL [0VMN

P 0 3 T 3 4 P 7 5 P 12 3

T 15 5 P 20 5 T 25 2 P 27 4

Fig. 5.7 – Représentation de l’occupation de la mémoire par une liste chaı̂née

pas de compter les blocs occupés et libres. Ainsi on trouvera plusieurs segments P contigus, là
où le bitmap ne verra qu’un seul segment occupé.

5.3.5.3 Listes de trous de taille spécifiques


Il est possible d’accélerer la recherche de aussi bien dans le cas d’un représentation de
type bitmap que de la liste chaı̂née. Pour cela on peut maintenir des listes de trous de tailles
fréquemment demandées, le plus souvent des puissances de 2 en octets (16K, 32K, 64K).

5.3.6 Limitations de ces techniques


Le principal défaut de ces techniques, est qu’elles requièrent que la taille des partitions soit
définie de manière statique. Lorsqu’un processus a obtenu une partition, il est très difficile voir
impossible de lui allouer d’avantage de mémoire en cours d’exécution.

Yann MORÈRE
5.4 La mémoire virtuelle 67 / 103

5.4 La mémoire virtuelle


5.4.1 Pallier le manque de mémoire
Un processus peut avoir besoin de plus de mémoire que ce qui est réellement disponible
en RAM. De manière plus générale, l’espace total nécessaire pour l’ensemble des processus ou
applications chargés par les usagers pour être exécutés dépassent souvent (largement) les
capacités de la mémoire primaire.
Il s’agit alors pour le concepteur de systèmes d’exploitation de régler ce problème de manque
de mémoire. Les solutions proposées reviennent toutes à découper l’espace de mémoire virtuelle
dont a besoin chaque processus en plusieurs morceaux, et à ne charger que quelques uns de ces
morceaux (ceux qui sont immédiatement nécessaires) en mémoire. On pourra distinguer :
¤ les structures de chevauchement (overlay),
¤ la segmentation,
¤ la pagination.

5.4.1.1 Structures de chevauchement (overlays)


Cette solution, utilisée par exemple sous MS-DOS, permet à un programme en cours d’exé-
cution de charger un fichier .exe ou .com en mémoire (par un appel système) puis de reprendre
le contrôle. Le programme peut alors appeler une procédure dans l’overlay. Quand celle-ci a
fini son exécution, le programme reprend son exécution et peut libérer la mémoire utilisée par
l’overlay.
L’idée des recouvrements (chevauchements) est de maintenir en mémoire seulement les ins-
tructions et les données nécessaires à chaque moment donné. Quand on a besoin d’autres ins-
tructions, elles sont chargées dans l’espace précédemment occupé par des instructions qui ne
sont plus necéssaires.
Les recouvrements ne requièrent aucun support spécial de la part du système d’exploitation.
C’est au programmeur d’écrire la structure de recouvrement adéquate. Cette tâche demande
une connaissance complète de la structure du programme, de son code et de ses structures de
données. C’est pour cette raison que ces structures de recouvrement ne s’utilisent que sur les
micro-ordinateurs et sur d’autres systèmes qui ne possèdent pas beaucoup de mémoire.

5.4.1.2 Segmentation
Comme dans le cas des structures de recouvrement, l’espace adresse du processus est expli-
citement décomposé par le programmeur en blocs logiques (données, code d’E/S, code d’initia-
lisation, calculs numériques, etc...) appelés segments. Il est important de noter que les segments
ne sont pas tous de la même taille (contrairement à ce que nous verrons avec la pagination).
On identifie les éléments dans un segment par leurs déplacements à partir du début du
segment. Un espace d’adresse logique est un ensemble de segments. Chaque segment possède
un nom et une longueur. Les adresses spécifient le nom du segment et le déplacement dans
ce segment. Chaque adresse est donc désignée par deux quantités : un nom de segment et un
déplacement. Pour des raisons de simplification les segments sont référencés par des numéros
de segments. Au final un adresse logique est définie par le doublet :
<numéro du segment, déplacement>
Un prérequis pour la segmentation est donc bien évidemment que le code généré soit (au
minimum) relogeable.
La transformation des adresses logiques en adresses physiques s’effectuent à l’aide d’une
table des segments, comme montré sur la figure 5.9.

Cours de Systèmes d’Exploitation


68 / 103 Chapitre 5 : Gestion de la mémoire



)*,+'-./.'-10 234 56-

  ! "$#%'&

Fig. 5.8 – Accès à un élément d’un segment grâce à son déplacement

/10 230 4 5 687*9 :


   !!

    "$#%'& (*) +, +-.

>?@ AB BACED1FGB H I1JGA


K1LGM ced!fhg8i jk
< = lnmo$p'i qsrEk

N!O1N
P*QR'SSUTWVEP*QRX P TYTZP\[!T^] _ TQ QT`GQ[_ R[GQTa a*R'bT

Fig. 5.9 – Transformation d’une adresse logique en adresse physique dans le cas de la mémoire
segmentée

Yann MORÈRE
5.4 La mémoire virtuelle 69 / 103

Si le nombre de segments est faible, la table des segments sera le plus souvent conservées
dans des registres. Sinon, une partie de la RAM sera utilisée à cette effet. La table des segments
fait bien entendu partie des informations sur un processus que le système d’exploitation doit
préserver lorsque le processus élu est désactivé et rétablir lorsqu’un nouveau processus prend
le contrôle de l’UCT.
De manière plus générale, contrairement au cas des structures de recouvrement, le système
d’exploitation est en charge des segments.
Les principaux avantages de la segmentation sont :
¤ elle élimine pratiquement le problème de fragmentation interne et reduit le problème de
la fragmentation externe,
¤ elle permet une gestion fine et fonctionnelle de la gestion mémoire,
¤ elle permet de partager la totalité ou des parties du code entre plusieurs processus et de
protéger les accès à ces segments.
Les principaux inconvénients de la segmentation sont :
¤ les segments doivent être définis explicitement par le programmeur,
¤ elle n’élimine pas complètement le problème de la fragmentation externe,
¤ les segments sont de taille variable et la gestion des allocations de trous reste aussi
complexe que dans le cas des partitions contigües.

5.4.1.3 Pagination

lespace adresse du processus est découpé en morceaux de petite taille fixe (pour le système),
ou pages qui occupent des cadres de page (frames) en mémoire.

La taille des pages est un paramètre d’environnement du système d’exploitation. Il est fixé
à l’initialisation du système et ne peut changer en cours dexécution. Typiquement, cette taille
est une puissance de 2 : p = 2n, (le plus souvent, de 512 octets à 8 K = 8192 octets). De cette
façon, si la taille de l’espace adressable est 2m, alors les mn premiers bits de l’adresse logique
indiquent le numéro de page et les n bits suivants indiquent le déplacement (offset) à l’intérieur
de cette page.

Cours de Systèmes d’Exploitation


70 / 103 Chapitre 5 : Gestion de la mémoire

Fig. 5.10 – Pagination : décomposition de l’espace d’adresse d’un processus en pages de taille
fixe

Les coupures entre deux pages consécutives peuvent se produire à des endroits arbitraires
dans l’espace adresse : au milieu dune instruction en langage machine, de données, etc.

En pratique, les frontières entre blocs de types différents (code, données statiques, pile,
monceau) sont toutefois généralement respectées.

Plusieurs solutions, chacune ayant ses avantages, ont été proposées pour implanter la liaison
(binding) entre les adresses logiques et les adresses en mémoire paginée. La plus simple nutilise
qu’une table de pages.

Fig. 5.11 – Implantation de la mémoire paginée à l’aide d’une table de pages

Dans la mesure où la taille des cadres est relativement réduite, un processus nécessite gé-
néralement un grand nombre de pages. Il est donc le plus souvent impossible de conserver la
totalité de la table de pages dans des registres. Il en résulte un important ralentissement des
accès à la mémoire.

On peut combiner les avantages de rapidité d’accès et de grands nombres de pages (grandes
partitions pour les processus) en utilisant des registres associatifs (translation look-aside buffers
ou TLBs).

Yann MORÈRE
5.4 La mémoire virtuelle 71 / 103

Fig. 5.12 – Pagination à l’aide des registres assocoatifs

Gain tiré de l’utilisation de TLBs :


Supposons que le temps d’accès à un registre est de 10 ns tandis que le temps d’accès à la
mémoire est de 60 ns, alors :
¤ Si on n’utilise qu’une table de pages, le temps d’accès moyen est T = 60 + 60ns = 120ns
(accès à la table plus accès à l’information souhaitée en mémoire), soit un ralentissement
de 100%.
¤ Si on utilise un TLB et que dans 80% des cas l’un des registres associatifs contient
l’adresse du cadre de page désiré (80% hit ratio), alors T = 0.8 × (10 + 60) + 0.2 ×
(10 + 60 + 60) = 82ns, soit un ralentissement de 37%.
¤ Si on utilise un TLB et que dans 95% des cas l’un des registres associatifs contient
l’adresse du cadre de page désiré (95% hit ratio), alors T = 0.95 × (10 + 60) + 0.05 ×
(10 + 60 + 60) = 73ns, soit un ralentissement de 22%.
¤ Des taux de réussite (hit ratios) de lordre de 80% 98% sont communs pour les micropro-
cesseurs actuel, qui utilisent de 8 à 2048 registres pour leur TLB.
La pagination à plusieurs niveaux (multilevel paging)
Les systèmes informatiques d’aujourdhui supportent des espaces adresse logiques de très grande
taille (232 à 264 octets). Si l’on considère un espace adresse de 232 octets et une taille de page
de 4 K = 212 octets, alors la table de pages devrait contenir 232-12 = 220 entrées, cest-à-dire
plus d’un million. Un bloc contigu de 4 MB en mémoire devrait donc être réservé pour la seule
table de pages !
Pour résoudre ce problème on pourra avoir recours à une pagnation à plusieurs niveaux.
Dans l’exemple précédent, le numéro de page qui occupe 20 bit pourrait être décomposé en
numéro de page de 10 bits et un décalage de page de 10 bits également

Fig. 5.13 – Pagination à l’aide des registres assocoatifs

On va utiliser une table principale (outer page table) pour accéder à la table de pages
(elle-même paginée) qui contient les numéros de cadres.

Cours de Systèmes d’Exploitation


72 / 103 Chapitre 5 : Gestion de la mémoire

La première moitié du numéro de page, p1, est utilisée pour trouver dans la table principale
le numéro de la page dans la page de tables qui contient lentrée pour p2, cest-à-dire le numéro
de cadre en mémoire physique. Finalement, le déplacement d est appliqué.

Fig. 5.14 – Implémentation de la pagination à deux niveaux

Avantage de cette technique : Comme la page de tables est elle-même paginée, elle n’a
plus besoin d’occuper un bloc contigu en mémoire.

De nos jours, la plupart des microprocesseurs supportent trois voire quatre niveaux de pagi-
nation. Afin de ne pas trop dégrader les temps daccès à la mémoire, la pagination à plusieurs
niveaux est toujours utilisée en conjonction avec des registres associatifs (TLB). Les calculs que
nous avons faits pour un niveau de pagination se généralisent sans peine à plusieurs niveaux.
Les principaux avantages de la pagination sont que :
¤ Elle est complètement transparente pour le programmeur qui n’a pas besoin de définir
explicitement la décomposition et peut ne se soucier que de son espace mémoire logique
contigu.
¤ Elle permet de partager du code ré-entrant entre plusieurs processus (en particulier après
un fork()).
¤ Elle permet un bon niveau de protection de la mémoire.
Elle élimine pratiquement les problème de fragmentation interne et de fragmentation externe.
Les principaux inconvénient de la pagination sont que :
¤ Comme elle est effectuée de manière aveugle, selon une taille de page fixe, les coupures
entre pages ne sont pas toujours judicieuses.
¤ Le programmeur qui souhaite un degré de contrôle très fin de l’exécution de son code
aura beaucoup plus de mal à le faire que dans le cas de la segmentation.
¤ Contrairement à la segmentation, il nest pas possible de distinguer différentes parties du
code qui peuvent être partagées par différents processus : cest le code en entier qui est
partagé.
¤ Il faut créer une table de pages pour chaque processus qui est chargé dans le système.
Malgré ces inconvénients, la pagination est de loin la technique la plus utilisée de nos jours par
les systèmes de gestion de la mémoire (Memory Management Unit ou MMU).

Yann MORÈRE
5.4 La mémoire virtuelle 73 / 103

5.4.2 Généralisation à plusieurs processus


La segmentation et surtout la pagination constituent les bases des techniques de mémoire
virtuelle moderne. Leur seul objectif n’est toutefois pas de simuler plus d’espace mémoire pour
un seul processus que ce qui est effectivement disponible, mais de permettre l’exécution concu-
rente de plusieurs processus, c’est à dire la multiprogrammation en temps partagé.
Dans ce chapitre, nous verrons deux mécanismes par lesquels ceci peut être assuré :
¤ Le va-et-vient (swap inswap out), selon lequel des processus de la file des processus prêts
sont transférés entre la mémoire centrale et une mémoire secondaire, comme un disque
dur.
¤ La demande de page (paging) selon laquelle des pages de l’espace adresse du processus élu
ou de processus prêts sont transférés entre la mémoire centrale et une mémoire secondaire,
comme un disque dur.

5.4.3 Le va-et-vient (swap inswap out)


Le va-et-vient est la procédure par laquelle le gestionnaire de la mémoire va retirer de la
mémoire principale un processus qui ne va pas avoir accès à lUCT avant un temps relativement
long (par exemple parce qu’il est bloqué en attente dE/S), libérer la partition que ce processus
occupait, l’accorder à d’autres processus, et finalement recharger le processus initial en mémoire
quand il revient à l’état prêt.
L’avantage du va-et-vient est qu’il permet d’accepter plus de processus dans le système
et donc d’augmenter le degré de multiprogrammation. L’inconvénient est qu’il faut mainte-
nant déterminer quand le gestionnaire de la mémoire doit sortir un processus de la mémoire
principale.
Supposons que le processus P occupe un espace M en mémoire et doit rester bloqué pendant
un temps T suite à une demande dE/S. Les questions qui se posent sont :
¤ Comment décider si le va-et-vient d’un processus est justifié ?
¤ Comment le gestionnaire de la mémoire peut-il décider, lorsqu’il manque de place en
mémoire principale quel processus doit être évacué ?
On utilisera souvent le produit M ´ T pour estimer l’espace perdu par un processus qui reste
présent en mémoire principale alors qu’il est bloqué sur une demande dE/S. Si M est très petit,
il se peut qu’il ne vaille pas la peine de sortir ce processus de la mémoire principale.
Supposons en effet que S soit le temps nécessaire au swap out de ce processus ainsi qu’au
swap in symmétrique (si l’on ne tient pas compte du temps pris par la recherche despace
disponible en mémoire principale pour recharger P). S est une fonction croissante quasi-linéaire
de M. La valeur de M, et donc celle de S est connue par le gestionnaire de la mémoire. La
difficulté consiste à évaluer T.
Il est préférable de surévaluer T que de le sous-évaluer. En effet, si T£S, alors le swap out
n’est même pas encore terminé qu’il faut déjà recharger le processus en mémoire. En fait, le
va-et-vient de P na vraiment de sens que si T > > 2S.
Une évaluation de T assez communément utilisée est le temps T écoulé pendant lequel P a
détenu M unités de mémoire. De cette façon, un processus qui est présent depuis longtemps en
mémoire sera pénalisé pour laisser l’accès à des processus plus jeunes.

5.4.4 La demande de page


Nous avons vu comment la pagination de la mémoire permet de résoudre le problème de la
fragmentation externe et de réduire sensiblement le problème de la fragmentation interne. Nous
avons vu que la pagination est transparente pour le programmeur, qui peut considérer que son

Cours de Systèmes d’Exploitation


74 / 103 Chapitre 5 : Gestion de la mémoire

programme dispose d’un bloc contigu en mémoire, alors que l’espace adresse de son programme
est effectivement disséminé en pages occupant des cadres en mémoire physique.
Nous venons également de voir comment la partition d’un processus chargé dans le système
peut être transférée entre la mémoire principale et la mémoire secondaire afin de permettre
d’augmenter le degré de multiprogrammation du système.
L’étape suivante consiste à se demander si, lorsque la partition dun processus est chargée
en mémoire (swap in), toutes les pages ont effectivement besoin dêtre présentes en RAM.
¤ Un lazy swapper ne charge une page en mémoire que si elle est effectivement nécessaire
(demandée).
En fait, le terme swapper n’est pas approprié quand on parle du chargement/déchargement
de pages individuelles, car un swapper manipule des partitions entières. On emploiera donc le
terme pager dans le contexte de la demande de page.
Si seulement un sous-ensemble des pages dun processus est effectivement présent dans des
cadres de pages, il se produira de temps à autre, au cours de l’exécution de ce processus, une
faute de page, (page-fault) cest-à-dire une demande d’accès à une page qui n’est pas présente
dans un cadre de page.
Traitement d’une faute de page durant l’exécution d’un processus P :
1. Il se produit une trappe matérielle (hardware trap) vers le kernel. Le compteur programme
et éventuellement l’état de l’instruction courante du processus interrompu sont préservés
dans une pile
2. Une routine (généralement écrite en langage dassemblage) est exécutée qui sauvegarde les
registres et appelle le kernel qui prend alors le contrôle de lUCT.
3. Le kernel découvre qu’une faute de page s’est produite, et sur quelle page virtuelle cette
faute sest produite.
4. Le gestionnaire de la mémoire virtuelle vérifie si la page est valide et si le processus P a
le droit d’accéder à cette page.

(a) En cas déchec le processus P est tué ou reçoit un message.


(b) Sinon on charge la page dans un cadre.
¤ S’il y a un cadre de page libre, il est choisi.
¤ Sinon, il faut tuer une page pour la remplacer par la page demandée.

5. Si la page à tuer avait été modifiée, elle doit être écrite dans lespace de swap (sur disque).
Elle est marquée busy jusqu’à ce que lopération d’E/S soit terminée.
6. Un changement de contexte se produit. Le processus P est bloqué en attente dE/S (lecture
dune page et éventuellement écriture dune autre), son PCB du est préservé, et un nouveau
processus est activé.
7. Pendant que l’UCT traite d’autres processus, les E/S de pages s’effectuentet la table de
pages est remise à jour. P revient à l’état prêt.
8. Quand P revient à l’état élu, le PCB et l’intruction ayant causé la faute de page sont
rétablis et l’exécution reprend.

5.4.5 Performance de la mémoire virtuelle


Typiquement, le temps de traitement d’une faute de page (code exécuté, accès au disque,
transfert) sera de l’ordre de 20 ms, à comparer avec un temps daccès à la mémoire de l’ordre
de 80 ns, une fois que l’on a pris en compte le délai ajouté par la gestion des pages.
Si le taux de faute de pages est p, alors le temps d’accès affectif à la mémoire virtuelle est :

Yann MORÈRE
5.4 La mémoire virtuelle 75 / 103

T = (1 − p) × (80ns) + p × (20ns)
= 80 + 19.999.920p

¤ Si p = 0.001, alors on obtient T  20 µs.


¤ Si on voulait une dégradation de lordre de 10%, il faudrait que p > > 4 10-7
Il y a deux angles d’attaque pour améliorer la performance de la mémoire virtuelle :
¤ Réduire les temps d’accès au disque. Nous verrons cela au chapitre 4.
¤ Réduire la valeur de p par le choix d’un algorithme de demande de page judicieux

5.4.6 Caractéristiques principales dun algorithme de demande de


page
¤ Statique ou dynamique : Un algorithme statique alloue un nombre fixe de cadres de page
à chaque processus. Un algorithme dynamique permet de changer le nombre de cadres
de page dun processus en cours d’exécution.
¤ Stratégie de chargement : quand une page doit-elle être chargée dans un cadre ? On peut
choisir la demande de page pure (une page nest chargé que si elle est demandée), mais
on peut aussi décider de pré-paginer, c’est-à-dire de charger à l’avance un certain nombre
de pages dans des cadres.
¤ Stratégie de remplacement : s’il n’y a pas de cadre libre, quelle page occupant un cadre
doit être tuée pour faire place à la nouvelle page ?
¤ Stratégie de placement : dans quel cadre une nouvelle page doit-elle être chargée ?
Certains de ces choix sont liés. Par exemple, si l’on choisit un algorithme statique, alors la
stratégie de placement est imposée : une nouvelle page est toujours chargée dans le cadre qui
vient dêtre libéré par l’algorithme de remplacement de page.

5.4.7 Algorithmes statiques de remplacement de pages


Le meilleur algorithme est celui qui produit le moins de fautes de pages. Nous allons les
comparer à partir de l’exemple simple d’un processus disposant de 8 pages et qui s’est vu allouer
3 cadres de pages en mémoire principale. Nous numéroterons les pages de 0 à 7 et supposerons
qu’au cours de l’exécution du processus, les demandes de pages suivantes se produisent en
séquence :
0 1 2 3 0 1 2 0 3 0 2 3 4 5 6 7.

5.4.7.1 PAPS
L’algorithme le plus simple : lorsqu’une faute de page se produit, c’est la page qui occupe
depuis le plus longtemps un cadre de page qui est tuée.

Cours de Systèmes d’Exploitation


76 / 103 Chapitre 5 : Gestion de la mémoire

Nous obtenons donc avec cette séquence un total de 13 fautes de pages.


PAPS se base sur la duré de temps qu’une page a passé en mémoire, et non sur son taux
d’utilisation. Son comportement n’est donc pas approprié au comportement de la plupart des
programmes. PAPS est très peu utilisé en pratique, son seul avantage étant sa simplicité d’im-
plantation.

5.4.7.2 Remplacement aléatoire


Cet algorithme exhibe à peu près les mêmes avantages et inconvénients que PAPS. Il a
été étudié dans les années 60, mais on a alors réalisé que les demandes de pages d’un même
processus étaient en fait assez fortement corrélées et que d’autres alogorithmes permettraient
d’obtenir des résultats plus satisfaisants.
On désigne souvent par le terme comportement de localité (locality behavior) le fait qu’un
processus exécutant un programme bien conçu tend à rester dans les mêmes régions de son code
et de ses données pendant un certain temps et non à sauter d’un bout à l’autre de son espace
adresse.
Nous allons maintenant voir des algorithmes qui tentent de tenir compte de l’utilisation
récente des pages présentes en mémoire.

5.4.7.3 LRU (Least Recently Used)


Lorsqu’une faute de page se produit, c’est la page qui a la référence la plus ancienne qui est
tuée.

Nous obtenons donc avec cette séquence un total de 12 fautes de pages. La ligne marquée
LRU contient le classement de plus récente utilisation. La page en bas de cette liste doit être
la prochaine victime si une faute de page se produit.
L’inconvénient principal de LRU est son implantation qui nécessite le classement des pages
selon leur dernière référence.

5.4.7.4 LFU (Least Frequently Used)


Lorsqu’une faute de page se produit, c’est la page qui a été la moins référencée qui est
tuée. Les cas d’ex-æquo sont réglés par tirage aléatoire, LRU, PAPS, ou toute autre stratégie
Cet algorithme est encore plus problématique à implanter que LRU, vu qu’il nécessite que l’on
maintienne à jour un compteur d’utilisation pour chaque processus.
De plus, LFU réagit très lentement aux changement de localité d’un processus. Il pourra
continuer à remplacer les pages de la nouvelle localité simplement parce que leur compteur est

Yann MORÈRE
5.4 La mémoire virtuelle 77 / 103

faible. Après un certain temps, cette inertie finira par être vaincue (si le processus conserve la
même localité assez longtemps), mais il en résulte un nombre important de fautes de pages.
On a aussi proposé lutilisation des algorithmes suivants, qui ne se sont pas avérés bien
efficaces :
¤ LFU (Least Frequently Used),
¤ MRU (Most Recently Used).

5.4.7.5 L’anomalie de Belady et lalgorithme de remplacement optimal


Considérons la séquence de références de pages suivante :
012301401234
Dans un premier temps, nous considérerons le cas où 3 cadres sont disponibles et nous
choisissons PAPS comme algorithme de remplacement de page.

Nous obtenons donc avec cette séquence un total de 9 fautes de pages.

Nous obtenons cette fois ci 10 fautes de pages, bien que le nombre de cadres soit plus grand !
C’est l’anomalie de Belady.

Cours de Systèmes d’Exploitation


78 / 103 Chapitre 5 : Gestion de la mémoire

Afin de mesurer la performance dun algorithme de remplacement de page, nous comptons


le nombre de fautes de pages qui se produisent pour diverses séquences de références de pages.
Nous pouvons ainsi dire que, pour une séquence donnée, LRU se comporte mieux que PAPS,
mais comment savoir si ce comportement est lui-même médiocre, bon ou excellent ?
Belady a proposé comme point de référence un algorithme optimal, parfois appelé OPT ou
MIN, qui aurait le plus petit nombre de fautes de pages posssible.
L’algorithme optimal choisit de remplacer la page dont la prochaine référence est la plus
lointaine dans le futur. Autrement dit, l’algorithme optimal ne fait que répondre à la question
suivante :
Si on avait pu connaı̂tre à l’avance la séquence de références de pages, quelles pages aurait-il
fallu remplacer de manière à minimiser le nombre de fautes de pages ?
L’algorithme optimal n’a bien évidemment pas d’implantation possible. Cest simplement
une construction théorique, un idéal dont on souhaiterait rapprocher les algorithmes de rem-
placement de page que l’on conçoit ou concevra dans le futur.
À titre d’exemple d’exécution de l’algorithme optimal, nous reprenons la séquence de l’ano-
malie de Belady dans le cas de 4 cadres de pages.
012301401234

Nous obtenons donc un total de 6 fautes de pages. Cest le plus petit nombre possible avec
4 cadres de pages et cette séquence.

5.4.8 Une approximations de LRU : l’algorithme de la seconde chance


LRU est aujourd’hui, de tous les algorithmes statiques proposés, celui qui a en général le
moins mauvais comportement. Malheureusement, son implantation est délicate, vu quelle né-
cessite le maintien à jour d’une liste des références aux pages. Divers algorithmes approximant
LRU tout en offrant un comportement assez similaire ont donc été proposés. Tous ces algo-
rithmes nécessitent l’ajout de bits d’information supplémentaire dans les entrées de la table de
page :

Yann MORÈRE
5.4 La mémoire virtuelle 79 / 103

¤ Le bit de référence indique si une demande d’accès à la page (en lecture ou en écriture)
a eu lieu depuis la dernière vérification et remise à zéro de ce bit. Ce bit est remis à 1
par le MMU lors de chaque accès à la page.
¤ Le bit dirty indique si le contenu de la page a été modifiée, cest-à-dire s’il diffère du
contenu de l’image conservée sur disque.
¤ Le bit de protection indique si le contenu de la page peut être modifié (en général, ce bit
est à 0 pour une page de code).
¤ Le bit de présence indique si la page occupe présentement un cadre de page.

5.4.8.1 L’algorithme de la seconde chance


Cet algorithme consiste en un balayage de la table des pages à la recherche d’une page à
laquelle aucun accès ne s’est produit récemment :
La recherche d’une page à tuer commence au début de la table de cadres
répéter
Test du bit de référence de la page occupant le cadre inspecté
Sil vaut 1, on le remet à 0 et on avance au cadre suivant.
Sil vaut 0, on a trouvé une victime.
jusquà ce que lon ait trouvé une page à tuer

Cours de Systèmes d’Exploitation


81 / 103

Chapitre 6
Interblocage

6.1 Introduction : définition et caractérisation


Nous avons vu au second chapitre que le modèle le plus commun d’opération d’un système
informatique est celui de processus qui doivent partager l’accès à des ressources (UCT, mémoire,
périphériques dE/S, etc.). Nous avons vu qu’un des problèmes d’accès à ces ressources, celui
des conditions de course, pouvait être résolu par des stratégies de synchronisation faisant appel,
par exemple, à des sémaphores. Autrement dit, il est possible d’empêcher que deux processus
n’accèdent en même temps à une ressource. Même la meilleure technique de synchronisation ne
peut toutefois garantir que chaque processus aura accès aux ressources dont il a besoin et que
des processus ne resteront pas éternellement bloqués en attente.

Fig. 6.1 – Interblocage


82 / 103 Chapitre 6 : Interblocage

6.1.1 Définition de l’interblocage (deadlock)


Un ensemble de processus est dans un état d’interblocage si chaque processus de l’ensemble
est bloqué en attente d’un événement qui ne peut être causé que par un autre processus de
l’ensemble. Exemples d’interblocage :
¤ La forme la plus simple de la figure de la page précédente : deux trains devant circuler
en sens inverse sur une même voie et arrêtés l’un face à l’autre.
¤ Une personne X a $20 en poche et doit $30 à une personne Y qui, par contre, a $10 et
doit $20 à X. Chacun des deux attend de pouvoir payer sa dette d’un seul coup.
¤ Mamihlapinatapaı̈ : holophrase fuegienne signifiant l’état de deux personnes qui, se
regardant, espèrent que l’une dentre elles fera ce que toutes deux désirent, mais n’osent
pas entreprendre. Histoire du Monde, Unesco

6.1.2 Caractérisation de l’interblocage


Les exemples précédents nous donnent une idée des conditions nécessaires pour qu’un état
puisse être ou devenir un état d’interblocage. Par exemple, dans le cas des trains, il est clair
que la ressource (un segment de voie) ne peut pas être partagée. Dans le cas du 2e exemple,
le problème existe seulement parce que les deux personnes veulent rembourser leur dette en
un seul coup. Autrement dit, elles ne sont pas prêtes à relâcher une partie de leurs ressources
en cours dexécution. De manière plus formelle, on peut identifier (Coffman et al., 1971) quatre
conditions nécessaires pour qu’un interblocage soit possible. Il faudra que les quatre conditions
soient vérifiées simultanément pour qu’un interblocage se produise.
Les quatre conditions nécessaires :
1. Condition d’exclusion mutuelle. Il doit exister au moins deux ressources qui ne sont pas
partageables : à un moment donné, chacune est soit assignée à un seul processus soit
disponible.
2. Condition de détention et d’attente. Il existe un processus qui détient au moins une
ressource et attend de pouvoir acquérir (donc, demande) des ressources détenues par
d’autres processus.
3. Condition de non réquisition. Un processus ne peut pas être forcé (par un autre processus)
à relâcher les ressources déjà acquises.
4. Condition d’attente circulaire. Il doit exister une chaı̂ne circulaire d’au moins deux pro-
cessus, dont chacun attend une ressource détenues par le membre suivant de la chaı̂ne.

6.1.3 Graphe dallocation des ressources


On utilise les conventions pictographiques suivantes : Un processus est représenté par un
cercle

Fig. 6.2 – Processus

Yann MORÈRE
6.1 Introduction : définition et caractérisation 83 / 103

Une ressource est représentée par une boı̂te rectangulaire. S’il existe plusieurs instances
indifférentiables de la ressource, on les représente dans la même boı̂te.

Fig. 6.3 – Ressources

Par indifférentiable, on entend qu’un processus ne devrait pas pouvoir choisir une instance
de cette ressource plutôt qu’une autre : il prend la première disponible. Dès lors qu’un processus
peut choisir une instance plutôt qu’une autre, elles sont différentiables, et on les modélisera alors
comme des instances de ressources différentes.
Un arc émanant d’une instance d’une ressource R et pointant vers un processus signifie que
cette instance de R est détenue par P.

Fig. 6.4 – Ressources allouées

Un arc émanant d’une processus P et pointant vers une ressource R signifie que P est bloqué
en attente d’un instance de ressource de type R.

Fig. 6.5 – Ressources demandées

Cours de Systèmes d’Exploitation


84 / 103 Chapitre 6 : Interblocage

Dans l’état représenté à la figure 6.4, le processus P1 détient la seule instance de la ressource
RB et est bloqué en attente de RA, que détient P2. Le processus P2 lui-même détient une
instance de la ressource RC mais il est bloqué en attendant qu’une seconde instance se libère,
ce qui ne pourra se faire que lorsque P3 , qui détient 3 instances de RC en libèrera une. Si à ce
point de son exécution P3 venait à demander une ressource de type RB, on obtiendrait alors le
graphe suivant, qui correspond à un état d’interblocage :

Fig. 6.6 – État dinterblocage après que P3 a demandé R B.

6.1.4 Remarques
Pour des raisons évidentes, le graphe ci-dessous correspond à un état impossible : si les
instances de RC sont effectivement indifférentiables, alors P2 ne devrait pas pouvoir être bloqué
en attente de RC alors qu’un instance de cette ressource est libre.

Fig. 6.7 – État impossible

La présence d’un cycle dans le graphe d’allocation de ressources est une condition nécessaire
pour que l’état courant corresponde à un interblocage. Si chacune des ressources impliquées dans
le cycle compte une seule instance, alors cette condition est aussi suffisante. Les figures suivantes
illustrent cette remarque.
Pour qu’il y ait interblocage, il faut qu’il y ait blocage des processus impliqués, chacun
en attente d’une ressource que détient l’un des autres processus, comme par exemple dans le
graphe d’allocation de ressources ci-dessous.

Yann MORÈRE
6.1 Introduction : définition et caractérisation 85 / 103

Fig. 6.8 – Graphe d’allocation de ressources (une ressource de chaque type).

De ce graphe d’allocation de ressources, on peut déduire le graphe indiquant qui attend qui,
et dans lequel on distingue aisément quelques cycles :

Fig. 6.9 – Graphe d’attente correspondant

On pourrait faire de même pour une situation où l’on a plusieurs ressources de chaque type :

Cours de Systèmes d’Exploitation


86 / 103 Chapitre 6 : Interblocage

Fig. 6.10 – Graphe d’allocation de ressources (plusieurs de chaque type).

Dans ce cas, on obtient le graphe d’attente suivant :

Fig. 6.11 – Graphe d’attente correspondant (plusieurs de chaque type).

6.1.5 Comment traiter le problème de linterblocage ?


On peut distinguer quatre types de stratégies de traitement automatique du problème de
l’interblocage :
1. l’esquive,
2. la prévention,

Yann MORÈRE
6.2 Vagabondage théorique sur linterblocage 87 / 103

3. la détection/résolution,
4. la stratégie de lautruche (ne rien faire).

Nous allons considérer dans les sections suivantes les motivations pour chacunes de ces approches
ainsi que les techniques qui ont été développées pour les mettre en application. Nous ferons
cela par le biais dun vagabondage à travers un certains nombre de concepts de la théorie de
l’informatique.

6.2 Vagabondage théorique sur linterblocage


6.2.1 Préambule
Cette section du cours découle directement d’une discussion par email avec un étudiant du
trimestre d’automne 1996 qui n’était pas d’accord avec mon interprétation de la notion d’état
incertain (que nous verrons un peu plus loin). Après plusieurs échanges de messages, j’ai fini par
poster (deux jours avant l’examen final) un long document illustré de plusieurs pages sur mon
site web discutant ces points de manière plus complète. Je ne fais ici que retranscrire ce texte
et en consolider quelques segments par un peu de théorie de l’informatique, ce qui me donne
l’occasion de vous exposer un peu à ces matières qui ne font (hélas) pas partie du curriculum.
Dans la mesure où je ne saurais obliger qui que ce soit à entrer dans des détails de théorie
de l’informatique, j’ai isolé les compléments d’information non essentiels à la compréhension
du cheminement logique. Les segments apparaissant à l’intéreur d’encadrés gris de ce type
apportent des détails et des informations qui me semblent intéressants, mais que vous pouvez
sauter en première lecture (et en fait, éviter complètement).

6.2.2 Bibliographie réduite


[1] J.E. Hopcroft and J.D. Ullman, Introduction to Automata Theory, Languages, and Com-
putation, Addison-Wesley, Reading, MA, 1979.
[2] H.R. Lewis and C.H. Papadimitriou, Elements of the Theory of Computation, Prentice-
Hall, Englewood Cliffs, NJ, 1981.
Ces deux livres couvrent à peu près lensemble des bases de la théorie de linformatique, en
se basant largement sur la théorie des langages formels.
[3] M.R. Garey and D.S. Johnson, Computers and Intractability : a Guide to the Theory of
NP-completeness, Freeman, New York, 1979.
Cet ouvrage constitue encore la référence fondamentale en matière détude des problèmes
NP-complets, et contient en particulier une (longue) liste de problèmes NP-complets pour la
plupart des domaines de linformatique.
[4] A.J. Kfoury, R.N. Moll, and M.A. Arbib, A Programming Approach to Computability,
Springer-Verlag, New York, 1982.
Ce livre-ci ne couvre que létude de la calculabilité, mais ce qui le rend intéressant est quil le
fait dune manière bien plus informatique que les autres, en se basant sur une forme simplifiée
du langage Pascal (ce qui démontre en plus le bon goût des auteurs).
Finalement, si vous voulez aussi raffraichir vos notions de complexité algorithmique ou tout
simplement disposer dun bon ouvrage dalgorithmique de référence (un autre grand classique) :
[5] A.V. Aho, J.E. Hopcroft, and J.D. Ullman, The Design and Analysis of Computer Al-
gorithms, Addison-Wesley, Reading, MA, 1974.

Cours de Systèmes d’Exploitation


88 / 103 Chapitre 6 : Interblocage

6.2.3 Mécanisme de demande de ressource

Afin de simplifier la discussion qui suit, nous supposerons que chaque processus effectue ses
requêtes d’allocation de ressources en faisant appel à une procédure similaire au Down() que
nous avons vu au chapitre 2. Autrement dit, lorsque le processus P demande 2 ressources de
type RA, 1 de type RB et 3 de type RC : Si toutes ces ressources sont disponibles et que le SE
les lui alloue, il peut continuer son exécution. Si elle ne sont pas toutes disponibles ou si le SE
ne lui en alloue quune partie, le processus P bloque en attendant que les ressources dont il avait
fait la requête lui soient accordées. Nous pouvons donc maintenant par exemple considérer que
les files dattente dE/S (resp. la file dattente des processus prêts) du chapitre 2 contiennent les
processus qui ont fait une demande dallocation de ressource de périphérique dE/S (resp. daccès
à lUCT) qui na pas pu être satisfaite.

6.2.4 Interblocage et cycles dattente

Revenons un peu sur la signification des arcs dun graphe d’allocation des ressources, et
considérons pour ça le cas plus simple de la figure 10. Ce qui est important dans la figure 10
est qu’un arc partant d’un processus et allant vers une ressource nindique pas que le processus
va avoir besoin tôt ou tard de cette ressource, mais qu’il l’a déjà demandée et qu’il est bloqué
en attendant quelle lui soit allouée.

Fig. 6.12 – Un autre graphe dallocation de ressources

Yann MORÈRE
6.2 Vagabondage théorique sur linterblocage 89 / 103

Fig. 6.13 – Graphe dattente correspondant à létat représenté à la figure précédente

On voit que tous les processus, sauf P3, sont bloqués. Il ny a malgré tout pas encore d’in-
terblocage. Autrement dit, si P3 termine son exécution normalement, il libèrera ses ressources,
et P2, P6 et P8 pourront reprendre leur propre exécution là où elle avait été interrompue (sur
une demande d’allocation de ressources). Si maintenant P3 demande une ressource de type Ph,
cette demande ne peut pas être satisfaite, P3 bloque, et l’on aboutit à l’état d’interblocage qui
avait été représenté à la figure 6.7.

En résumé : Le graphe d’allocation des ressources est uniquement un bon indicateur de l’état
courant du système processus + ressources. Il nous donne une vue instantanée de l’état de
chaque processus : bloqué (ce que nous avons appelé jusquà présent en attente) ou actif
(prêt ou élu). De même, il nous donne une vue instantanée de chaque instance de chaque
ressource (allouée ou libre), mais il ne nous donne aucune indication sur le futur de ce
système. En particulier, il ne nous informe en rien sur les besoins des processus : il ne nous
dit pas de quelles ressources un processus donné va devoir faire une requête. A fortiori, il
ne nous dit rien sur la chronologie de ces requêtes. L’état représenté à la figure 6.10 ne
pose un problème que si P3 demande une ressource qui est déjà allouée. Autrement dit,
si on savait à l’avance de quelle ressources P3 va faire la demande, on pourrait décider si
cet état doit être évité ou pas. La question que l’on peut se poser est : Est-il possible de
déterminer à l’avance les besoins des processus en matière d’allocation de ressources ?

6.2.5 Mauvaises nouvelles (1ère partie) : problèmes non décidables


Idéalement, ce que nous souhaiterions serait quelque chose similaire au module de prédiction
de la figure : un programme qui, recevant en entrée le code (il devrait être évident qu’il importe
peu s’il sagit de code objet ou de code source) et les données d’entrée d’un processus, puisse
déterminer à l’avance de quelles ressources ce processus va avoir besoin, et à quel moment au
cours de son exécution. Dans la mesure où le processus doit sexécuter dans des conditions de
temps partagé, l’instant auquel une requête sera présentée dépend nécessairement des autres
processus avec lesquels lUCT (ou les UCTs) est partagée. Il nous suffirait donc de savoir dans
quel ordre les requêtes d’allocation de ressources et les commandes de libération de ressources
seront présentées (ou, pour reprendre une expression rencontrée souvent dans les exercices,
lunité de temps est ici arbitraire).

Cours de Systèmes d’Exploitation


90 / 103 Chapitre 6 : Interblocage

Fig. 6.14 – Module de prédiction de chronologie des besoins en ressources.

Le problème est qu’un tel module de prédiction est impossible. Il fait en effet partie de
la classe de problèmes dont on peut prouver qu’il ne sont pas calculables (ou décidables).
Autrement dit, il n’existe pas d’algorithme qui soit capable de faire ce genre de chose. Notez
bien que l’on dit bien ”il n’existe pas”, et non pas il n’existe pas encore. Tous ces problèmes
indécidables se ramènent au problème de l’arrêt (halting problem) pour lequel il est facile de
prouver qu’il nexiste pas de solution algorithmique. Le problème de l’arrêt dit à peu près la
chose suivante :
¤ Il n’existe pas d’algorithme capable de prendre comme paramètres le code d’un pro-
gramme P et des entrées pour ce programme, et de décider si P terminera normalement
sur ces entrées ou pas (auquel cas, on supposera quil boucle à l’infini). N.B. Il est im-
portant que le code de P fasse partie des entrées. Il est bien évident que l’on pourrait
effectuer de manière automatique une telle vérification des données pour un programme
P spécifique.

6.2.5.1 Introduction plus formelle à la théorie de la calculabilité


Cette théorie repose sur la correspondance entre les algorithmes et les fonctions qu’ils im-
plantent. La définition d’un algorithme est ici plus lâche qu’en génie logiciel, et correspond
à des ensembles de règles ou des programmes. Une fonction est dite calculable s’il existe un
algorithme qui la réalise. Par exemple, la fonction suivante est calculable : f (x) = 1, si x est
pair, 0, sinon. En effet, elle est implantée par l’algorithme (programme) suivant :
begin read(X) ;
if X mod 2 = 0 then
Y := 1 ;
else Y := 0 ;
write(Y) ;
end.
De même, la fonction suivante est calculable : g (x) = 1, s’il existe de la vie sur Mars, 0, sinon.
Dans ce cas, on ne connaı̂t pas encore l’algorithme qui implante cette fonction (parce quon

Yann MORÈRE
6.2 Vagabondage théorique sur linterblocage 91 / 103

ne connaı̂t pas encore la réponse à la question), mais il est de toute façon trivial (fonction
constante).
N.B. : Nous ne traiterons ici que des fonctions/programmes à une seule entrée et une seule
sortie. La généralisation à n entrées et p sorties est triviale. Nous allons chercher à éta-
blir une liste indexée complète de tous les programmes Pascal possibles (nous aurions pu
prendre C, mais Pascal est un langage plus propre qui se prête mieux à ce genre d’ana-
lyses). Nous procédons de la manière suivante : Soit un entier n. Nous convertissons n
en sa forme binaire qui nous décomposons en octets, puis, à chaque octet, nous associons
le caractère dont il est le code ASCII. Nous obtenons donc, pour tout nombre n, le code
source d’un programme Pascal (ce programme peut être correct comme il peut ne pas
l’être).
Si les caractères correspondant à n ne forment pas le code d’un programme Pascal syntaxique-
ment correct (qui ne compile pas), on associe à n le programme suivant (boucle infinie) :
begin read(X) ;
while X = X do
Y := X ;
write(Y) ;
end.
Nous avons défini une correspondance entre l’ensemble des nombres entiers naturels et celui
de tous les programmes PascaL, et nous disposons donc maintenant d’une liste exhaustive de
tous les programmes Pascal : P1, P2,..., Pn,... Nous pouvons alors énoncer le théorème suivant :
Théorème (problème dit de l’arrêt) : Il n’existe pas de programme P capable de décider, pour
tout nombre n, si le programme Pn terminera (correctement) si on lui soumet n comme entrée.
Démonstration (par labsurde) Supposons quun tel programme existe, et appelons-le Ar-
rêt(X). Ce programme implante la fonction suivante : f (x) = 1, si Px termine quand on lui
soumet x comme entrée, 0, sinon ( Px plante si on lui soumet x comme entrée). On peut par
conséquent définir le programme Confondre(X)suivant :
begin read(X) ;
Y := 1 ;
if Arr^
et(X) <> 0 then
while Y <> 0
do Y := Y ;
write(Y) ;
end.
Ce programme implante la fonction y suivante : y (x) = 1, si f (x) = 0, non définie, sinon
(plante si on lui soumet x comme entrée).
Par conséquent (en résumé), si le programme Arrêt(X) existe, alors le programme Confondre(X)
est défini, et il existe un nombre entier naturel e tel que Pe = Confondre(X). La question que
l’on se pose alors est : que retourne Pe si on lui soumet e comme entrée, c’est-à-dire, que vaut
y (e) ? Si y (e) = 1), cela signifie que Arrêt(e) = 0, et donc que Pe (Confondre) ne termine pas si
on lui soumet e comme entrée, et donc que y (e) est indéfini, ce qui constitue une contradiction.
Si y (e) est indéfini, cela signifie que Arrêt(e) = 1, et donc que Pe (Confondre) termine si on lui
soumet e comme entrée, et donc que y (e) termine correctement, ce qui constitue une contra-
diction. Nous avons dans les deux cas une contradiction. Le programme Arrêt(X) ne peut donc
pas exister.
De manière générale, ce qu’il faut retirer de cette longue parenthèse théorique est qu’il nest
pas possible de prédire de manière automatique et à l’avance le comportement d’un programme
à partir de son code et des données qui lui sont soumises. On peut par contre déveloper des

Cours de Systèmes d’Exploitation


92 / 103 Chapitre 6 : Interblocage

techniques d’analyse d’un programme particulier, ou des techniques générales d’évaluation de


conformité à des critères généraux de bonne qualité. Ces sujets-là (entre autres) sont étudiés
en génie logiciel.

6.2.6 Une version plus faible du problème de linterblocage

Nous venons de voir qu’il n’était pas possible de prédire à l’avance quels seraient les besoins
en ressources d’un processus donné (pour une certain vecteur d’entrée de paramètres). Nous
nous replions donc sur une version plus faible de ce problème : Supposons que l’on dispose,
pour chaque processus présent dans le système de la séquence ordonnées de ses demandes et
libérations de ressources. Étant donné un état initial du système, est-il possible de déterminer
s’il existe un ordonnancement de ces processus (autrement dit, la façon dont on leur alloue le
temps UCT) tel que l’on évite tout interblocage ?
Autrement dit, si on pouvait (par des processus incantatoire impliquant le sacrifice de petits
animaux pelucheux) parvenir à prédire le résultat de la boite noire de la figure 6.14, saurait-on
comment esquiver les interblocages (faire en sorte quils ne se produisent pas) ?

Fig. 6.15 – On suppose que lon dispose dune chronologie des demandes de ressources

6.2.7 Mauvaises nouvelles (2e partie) : problèmes NP-complets

Malheureusement, même sous cette forme simplifiée, le problème reste trop complexe. Cette
fois-ci, il existe bien un algorithme qui trouvera une solution, mais sa complexité algorithmique
est trop élevée. Rappel : On dira qu’une fonction f (n) est d’ordre O (g (n)) s’il existe un entier
N et un réel c > 0 tel que, pour tout n > N , on ait : f (n) > c × g (n). Il est à noter que la
notion de complexité algorithmique est de nature asymptotique : elle ne décrit pas ce qui se
passe pour de petites valeurs de n (voir figure 6.16).

Yann MORÈRE
6.2 Vagabondage théorique sur linterblocage 93 / 103

Fig. 6.16 – Exemple de fonction dordre O(n)

Dans le cas d’un algorithme P admettant n entrées (points, nombres, mots, etc.), la com-
plexité algorithmique de P est la fonction f faisant correspondre à la taille de l’entrée n le
nombre d’opérations élémentaires nécessaire à l’exécution de P .On dira alors par abus de lan-
gage que P est de complexité algorithmique O (g (n)) si et seulement si la fonction f (n) est
d’ordre O (g (n)).

Fig. 6.17 – Temps dexécution approximatif en fonction de la complexité algorithmique et de


la taille n de lentrée (tiré du livre de Garey & Johnson cité plus haut). On suppose ici quune
instruction élémentaire sexécute en 1 µs.

On remarque que du point de vue de la complexité algorithmique, les facteurs linéaires ne


sont pas pris en compte. Cest comme cela que l’on parvient à faire abstraction du type de
machine sur lequel l’algorithme est implanté. La table suivant donne la taille du plus grand
problème traitable en 1 heure en fonction de la complexité algorithmique et de la vitesse de
calcul des ordinateurs.

Cours de Systèmes d’Exploitation


94 / 103 Chapitre 6 : Interblocage

Fig. 6.18 – Effet de progrès technologiques sur la taille du plus grand problème traitable en 1
heure de calcul.

Après avoir lu ce préambule, on s’attend sans doute à lire ici que la complexité des algo-
rithmes classiques d’esquive de l’interblocage est mauvaise (élevée). La situation est en fait
bien pire que ça : le problème d’esquive de l’interblocage en version simplifiée à partir de la
chronologie ”devinée” est NP-complet.
Autrement dit (en trivialisant ce-faisant vraiment beaucoup la notion de problème NP-
complet), non seulement personne ne connaı̂t de solution algorithmique à ce problème qui ait
une complexité polynomiale (de type n2 ou n3 , où n serait par exemple le nombre de de-
mandes/libérations de ressources), mais il est fort à douter qu’un tel algorithme existe. En fait
il existe une classe de problèmes, dits NP-complets, qui couvrent par exemple des problèmes
de réseaux (avec application en informatique et en téléphonie), de gestion de ressources, d’or-
donnancement, de logique (diagnostic de systèmes). Tous ces problèmes peuvent être ramenés
de l’un à l’autre, ce qui veut dire que si un jour quelqu’un trouve une solution polynomiale à
l’un de ces problèmes, alors cela signifiera que tous les autres ont une solution algorithmique
de complexité algorithmique polynomiale. En attendant, personne ne retient son souffle....

6.2.8 Que faire ?


La première chose à faire, quand on ne parvient pas à trouver de solution algorithmique
de complexité polynomiale à un problème est de se demander si cela est vraiment gênant.
Après tout, si on travaille avec un nombre d’entrées faible, il est possible qu’une solution de
complexité algorithmique 2n soit acceptable. En général, toutefois, ce n’est pas le cas. La
seconde chose à faire, dans ce cas, consiste à déterminer si l’on se montre incapable de produire
un algorithme de complexité polynomiale simplement parce qu’on n’est pas en forme ce jour
(semaine/mois/année/vie) là, ou tout simplement parce que le problème est NP-complet. Il ne
saurait être question dans ce document très limité de décrire en détail la démarche à suivre.
Disons simplement que l’on doit prouver que le problème que l’on traite se ramène (en temps
polynomial) à un problème NP-complet connu. Il en existe une longue liste et il existe des
techniques bien établies pour ce genre de preuve.
Avoir prouvé qu’un problème (par exemple celui de l’esquive de l’interblocage) était NP-
complet est bien intéressant, mais cela ne signifie pas que nous devons abandonner nos efforts
(après tout, le problème est réel), simplement les rediriger. Puisque le problème général n’ad-
met pas de solution exacte de complexité polynomiale, nous avons deux voies possibles pour
continuer notre étude : Rechercher des solution approximatives plutôt que des solutions exactes.
Déterminer si le problème que nous avons vraiment à résoudre est bien celui du cas général.
Si possible, on devrait chercher à travailler sur un sousproblème qui lui n’est pas NP-complet.
Dans le cas de l’interblocage, même si l’on simplifie considérablement le problème, par exemple

Yann MORÈRE
6.2 Vagabondage théorique sur linterblocage 95 / 103

en exigeant que les demandes et libérations de ressources soient correctement imbriquées, le


problème reste NP-complet. Finalement, nous sommes obligés de rechercher une solution cor-
respondant à des simplifications très conservatrices du problème de l’interblocage

6.2.9 Le modèle qui en résulte


On va devoir faire maintenant les hypothèses simplificatrices suivantes :
1. On connait à l’avance les besoins maximaux en ressources de chaque processus (son
nombre Max de ressource de chaque type) ;
2. Afin de pouvoir terminer son exécution, un processus doit détenir à un même moment
donné son nombre Max de tous les types de ressources ;
3. Un processus ne libère de ressources que lorsqu’il termine son exécution.
Autrement dit, selon notre nouveau modèle, un processus accumule les ressources jusqu’au
moment où il détient son nombre Max (nécessaire) pour chaque type de ressources. Il a besoin
de toutes ces ressources en même temps. Le nombre de ressources qu’il détient ne fait donc
que croı̂tre, jusqu’à ce qu’il puisse terminer son exécution et libérer les ressources qu’il avait
obtenues.

6.2.10 Les matrices dallocation de ressources


Nous considérons un système dans lequel existent un certain nombre d’instances de diffé-
rentes ressources. À tout instant, l’état de chaque processus présent dans le système va être
partiellement décrit par le nombre d’instances de chaque type de ressource qu’il détient, le
nombre maximum d’instances de ces ressources dont il a besoin pour terminer son exécution et
(par différence), le nombre d’instances de ressources qu’il doit encore obtenir.

Fig. 6.19 – Matrices dallocation de ressources : Requises = Max Détenues

Pour avoir une image complète de l’état du système vis-à-vis de l’utilisation de ses ressources,
il nous faut encore préciser le nombre total d’instances de chaque ressource dans le système
Nombre total d’instances de chaque type de ressource dans le système

On en déduit le nombre dinstances disponibles pour chaque type de ressource.


Nombre dinstances de chaque type de ressource qui sont encore disponibles

Cours de Systèmes d’Exploitation


96 / 103 Chapitre 6 : Interblocage

6.3 Solutions au problème de l’interblocage

6.3.1 Évitement de l’interblocage


Idéalement, on souhaiterait que les processus puissent librement faire la demande des res-
sources dont ils ont besoin. Le système d’exploitation, en charge de l’allocation de ces ressources,
pourrait satisfaire ces demandes si les ressources demandées sont disponible et si les accorder
au processus ne risque pas de conduire à un état d’interblocage. Nous avons vu à la section
précédente que, sous sa forme générale, ce problème ne peut pas être résolu. Le modèle auquel
nous avons fini par aboutir, après une série de simplifications, est très artificiel et d’un intérêt
pratique quasiment inexistant. Nous nétudions ce problème de l’évitement de l’interblocage
(deadlock avoidance) que parce que l’algorithme du banquier fait partie des grands classiques
qu’il faut connaı̂tre.

6.3.1.1 États sûrs, risqués (unsafe) et interblocage

Nous reprenons ici l’exemple du manuel de Tannenbaum (pp. 252–254). Nous considérons
deux processus, P0 et P1 , qui doivent partager l’accès à deux ressources : une table traçante T
et une imprimante I. La figure 6.20 donne un exemple de séquence d’allocation/libération de
ces deux ressources par les deux processus (dont on donne le pseudo-code déroulé).

Fig. 6.20 – Exemple de séquences dallocation/libération de deux ressources de type imprimante,


I, et table traçante, T, par deux processus. Les instants tij correspondent à des points dans le
code des processus.

La possibilité d’interblocage survient lorsque les deux processus exécutent de manière concu-
rente et que leurs besoins de ressources sont en conflit. La figure suivante donne l’ensemble des
possibilités de scénarios.

Yann MORÈRE
6.3 Solutions au problème de l’interblocage 97 / 103

Fig. 6.21 – Séquencements possibles des allocations et libérations de ressources pour les deux
processsus.

Un point sur ce graphe correspond à un état d’avancement dexécution pour P0 et pour


P1 . Lorsque le répartiteur accorde du temps de l’UCT à un processus, celui-ci avance le long
de son axe d’exécution. Il en résulte un chemin en escalier qui monte de la gauche vers la
droite. Il n’est pas possible de redescendre ni de se déplacer de la droite vers la gauche, ce
qui reviendrait à revenir en arrière (remonter le temps) dans l’exécution de l’un des processus.
Lorsque, par exemple, le niveau horizontal t11 est dépassé, cela signifie que le processus P1 a
demandé et obtenu la table traçante. Dans ce cas, il ne sera possible de dépasser le niveau
vertical t02 (P0 demande et obtient la table traçante) qu’après que le niveau horizontal t14 (P1
libère la table traçante) ait été dépassé. De même, si on a dépassé le niveau vertical t01 (P0
a demandé et obtenu l’imprimante), il n’est possible de dépasser le niveau horizontal t12 (P1
demande et obtient l’imprimante) qu’après que le niveau vertical t03 (P0 libère l’imprimante)
ait été dépassé. On voit donc que, si l’on veut éviter l’interblocage, il convient de contourner
les différentes zones ombrées de la figure 6.21.
La région hachurée de la figure 6.21 mérite toutefois un examen plus détaillé. Dès lors que
l’on entre dans cette région (par le fait de l’allocation de temps UCT aux deux processus),
on se trouve dans la situation où P0 détient l’imprimante et va bientôt demander la table
traçante (avant d’avoir libéré l’imprimante), tandis que P1 détient la table traçante et va bientôt
demander l’imprimante (avant d’avoir libéré la table traçante). En d’autres termes, d’après les
hypothèses que nous nous sommes fixés (un processus ne relâche de ressource que lorsqu’il est
sur le point de terminer), l’interblocage est maintenant inéluctable. Il peut se produire dans
une fraction de seconde comme dans une heure, mais il ne peut plus être évité. Nous dirons
alors que la région hachurée correspond à l’ensemble des états risqués (unsafe).
Note : vous pourrez rencontrer des textes en français dans lesquels le terme employé pour
unsafe est incertain, ce qui est un contresens car, à l’intérieur des hypothèses de travail
choisies, il n’y a rien d’incertain sur ces états : ils conduisent tous à l’interblocage. Après
beaucoup de tergiversations (et de discussions avec des étudiants au sujet de ce terme
ambigu), j’ai fini par choisir (pour le moment) le terme risqué, qui au moins garde la
notion de danger.
En résumé, nous pouvons distinguer trois types détats dun système informatique dans lequel
plusieurs processus se partagent plusieurs instances de ressources selon les hypothèses de travail
énoncées au 6.2.9 : États d’interblocage, tels que définis en 6.1.1 et 6.1.2, États risqués (unsafe),
à partir desquels l’interblocage est inéluctable, États sûrs (safe), à partir desquels il existe au

Cours de Systèmes d’Exploitation


98 / 103 Chapitre 6 : Interblocage

moins une séquence d’allocation de ressources aux processus permettant déviter l’interblocage.
Afin déviter que ne se produise un interblocage, il suffit donc, lorsqu’un processus présente au
système une demande d’allocation de ressource, de vérifier si le nouvel état obtenu serait un
état risqué. Si c’est le cas, il faut refuser la ressource au processus (qui va donc bloquer en
attente de cette ressource). Sinon, le processus obtient la ressource.

6.3.1.2 L’algorithme du banquier


Cet algorithme, proposé par Djikstra (1965), utilise les matrices d’allocation de ressources
pour trouver si un état est risqué (unsafe).
répéter
1. Chercher, parmi tous les processus qui nont pas terminé, s’il est un processus P dont les
besoins (nombre d’instances de ressources requises) peuvent être satisfaits (inférieurs au
nombre d’instances de ressources disponibles).
2. Si P existe, alors on lui accorde les ressources dont il a besoin et on le fait terminer (il
libère alors ces ressources, plus celles qu’il détenait déjà). S’il existait plusieurs choix pour
P , peu importe celui que l’on choisit, vu que l’on dispose de plus de ressources après que
l’on ait fait terminer un processus. On obtient un nouvel état du système.
jusquà ce qu’il n’y ait plus de processus P pouvant terminer. S’il reste des processus qui n’ont
pas terminé, alors l’état est risqué (unsafe). Sinon, il est sûr.
Remarque : Les matrices d’allocation de ressources ne permettent pas de décider si un état
est un état d’interblocage, vu quelles n’indiquent pas si un processus a déjà demandé une
instance de ressource dont il a besoin (auquel cas il est bloqué) ou s’il est encore à l’état
élu ou prêt (du point de vue du répartiteur de bas niveau). Les matrices d’allocation de
ressources apportent donc une information complètement différente de celle offerte par
les graphes d’allocation de ressource : les graphes nous donnent une image instantanée
de l’état des processus, mais ne nous permettent pas de prédire le futur, tandis que
les matrices d’allocation de ressources nous informent sur les besoins futurs de chaque
processus, et donc sur les risques d’interblocage. Les graphes nous permettent de répondre
à la question : l’état courant est-il un état d’interblocage ? Les matrices nous permettent
de répondre aux questions suivantes : L’état courant est-il sûr ou risqué (unsafe) ? Faut-il
accorder à un processus les ressources qu’il demande (l’état obtenu serait-il sûr ou risqué) ?

6.3.2 La prévention de l’interblocage


L’algorithme du banquier repose sur un modèle irréaliste : celui de l’accumulation de res-
sources par les processus ainsi que sur la connaissance a priori des besoins maximaux de res-
sources de chaque processus. Si l’on veut faire en sorte que des interblocages ne se produisent
pas, il existe une seconde stratégie : la stratégie de prévention de l’interblocage. Pour prévenir
l’interblocage, il suffit de revenir aux quatre conditions nécessaires vues au 6.1.2, et faire en
sorte qu’elle ne se produisent jamais simultanément :
1. condition d’exclusion mutuelle,
2. condition de détention et d’attente,
3. condition d’attente circulaire,
4. condition de non réquisition.
Autrement dit, le gestionnaire de ressources va devoir de temps à autre invalider l’une ou l’autre
de ces conditions.

Yann MORÈRE
6.3 Solutions au problème de l’interblocage 99 / 103

6.3.2.1 Condition d’exclusion mutuelle


De toute évidence, il n’est pas question d’invalider cette condition : il n’est pas possible que
deux processus aient accès simultanément à une même imprimante ou à un même brûleur de
CD-ROM. Condition de détention et d’attente Il existe deux façons d’empêcher qu’un processus
qui détient déjà des ressources n’en demande dautres : obliger un processus à demander toutes
ses ressources d’un coup, en début d’exécution ; à chaque fois qu’un processus doit faire la
demande de nouvelles ressources, le forcer à relâcher au préalable celles qu’il détient déjà. La
nouvelle demande doit ici encore être accordée en bloc. De cette façon, il devient impossible de
rencontrer un état risqué (unsafe), vu qu’un processus, s’il détient des ressources, ne peut plus
être bloqué jusqu’à ce qu’il les ait relâchées.

6.3.2.2 Condition d’attente circulaire


La technique la plus communément utilisée pour prévenir l’attente circulaire est de définir
une relation d’ordre parmi les ressources du système. On numérote ainsi les ressources Ri , avec
i de 0 à n. On décide qu’un processus P ne peut demander d’instance d’une ressource Ri que si
l’indice i est supérieur à l’indice de toutes les ressources dont P détient déjà des instances. De
cette façon, il est impossible qu’un processus P qui bloquerait sur une demande de ressource Ri
détenue par P détienne lui-même une ressource Rk dont P aurait besoin. En effet, si k < i, alors
P ne pourrait plus demander d’instance de Rk et il n’est pas possible d’avoir k > i, puisque P
aurait dû demander Ri avant Rk . On peut envisager, pour implanter cette restriction, soit que
les processus demandent effectivement leurs ressources par ordre d’indice croissant soit qu’un
processus demandant des instances d’une ressource Ri relâche au préalable toutes les instances
de ressources Rk telles que k > i qu’il détient.

6.3.2.3 Condition de non réquisition


Une des deux solutions permettant déliminer la condition de détention et attente que nous
avons vues s’attaque également à la condition de non-réquisition. On pourrait aussi penser
à définir un ordre de priorité entre les processus et retirer des ressources aux processus peu
prioritaires plutôt que de bloquer un processus prioritaire. Il est certain qu’il nest pas possible
de retirer des ressources à un processus sans prendre au préalable certaines précautions (après
tout, ce processus pourrait être en train d’utiliser ces ressources). Nous verrons à la prochaine
section (détection et résolution) ces précautions à prendre.
1. La prévention de l’interblocage est donc une stratégie très conservatrice qui dégrade glo-
balement la performance du système afin d’empêcher l’occurence de problèmes de temps
à autre.
Si on fait le bilan des approches visant à empêcher qu’un interblocage ne se produise, on
en trouve une qui est irréaliste et ne peut être implantée (lévitement) et une qui entrave de
manière importante le fonctionnement normal du système (la prévention). On en est donc
amené à se demander s’il nest pas préférable de laisser les processus s’exécuter normalement et
de n’intervenir que lorsqu’un problème se produit.

6.3.3 Détection et résolution de l’interblocage


Il s’agit maintenant pour le système d’exploitation de vérifier de temps à autre si des proces-
sus sont en état d’interblocage et, le cas échéant, de tenter de les débloquer. Cette vérification
pourrait se faire à intervalles réguliers (toutes les secondes, toutes les 10 secondes, etc.) ou lors-
qu’un indicateur de probabilité d’interblocage est activé (par exemple si le taux d’utilisation de
lUCT tombe à un niveau très faible alors que le nombre de processus présents dans le système

Cours de Systèmes d’Exploitation


100 / 103 Chapitre 6 : Interblocage

est normal). En premier lieu, il est bien évident que pour qu’un processus participe à un inter-
blocage, il qu’il soit bloqué. Par conséquent, l’algorithme de détection d’interblocage que nous
allons voir ne doit prendre en compte que les processus bloqués en attente d’une ressource, et
donc ignorer le(s) processus élu(s) ainsi que les processus présents dans la file d’attente de bas
niveau. D’autre part, dès lors qu’il existe plusieurs instances de ressources, il ne suffit pas de
détecter des cycles dans le graphe d’attente (figures 4.8 à 4.11). Il faudra utiliser un algorithme
plus complexe s’apparentant à l’algorithme du banquier.
Ici encore, il va donc falloir supposer que l’on connaı̂t à l’avance les besoins maximaux
de ressources de chaque processus, ce qui nous permet d’utiliser les matrices d’allocation de
ressources. Construire la liste L des processus bloqués.
répéter
1. Chercher, parmi tous les processus sur L, s’il est un processus P dont les besoins (nombre
d’instances de ressources requises) peuvent être satisfaits (inférieurs au nombre dinstances
de ressources disponibles).
2. Si P existe, alors on lui accorde les ressources dont il a besoin et on le fait terminer. S’il
existait plusieurs choix pour P , peu importe celui que l’on choisit, vu que l’on dispose
de plus de ressources après que l’on ait fait terminer un processus. On obtient un nouvel
état du système.
jusqu’à ce quil ny ait plus de processus P pouvant terminer. S’il reste des processus sur L, alors
ils participent à un interblocage.

6.3.3.1 Résolution de l’interblocage


Une fois que l’on a détecté l’existence d’un interblocage (au moins) et identifié les processus
qui y participaient, il reste encore à résoudre l’interblocage. Il est important d’insister sur le
fait que l’algorithme de détection présenté plus haut permet juste d’identifier les processus
interbloqués, mais pas de décider s’il y a un seul cycle d’interblocage ou plusieurs. Il ne permet
pas a fortiori de savoir quels processus se bloquent mutuellement. On peut identifier trois
classes de techniques permettant la résolution : Tuer des processus. C’est la technique la plus
radicale, qui est inacceptable dans le cas de transactions banquaires ou de bases de données.
On peut choisir de tuer un processus participant à l’interblocage, mais on pourrait aussi tuer
un processus innocent afin de récupérer ses ressources. Le problème est donc de choisir un
ou plusieurs processus dont la libération des ressources permettrait de relancer l’exécution de
l’ensemble. Le choix de ce(s) processus peut se faire à l’aide des matrices d’allocation, mais à
un coût calculatoire important. Cette solution est rarement utilisée de manière automatique :
on préfère laisser la responsabilité à un opérateur humain (voir la section suivante).
Résoudre par la réquisition de ressources. Il s’agit dans ce cas de déterminer à quel processus
il convient de retirer des ressources pour permettre à d’autres de reprendre leur exécution, et
donc de sortir de l’état d’interblocage. En pratique, il est peu de ressources que l’on puisse ainsi
retirer sans devoir aussi interrompre l’exécution du processus qui les détenait. On se retrouve
donc le plus souvent à utiliser la troisième technique de résolution.

6.3.3.2 Résoudre par rollback.


On voit bien quil faut que, d’une façon ou d’une autre, des processus relâchent des ressources
qu’il détiennent. Si nous ne voulons pas tuer ces processus, alors il faut les ramener à l’état
dans lequel ils se trouvaient avant que la ressource en question leur ait été allouée. Cela signifie
que le système doit maintenant, à intervalles réguliers, sauver un état complet (au minimum,
le PCB) du processus de manière à permettre de revenir à un état donné si le processus doit

Yann MORÈRE
6.3 Solutions au problème de l’interblocage 101 / 103

se voir retirer des ressources. Le problème dans ce cas est bien entendu le compromis à établir
entre la fréquence souhaitable des sauvegardes d’état et la performance globale du système.

6.3.4 La stratégie de l’autruche (ne rien faire)


Généralement, juste pour augmenter l’effet de choc, il est de bon ton d’ajouter à ce point c’est
la stratégie adoptée par Unix. En fait, quand on dit que l’on ne fait rien, il faut comprendre que
l’on ne fait rien de manière automatique, c’est-à-dire que ce n’est pas un programme qui est en
charge de la détection et surtout de la résolution des interblocages. Il reste toujours la possibilité
qu’un opérateur humain intervienne pour résoudre les cas difficiles. La motivation principale
pour cette stratégie est que, dans le cas des systèmes informatiques génériques (par opposition
par exemple aux systèmes dédiés à la gestion de bases de données ou au traitement d’information
banquaire) Les occurences d’interblocages ne sont en fait pas très fréquentes. Il est très rare
qu’un interblocage bloque une fraction significative des processus présents dans le système. Les
usagers eux-mêmes contribuent (par leur impatience) à la résolution des interblocages dans
lesquels leurs processus sont engagés, en tuant-ceux-ci pour les relancer à nouveau.

Cours de Systèmes d’Exploitation


103 / 103

Bibliographie

[Carrez, 2000] C. Carrez. Les systèmes informatiques : Vision cohérente et utilisation. 2000.
[Hervé et al., 1999] J.Y Hervé, K. Titus, et S. Drouin. Systèmes d’exploitation. 1999.
[Silberschatz et Galvin, 1994] A. Silberschatz et P.B. Galvin. Principes des systèmes d’exploi-
tation. 4 édition, 1994.

Vous aimerez peut-être aussi