b La commutation de contexte
La commutation (ou changement) de contexte est l’opération qui, d’une part, sauvegarde le contexte
du processus à qui on retire le processeur et qui, d’autre part, rétablit le contexte du processus qui vient
d’être choisi pour bénéficier du processeur (figure 1.3.b). La commutation de contexte assure à un processus
qu’il retrouvera toutes les informations dont il a besoin pour reprendre l’exécution de son programme là où il
a été interrompu. Les commutations de contexte arrivent très souvent : après chaque quantum de temps,
chaque fois que le noyau du système d’exploitation prend la main et chaque fois qu’un processus est dessaisi
du processeur avant la fin du quantum qui lui était attribué (parce qu’il attend une entrée/sortie, par
exemple). L’opération de commutation de contexte doit donc être très rapide et, en pratique, elle est souvent
assurée en grande partie par les couches matérielles de la machine. Les machines les plus performantes
effectuent un changement de contexte en quelques microsecondes et cette opération influe donc peu sur les
quantums attribués aux processus (qui, eux, sont de l’ordre de la dizaine de millisecondes).
Le modèle de processus décrit précédemment est un programme qui s'exécute selon un chemin
unique avec un seul compteur ordinal. On dit qu'il a un flot de contrôle unique ou un seul thread. De
nombreux systèmes d'exploitation modernes offrent la possibilité d'associer à un même processus plusieurs
chemins d'exécution ou multithread (figure1.5). Ils permettent ainsi l'exécution simultanée des parties d'un
même processus. Chaque partie correspond à un chemin d'exécution du processus. Le processus est vu
comme étant un ensemble de ressources (code exécutable, segments de données, de fichiers, de
périphériques, etc.) que ces parties appelées flots de contrôle ou processus légers (threads en anglais)
partagent. Chaque flot de contrôle (thread) a cependant, en plus des ressources communes, sa propre zone de
données ou de variables locales, sa propre pile d'exécution, ses propres registres et son propre compteur
ordinal.
Comparativement aux processus à un flot de contrôle unique, un thread ou processus léger avec plusieurs
flots de contrôle présente plusieurs avantages, notamment :
• Réactivité : Le processus léger continue à s'exécuter même si certaines de ses parties sont bloquées.
• Partage de ressources.
• Économie d'espace mémoire et de temps : Par exemple sous Solaris, la création d'un processus est
30 fois plus lente que celle d'un processus thread.
Pour qu’un processus puisse s’exécuter, il a besoin de procédures et de données, de mémoire destinée
à les contenir, de l’unité centrale, éventuellement de fichiers et de périphériques. Nous appelons toutes ces
entités des ressources. Etant une entité nécessaire à l’exécution d’un processus, une ressource peut être
matérielle (unité centrale, mémoire centrale, périphériques,…) ou logicielle (variable partagée, fichier,…). Il
faut noter qu’à chaque type de ressource est associé dans le système une procédure d’allocation et qu’à
chaque ressource correspond un descripteur. Le descripteur minimal se réduit à un bit représentant l’état
libre ou alloué de la ressource.
Pour chaque ressource, une caractéristique importante est le nombre de processus qui peuvent utiliser
la ressource au même moment (c’est-à-dire le nombre de points d'accès) :
- Il peut y avoir un nombre quelconque : Il n'y a alors pas de contrôle à mettre en œuvre.
- Il peut y en avoir plusieurs, mais en nombre limité : Il faut alors 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 aussi que les processus sont en exclusion mutuelle pour l'accès à cette ressource.
Pour des raisons économiques, les ressources sont dans la majorité des cas, en quantité insuffisante
pour répondre aux demandes de pointes. Ils se forment donc des files d’attente de processus demandeurs de
ressources. Le mécanisme général est le suivant : Une demande de ressource est adressée au système
d’exploitation par un processus, si la ressource n’est pas disponible, il y a blocage et mise en file d’attente du
processus demandeur, sinon l’allocation de la ressource a lieu et le processus demandeur poursuit son
déroulement. Le système d’exploitation reçoit aussi des demandes de restitution de ressources. Le
mécanisme est alors le suivant : La ressource libérée est remise dans l’ensemble des ressources disponibles,
si des processus sont en attente sur la disponibilité de cette ressource, un ou plusieurs d’entre eux seront
débloqués. Il est important de distinguer le programme système (l’ordonnanceur) de sélection du prochain
processus à activer du programme système (le dispatcher, distributeur ou commutateur) qui provoque la
réactivation du processus choisi.
Il est aussi nécessaire d’étudier l’allocation des ressources sous deux aspects : les mécanismes et les
politiques. Les mécanismes sont les moyens mis en œuvre pour réaliser l’allocation des ressources
(structures de données pour décrire les ressources, les techniques pour assurer l’usage exclusif des ressources
critiques, les files d’attente des requêtes qui ne peuvent être servies immédiatement,…). Les politiques
gouvernent la façon d’utiliser les mécanismes pour garantir le service des requêtes. Une allocation mal
avisée peut entraîner une surcharge du système sur une classe particulière de ressources ou une impossibilité
d’exploitation des processus.
Il s’agit d’un anglicisme : il ne faut pas comprendre « concurrence » au sens traditionnel du mot, qui
suggère la notion de compétition; il faut comprendre plutôt concurrency, qui suggère la notion de
simultanéité ou de concourance, l’idée que plusieurs actions ou événements se déroulent au même
moment. Nous parlerons de concurrence lorsque plusieurs processus s’exécutent de façon simultanée. Au
sein de chaque processus, l’exécution est séquentielle, c’est-à-dire que les instructions sont exécutées les
unes après l’autre, dans l’ordre dicté par le programme. Cette définition est (intentionnellement) très
générale et peut recouvrir déférentes situations particulières. En particulier, la notion de simultanéité peut
être comprise de déférentes manières. Il peut par exemple y avoir partage du temps (time-sharing ) entre
déférents processus exécutés par un unique processeur. Il peut y avoir, au contraire, exécution de chaque
processus par un processeur distinct. Enfin, il peut y avoir combinaison de ces deux idées : on peut imaginer
n processus exécutés par p processeurs, avec n > 1 et p > 1. On parle de parallélisme lorsque plusieurs
processeurs (ou plusieurs cœurs d’un même processeur, ce qui pour nous revient au même) sont utilisés.
Lorsque l’on souhaite diviser un travail complexe en un certain nombre de tâches, afin que chacune
de ces tâches puisse être exécutée par un processus distinct, on est immédiatement confronté à la nécessité
d’autoriser communication et synchronisation entre processus. En effet, déférents processus doivent
pouvoir communiquer, c’est-à-dire échanger des données : par exemple, chaque processus doit recevoir
initialement une description de sa tâche, et doit une fois cette tâche terminée transmettre une description du
résultat. De plus, déférents processus doivent pouvoir se synchroniser, c’est-à-dire attendre : par exemple, un
processus qui souhaite exploiter le résultat d’une tâche doit pouvoir attendre que ce résultat soit produit par
le processus à qui cette tâche a été attribuée.
Un système de tâches (E, <) est un ensemble E de tâches muni d'une relation de précédence <. Un
système de tâches peut se représenter sous forme de graphe orienté. Les sommets sont les tâches et les arcs
représentent les relations de précédence entre les tâches. Un arc de la tâche Ti vers la tâche Tj existe si et
seulement si Ti < Tj.
On introduit deux lois de composition S(a,b) et P(a,b) où a et b désignent des processus, telle que :
S(a,b) représente l’exécution en série des processus a et b, alors que P(a,b) représente leur exécution en
parallèle (figure 1.7).
L’instruction Join n permet de joindre (combiner) n activités parallèles en une seule activité. Les (n-
1) premières activités qui exécutent l’instruction join n se terminent, tandis que la nième activité continue en
séquence. L’exécution de l’instruction Join n est équivalent à :
n=n-1 ;
Si n≠0 alors terminer.