Ordonnancement
Transcription
Ordonnancement
Ordonnancement 1- Notions liées à l'ordonnancement de processus 1.1- Qu'est-ce que l'ordonnancement de processus ? La figure 1 schématise le fonctionnement d'une machine multiprocessus. Plusieurs processus sont présents en mémoire centrale. P1 est élu et s'exécute sur le processeur. P2 et P4 sont dans l'état bloqué car ils attentent tous les deux une fin d'entrée/sortie avec le disque. Les processus P3, P5 et P6 quant à eux sont dans l'état prêt : ils pourraient s'exécuter (ils ont à leur disposition toutes les ressources nécessaires) mais ils ne le peuvent pas car le processeur est occupé par P1. Lorsque P1 quittera le processeur parce qu'il a terminé son exécution, les trois processus P3, P5 et P6 auront tous les trois le droit d'obtenir le processeur. Mais le processeur ne peut être alloué qu'à un seul processus à la fois : il faudra donc choisir entre P3, P5 et P6 : c'est le rôle de l'ordonnancement qui élira un des trois processus. Système multiprocessus Mémoire Centrale P1 elu P2 attente E/S P3 attente cpu P4 attente E/S P51 attente cpu P6 attente cpu P4 P2 Processeur UE P1 Bus Fig 1 : qu'est-ce l'ordonnancement ? 1.2- Définition de l’ordonnancement La fonction d'ordonnancement gère le partage du processeur entre les différents processus en attente pour s'exécuter, c'est-à-dire entre les différents processus qui sont dans l'état prêt. L'opération d'élection consiste à allouer le processeur à un processus. 1.3- Ordonnancement préemptif ou non préemptif La figure 2 reprend le graphe d'états tel qu'il a été vu à la leçon précédente concernant les processus. Une transition a été cependant ajoutée entre l'état élu et l'état prêt : c'est la transition de préemption. La préemption correspond à une opération de réquisition du processeur, c'est-à-dire que le processeur est retiré au processus élu alors que celui-ci dispose de toutes les ressources nécessaires à la poursuite de son exécution. Cette réquisition porte le nom de préemption. Système multiprocessus Etats des processus Fin Election Réveil Elu En exécution Prêt En attente du processeur Préemption (réquisition) Blocage Déblocage Bloqué En attente de ressources Fig 2 : Ordonnancement préemptif 1.3.1- Définition de la préemption. Selon si l'opération de réquisition du processeur est autorisée ou non, l'ordonnancement sera qualifié d'ordonnancement préemptif ou non préemptif : - si l'ordonnancement est non préemptif, la transition de l'état élu vers l'état prêt est interdite : un processus quitte le processeur si il a terminé son exécution ou si il se bloque. - si l'ordonnancement est préemptif, la transition de l'état élu vers l'état prêt est autorisée : un processus quitte le processeur si il a terminé son exécution , si il se bloque ou si le processeur est réquisitionné. 2- Les entités système responsables de la fonction d'ordonnancement Le système d'ordonnancement gère une file des processus (bloc de contrôle) prêts et une file des processus bloqués. La file des processus prêts est classée selon une politique d'ordonnancement qui assure que le processus en tête de file est le prochain processus à élire au regard de la politique mise en oeuvre. Ce classement est effectué par l'ordonnanceur et est réactualisé à chaque fois qu'un processus est préempté ou qu'un processus est débloqué (ajout d'un nouvel élément prêt). C'est le répartiteur qui élit le processus en tête de file, c'est-à-dire qui lui alloue un processeur libre. La figure montre le cas d'un système multiprocesseur ; le répartiteur doit alors choisir quel processeur allouer. Dans un système monoprocesseur, cette question ne se pose évidemment pas et souvent alors, ordonnanceur et répartiteur sont confondus. Une fois élu par le répartiteur, le processus s'exécute. S'il quitte le processeur, sur préemption, il réintègre la file des processus prêts ; au contraire, s'il quitte le processeur sur un blocage, il intègre la file des processus bloqués. Dans les deux cas, un nouveau processus est élu (la tête de file des processus prêts). Plus précisément, la file d'attente des processus bloqués est souvent organisée en files multinivaux, chaque niveau correspondant à une attente d'un événement/ressource particulier auquel peut être éventuellement associé une priorité. Dans le cas où des événements se produisent simultanément, cette priorité sert alors à déterminer quel événement doit être traité en premier. Système multiprocessus Ordonnanceur et répartiteur Préemption Cpu Ordonnanceur Répartiteur Prêts Classement selon une Cpu Politique d'ordonnancement Blocage Déblocage PCB PCB PCB PCB PCB PCB Cpu PCB PCB PCB PCB PCB PCB Election Bloqués Fig 3 : Ordonnanceur et répartiteur 2.1- Ordonnanceur L'ordonnanceur est un programme système dont le rôle est d'allouer le processeur à un processus prêt. 3- Les politiques d'ordonnancement 3.1- Définition : Politique d'ordonnancement La politique d'ordonnancement détermine le choix d'un processus à élire parmi tous ceux qui sont prêts. 3.2- Objectifs des politiques d'ordonnancement Les buts de l'ordonnancement diffèrent en fonction du type de systèmes. - Systèmes de traitements par lots. Le but est de maximiser le débit du processeur ; c'est-à-dire le nombre moyen de processus traités par unité de temps. - Systèmes interactifs. Les buts principaux de l'ordonnancement sont premièrement de maximiser le taux d’occupation du processeur, c’est-à-dire le rapport entre le temps où le processeur est actif et le temps total. En théorie, ce taux peut varier entre 0% et 100 % ; dans la pratique, on peut observer un taux d’occupation variant entre 40 % et 95 %. Deuxièmement, on va chercher à minimiser le temps de réponse des processus, c’est-à-dire la durée séparant l’instant de soumission du processus au système de la fin d’exécution du processus. Au mieux, le temps de réponse peut être exactement égal au temps d’exécution du processus, lorsque le processus a immédiatement été élu et s’est exécuté sans être préempté. - Systèmes temps réel. Le but principal est d'assurer le respect des contraintes de temps liées aux processus. 3.3- Les principales politiques d'ordonnancement Nous décrivons ci-dessous les politiques mises en oeuvre dans des systèmes interactifs ou à traitements par lots. Les politiques spécifiques aux systèmes temps réel sont abordées dans la leçon suivante. 3.3.1- Politique d’ordonnancement “premier arrivé, premier servi” (FIFO). C’est une politique à l’ancienneté, sans réquisition ; l’unité centrale est allouée selon l’ordre de soumission des processus. Dans cette politique, des processus de faible temps d’exécution peuvent être pénalisés parce qu’un processus de longue durée les précède dans la file. Algorithme : Premier Arrivé Premier Servi • FIFO, sans réquisition Prêt P1 24 Prêt P2 P3 3 3 Actif Bloqué Temps de cycle P3 Temps moyen d'attente P2 17 millisecondes (0 + 24 + 27)/3 P1 24 27 30 Fig 4 : Politique "Premier Arrivé, Premier Servi" 3.3.2- Politique d’ordonnancement “plus court d’abord” Cette politique tente de remédier à l’inconvénient mentionné pour la politique précédente. Maintenant, l’unité centrale est allouée au processus de plus petit temps d’exécution. Cette politique est également une politique sans réquisition. Elle a la propriété de minimiser le temps de réponse moyen pour l’ensemble des algorithmes d’ordonnancement sans réquisition. Elle pénalise les travaux longs. Elle impose également d’estimer la durée des processus ce qu’on ne connaît pas habituellement. Il existe une version avec réquisition de cette politique appelée “temps restant le plus court d’abord” : dans ce cas, le processus en exécution restitue le processeur lorsqu’un nouveau processus de temps d’exécution inférieur à son temps d’exécution restant devient prêt. 3.3.3- Politique d’ordonnancement par tourniquet (Round Robin) On définit une tranche de temps appelée quantum qui peut varier de 10 ms à 100 ms. Chaque processus présent dans la file des processus prêts acquiert le processeur à tour de rôle, et ce pour au maximum un temps égal au quantum de temps. Si le processus a terminé son exécution avant la fin du quantum, il libère le processeur et le processus suivant dans la file des processus prêts est élu. Si le processus n’a pas terminé son exécution avant la fin du quantum, il perd le processeur et est réinséré en fin de file des processus prêts. Cette politique du tourniquet est usuellement utilisée dans les systèmes en temps partagé. Sa performance dépend largement de la taille du quantum. Un quantum trop grand augmente les temps de réponse alors qu’un quantum trop petit multiplie les commutations de contexte jusqu’à les rendre non négligeables. Algorithme : tourniquet Prêt P1 20 P2 P3 7 3 Quantum = 4 Temps de cycle P3 P2 P1 4 8 11 15 18 22 26 30 Fig 6 : Politique par tourniquet 3.3.4- Politique d’ordonnancement par priorités constantes Un niveau de priorité constant est affecté à chaque processus et à un instant donné, le processus élu est toujours celui de plus forte priorité. Cet algorithme présente une version sans réquisition et une version avec réquisition. Le défaut de cette politique est le risque de famine $popbw (compl_famine) encouru par le processus de faible priorité. Une solution à ce problème est de faire “vieillir” la priorité des processus en attente, c’est-à-dire d’augmenter celle-ci en fonction du temps d’attente. La priorité des processus devient ainsi variable. $popd (compl_famine) La famine est la situation où un processus ne peut disposer d'une ressource qu'il demande (ici le processeur) Algorithme : avec priorités Prêt 3 1 3 2 prio 10 1 2 5 P1 P2 P3 Tps exec P4 P4 P3 Temps moyen d'attente P2 8,2 millisecondes P1 Fig 7 : Politique par priorités constantes 3.3.5- Politique d’ordonnancement par files de priorités constantes multiniveaux avec ou sans extinction de priorité Les politiques présentées jusqu’à présent utilisent une seule file d’attente des processus prêts. On choisit ici de définir plusieurs files de processus prêts, chaque file correspondant à un niveau de priorité ; on peut alors avoir n files de priorités différentes variant de 0 à n-1. Dans une file donnée, tous les processus ont la même priorité et sont servis soit selon une politique à l’ancienneté sans préemption, soit selon une politique de tourniquet. Le quantum peut être différent selon le niveau de priorité de la file. L’ordonnanceur sert d’abord tous les processus de la file de priorité n, puis ceux de priorité n-1 dès que la file de niveau n est vide et ainsi de suite... On peut définir deux variantes de l’algorithme, fonction de l’évolution de la priorité des processus : • les priorités des processus sont constantes tout au long de leur exécution. A ce moment-là, un processus en fin de quantum est toujours réinséré dans la même file d’attente, celle correspondant à son niveau de priorité. • les priorités des processus évoluent dynamiquement en fonction du temps de service dont a bénéficié le processus. Ainsi un processus de priorité n, à la fin du quantum de la file n, si il n’a pas terminé son exécution, n’est pas réinséré dans la file de priorité n, mais dans la file de priorité n-1. Et ainsi de suite... On cherche ici à minimiser les risques de famine pour les processus de faible priorité en faisant petit à petit baisser la priorité des processus de plus forte priorité. Algorithme : multifiles sans extinction Prêt Prêt Arrivée F1 q1 F2 q2 Priorité Arrivée F1 q1 F2 q2 Election Election F3 q3 Fn qn F3 q3 Fn FIFO -- Fig 8 : Politique par priorités multiniveaux avec ou sans extinction de priorité 3.3.6- Les politiques d'ordonnancement sous le système Linux Il existe trois politiques (classes) d’ordonnancement dans Linux qui correspondent aux politiques définies dans la norme pour les systèmes d'exploitation ouverts, POSIX.: deux sont dites temps réel, SCHED_FIFO et SCHED_RR, la troisième est classique SCHED_OTHER. On peut attribuer un processus à l’une de ces trois classes grâce à l’appel système : sched_setscheduler(). Comme dans les systèmes Unix, la politique d’ordonnancement SCHED_OTHER de Linux a plusieurs objectifs : avoir de bons temps de réponses, assurer une bonne capacité de traitement et essayer de répartir équitablement le temps CPU entre les différents processus. Elle correspond à ce qu’on appelle la politique « en temps partagé ». Chaque processus Linux a une priorité variable qui dépend de nombreux facteurs parmi lesquels nous pouvons citer le taux d’utilisation de la CPU et l’intensité des entrées / sorties. $p Il existe deux autres classes d’ordonnancement dites temps réel : SCHED_FIFO et SCHED_RR.Les rubriques « policy » et « rt_priority » stockées dans la structure des processus Linux permettent de leur attribuer une classe d’ordonnancement et une priorité temps réel. SCHED_FIFO et SCHED_RR ne sont pas des classes temps réel strictes, mais relatives. Quand un processus temps réel est prêt, tous les processus de priorité inférieure sont mis de côté. Un processus de la classe SCHED_FIFO peut s’exécuter jusqu’à la fin de son traitement ou jusqu’à ce qu’un processus de la classe SCHED_FIFO de priorité temps réel plus forte soit prêt. Un processus de la classe SCHED_RR est interrompu à la fin de son quantum de temps ou dès qu’un processus de priorité temps réel (SCHED_FIFO ou SCHED_RR) supérieure est prêt. Il existe dans cette classe un principe de tourniquet parmi les processus de même priorité. 4- Ordonnancement sous le système Unix Les opérations relatives à l'ordonnancement sont réalisées par le système Unix à chaque fois qu'un processus s'apprête à passer du mode système au mode utilisateur, donc à la suite de l'occurrence soit d'une interruption, d'un appel système ou d'une trappe. La politique d'ordonnancement du système Unix est une politique à multiples niveaux de priorité avec quantum de temps. Le noyau recalcule la priorité d'un processus quand il passe du mode noyau au mode utilisateur. Dans une première approche, ce calcul permet une extinction de priorité des processus qui s'exécutent de manière à permettre l'exécution de tous les processus et à éviter les problèmes de famine 4.1- Structures de données liées à l'ordonnancement La priorité de chaque processus est codée dans l'entrée de la table des processus qui lui correspond. La plage des priorités des processus est partitionnée en deux sous-ensembles. Chaque ensemble contient plusieurs niveaux de priorités et à chaque niveau de priorité est associé une file d'attente (fig 9) : Ordonnancement dans le système Unix Priorités du mode noyau (sleep) Non interruptible Interruptible Swapper Attente E/S disque Attente tampon Attente sortie tty Attente entrée tty Attente exit du fils Priorités du mode user (préempté) Niveau utilisateur 0 Niveau utilisateur 1 Niveau utilisateur n Fig 9 : Files de priorités pour l’ordonnancement sous Unix - les priorités utilisateurs : ce sont les processus préemptés par l'ordonnanceur au moment de leur retour en mode utilisateur. Un processus qui se réveille quitte la priorité noyau pour réintégrer les priorités utilisateur. La procédure de traitement de l'interruption horloge ajuste les priorités des processus en mode utilisateur toutes les secondes et fait entrer le noyau dans son algorithme d'ordonnancement pour éviter qu'un processus monopolise l'unité centrale - les priorités noyau : ce sont des processus endormis en attente d'un événement. La file où le processus s'endort est fonction de l'événement attendu et la priorité correspond à une "préférence" sur les réveils suite à un événement. Un processus endormi en priorité noyau demeure toujours dans la file où il s'est endormi 4.2- Algorithmes d'ordonnancement mis en oeuvre L’algorithme mis en œuvre s’appuie sur la routine d’interruption horloge qui fait régulièrement passer le processus en mode noyau. Plus précisément : - A chaque interruption horloge, le système incrémente de une unité la valeur du champ "utilisation du processeur" pour le processus élu. - Toutes les secondes c'est-à-dire toutes les 50 à 100 interruptions horloge, la valeur du champ "utilisation du processeur" pour tous les processus est divisée par deux. Puis la priorité des processus est recalculée selon la formule suivante : priorité du processus = Utilisation du processeur /2 + (priorité de base du niveau utilisateur) Du fait de ce recalcul des priorités; les processus se déplacent dans les files de priorité 4.3- Un exemple de mise en œuvre On considère trois processus A, B et C qui ont chacun une priorité initiale de 60. La priorité de base du niveau utilisateur est également la priorité 60. L'interruption horloge se déclenche 60 fois par seconde. A l'instant 0, le processus A est élu. Chaque seconde, l'interruption horloge survient et le champ "utilisation du processeur " (compte UC sur la figure 10) est incrémenté de une unité. Au bout de 60 secondes (instant t = 1), la priorité des processus est recalculée. Du fait que les processus B et C ne se sont pas encore exécutés leur priorité est inchangée (le compte UC est nul pour ces processus). La priorité du processus A par contre baisse et devient égale à 75 (60 + 30/2). C'est donc le processus B qui est à présent élu. A l'instant 2, de nouveau les priorités sont recalculées. Le compte UC du processus C étant toujours nul , la priorité de ce processus n'est pas modifiée. Les priorités du processus A et B par contre évoluent et deviennent respectivement égales à 67 pour le processus A (60 + 15/2) et à 75 pour le processus B. On voit à présent que le processus A a une priorité qui remonte du fait qu'il n'a pas pu utiliser l'unité centrale. Proc A Priorité 0 A Proc B Compte UC 60 0 1 Priorité Proc C Compte UC Priorité Compte UC 60 0 60 0 60 0 1 60 0 60 0 1 60 1 B 75 (60+30/2) 30 (60/2) 60 2 67 (60 + 15/2) C 15 (30/2) 75 30 60 3 A 4 B 63 (60 +7/2) 7 (15/2) 8 67 76 33 (60 + 33/2) (67/2) 67 63 15 7 8 75 67 30 15 67 5 Fig 10 : Exemple de mise en œuvre de l’ordonnancement sous Unix 5- Ordonnancement temps réel 5.1- Caractéristiques de l'ordonnancement temps réel Dans un système temps réel, le but principal de l'ordonnancement est de permettre le respect des contraintes temporelles associées à l'application et aux tâches. Chaque tâche possède un délai critique qui est le temps maximal dont elle dispose pour s'exécuter depuis sa date de réveil. La date butoir résultante est appelée échéance. Le dépassement d'une échéance est appelé faute temporelle. Beaucoup d'applications temps réel sont des applications embarquées et critiques et il est nécessaire de certifier l'ordonnancement réalisé, c'est-à-dire de vérifier avant le lancement de l'application (hors ligne) le respect des contraintes temporelles. Cette certification s'effectue à l'aide de tests d'acceptabilité qui prennent en compte les paramètres temporels des tâches et notamment les temps d'exécutions des tâches. Il faut donc pouvoir connaître ces temps d'exécutions et surtout pouvoir les borner. Pour cela l'exécutif doit être déterministe. Les tests d'acceptabilité sont rarement des conditions nécessaires et suffisantes. On établit donc soit des conditions nécessaires, soit des conditions suffisantes. Les tests d'acceptabilité utilisent les temps d'exécution maximum des tâches pour pouvoir certifier les exécutions. Il faut donc être capable de calculer ces temps d'exécutions maximums. Pour cela, le support d'exécution, l'exécutif temps réel, doit être déterministe. Un exécutif temps réel déterministe est un exécutif pour lequel les temps de certaines opérations système et matérielles élémentaires peuvent être bornés : temps de commutation, temps de prise en compte des interruptions, etc… 5.2- Les algorithmes d'ordonnancement temps réel 5.2.1- Classification des algorithmes d'ordonnancement temps réel L'ordonnancement peut être en ligne ou hors ligne. Un ordonnancement hors ligne établit avant le lancement de l'application une séquence fixe d'exécution des tâches à partir de tous les paramètres de celles-ci. Cette séquence est rangée dans une table et exécutée en ligne par un automate. Avec un ordonnancement en ligne, la séquence d'exécution des tâches est établie dynamiquement par l'ordonnanceur au cours de la vie de l'application en fonction des événements qui surviennent (réveils des tâches, blocage, etc…). Dans ce dernier cas, l'ordonnanceur choisit le prochaine tâche à élire en fonction d'un critère de priorité. 5.3- Modélisation de l'application temps réel pour la certification On distingue deux types de tâches pour la modélisation de l'application : - les tâches périodiques : Elles correspondent aux mesures sur le procédé ; elles se réveillent régulièrement (toutes les P unités de temps) - les tâches apériodiques : Elles correspondent aux événements ; elles se réveillent de manière aléatoire 5.3.1- Les tâches périodiques On distingue : - périodiques strictes : contraintes temporelles dures à respecter absolument - périodiques relatives : contraintes temporelles molles qui peuvent être non respectées de temps à autre (sans échéance) - périodiques à échéance sur requête (délai critique = période) Une tâche périodique Tp (r0, C, R, P) est caractérisée par les paramètres temporels suivants avec 0 <= C <= R <= P : - r0, sa date de premier réveil - P, sa période - rk, la date de réveil de la kème requête, rk = r0 + kP - C, son temps d'exécution maximum - R, son délai critique et dk, sa date d'échéance qui est telle que échéance d = rk + R - C(t) : le temps d'exécution restant à t - R(t) : le délai critique dynamique c'est-à dire le temps restant à t jusqu'à d. Une tâche périodique relative n'a pas de paramètre R défini. Une tâche périodique à échéance sur requête est une tâche pour laquelle R = P. Modèle de tâches Périodique stricte Tp (r0, C, R, P) 0£C£R£P Tp (t, C(t), R(t)) R = P, à échéance sur requête dk = r k+1 R • r0, date de premier réveil • P, période • rk, date de réveil de la kème requête rk = r0 + kP • C, temps d'exécution • R, délai critique • dk, échéance = rk + R • C(t) : temps d'exécution restant à t • R(t) : délai critique dynamique (temps restant à t jusqu'à d) kème requête C max r0 t d0 C(t) r0 + kP t R(t) P Fig 11 : Modèle de tâche périodique 5.3.2- Les tâches apériodiques On distingue : - apériodiques strictes : contraintes temporelles dures à respecter absolument - apériodiques relatives : contraintes temporelles molles qui peuvent être non respectées de temps à autre (sans échéance) Une tâche apériodique Tap (r0, C, R) est caractérisée par les paramètres temporels suivants avec 0 <= C <= R : - r, sa date de réveil - C, son temps d'exécution maximum - R, son délai critique et dk, sa date d'échéance qui est telle que échéance d = rk + R - C(t) : le temps d'exécution restant à t - R(t) : le délai critique dynamique c'est-à dire le temps restant à t jusqu'à d. Une tâche apériodique relative n'a pas de paramètre R défini. Modèle de tâches Apériodique stricte Tap (r, C, R) Tap (t, C(t), R(t)) R • r, date aléatoire de réveil • C, temps d'exécution • R, délai critique • dk, échéance = rk + R • C(t) : temps d'exécution restant à t • R(t) : délai critique dynamique (temps restant à t jusqu'à d) kème requête C max r t d r' t C(t) R(t) Fig 12 : Modèle de tâche apériodique 5.3.3- Ordonnancement en ligne préemptifs pour des tâches périodiques indépendantes. Nous ordonnançons un ensemble de tâches périodiques (configuration). Les priorités affectées aux tâches sont soit constantes (évaluées hors ligne et fixes par la suite), soit dynamiques (elles changent dans la vie de la tâche) L'ordonnancement d'un ensemble de tâches périodiques est cyclique et la séquence se répète de manière similaire sur ce que l'on appelle la période d'étude. Pour un ensemble de tâches à départ simultanée (t = 0), la période d'étude est : [0, PPCM(Pi)] 5.4- L'ordonnancement Rate Monotonic (RM) Avec cet algorithme, la priorité d’une tâche est fonction de sa période, de telle sorte que la tâche de plus petite période est la tâche la plus prioritaire. Pour un ensemble de tâches à échéance sur requête, le test d'acceptabilité d’une configuration de n tâches est (condition suffisante) donné sur la figure qui suit. La figure ci-dessous donne un exemple pour trois tâches périodiques à échéance sur requête, Tp1(r0=0, C=3, P=20), Tp2(r0=0, C=2, P=5) et Tp3(r0=0, C=2, P=10). La tâche la plus prioritaire est la tâche Tp2 et la tâche la moins prioritaire est la tâche Tp1. La séquence est décrite sur la période d’étude, soit l’intervalle [0, 20]. Les trois tâches respectent leurs contraintes temporelles. La condition suffisante est vérifiée ; on a : 3/20 + 2/5 + 2/10 = 0.75 < 0.77 Algorithmes d'ordonnancement pour les tâches périodiques indépendantes Test d ’acceptabilité Rate Monotonic n Ci ÂP i =1 £ n (21/ n - 1) i Tp1 (r0 = 0, C = 3,P = 20)Tp2 (r0 = 0, C = 2, P = 5)020020510Réveil Echéance sur requêteTp3 (r0 = 0, C = 2, P=10)15020107122172412149754ttt Fig 13 : Ordonnancement Rate Monotonic 5.5- Ordonnancement Inverse Deadline (ID) Avec cet algorithme, la priorité d’une tâche est fonction de son délai critique. La tâche la plus prioritaire est la tâche de plus petit délai critique. Cet algorithme constitue une généralisation de l'algorithme Rate Monotonic à des tâches quelconques. Une condition suffisante d’acceptabilité de tâches est donné sur la figure qui suit : La figure ci-dessous donne un exemple pour trois tâches périodiques Tp1(r0=0, C=3, R=7, P=20), Tp2(r0=0, C=2, R=4, P=5) et Tp3(r0=0, C=2, R=9, P=10). La tâche la plus prioritaire est la tâche Tp2 et la tâche la moins prioritaire est la tâche Tp3. La condition suffisante n’est pas vérifiée ; en effet on a : 3/7 + 2/4 + 2/9 = 1.14 > 1. Mais le chronogramme construit sur la période d’étude de la configuration prouve que l’ordonnancement des trois tâches s’effectue sans faute temporelle. Algorithmes d'ordonnancement pour les tâches périodiques indépendantes Test d ’acceptabilité Inverse Deadline n i =1 Tp1(r0 = 0, C = 3, R = 7, P = 20)Tp2(r0 = 0, C = 2, R = 4, P = 5)020020510Réveil Échéance Ci ÂR Tp3(r0 = £ n (21/ n - 1) i 0, C = 2, R = 9, P = 10)150201071221741214975291919714ttt Fig 14 : Ordonnancement Inverse Deadline 5.6- Ordonnancement Earliest Deadline (EDF) La priorité maximale à l’instant t est accordée à la tâche dont l’échéance est la plus proche. La priorité des tâches est maintenant dynamique. La figure ci-dessous donne un exemple pour trois tâches périodiques Tp1(r0=0, C=3, R=7, P=20), Tp2(r0=1, C=2, R=4, P=5) et T p3(r0=0, C=1, R=8, P=10). À l’instant t=0, les trois tâches sont réveillées et la tâche pour laquelle l’échéance est la plus proche est la tâche Tp2, qui donc s’exécute. À l’instant t=2, la tâche Tp2 a terminé son exécution et c’est maintenant la tâche Tp1 qui est la plus prioritaire. À l’instant t=5, la tâche Tp1 se termine et la tâche Tp2 se réveille de nouveau. Mais, c’est maintenant la tâche Tp3 pour laquelle la date d’échéance est à t=8 qui est devenue la plus prioritaire. C’est donc elle qui s’exécute. On voit donc que contrairement à ce qui se passe avec les algorithmes à priorité fixe où les priorités des tâches sont calculées une fois pour toutes à l’initialisation du système, ici les priorités des tâches évoluent les unes par rapport aux autres en fonction de leur urgence. Ainsi, à l’instant t=0, la tâche Tp2 est plus prioritaire que la tâche Tp3, mais le rapport d’urgence est inversé à l’instant t=5. Algorithmes d'ordonnancement pour les tâches périodiques indépendantes Earliest Deadline Tp1 (r0 = 0, C = 3, R = 7, P = 20)Tp2 (r0 = 0, C = 2, R = 4, P = 5)020020510Réveil Echéance Tp3 (r0 = 0, C = 1, R = 8, P = 10)15020108122174121497528181961356ttt Fig 15 : Ordonnancement Earliest Deadline 6- Une application temps réel sous RT-Linux Le système RT-Linux est une extension du système Linux classique vers le temps réel. Il est constitué par un noyau temps réel Rt-Linux qui partage le processeur avec le noyau de base Linux et exécute des tâches temps réel. 6.1- Les tâches RT-Linux. 6.1.1- Mise en œuvre. Les tâches RT-Linux s’exécutent dans l’espace adresse du noyau Linux au même titre que l’ordonnanceur temps réel. Il existe dans les versions récentes de Linux la possibilité de charger dynamiquement des modules dans l’espace adresse du noyau et de les « lier » au code du noyau. On parle de « modules chargeables ». En effet, pour limiter la taille du noyau Linux et libérer ainsi plus de place mémoire pour l’utilisateur, on évite de compiler le noyau avec des composants qui ne sont pas nécessaires à tout moment. Ces composants sont par exemple des gestionnaires de périphériques. Lorsqu’on a besoin d’ajouter ou de supprimer l’un de ces composants, on n’a plus besoin de recompiler tout le noyau comme cela était le cas auparavant: le composant est chargé ou déchargé dynamiquement à l’aide d’un module. Il faut simplement configurer le noyau avant sa compilation pour qu’il accepte de gérer les modules chargeables: Ainsi, les tâches RT-Linux sont créées à l’aide d’un « module chargeable ». Il y a plusieurs avantages à mettre les tâches dans l’espace adresse du noyau: - Elles partagent le même espace adresse. Comme elles sont dans l’espace adresse du noyau, on élimine la charge occasionnée par tout changement de niveau de protection. Un avantage plus pratique que performant. Dans l’espace adresse du noyau, il est permis de faire référence aux objets et aux fonctions par leur nom plutôt que par leur descripteur. C’est l’édition de lien dynamique des « modules chargeables » qui résoudra les symboles en adresse. En conclusion cette solution, qui consiste à mettre les tâches temps réel dans l’espace adresse du noyau permet d’optimiser les performances de RT-Linux. En contrepartie, cette solution comporte un risque : Un « Bug » dans une tâche temps réel peut mettre en danger tout le système. Ce risque est renforcé avec l’utilisation du langage C, notamment les pointeurs. La seule garantie à ce niveau est la rigueur du programmeur. Le mode opératoire consiste en général à créer toutes les tâches lors du chargement du module (fonction init_module()) et à les supprimer uniquement au déchargement (fonction cleanup_module). La structure « rt_task_struct » (ou RT_TASK par « typedef struct rt_task_struct RT_TASK ;») des tâches temps réel dans RT-Linux est la suivante : - int *stack ; /* hardcoded */ int uses_fp ; /* this one is too*/ int magic ; int state; Etat de la tâche : RT_TASK_READY,… int *stack_bottom ; Pointeur de pile. int priority ; Politique d’ordonnancement basée sur la priorité seule. RTIME period; P : Période si la tâche est périodique. RTIME resume_time; r : Prochaine heure de réveil . struct rt_task_struct *next; Chaînage simple de l’ensemble des tâches créées dans l’application. RTL_FPU_CONTEXT fpu_regs ; Les fonctions de gestion des tâches temps réel dans RT-Linux sont listées ci-dessous. Les états évoqués sont présentés dans les figures qui suivent. - int rt_task_init (RT_TASK *task, void (fn)(int data), int data, int stack_size, int priority) : Création d’une tâche RT_Linux pointée par « task ». Il y a réservation d’espace mémoire dans le noyau (kmalloc) à hauteur de stack_size. « fn » est le code exécuté par la tâche. « data » est le paramètre passé à « fn » au démarrage. « priority » est la priorité de la tâche dont la valeur peut aller de 1, la plus forte priorité, à « RT_LOWEST_PRIORITY » (1000000), la plus faible. - int rt_task_delete (RT_TASK *task) : Suppression logique d’une tâche : Pas de libération de l’espace mémoire « kfree » à ce niveau. Son état passe à RT_TASK_ZOMBIE. - int rtl_delete_zombies (void) : Suppression réelle de l’ensemble des tâches logiquement supprimées par « rt_task_delete ». « kfree » est effectué ici. - - - int rt_task_make_periodic (RT_TASK *task, RTIME start_time, RTIME period) : Rend la tâche « *task » périodique avec une période « period » à partir de la première date de réveil « start_time ». int rt_task_wait (void) : Suspension de la tâche périodique jusqu’à sa prochaine date de réveil. Elle met la tâche qui l’a appelée dans l’état RT_TASK_DELAYED. En général toute tâche périodique indique par cette fonction qu’elle a terminé son traitement : ce doit donc être la dernière instruction du code de la tâche. Les tâches apériodiques ne s’en servent pas car l’état RT_TASK_DELAYED ne leur correspond pas. int rt_task_wakeup (RT_TASK *task) : Déclenche la tâche « task », c’est-à-dire la met dans l’état prêt RT_TASK_READY. int rt_task_suspend (RT_TASK *task) : Inactive la tâche « task », c’est-à-dire la met dans l’état RT_TASK_DORMANT. En général, cette routine est utilisée par les tâches apériodiques à la place de rt_task_wait pour signaler la fin de leur traitement. Une tâche périodique ou apériodique peut être suspendue par une autre tâche ou par l’ordonnanceur. Les figures jointes donnent les graphes d'état des tâches périodiques et apériodiques pour RtLinux 6.1.2- Format d'une application. La déclaration des tâches périodiques et apériodiques dans RT-Linux se fait à l’aide d’un module noyau dont nous présentons le squelette ci-dessous : On suppose que ce module s’appelle RT_PROCESS.C et qu’il va créer deux tâches : T1 et T2. T1 est une tâche périodique et T2 apériodique. Le module s’installe dans le noyau par : insmod RT_PROCESS.O On le décharge du noyau par : rmmod RT_PROCESS $tfig #define MODULE #include <linux/module.h> #include <linux/kernel.h> #include <linux/version.h> #include <linux/errno.h> #include <rtl_sched.h > #include <linux/arch/i386/kernel/irq.h> #define NTASKS 2 RT_TASK tasks[NTASKS] $e Fonction 1 /*-- Fonction 1 = traitement de la tâche périodique 1 --*/ Code de la fonction exécutée par la tâche périodique 1; l’affectation de cette fonction à la tâche 1 est faite par rt_task_init (Cf. plus bas). Toute tâche périodique doit terminer son traitement par l’appel à la fonction rt_task_wait(). Cette routine met la tâche dans l’état RT_TASK_DELAYED, réinitialise la prochaine date de réveil et fait appel à l’ordonnanceur rtl_schedule(). $tfig void f_tache_1(int par1) { rt_task_wait() ; } Fonction 2 /*-- Fonction 2 = traitement de la tâche apériodique 2 --*/ Code de la fonction exécutée par la tâche apériodique 2. Toute tâche apériodique doit terminer son traitement par l’appel à la fonction rt_task_suspend() sur elle-même. Cette routine met la tâche dans l’état RT_TASK_DORMANT et fait appel à l’ordonnanceur rtl_schedule(). void f_tache_2(int par2) { rt_task_suspend(&(tasks[2])) ; } Interruption Tache 2 /*-- Gestionnaire de l’interruption associée à la tâche 2 --*/ La tâche apériodique 2 est associée à un niveau d’interruption dont l’apparition active le gestionnaire T2_Handler(). L’association entre le numéro d’interruption et le gestionnaire sera vue plus bas. Ce gestionnaire a au moins pour rôle de réveiller la tâche apériodique concernée. Cette fonction met la tâche dans l’état RT_TASK_READY et appelle l’ordonnanceur. int T2_handler() { rt_task_wakeup(&(tasks[2])) ; } /*-- Ordres lancés à l’installation du module: RT_PROCESS --*/ Lorsqu’un module est chargé sous Linux (>insmmod RT_Process.o), il commence par effectuer la fonction init_module(). Cette fonction doit donc figurer dans tous les modules chargeables. Dans RT-Linux, on lui attribue le rôle de l’initialisation des tâches temps réel (rt_task_init), de l’initialisation des périodes et des dates de réveil pour les tâches périodiques (rt_task_make_periodic) et de l’association des niveaux d’interruptions aux gestionnaires associés (request_RTirq). int init_module(void) { rt_task_init(&(tasks[0]), f_tache_1, 0, 3000, 4) ; rt_task_init(&(tasks[1]), f_tache_2, 1, 3000, 5) ; rt_task_make_periodic(&(tasks[0]), 5, 10) ; request_RTirq(2, &T2_handler) ; return 0 ; } /*-- Ordres lancés à la suppression du module: RT_PROCESS --*/ void cleanup_module(void) Lorsqu’un module est déchargé sous Linux, il exécute systématiquement la fonction cleanup_module(). Cette fonction doit donc figurer dans tous les modules chargeables. Dans RT-Linux, on lui attribue le rôle de suppression des tâches temps réel (rt_task_delete) ainsi que celui de la libération des interruptions (free_Rtirq). { rt_task_delete(&(tasks[0])) ; rt_task_delete(&(tasks[1])) ; free_Rtirq(2) ; } 6.1.3- L’ordonnancement. Dans la version 1.2 de RT-Linux, présentée sur le site officiel du système, trois ordonnanceurs préemptifs ont déjà été installés : - Un ordonnanceur « à priorité fixe » : il s’appuie sur le paramètre « priority » défini au niveau de chacune des tâches. Lorsque plusieurs tâches sont prêtes, celle qui a la priorité la plus forte (la valeur la plus petite) est élue. Une tâche qui se réveille (RT_TASK_READY) pourra préempter la tâche active si sa priorité est plus forte. - Un ordonnanceur « Rate-Monotonic » : nous ne nous y intéresserons pas, compte-tenu des limites de cette politique vis-à-vis des tâches apériodiques. - Un ordonnanceur « EDF » (« Earliest Deadline First ») : Il utilise l’urgence comme critère de sélection de la prochaine tâche à élire.