Professional Documents
Culture Documents
Blaess
Pour concevoir un systme quilibr, stable et ractif aux vnements externes, il est indispensable de bien
comprendre le rle et lorganisation de ses divers composants. Cest lun des premiers buts de ce livre, qui dtaille
et commente les interactions, les activations et les commutations des tches. De trs nombreux exemples illustrant
le propos permettront au lecteur de raliser ses propres expriences sur son poste Linux.
Au sommaire
Multitche et commutation Multitche sous Linux Systmes multiprocesseurs tats des tches Interruptions,
exceptions et appels systme Mode noyau Interruptions et exceptions Appels systme Threads du noyau
Ordonnancement temps partag et priorits Temps partag Configuration des priorits Limitations de lordonnancement
temps partag Mesure du temps Tches priodiques Premption des tches Principes du temps rel Traitement
direct dans le noyau Temps rel sous Linux Performances du temps rel souple Timers temps rel Temps de
commutation Premptibilit du noyau Problmes temps rel classiques Dmarrage en Round Robin Inversion de
priorit Prise de mutex Limites et amliorations du temps rel Linux Traitement des interruptions PREEMPT-RT
Outils de mesure des performances conomies dnergie Extensions temps rel de Linux Les nanokernels temps
rel Installation de Xenomai Expriences avec Xenomai Programmer avec Xenomai Programmation de tches
simples Alarmes et tches priodiques Synchronisation des tches Traitement des interruptions Programmation
Aux dveloppeurs, architectes logiciels et ingnieurs devant mettre en uvre des applications temps rel sous Linux
Aux dcideurs et industriels souhaitant installer un systme temps rel sous Linux
Aux tudiants en informatique
2e dition
C. Blaess
Solutions
temps rel
sous Linux
2e dition
Christophe Blaess
35
06/11/15 14:13
C. Blaess
Pour concevoir un systme quilibr, stable et ractif aux vnements externes, il est indispensable de bien
comprendre le rle et lorganisation de ses divers composants. Cest lun des premiers buts de ce livre, qui dtaille
et commente les interactions, les activations et les commutations des tches. De trs nombreux exemples illustrant
le propos permettront au lecteur de raliser ses propres expriences sur son poste Linux.
Au sommaire
Multitche et commutation Multitche sous Linux Systmes multiprocesseurs tats des tches Interruptions,
exceptions et appels systme Mode noyau Interruptions et exceptions Appels systme Threads du noyau
Ordonnancement temps partag et priorits Temps partag Configuration des priorits Limitations de lordonnancement
temps partag Mesure du temps Tches priodiques Premption des tches Principes du temps rel Traitement
direct dans le noyau Temps rel sous Linux Performances du temps rel souple Timers temps rel Temps de
commutation Premptibilit du noyau Problmes temps rel classiques Dmarrage en Round Robin Inversion de
priorit Prise de mutex Limites et amliorations du temps rel Linux Traitement des interruptions PREEMPT-RT
Outils de mesure des performances conomies dnergie Extensions temps rel de Linux Les nanokernels temps
rel Installation de Xenomai Expriences avec Xenomai Programmer avec Xenomai Programmation de tches
simples Alarmes et tches priodiques Synchronisation des tches Traitement des interruptions Programmation
2e dition
C. Blaess
Solutions
temps rel
sous Linux
2e dition
Christophe Blaess
06/11/15 14:13
Solutions
temps rel
sous Linux
2e dition
G00000_TempsReel-PDT_2e.indd 1
29/10/15 10:44
Solutions
temps rel
sous Linux
2e dition
Christophe Blaess
G00000_TempsReel-PDT_2e.indd 2
29/10/15 10:44
DITIONS EYROLLES
61, bd Saint-Germain
75240 Paris Cedex 05
www.editions-eyrolles.com
En application de la loi du 11 mars 1957, il est interdit de reproduire intgralement ou partiellement le prsent ouvrage,
sur quelque support que ce soit, sans lautorisation de lditeur ou du Centre Franais dexploitation du droit de copie,
20, rue des Grands Augustins, 75006 Paris.
Groupe Eyrolles, 2012, 2016, ISBN : 978-2-212-14208-2
Multitche et commutation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Multitche sous Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Cration de processus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Paralllisme multithreads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Systmes multiprocesseurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Multiprocesseurs, multicurs et hyperthreading . . . . . . . . . . . . . . . . . . . . . . . 8
Affinit dune tche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
tats des tches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Ordonnancement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Premption . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Points cls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice1 (*) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice2 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice3 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice4 (***) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
21
21
21
22
22
CHAPITRE 2
Blaess.indb 7
22/10/2015 14:15
VIII
Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Fichier core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Appels systme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Suivi dun appel systme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Threads du noyau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Points cls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice1 (*) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice2 (*) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice3 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice4 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice5 (***) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
39
39
39
39
40
40
CHAPITRE 3
41
41
44
45
46
47
51
Blaess.indb 8
22/10/2015 14:15
IX
Limitations
delordonnancement temps partag . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Mesure du temps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Heure Unix avec gettimeofday() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Prcision des mesures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Horloges Posix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
57
58
58
61
Tches priodiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Timers Unix classiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Timers Posix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Granularit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Prcision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
64
64
66
68
70
83
83
83
83
83
84
CHAPITRE 5
TOC_blaess.indd 9
85
85
86
86
87
87
88
28/10/2015 10:19
94
94
96
98
100
101
106
109
110
111
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
Points cls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice1 (*) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice2 (*) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice3 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice4 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice5 (***) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
118
118
118
118
118
118
CHAPITRE 6
Blaess.indb 10
119
119
122
122
Temps de commutation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Commutation entre threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Commutations entre processus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Comparaison processus et threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Imprvisibilits dues la mmoire virtuelle . . . . . . . . . . . . . . . . . . . . . . . . . . .
123
123
127
128
130
22/10/2015 14:15
Premptibilit du noyau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Principes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Premptibilit du noyau standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Connatre la configuration dun noyau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Expriences sur la premptibilit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
XI
132
132
134
135
136
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
Points cls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice1 (*) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice2 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice3 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice4 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice5 (***) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
145
145
145
145
145
146
CHAPITRE 7
156
159
162
165
167
168
TOC_blaess.indd 11
169
169
169
169
169
169
28/10/2015 10:19
XII
CHAPITRE 8
178
181
183
183
186
187
189
190
190
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
Points cls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice1 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice2 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice3 (**) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice4 (***) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice5 (***) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
192
192
192
192
192
192
CHAPITRE 9
193
193
195
195
198
200
Blaess.indb 12
22/10/2015 14:15
XIII
211
211
211
212
212
CHAPITRE 10
213
213
215
216
218
220
221
223
223
232
235
Blaess.indb 13
22/10/2015 14:15
XIV
247
247
249
255
261
276
276
276
276
276
Conclusion
277
277
278
278
278
Perspectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
ANNEXEA
Blaess.indb 14
22/10/2015 14:15
XV
Bibliographie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Livres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Articles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Sites web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
Blaess.indb 15
22/10/2015 14:15
Blaess.indb 16
22/10/2015 14:15
1
Multitche et commutation
Lordonnancement sous Linux est avant tout une affaire de gestion des tches en attente. Sur
un poste de travail courant, il tourne en permanence une bonne centaine de processus, dont
la plupart sont endormis, en attente dvnements extrieurs (actions de lutilisateur, donnes
provenant du rseau, etc.), et quelques-uns seulement sont actifs un moment donn. Dans ce
chapitre, nous allons examiner la reprsentation et ltat des tches, ainsi que la notion dordonnancement premptif.
Blaess.indb 1
22/10/2015 14:15
Figure1-1
Processus et threads
On peut noter galement sur cette figure une frontire entre lespace utilisateur celui dans
lequel sexcutent toutes les applications, et lespace noyau. Cette frontire est trs importante
et repose sur une transition du mode de fonctionnement du processeur. Linux sexcute sur des
microprocesseurs qui disposent dau moins deux modes de fonctionnement.
Un mode privilgi, dans lequel le processeur peut excuter nimporte quelle opration de
son jeu dinstructions dfini par le constructeur. Il peut galement y raliser des entressorties directes vers les priphriques matriels externes. Enfin, il lui est possible de modifier
la configuration de la mmoire virtuelle travers le composant MMU (dont nous reparlerons
plus loin).
Un mode restreint, dans lequel le processeur est limit un sous-ensemble de son jeu dinstructions. Dans ce cas, il ne peut accder qu certaines oprations dentres-sorties et
certaines plages de mmoire virtuelle qui ont t explicitement configures depuis le mode
privilgi.
Le code du noyau sexcute toujours en mode privilgi, celui des applications (mme celles qui
disposent des droits de ladministrateur root) uniquement en mode restreint. Ainsi, un processus
en mode utilisateur ne pourra ni accder indment au matriel, ni toucher des pages de mmoire
qui ne lui auraient t volontairement et explicitement accordes par le noyau. Toute tentative de
violer ces limites (par exemple, en essayant dexcuter une instruction assembleur rserve au
mode privilgi ou en accdant une page mmoire non attribue) se solderait immdiatement
par la leve dune exception, cest--dire une interruption interne aussi appele une trappe
sur certains systmes dexploitation qui rendrait immdiatement le contrle au noyau afin quil
prenne des dispositions adquates (comme tuer le processus coupable ou au contraire lui attribuer les ressources demandes).
Lorsquun processus sexcutant en mode utilisateur dsire obtenir un accs un priphrique
matriel, par exemple, il devra demander au noyau de raliser pour lui les oprations voulues
(lecture, criture, paramtrage, projection en mmoire...) en invoquant un appel systme.
Ces appels systme sont des routines dassistance, o le processus utilisateur sous-traite au
noyau certaines oprations ncessitant des privilges. Avant de raliser le travail demand, ce
dernier vrifiera que le processus dispose bien de toutes les autorisations adquates.
Blaess.indb 2
22/10/2015 14:15
Multitche et commutation
Chapitre 1
Cration de processus
La cration dun nouveau processus seffectue via lappel systme fork(). Celui-ci duplique
le processus appelant (traditionnellement appel pre) pour crer un nouveau processus
(fils). Les deux processus continuent leurs progressions en parallle partir du point de retour
de fork(). Le programme excut est le mme dans les deux processus, mais leur comportement
peut sappuyer sur le code de retour de fork() afin de raliser des oprations diffrentes. Dans le
processus pre, fork() renvoie le PID (Process IDentifier) du fils nouvellement cr, tandis que
dans le fils, fork() renvoie toujours zro.
pid_t fork (void);
La fin du processus survient lors dun appel exit() ou le retour de la fonction main() ou si le
processus reoit un signal fatal quil ne traite pas. Le processus pre peut attendre la fin de son
fils en invoquant waitpid(). Cet appel systme lui permet de connatre, par lintermdiaire dune
variable entire, les conditions de terminaison de son fils (a-t-il appel exit()? si oui, avec quel
argument? sinon, a-t-il t tu par un signal? lequel? etc.).
void exit (int status);
pid_t waitpid (pid_t fils, int * status, int options);
Enfin, un processus peut charger dans sa mmoire un nouveau code excutable, abandonnant
totalement son programme prcdent pour commencer le droulement dune nouvelle fonction
main(). Ceci seffectue avec lune des fonctions de la famille exec(), dont seul execve() est
rellement un appel systme, les autres tant des fonctions de bibliothques qui arrangent leurs
arguments avant de linvoquer.
int
int
int
int
int
int
Avec cet ensemble de primitives systme fork(), execve(), exit(), waitpid() , on peut
organiser tout le multitche classique Unix fond sur des processus. Bien sr, des fonctions
de bibliothques comme system() ou posix_spawn() simplifient le travail du programmeur en
encadrant ces appels systme et rendent plus ais le dmarrage dun nouveau processus.
Voici un petit programme qui se prsente comme un shell (trs) minimal, il lit des lignes de
commandes et les fait excuter par un processus fils.
exemple-processus:
#include
#include
#include
#include
#include
Blaess.indb 3
<stdio.h>
<stdlib.h>
<string.h>
<unistd.h>
<sys/wait.h>
22/10/2015 14:15
exit(EXIT_FAILURE);
} else {
// --- PROCESSUS PRE --// Attendre la fin de son fils
Lors de son excution, on peut lui faire raliser quelques commandes simples:
$ ./exemple-processus
--> ls
exemple-processus exemple-processus.c Makefile
--> date
dim. mars 27 22:29:06 CEST 2011
--> who
cpb
tty1
2011-03-27 19:12 (:0)
cpb
pts/0
2011-03-27 21:52 (:0.0)
cpb
pts/1
2011-03-27 21:56 (:0.0)
-->
Blaess.indb 4
22/10/2015 14:15
Multitche et commutation
Chapitre 1
Toutefois, ds que lon essaie de passer des arguments sur la ligne de commandes, la fonction
execlp() recherche un fichier excutable du nom complet (y compris les espaces et arguments)
et choue:
--> ls -l
ls -l: No such file or directory
--> (Contrle-D)
$
Si lon souhaitait crire un vrai interprteur de commandes, il faudrait analyser la ligne saisie,
dcouper les mots, etc. Ce petit programme est toutefois intressant dans sa gestion des primitives de base du multitche Unix. Ce sont les seules dont on disposait de manire standard
jusque dans les annes1990 environ.
Paralllisme multithreads
Au cours des annes1980, plusieurs implmentations ont t proposes pour obtenir un mcanisme multitche lger, fonctionnant lintrieur de lespace mmoire dun processus. Certaines
sappuyaient sur une commutation entre tches organises au sein mme du processus par une
bibliothque, tandis que dautres rclamaient une extension des primitives Unix classiques
pour permettre plusieurs tches de partager le mme espace mmoire. Dans les annes1990,
une volont duniformisation de lAPI des systmes Unix a donn naissance la norme Posix,
dont une section (Posix.1c) tait consacre aux threads. Cette srie de fonctions permet de grer
des Posix Threads, aussi appels Pthreads.
La cration dun nouveau thread sobtient en appelant pthread_create() qui on indique la fonction sur laquelle le thread nouvellement cr devra dmarrer. Lidentifiant du thread (de type
pthread_t) sera renseign durant cet appel. On peut galement prciser des attributs spcifiques
pour le thread et un argument pour la fonction excuter. Nous verrons ultrieurement des attributs (enregistrs dans lobjet pthread_attr_t); pour le moment nous nous contenterons de passer
un pointeur NULL en second argument de pthread_create().
int pthread_create (pthread_t * thread,
pthread_attr_t * attr,
void * (*fonction) (void *),
void * argument);
Ds que la fonction pthread_create() se termine avec succs, nous savons quun nouveau fil
dexcution se droule dans notre processus.
La fin de ce thread se produira lorsquil invoquera pthread_exit() ou terminera sa fonction principale par un return, en renvoyant un pointeur (ventuellement NULL sil na rien de particulier
retourner).
void pthread_exit (void * valeur);
Blaess.indb 5
22/10/2015 14:15
Le pointeur renvoy lors de la terminaison peut tre rcupr par nimporte quel autre thread qui
invoque pthread_join().
int pthread_join (pthread_t thread, void ** valeur);
Dans lexemple suivant, le thread main() de notre programme va dmarrer autant de nouveaux
threads quon lui a pass darguments sur sa ligne de commandes. Chacun dentre eux recevra
en paramtre de sa fonction principale un nombre (pass travers un cast dans un pointeur).
Chaque thread calculera alors la factorielle de son nombre en effectuant des boucles et en nous
affichant sa progression. Le rsultat du calcul sera renvoy la fin de la fonction du thread, et
rcupr dans le thread main(). Lintrt de ce programme est dutiliser les diffrentes primitives
que nous avons prsentes prcdemment.
exemple-threads.c:
#include
#include
#include
#include
<pthread.h>
<stdio.h>
<stdlib.h>
<unistd.h>
Blaess.indb 6
22/10/2015 14:15
Multitche et commutation
Chapitre 1
Blaess.indb 7
22/10/2015 14:15
Systmes multiprocesseurs
Multiprocesseurs, multicurs et hyperthreading
Une part importante (et croissante) des ordinateurs actuels offre un degr plus ou moins avanc
de paralllisation physique des traitements. cet gard, le noyau Linux considre quil existe
deux types de systmes: les machines uniprocesseur sur lesquelles un seul fil dexcution est
prsent un moment donn, et les machines multiprocesseurs symtriques (SMP) qui permettent lexcution parallle de plusieurs tches. Ceci regroupe les plates-formes:
multiprocesseurs contenant rellement plusieurs processeurs physiques distincts;
multicurs: un seul processeur est prsent, mais il dispose de plusieurs dcodeurs dinstructions travaillant en parallle;
hyperthreading1: un seul processeur assure une commutation entre deux squences dinstructions diffrentes.
On retrouve bien sr des combinaisons de ces diffrentes possibilits. Ainsi, une machine
disposant de deux processeurs physiques diffrents, chacun deux comportant deux curs et
chaque cur tant hyperthread, donne lutilisateur la sensation de disposer de huit processeurs
virtuels diffrents.
Pour connatre les caractristiques du processeur, telles quelles ont t dtectes par le noyau
Linux, on peut interroger le pseudo-fichier /proc/cpuinfo. Voici un exemple sur un processeur
Intel quatre curs:
$ cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model
: 58
model name : Intel(R) Core (TM) i3-3100M CPU @ 2.40 GHz
stepping
: 9
cpu MHz
: 1273.406
cache size : 3072 KB
physical id : 0
siblings
: 4
core id
: 0
cpu cores : 2
[...]
processor : 1
[...]
processor : 2
[...]
processor : 3
1. Le mot hyperthreading correspond une implmentation particulire (celle dIntel) dun
concept plus gnral nomm Simultaneous Multi Threading. Je prfre nanmoins conserver ce
terme car il est beaucoup plus rpandu.
Blaess.indb 8
22/10/2015 14:15
Multitche et commutation
Chapitre 1
vendor_id
cpu family
model
model name
stepping
cpu MHz
cache size
physical id
siblings
core id
cpu cores
[]
$
:
:
:
:
:
:
:
:
:
:
:
GenuineIntel
6
58
Intel(R) Core(TM) i3-3110M CPU @ 2.40GHz
9
1200.093
3072 KB
0
4
1
2
Nous voyons que nous disposons de quatre processeurs virtuels (lignes processor: 0 processor: 3). Toutefois, il sagit du mme processeur physique (lignes physical id: 0 identiques)
qui contient deux curs distincts (lignes core id : 0 et core id : 1). Chacun des curs est
hyperthread. On peut remarquer que la frquence CPU est diffrente sur des deux curs affichs, en effet sur de nombreux processeurs cette frquence est modifiable dynamiquement. Ici,
le noyau Linux la fait voluer en fonction de la charge du systme. Nous reviendrons sur ce sujet
dans le chapitre 8.
La commande lscpu, disponible sur de nombreux systmes Linux, prsente de manire plus
lisible le contenu de /proc/cpuinfo:
$ lscpu
Architecture:
x86_64
Mode(s) opratoire(s) des processeurs: 32-bit, 64-bit
Byte Order:
Little Endian
CPU(s):
4
On-line CPU(s) list:
0-3
Thread(s) par cur:
2
Cur(s) par socket:
2
Socket(s):
1
Nud(s) NUMA:
1
Identifiant constructeur: GenuineIntel
Famille de processeur: 6
Modle:
58
Rvision:
9
Vitesse du processeur en MHz: 1199.906
BogoMIPS:
4789.00
Virtualisation:
VT-x
Cache L1d:
32K
Cache L1i:
32K
Cache L2:
256K
Cache L3:
3072K
NUMA node0 CPU(s):
0-3
$
Blaess.indb 9
22/10/2015 14:15
10
Parfois lscpu est moins volubile, en voici un exemple dexcution sur Raspberry Pi 2:
$ lscpu
Architecture:
Byte Order:
CPU(s):
On-line CPU(s) list:
Thread(s) per core:
Core(s) per socket:
Socket(s):
$
armv7l
Little Endian
4
0-3
1
4
1
Toutefois, avec largument _SC_NPROCESSORS_ONLN, il est important de savoir que cette fonctionnalit nest pas normalise et ne fonctionnera peut-tre que sous Linux. En voici un exemple:
exemple-sysconf.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
printf("Nombre de CPU: %ld\n",
sysconf(_SC_NPROCESSORS_ONLN));
return EXIT_SUCCESS;
}
Blaess.indb 10
22/10/2015 14:15
Multitche et commutation
Chapitre 1
11
remarque un bref passage sur le cur 1 pendant quelques secondes et un retour prolong sur
lecur 2.
Figure1-2
Migration de tches
qui nous renvoie le numro de processeur depuis lequel elle a t invoque, ou -1 si elle ne peut
le dterminer.
Le petit programme suivant va vrifier en permanence son emplacement et nous indiquer
lorsquil dtectera des migrations:
exemple-sched-getcpu.c:
#define _GNU_SOURCE // sched_getcpu() extension GNU
#include
#include
#include
#include
#include
<sched.h>
<stdio.h>
<stdlib.h>
<time.h>
<unistd.h>
int main(void)
{
int n;
int precedent = -1;
time_t heure;
struct tm * tm_heure;
Blaess.indb 11
22/10/2015 14:15
12
while (1) {
n=sched_getcpu();
if (n == -1) {
perror("sched_getcpu");
exit(EXIT_FAILURE);
}
if (precedent == -1)
precedent = n;
if (n != precedent) {
heure = time(NULL);
tm_heure = localtime(& heure);
print("%02d:%02d:%02d migration %d -> %d\n",
tm_heure->tm_hour, tm_heure->tm_min,
tm_heure->tm_sec,
precedent, n);
precedent = n;
}
}
return EXIT_SUCCESS;
}
Nous voyons que le noyau dplace le processus alternativement sur les quatre curs en fonction
de la charge du systme.
$ ./exemple-sched-getcpu
08:29:40 migration
08:30:01 migration
08:30:07 migration
08:30:12 migration
08:30:12 migration
08:30:15 migration
08:30:21 migration
(Contrle-C)
3
0
2
0
1
2
3
->
->
->
->
->
->
->
0
2
0
1
2
3
0
Il nous est galement possible de contrler lemplacement dun processus, avant son lancement
ou pendant son excution. Pour cela, la commande shell taskset est trs utile. Toutefois, son
utilisation nest pas vraiment intuitive. En voici quelques exemples:
$ taskset -c 1 ./commande
// Lance la commande sur le CPU1
$ taskset 0,2 ./commande
// Autorise la commande sexcuter sur les CPU0 et 2
$ taskset -pc 0 1234
// Migre le processus1234 sur le CPU0
$ taskset -p 1234
// Affiche laffinit du processus1234
Blaess.indb 12
22/10/2015 14:15
Multitche et commutation
Chapitre 1
13
Laffinit dune tche est la liste des CPU sur lesquels elle peut sexcuter. On peut la consulter
ou la fixer laide des fonctions suivantes:
int sched_setaffinity (pid_t
pid,
size_t
taille,
const cpu_set_t * cpuset);
int sched_getaffinity (pid_t
pid,
size_t
taille,
cpu_set_t * cpuset);
Ces fonctions sont des extensions GNU non portables sur dautres systmes Unix qui ncessitent donc de dfinir la constante symbolique _GNU_SOURCE avant dinclure <sched.h>.
Le second argument de ces fonctions correspond la taille du type de donne cpu_set_t pour
le systme.
Les listes de CPU sont reprsentes par les variables de type cpu_set_t, que lon manipule avec
les fonctions suivantes:
void
void
void
int
CPU_ZERO
CPU_SET
CPU_CLR
CPU_ISSET
(cpu_set_t * ensemble);
(int cpu, cpu_set_t * ensemble);
(int cpu, cpu_set_t * ensemble);
(int cpu, cpu_set_t * ensemble);
La fonction CPU_ZERO() permet de vider un ensemble, CPU_SET() et CPU_CLR() permettent respectivement dinsrer ou de retirer un CPU dun ensemble, enfin CPU_ISSET() permet de vrifier si
un CPU est mentionn dans un ensemble ou non.
Voici un petit exemple daccrochage dun processus sur un CPU donn en argument. On notera
que la valeur0 en premier argument de sched_setaffiny() indique que lon fixe le masque daffinit pour le processus appelant.
exemple-sched-setaffinity.c:
#define _GNU_SOURCE
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<unistd.h>
<sched.h>
Blaess.indb 13
22/10/2015 14:15
14
CPU_ZERO(& cpuset);
CPU_SET(cpu, &cpuset);
// Fixer l'affinit
if (sched_setaffinity(0, sizeof(cpuset), &cpuset)!=0){
perror(argv[1]);
exit(EXIT_FAILURE);
}
while (1) {
printf("Je suis sur le CPU %d\n", sched_getcpu());
sleep(1);
}
return EXIT_SUCCESS;
}
Contrle-C
$ ./exemple-sched-setaffinity 3
Je
Je
Je
Je
suis
suis
suis
suis
sur
sur
sur
sur
le
le
le
le
CPU
CPU
CPU
CPU
3
3
3
3
Contrle-C
$ ./exemple-sched-setaffinity 4
4: Invalid argument
$
Blaess.indb 14
22/10/2015 14:15
Multitche et commutation
Chapitre 1
15
On peut aussi fixer laffinit dun futur thread avant sa cration. Pour cela, on initialise un objet
pthread_attr_t qui contient les attributs du thread crer et on passe cette structure en second
argument de pthread_create().
Pour remplir la structure dattributs, on utilise les fonctions:
int pthread_attr_setaffinity_np (
pthread_attr_t
size_t
const cpu_set_t
int pthread_attr_getaffinity_np (
pthread_attr_t
size_t
cpu_set_t *
attr,
taille,
* cpuset);
attr,
taille,
cpuset);
Dans lexemple suivant, nous allons lancer autant de threads en parallle quil y a de processeurs disponibles. Nous commenons par le CPU0, puis incrmentons le numro jusqu ce que
lappel pthread_create() choue. Notons que la variable de type pthread_t est crase chaque
appel avec le nouvel identifiant affect au thread cr.
exemple_pthread_attr_setaffiniy.c:
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
void * fonction(void * arg)
{
long num = (long) arg;
while (1) {
printf("[Thread %ld] Je suis sur le CPU %d\n",
num, sched_getcpu());
sleep(5);
}
}
int main(void)
{
cpu_set_t
cpuset;
pthread_t
thr;
pthread_attr_t attr;
long num;
num = 0;
while (1) {
// Initialiser avec les attributs par dfaut
pthread_attr_init(& attr);
// Prparer le cpuset
CPU_ZERO(&(cpuset));
Blaess.indb 15
22/10/2015 14:15
16
CPU_SET(num, &(cpuset));
// Fixer l'affinit
pthread_attr_setaffinity_np(& attr,
sizeof(cpu_set_t),
& cpuset);
// Lancer le thread
if (pthread_create(& thr, & attr, fonction,
(void *) num) != 0)
break;
num++;
}
// Terminer le thread main en continuant les autres
pthread_exit(NULL);
}
Contrle-C
$
On peut remarquer que lordre daffichage des messages nest pas fig. Les threads faisant des
accs simultans la console, le noyau est oblig de se livrer un arbitrage qui peut voluer en
fonction de lactivit du systme. Nous allons examiner les diffrents tats dans lesquels peuvent
se trouver les tches et les transitions possibles entre ces tats.
Il est important de noter que laffinit ainsi fixe ne prsente pas de caractre obligatoire pour la
tche. tout moment, il lui est possible de modifier son affinit et de se dplacer ainsi vers un
autre processeur. Ce principe est tout fait suffisant pour les applications temps rel, o toutes
les tches dun systme sont habituellement configures pour un fonctionnement optimal. Si
on dsire verrouiller imprativement une tche sur un CPU ou un ensemble de CPU sans
quelle puisse en sortir ensuite, on utilisera plutt le mcanisme des cpuset. Un exemple clair est
prsent dans la page de manuel cpuset(7) accessible ainsi:
$ man 7 cpuset
Blaess.indb 16
22/10/2015 14:15