Skip to content

Commit a79a00f

Browse files
committed
mise a jour syllabus
1 parent 628502c commit a79a00f

6 files changed

Lines changed: 98 additions & 89 deletions

File tree

Theorie/MemoireVirtuelle/vmem.rst

Lines changed: 26 additions & 26 deletions
Large diffs are not rendered by default.
33 Bytes
Binary file not shown.
2.42 KB
Loading

Theorie/Ordonnancement/scheduling.rst

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
Ordonnancement (Scheduling)
99
===========================
1010

11-
Nous avons vu dans le chapitre précédent qu'un système d'exploitation comme Linux pouvait supporter de nombreux threads (appartenant à divers processus) avec un nombre limité (ou même unique) de processeur(s).
11+
Nous avons vu dans le chapitre précédent qu'un système d'exploitation comme Linux pouvait supporter de nombreux threads (appartenant à divers processus) avec un nombre limité de (ou même un seul) processeur(s).
1212
Un processeur n'exécute pourtant qu'un seul thread à la fois.
1313
Le partage des processeurs est rendu possible par un mécanisme de *partage de temps* : le système d'exploitation peut basculer de l'utilisation d'un processeur par un thread à une utilisation par un autre thread.
1414
L'enchaînement rapide de l'exécution des différents threads sur les processeurs donne l'illusion à l'utilisateur que ceux-ci s'exécutent simultanément.
@@ -31,19 +31,19 @@ Un thread exécute ses instructions par phases, alternant deux types d'opératio
3131

3232
À la suite de l'appel bloquant, le thread ne peut pas faire de progrès tant que le résultat de l'opération n'est pas disponible.
3333

34-
La longueur des burst CPUs, et la fréquence des opérations bloquantes comme les entrée/sorties, peut varier fortement d'une application à l'autre.
34+
La longueur des burst CPUs, et la fréquence des opérations bloquantes comme les entrées/sorties, peut varier fortement d'une application à l'autre.
3535
Ceci est illustré par la figure suivante.
3636

3737
.. figure:: figures/cpu_bursts.png
3838
:align: center
3939
:scale: 20
4040

4141
Dans cet exemple, une application de copie de fichier comme `cp(1)`_ effectue de nombreuses opérations d'entrée/sortie pour lire et écrire un fichier à copier en utilisant le système de fichiers, entremêlées de bursts CPU courts.
42-
Une application de calcul numérique présentera, au contraire, des bursts CPU très longs avec des entrée/sorties seulement au début et à la fin des calculs.
42+
Une application de calcul numérique présentera, au contraire, des bursts CPU très longs avec des entrées/sorties seulement au début et à la fin des calculs.
4343

4444
Un application peut tout à fait être composée de plusieurs threads présentant des caractéristiques différentes.
45-
Par exemple, dans un jeu, le thread chargé de prendre en compte les commandes du joueur (à l'aide du clavier ou d'une manette) présentera souvent des bursts CPU courts et de longues périodes d'attente, tandis que le thread en charge de l'intelligence artificielle du jeu pourra avoir des bursts CPU périodiques mais de durée régulière.
46-
Enfin, le thread en charge de l'affichage pourrait utiliser des bursts CPU longs pour préparer la visualisation d'une scène suivie de son envoi au dispositif d'affichage.
45+
Par exemple, dans un jeu vidéo de simulation, le thread chargé de prendre en compte les commandes du joueur (à l'aide du clavier ou d'une manette) présentera souvent des bursts CPU courts et de longues périodes d'attente, tandis que le thread en charge de l'intelligence artificielle du jeu pourra avoir des bursts CPU périodiques mais de durée régulière.
46+
Enfin, le thread en charge de l'affichage pourrait utiliser des bursts CPU longs pour préparer la visualisation d'une scène suivie de sa mise au dispositif d'affichage par le principe de DMA vu en introduction.
4747

4848
L'alternance entre les bursts CPU et les phases d'attente est mise en œuvre par l'alternance de chaque thread entre différents états, permis par le mécanisme de changement de contexte.
4949

@@ -72,7 +72,7 @@ Les threads en état Blocked sont associés à une structure de donnée du noyau
7272
Certaines de ces structures d'attente n'ont d'utilité que pour un seul thread, par exemple lorsque ce thread a demandé une lecture vers le système de fichiers.
7373
D'autres peuvent contenir plusieurs threads en attente.
7474
C'est le cas, par exemple, d'une structure d'attente pour un sémaphore.
75-
Il peut y avoir effectivement plusieurs threads ayant appelé `sem_wait(3posix)`_ (T12, T4 et T10).
75+
Il peut y avoir effectivement plusieurs threads ayant appelé `sem_wait(3posix)`_ (ici T12, T4 et T10).
7676
Un appel à `sem_post(3posix)`_ va libérer l'un de ces threads, qui passera alors en état Ready.
7777

