next up previous contents
suivant: Développement d'un système d'exploitation monter: Table des matières précédent: Architecture du palm   Table des matières

Sous-sections

Concepts des systèmes d'exploitation temps-réel

Un système d'exploitation est un programme qui agit comme un intermédiaire entre l'utilisateur et la machine. Le but d'un système d'exploitation est de fournir un environnement dans lequel un utilisateur peut exécuter des programmes. Le système d'exploitation doit coordonner l'exécution simultanée de plusieurs tâches utilisateurs. Il a donc deux grandes fonctions :

Un système temps-réel est utilisé quand il y a des exigences temporelles fixe sur les opérations d'un processeur ou sur le flux de données. Un système temps-réel possède des contraintes de temps fixes et bien définies. Le traitement doit être effectué dans la contrainte de temps sinon le système échoue. Le système de gestion des airbags dans une voiture possède des contraintes temps-réel très importantes. Quand un capteur détecte une déformation de la carrosserie suffisamment importante, il envoie un signal au contrôleur qui doit avoir gonflé les airbags dans les 10 ms sous peine d'arriver trop tard, ce qui aurait des conséquences désastreuses pour les occupants de la voiture. Même si un capteur tombe en panne et qu'il n'envoie plus d'informations, le système doit continuer à fonctionner et fournir les meilleures réponses possibles.

Citons quelques concepts cléfs concernant les systèmes d'exploitation temps-réel. Les chapitres suivants seront consacrés à l'analyse et au développement du système d'exploitation temps-réel appelé Expresso.

Section critique et exclusion mutuelle

Une section critique est une partie de code qui doit être traitée indivisiblement. Dès qu'une section critique commence sont exécution, elle ne peut pas être interrompue. Pour assurer cela, les interruptions sont désactivées avant de rentrer dans le code de la section critique et réactivées quand le code est terminé.

Une ressource partagée est une ressource (LCD, port série, variable partagée, etc) qui peut être utilisée par plus d'une tâche. Chaque tâche doit gagner l'accès exclusif à la ressource partagée pour éviter la corruption des données, on appele ce mécanisme l'exclusion mutuelle qui est facilement implémentée grâce au sémaphore.

Tâche

Une tâche est un simple programme qui s'exécute comme s'il était le seul à utiliser le CPU. Le processus de conception pour des applications temps-réel implique de diviser le travail à effectuer entre différentes tâches qui sont chacune responsable d'une partie du problème. Chaque tâche possède une priorité, du code à exécuter, une partie de la mémoire et une zone de pile (stack).

Une tâche est typiquement une boucle infinie qui peut être dans un des états suivants :

Dormante :
la tâche est toujours en mémoire mais n'est pas considérée par les opérations d'ordonnancement.
Exécutable :
la tâche est prête à être exécutée mais n'a pas à ce moment l'usage du CPU.
Active :
la tâche est celle dont les instructions sont actuellement exécutées par le CPU.
Bloquée :
la tâche attend un signal ou une ressource afin de poursuivre son exécution.
Interrompue :
la tâche possédait l'usage du processeur lorsqu'elle a été suspendue par une interruption.

Figure: Etats d'une tâche
\begin{figure}\begin{center}
\epsfig{file=rtos/task_state, height=4cm}
\end{center}\end{figure}

Multi-tâches

Un système d'exploitation permet l'exécution simultanée de plusieurs tâches. Mais la machine ne dispose que d'un CPU unique qui exécute des instructions séquentiellement. Pour exécuter plusieurs tâches simultanément, on utilise le principe de l'entrelacement. On entrelace l'exécution des différentes tâches. Pour cela, il faut effectuer un ordonnancement des différentes tâches et changer l'attribution du CPU entre celles-ci.

L'ordonnanceur

L'ordonnanceur (scheduler) est le composant du noyau responsable de déterminer quelle tâche s'éxécute à un moment donné. Il gère l'état des tâches et répartit l'usage du processeur entre celles-ci. Un ordonnanceur temps-réel est basé sur la priorité, Expresso donne une priorité unique à chaque tâche.

