Le cadre exécutif : des tâches, des signaux, des processus et des messages
Un exécutif NJN peut être observé selon deux niveaux. Le premier est relatif à la l’architecture algorithmique de l’exécutif et le second concerne la manière dont cette architecture est distribuée.
Du point de vue de l’exécutif, une application NJN est composée de tâches et de signaux (figure 17). Les tâches renferment le code actif de l’application tandis que les signaux assurent la communication entre les tâches. D’un point de vue fonctionnel, les tâches lisent l’état des signaux et y inscrivent de nouvelles valeurs. Chaque tâche est contrôlée par une liste de sensibilité qui énonce ses conditions d’activation. Il s’agit en pratique de la liste des signaux que la tâche doit surveiller. Si un évènement se produit sur l’un des signaux de cette liste, la tâche est programmée pour être exécutée.
Lorsqu’une tâche est activée, un évènement d’exécution est créé qui associe la tâche et la date à laquelle elle doit être exécutée. Lorsque son exécution a lieu, chaque opération effectuée sur un signal donne lieu à un évènement d’écriture qui associe la nouvelle valeur affectée au signal, l’évènement d’exécution qui est à l’origine de l’opération et la date à laquelle elle a été effectuée. Les relations causales entre évènements sont représentées temporellement par des cycles Δ. Une tâche exécutée à une date t consulte l’état des signaux du système à cette même date et produit des évènements sur d’autres signaux à une date t+Δ. Il n’est donc pas possible pour une tâche d’observer les modifications qu’elle produit sur l’application.
Les tâches de l’application sont distribuées sur différents processus logiques (figure 17). Un processus logique héberge en général plusieurs tâches. Les évènements qui doivent être transférés entre deux tâches situées sur des processus logiques différents sont acheminés sous la forme de messages.
L’ordonnancement des processus logiques et l’acheminement des messages sont pris en charge par le système d’exploitation hôte. NJN se charge de gérer les évènements et l’ordonnancement des tâches au sein même des processus logiques.
Deux types de tâches : les tâches temps-virtuel et les tâches temps-réel
Le modèle d’exécution d’NJN s’appuie sur deux types de tâches (figure 17) : les premières sont dites « temps-virtuel » et sont d’ordinaire des tâches de traitement tandis que les secondes sont qualifiées de « temps-réel » et sont en principe chargées des entrées/sorties avec le monde extérieur.
Les tâches temps-virtuel correspondent à celles que l’on va trouver dans un simulateur. Elles sont déclenchées par les évènements qui se produisent sur les signaux de leur liste de sensibilité. La date qui est associée à leur exécution est sans rapport avec l’horloge murale. Elle correspond à la date de l’évènement qui les a activées. Un ordonnanceur spécifique est chargé d’établir l’ordre dans lequel doivent être exécutées ces tâches, indépendamment de toute considération temps-réel. En cas de nécessité, l’exécution de ces tâches peut être remise en cause ce qui donne lieu à un retour arrière.
Les tâches temps-réel assurent la synchronisation temporelle du système dans son ensemble. Contrairement aux tâches temps-virtuel, elles ne sont pas exécutées à n’importe quel moment. C’est lorsque l’horloge murale arrive en concordance avec leur date d’exécution que l’ordonnanceur leur donne la main. Leur exécution étant liée à une interaction avec l’extérieur, il est impossible de remettre en cause leur exécution. Les retours arrière ne les concernent donc pas.
Comme pour leur homologue virtuel, la date d’exécution qui leur est associée est déterminée à partir de la date de l’évènement qui est à l’origine de leur activation. En revanche, un retard appelé latence est systématiquement introduit entre l’activation et l’exécution. Ce paramètre est caractéristique de chaque tâche et peut être ajusté en cours d’exécution. Concrètement, si un évènement d’écriture se produit à la date t sur l’un des signaux de la liste de sensibilité associée à la tâche, celle-ci sera exécutée à la date t+L, L étant la latence associée à cette tâche.
Une tâche temps-réel observe les signaux de l’application tels qu’ils étaient au moment de son déclenchement et non tels qu’ils sont au moment de l’exécution. Elle voit donc le système avec un retard correspondant à la valeur de sa latence. Elle modifie l’état des signaux sur lesquels elle écrit un cycle Δ après la date de son exécution, de la même manière que son homologue virtuel. Ainsi, une durée correspondant à la latence sépare ses lectures de ses écritures.
Rôle de la latence
La latence associée aux tâches temps-réel donne à NJN la marge temporelle dont il a besoin pour fonctionner correctement, tout en permettant de conserver la cohérence temporelle des données durant toute la chaine de traitement.
Examinons la chaine de traitement de la figure 19. Elle est constituée de quatre tâches T1, T2, T3 et T4. La première, T1, et la dernière, T4, sont des tâches temps-réel. La latence de T4 est notée L. La latence de T1 n’a ici que peu d’importance. La tâche T1 produit des évènements qui sont traités successivement par T2, T3 et enfin T4.
La figure 19 montre l’enchainement de deux traitements successifs selon un axe temporel virtuel, c’est-à-dire un axe temporel relatant les dates associées aux différents évènements. On voit que les données consommées et produites par toutes les tâche de la chaine ont, au Δ près, la même date.
La figure 20 montre une exécution possible du même enchainement de traitements mais cette fois selon un axe temps-réel. Les évènements sont positionnés selon la date effective à laquelle ils sont traités. Par ailleurs, la durée de traitement est également représentée par la hauteur du symbole qui figure l’évènement. On voit que la position des évènements d’exécution associés à des tâches temps-réel n’a pas bougé. Les évènements des tâches temps-virtuel se sont positionnés au plus tôt sur la ligne temporelle et dans l’ordre induit par les relations causales naturelles.
Ce petit exemple montre le rôle fondamental joué par la latence des tâches temps-réel. En provoquant un retard d’exécution des tâches temps-réel de sortie vis-à-vis de l’origine temporelle de la chaine de traitement, la latence donne au système la marge temporelle nécessaire pour exécuter la chaine dans son ensemble.
Toutes les tâches qui entrent en jeu dans la chaine doivent être exécutées dans cet intervalle de temps.
Dans un contexte distribué, la latence des tâches temps-réel doit être fixée de manière à ce que tous les éléments de la chaine puissent être traités, c’est-à-dire l’exécution de chaque tâche mais aussi les échanges de message ainsi que les retours arrière éventuels (figure 21). Une latence trop faible conduit au non-respect de la contrainte temps-réel fixée. C’est donc le paramètre le plus déterminant du système.
On a montré comment se synchronisait NJN par l’intermédiaire des tâches temps-réel. Rappelons que les contraintes temps-réel sont d’autant plus faciles à respecter que le système évite les retours arrière. Voyons comment la politique d’ordonnancement des tâches au sein d’un processus logique parvient à restreindre l’usage des retours arrière.
Politique d’ordonnancement
Le rôle des tâches temps-réel est capital dans la synchronisation d’NJN, nous venons de le voir. L’ordonnancement doit donc garantir le fonctionnement correct de ces tâches.
Comment trop d’avance peut compromettre le respect des contraintes temps-réel
La politique adoptée vise deux objectifs : premièrement, donner une priorité absolue à l’exécution des tâches temps-réel arrivées à échéance ; deuxièmement, réserver les retours arrière aux situations exceptionnelles puisqu’on sait qu’ils compromettent le respect des contraintes temps-réel.
Le premier point n’est pas difficile à résoudre. Chaque processus logique gère deux files d’évènements d’exécution : la première concerne les évènements relatifs aux tâches temps-réel ; la seconde se charge des évènements relatifs aux tâches temps-virtuel. Les évènements de la première file sont prioritaires dès qu’ils arrivent à échéance de manière à garantir l’aspect temps-réel de leur exécution.
Le second point est plus délicat et nécessite de bien analyser les mécanismes qui sont à l’origine du retour arrière ainsi que ses conséquences sur le comportement temporel du système. La figure 22 montre un exemple d’application où le phénomène peut apparaitre.
L’application comporte cinq tâches, nommées T1 à T5, distribuées sur deux processus logiques, nommés LP1 et LP2. La tâche T1 est hébergée par le processus LP1, toutes les autres tâches sont hébergées par le processus LP2. Les tâches T1 et T2 sont des tâches temps-réel d’entrée. La tâche T5 est une tâche temps-réel de sortie et comporte une latence L.