7878
.. note:: Pas de garantie d'ordre sur le passage de l'état Blocked à l'état Ready !
@@ -88,7 +88,7 @@ Un thread passe de l'état Running à l'état Ready lorsqu'il libère le process
8888
On observe qu'avec uniquement les mécanismes définis précédemment, un thread qui ne génère aucun appel système pourrait rester dans l'état Running indéfiniment.
8989
C'est le cas, par exemple, d'un thread bloqué dans une boucle infinie ne comportant pas d'appel à la librairie standard.
9090
Si tous les processeurs venaient à être bloqués par des threads dans cette situation, alors la machine devient inutilisable.
91-
Par ailleurs, sans même considérer des boucles infinies, le temps d'occupation du processeur par le thread en cours d'exécution (son CPU burst) pourrait être particulièrement long, ce qui peut être problématique lorsque d'autres threads sont sujets à des contraintes de réactivité (par exemple, la réaction aux commandes utilisateurs ou la visualisation).
91+
Par ailleurs, sans même considérer des boucles infinies, le temps d'occupation du processeur par le thread en cours d'exécution (son CPU burst) pourrait être particulièrement long, ce qui peut être problématique lorsque d'autres threads sont sujets à des contraintes de réactivité (par exemple, la réaction aux commandes utilisateurs ou la mise à jour de la visualisation).
9292

9393
.. Un thread dans l'état Running peut tout d'abord générer volontairement un appel système bloquant pour passer en état Ready, libérant de facto le processeur qu'il utilise.
9494
.. Il faut utiliser pour cela la fonction `pthread_yield(3)`_ qui utilise elle même l'appel système `sched_yield(2)`_.
@@ -107,7 +107,7 @@ La dernière transition consiste à restaurer l'état précédemment sauvegardé
107107
Mise en œuvre du scheduler
108108
^^^^^^^^^^^^^^^^^^^^^^^^^^
109109

110-
La politique d'ordonnancement, que nous appellerons par la suite uniquement le *scheduler* par simplicité, est donc en charge de la prise de décision aux deux moments suivants :
110+
La politique d'ordonnancement, que nous appellerons par la suite uniquement de son nom anglais le *scheduler* par simplicité, est donc en charge de la prise de décision aux deux moments suivants :
111111