Expresso possède un ordonnanceur préemptif. Expliquons rapidement ce concept. Quand une tâche $T_{2}$ plus prioritaire que la tâche active $T_{1}$ passe de l'état bloqué à l'état éxécutable, la tâche $T_{1}$ est suspendue (état exécutable) et l'ordonnanceur attribue le processeur à $T_{2}$. A tout moment, c'est la tâche de plus haute priorité exécutable qui reçoit le contrôle du processeur. Avec ce type d'ordonnanceur, le temps de réponse des tâches est optimum et déterministe.

Figure: Ordonnanceur préemptif
\begin{figure}\begin{center}
\epsfig{file=rtos/preempt, height=5cm}
\end{center}\end{figure}

Changement de contexte

Quand l'ordonnanceur doit transférer l'usage du processeur d'une tâche à une autre, il opère un changement de contexte. Le contexte représente l'état du processeur à un moment donné.
Registre
La tâche suspendue devra pouvoir continuer son exécution sans être affectée. La première opération à effectuer est la sauvegarde de l'état du processeur au moment de la suspension. Le noyau possède pour chaque tâche non dormante un espace mémoire réservé à cet effet.
Pile
Les données temporaires utilisées par la tâche suspendue doivent être préservées lors des opérations de la nouvelle tâches active. Ces données sont organisées sous la forme d'une pile contenant :

Une tâche peut être suspendue à tout moment et l'usage du processeur transféré vers une autre tâche susceptible d'appeler des sous-routines et d'allouer des variables temporaires. Chaque tâche possède sa propre pile.

En général, les pointeurs des sommets et de base de pile sont des registres du processeur. Ils sont donc sauvegardés avec le reste de l'état de celui-ci lors d'un changement de contexte. Le noyau possède également sa propre pile.

Le changement de contexte ajoute de l'overhead à l'application. Plus le processeur possède de registres, plus il y a d'overhead. Le temps requit par un changement de contexte est déterminé par le nombre de registres à sauver et à restaurer par le CPU.

Priorité des tâches

Chaque tâche possède une priorité, plus la tâche est importante plus sa priorité doit être élevée.
Priorité statique :
la priorité de chaque tâche ne change pas durant l'exécution. Chaque tâche reçoit une priorité fixe lors de sa création. Toutes les tâches du système et leurs contraintes temporelles sont connues à la compilation.
Priorité dynamique :
la priorité de chaque tâche peut changer durant l'exécution, chaque tâche peut changer sa priorité durant l'exécution. Cette caractéristique des noyaux temps-réel permet d'éviter l'inversion de priorité. Expresso permet ce changement de priorité.

L'inversion de priorité
est un problème qui survient lorsqu'une tâche est suspendue dans l'attente d'une ressource contrôlée par une tâche moins prioritaire.

Soit 3 tâches $T_{1}$, $T_{2}$, $T_{3}$ avec des priorités respectives $P_{1}$, $P_{2}$, $P_{3}$ : $P_{1}\ >\ P_{2}\ >\ P_{3}$. $T_{1}$ et $T_{2}$ attendent qu'un événement survienne dans $T_{3}$ (par exemple la libération d'un sémaophore). Dans ce cas, la priorité effective de $T_{1}$ est réduite à celle de $T_{3}$ car elle est en attente d'une ressource détenue par $T_{3}$.

Solution :

Il suffit d'élever temporairement la priorié de $T_{3}$ (en la rendant égale à celle de $T_{1}$) pendant le laps de temps qu'elle utilise la ressource et ensuite de restaurer sa priorité normale. Certains systèmes d'exploitation fournissent un mécanisme d'héritage de priorité qui change automatiquement la priorité d'une tâche pour éviter les problèmes d'inversion de priorité.

Communication entre tâches

Le transfert correct de données d'une tâche à une autre soulève des problèmes plus importants que dans le cas d'un programme unique communiquant avec des routines d'interruption.

On utilise des services fournis par le noyau destinés à

Sémaphore

Un sémaphore $s$ est un objet possédant une valeur entière. Sa valeur est positive ou nulle et est uniquement manipulable à l'aide de deux opération wait(s) et signal(s) :

Notes :

Les files de communication

Une file d'attente est un objet permettant la communication synchrone ou asynchrone de valeurs entre des tâches. Caractéristiques :

Sous Expresso :


next up previous contents
suivant: Développement d'un système d'exploitation monter: Table des matières précédent: Architecture du palm   Table des matières
Fabian Skivee 2002-06-04