112112
- (1) Lorsqu'un processeur devient disponible, suite au passage d'un thread en mode Blocked, le scheduler doit sélectionner un thread dans l'état Ready et le promouvoir à l'état Running sur ce processeur.
113113
- (2) Lorsqu'une interruption périodique est traité, le scheduler doit décider si un thread actuellement en état Running doit être préempté pour passer en état Ready.
@@ -120,23 +120,24 @@ Objectifs
120120
"""""""""
121121

122122
Il n'existe pas de scheduler parfait convenant à toutes les applications.
123-
Pour s'en convaincre, considérons les deux applications que sont la copie de fichier et l'application de calcul dans notre exemple précédent.
123+
Pour s'en convaincre, considérons les deux applications que sont la copie de fichier et l'application de calcul de notre exemple précédent.
124124

125125
La priorité de l'application de copie de fichier est de subir le moins d'attente possible entre la disponibilité d'une valeur de retour d'un appel système vers le système de fichier, et l'envoi du prochain appel système pour continuer la copie, et éviter de ralentir l'opération de copie dans son ensemble.
126126
Pour ce thread, le délai d'attente entre sa mise en état Ready et l'obtention d'un processeur doit être la plus faible possible.
127127

128128
Pour l'application de calcul, le plus important est de pouvoir exécuter les instructions du long CPU burst avec le moins d'interruptions possibles.
129129
En effet, un changement de contexte est du temps perdu pour réaliser des opérations utiles (i.e., progresser dans la simulation).
130-
Par ailleurs, un thread qui est interrompu et replacé plus tard sur le processeur sera soumis à un phénomène de *cache froid* : les données qui étaient dans le cache, et donc accessibles avec un temps d'accès faible avant le changement de contexte, ont pu être remplacées par des données à des adresses différentes, utilisées par le thread qui a obtenu le processeur entre temps.
130+
131+
Par ailleurs, un thread qui est interrompu et replacé plus tard sur le processeur sera soumis à un phénomène de *cache froid* : les données qui étaient dans le cache, et donc accessibles avec un temps d'accès faible avant le changement de contexte, ont pu être remplacées par des données à des adresses différentes, utilisées par le thread qui a utilisé le processeur entre temps.
131132
Peupler de nouveau le cache avec les données nécessaire au calcul peut nécessiter de coûteux accès en mémoire principale et ralentir l'exécution.
132133

133-
Si l'on décide de privilégier l'application de copie, il est souhaitable d'interrompre le thread de l'application de calcul, mais cela va au détriment de ce dernier.
134-
À l'inverse, si on choisit de privilégier l'opération de calcul, alors l'opération de copie sera ralentie.
134+
Si l'on décide de privilégier l'application de copie, il est souhaitable d'interrompre le thread de l'application de calcul, mais cela va être au détriment de ce dernier.
135+
À l'inverse, si on choisit de privilégier l'opération de calcul, alors l'opération de copie pourrait être ralentie.
135136

136137
On peut définir cinq principaux critères pour mesurer la performance d'un scheduler :
137138

138139
- Du **point de vue du système** dans son ensemble tout d'abord :
139-
- On veut pouvoir maximimiser l'utilisation du ou des processeur(s), c'est à dire la proportion du temps où ceux-ci exécutent des instructions des applications. Les opérations de changement de contexte ne sont évidemment pas considérées comme du travail utile pour ce critère.
140+
- On veut pouvoir maximiser l'utilisation du ou des processeur(s), c'est à dire la proportion du temps où ceux-ci exécutent des instructions des applications. Les opérations de changement de contexte ne sont évidemment pas considérées comme du travail utile pour ce critère.
140141
- On peut vouloir maximiser le débit applicatif, c'est à dire le nombre de processus qui peuvent terminer leur exécution en une unité de temps donné (par exemple en une heure).
141142
- D'autres critères sont applicables, cette fois-ci **du point de vue de chaque application** individuellement. On pourra par ailleurs s'intéresser à la distribution de ces métriques pour l'ensemble des applications, afin de savoir s'il existe un déséquilibre entre la métrique telle que perçue par une application et la même métrique perçue par une autre application :
142143
- Une application peut souhaiter minimiser son temps total d'exécution, entre la création du processus et sa terminaison. Ce critère n'est pas nécessairement valide pour tous les types d'applications, par exemple il n'a que peu de sens pour une application interactive (par exemple, un shell), mais il est important pour des applications de calcul ou l'exécution d'un script par exemple.
@@ -190,13 +191,13 @@ C'est à dire qu'un thread utilisant le CPU pour de courtes périodes de temps r
190191
Un scheduler estimant SJF pourrait ainsi conserver dans une structure de données la durée des *x* derniers CPU bursts de chaque thread.
191192
En appliquant une moyenne sur cette durée, le scheduler peut alors tenter de prédire la durée du prochain CPU burst, et choisir le thread dont la durée prédite est la plus courte.
192193

193-
On note toutefois que, si SJF est optimal en terme de temps d'attente moyen, il n'offre que peu de propriétés d'équité.
194+
On note toutefois que, si SJF est optimal en terme de temps d'attente moyen, il n'offre aucune garantie d'équité.
194195
Si il existe de nombreux threads avec des CPU bursts à venir courts (ou prédits comme tels) alors un thread avec un CPU burst long (ou prédit comme tel) pourrait ne jamais avoir accès au processeur, ou bien n'y avoir accès que bien plus tard.
195196

196197
Le scheduler préemptif RR (Round Robin)
197198
"""""""""""""""""""""""""""""""""""""""
198199

199-
Un scheduler préemptif peut choisir de *préempter* un thread en cours d'exécution sur un processeur, c'est à dire de passer ce thread en état Ready pour libérer le processeur pour un autre thread.
200+
Un scheduler préemptif peut choisir de *préempter* un thread en cours d'exécution sur un processeur, c'est à dire de forcer le passage de ce thread en état Ready pour libérer le processeur pour un autre thread.
200201
Une décision de préemption peut être prise lorsque le système d'exploitation reprend la main sur le processeur lors de l'arrivée d'une interruption.
201202
Une horloge système dédiée à cet usage génère une interruption matérielle (tick) de manière périodique.
202203

@@ -220,7 +221,7 @@ En d'autres termes, le temps d'attente pour un thread sera toujours borné par l
220221

221222
On voit toutefois que ce scheduler n'est pas très efficace pour plusieurs raisons :
222223

223-
- Premièrement, il génère un grand nombre de changements de contexte (7 dans notre exemple). Comme discuté précédemment, non seulement ces changements de contexte nécessitent du temps processeur qui n'est pas utilisé pour des opérations utiles, mais ils entrainent surtout un phénomène de cache froid à chaque redémarrage d'un thread sur le processus à la suite d'un autre ayant rempli le cache avec ses propres données.
224+
- Premièrement, il génère un grand nombre de changements de contexte (7 dans notre exemple). Comme discuté précédemment, non seulement ces changements de contexte nécessitent du temps processeur qui n'est pas utilisé pour des opérations utiles, mais ils entrainent aussi un phénomène de cache froid à chaque redémarrage d'un thread sur le processus à la suite d'un autre ayant rempli le cache avec ses propres données.
224225
- Deuxièmement, comme le burst CPU d'un thread peut être interrompu avant sa complétion, il n'y a pas de relation directe entre le temps d'attente et le temps de réponse, et ce dernier peut devenir particulièrement long. Par exemple, bien que T3 ait un temps d'attente de 3 unités de temps, son temps de réponse (le temps entre son placement en état Ready et la fin de son burst CPU) est de 11 unités de temps.
225226
- Enfin, il n'y a pas de distinction entre les threads ayant besoin du processeur pour des bursts courts ou ceux ayant des bursts longs, ce qui peut conjointement réduire la réactivité des threads interactifs ou effectuant de nombreuses entrées/sorties et diminuer la performance de ceux réalisant des calculs.
226227

@@ -232,7 +233,7 @@ On voit toutefois que ce scheduler n'est pas très efficace pour plusieurs raiso
232233
Par exemple, les versions initiales de Linux utilisaient une fréquence d'horloge de 100 Hz (100 interruptions par seconde) tandis que des versions ultérieures permettaient une fréquence plus élevée de 1.000 Hz.
233234
Une fréquence plus élevée permet de diminuer le temps d'attente moyen et augmente la réactivité du système.
234235
Elle entraîne une utilisation processeur par le système plus élevée, ce qui est particulièrement problématique pour les systèmes embarqués ou pour les ordinateurs portables alimentés par une batterie.
235-
Une fréquence élevée peut aussi augmenter le risque de pollution de caches dues aux préemptions plus important.
236+
Une fréquence élevée peut aussi augmenter le risque de pollution de caches dues aux préemptions.
236237
Les versions modernes de Linux peuvent adapter la fréquence de l'horloge pour ne pas constamment réveiller un processeur lorsqu'il n'y a pas de tâche en état Ready, ou bien ne pas interrompre une tâche en état Running sur un processeur s'il n'y a pas de tâche en état Ready en attente pour le remplacer.
237238

238239
Schedulers à priorité
@@ -267,10 +268,10 @@ Ce thread pourra obtenir une priorité de base plus élevé, mais associée à u
267268
.. note:: Scheduler à priorité et synchronisation des threads
268269

269270
L'utilisation des primitives de synchronisation comme les mutex peut aller à l'encontre des priorités utilisées par le scheduler.
270-
Considérons par exemple le cas de deux threads TA et TB.
271+
Considérons le cas de deux threads TA et TB.
271272
TA doit répondre à des requêtes reçues depuis le réseau en mettant à jour une structure de données partagée, par exemple un graphe.
272273
Cette opération doit terminer le plus rapidement possible et ce thread est donc assigné à une priorité élevée.
273-
TB parcours de façon périodique la structure de données commune afin d'en extraire des statistiques (par exemple, toutes les 30 secondes).
274+
TB parcours de façon périodique la structure de données commune afin d'en extraire des statistiques (par exemple, toutes les 2 secondes).
274275
TB n'a pas de contrainte forte sur son temps de réponse mais l'opération qu'il exécute peut être assez longue.
275276
On assigne donc une priorité faible à TB.
276277
TA et TB accèdent à la structure de donnée en exclusion mutuelle, en utilisant un mutex *m*.

Theorie/Threads/coordination.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ Une première solution à ce problème est d'utiliser un mutex et un sémaphore
263263
sem_t db; // accès à la db
264264
int readcount=0; // nombre de readers
265265
266-
sem_init(&db, NULL, 1).
266+
sem_init(&db, 0, 1).
267267
268268
La solution utilise une variable partagée : ``readcount``. L'accès à cette variable est protégé par ``mutex``. Le sémaphore ``db`` sert à réguler l'accès des `writers` à la base de données. Le mutex est initialisé comme d'habitude par la fonction `pthread_mutex_init(3posix)`_. Le sémaphore ``db`` est initialisé à la valeur ``1``. Le `writer` est assez simple :
269269

0 commit comments

Comments
 (0)