Systèmes Temps Réel
Systèmes Temps Réel
Systèmes Temps Réel
Francis Cottet
Emmanuel Grolleau
SYSTÈMES
TEMPS RÉEL
DECONTRÔLE-
COMMANDE
Conception
et implémentation
Francis Cottet
Emmanuel Grolleau
Conception et implémentation
DU MÊME AUTEUR : FRANCIS COTTET
Avant-Propos V
III
6 • Programmation des systèmes multitâches 245
6.1 Programmation C, Ada et LabVIEW 245
6.2 Programmation multitâche en langage C 285
6.3 Programmation multitâche en langage Ada 314
6.4 Programmation multitâche en langage LabVIEW 331
Annexes
A • Représentation de l’information 513
Bibliographie 539
Sigles 546
Index 551
IV
AVANT-PROPOS
V
entre les développements de l’application informatique gérant un four à micro-
onde et celle pilotant une navette spatiale, la distance est immense : criticité de
l’application, taille du logiciel, évolution de l’application… Le seul lien en commun
est que toutes ces applications mettent en relation un programme informatique
avec un procédé externe.
D’autre part, le développement des applications de petite taille ou de taille moyenne
a souvent été conduit par des professionnels du monde de l’automatique ou de
l’électronique. Les régulations de type électromécanique ou électronique analogique
ont rapidement fait place à des systèmes purement numériques ; seuls les capteurs
et les actionneurs, faisant le lien vers le monde réel, sont toujours analogiques. Les
concepteurs de ces applications ont des méthodes basées sur l’aspect fonctionnel :
schémas blocs, grafcets, etc. En effet, la spécification et la conception de telles
applications sont plus aisées lorsqu’elles sont pensées en termes de fonctions ou de
traitements des données. Aussi les langages dédiés à ce type d’applications sont
tout naturellement des langages fonctionnels à exécution séquentielle comme le
langage C, les assembleurs ou le langage flot de données LabVIEW.
Dans le domaine informatique, en parallèle, ces applications ont conduit à la mise
en place de méthodes de spécification ou de conception répondant à ces besoins,
c’est-à-dire des méthodes basées sur la description de l’aspect fonctionnel, soit
l’analyse structurée (SA), l’analyse structurée temps réel (SA-RT), la conception
structurée (SD), etc. Ces méthodes correspondent parfaitement à ces besoins à
condition que les applications ne deviennent pas de grande taille et n’impliquent
des nombreuses données complexes (données structurées). D’autre part, il est impor-
tant de noter que ces méthodes ont une dérivation très directe vers une implémen-
tation multitâche qui permet de répondre à la fois à la logique du parallélisme du
monde réel en termes de conception et aux contraintes temporelles exigées.
En ce qui concerne les applications informatiques dites classiques (applications
bureautiques, bases de données…), des méthodes basées sur les données ont été
élaborées afin de répondre à un besoin fort de modélisation des données et de leurs
évolutions. Ces méthodes, dites orientées objets, permettent une spécification et une
conception des applications en pensant avant tout en termes de données et d’actions
sur ces données, mais en négligeant dans les premières étapes l’aspect fonctionnel,
c’est-à-dire l’enchaînement, le séquencement ou l’exécution des différentes parties
de l’application. Ces méthodes orientées objets, unifiées sous le nom UML (Unified
Modeling Language), sont devenues incontournables pour le développement des
applications informatiques.
Actuellement, ces méthodes orientées objets sont de plus en plus utilisées dans le
domaine des applications de contrôle-commande, domaine non concerné initia-
lement par ce type de modélisation objet. Il est concevable et acceptable de suivre une
telle démarche pour des applications informatiques, même de contrôle-commande,
de grande taille possédant souvent des données conséquentes de type complexe.
Mais il ne semble pas naturel de vouloir imposer ce type d’outils pour des applica-
tions de contrôle de procédés de petite ou moyenne taille ayant des données en
nombre réduit et de type simple. D’autant plus que, comme nous l’avons noté pré-
cédemment, les utilisateurs de ces outils ont une culture de conception de type fonc-
tionnel qui a une très grande efficacité. Ensuite les méthodes orientées objets vont
VI
conduire à des difficultés pour passer à l’étape de l’implémentation multitâche ;
cette rupture de la chaîne de développement diminue fortement l’intérêt de ces
méthodes de spécification et de conception.
Cet ouvrage a donc pris le parti de présenter une méthodologie complète de dévelop-
pement d’applications de contrôle-commande basé sur un aspect fonctionnel condui-
sant naturellement vers une implémentation multitâche. Sans rejeter les méthodes
orientées objets largement répandues aujourd’hui, il semble contraire aux règles
du génie logiciel d’utiliser une méthodologie non cohérente avec le domaine des
applications ciblées, non cohérente à la fois en termes de logique d’analyse de bout
en bout et d’objectifs applicatifs. Notre méthodologie, basée sur une approche
fonctionnelle au niveau de l’analyse et une approche multitâche au niveau de
la conception, s’adapte parfaitement aux applications de contrôle-commande
de petite taille ou de taille moyenne mettant en jeu des données simples.
Le premier chapitre présente l’environnement de développement des systèmes de
contrôle-commande en décrivant la spécificité de ces applications en termes d’archi-
tectures logicielles et matérielles. Le second chapitre traite de la méthode de spéci-
fication fonctionnelle choisie SA-RT (Structured Analysis for Real Time systems). La
méthode de conception DARTS (Design Approach for Real-Time Systems) qui est la
suite logique de SA-RT est décrite dans le chapitre 3. Les environnements matériels
et logiciels (exécutifs temps réel) très particuliers de ces applications sont présentés
dans les chapitres 4 et 5 afin de mieux comprendre la partie implémentation de ces
systèmes de contrôle-commande. Le chapitre 6 est dédié à l’implémentation des
applications de contrôle-commande en déclinant trois environnements : noyau temps
réel et langage de type langage C, langage Ada et enfin un environnement spécifique
basé sur LabVIEW. Les précédents chapitres sont illustrés par des exemples simples
mais réalistes ; en revanche, le chapitre 7 propose le développement complet d’une
application réelle industrielle. Enfin, le chapitre 8 ouvre le développement de ces
applications vers des aspects avancés concernant l’ordonnancement.
VII
1 • DÉVELOPPEMENT DES SYSTÈMES
DE CONTRÔLE-COMMANDE
1.1 Introduction
1.1.1 Définitions
Nous pouvons définir un système de contrôle-commande comme un système infor-
matique en relation avec l’environnement physique réel externe par l’intermédiaire
de capteurs et/ou d’actionneurs, contrairement aux systèmes d’informatiques scien-
tifiques (gestion de base de données, CAO, bureautique…) qui ont des entrées
constituées de données fournies par des fichiers ou éventuellement un opérateur.
Les grandeurs physiques acquises permettent au système de contrôle-commande
de piloter un procédé physique quelconque. Donnons ainsi une définition générale
d’un système de contrôle-commande (figure 1.1) :
« Un système de contrôle-commande reçoit des informations sur l’état du
procédé externe, traite ces données et, en fonction du résultat, évalue une
décision qui agit sur cet environnement extérieur afin d’assurer un état
stable ».
Cette notion d’état stable peut être différente selon les applications ou procédés.
Il dépend du cahier des charges de l’application (maintien d’une température de
consigne, régime moteur, qualité de service…). Deux caractéristiques font qu’un
système de contrôle-commande ne possède pas les propriétés classiques des systèmes
d’informatiques scientifiques :
– indépendance du résultat produit par rapport à la vitesse d’exécution. Le résultat
d’un calcul effectué à partir de données d’entrée similaires est indépendant de la
© Dunod – La photocopie non autorisée est un délit.
1
1 • Développement des 1.1 Introduction
systèmes de contrôle-commande
Système informatique
Entrées
Procédé Système
externe de contrôle-commande
Sorties
Mesures
Interruptions
capteur
capteur
capteur
Procédé externe Système informatique
à piloter de contrôle-commande
actionneur
Commandes
Affichages
2
1 • Développement des 1.1 Introduction
systèmes de contrôle-commande
Aspect
comportemental
Propriétés temporelles
Aspect temporel Systèmes réactifs
Données produites par l’environnement ou de contrôle-commande
Contraintes de temps
Synchronisations
ou communications Aspect relation Systèmes
entre entités interactifs
Données produites par l’environnement
du programme (ex. : bureautique, CAO)
Pas de contraintes de temps
Algorithmique
Aspect Systèmes
transformationnel transformationnels
Données à l’initialisation (ex. : code de calcul)
Pas de contraintes de temps
l’aspect « temps » a une place importante sous la forme d’un temps de réaction,
d’une échéance à respecter, etc.
Nous terminons cette section par des définitions qualifiant des systèmes de contrôle-
commande ayant des spécifications particulières. La première de ces catégories
concerne les systèmes temps réel (real-time system) dont la définition est : « un sys-
tème de contrôle-commande dans lequel l’exactitude des applications ne dépend
pas seulement du résultat mais aussi du temps auquel ce résultat est produit. Si les
contraintes temporelles de l’application ne sont pas respectées, on parle de défail-
lance du système ». Ces contraintes temporelles peuvent être de deux types :
– contraintes temporelles relatives ou lâches (temps réel mou : soft real-time) :
les fautes temporelles sont tolérables (ex. : jeux vidéo, applications multimédia,
téléphonie mobile…) ;
– contraintes temporelles strictes ou dures (temps réel dur : hard real-time) : les
fautes temporelles ne sont pas tolérables (ex. : avionique, véhicules spatiaux,
© Dunod – La photocopie non autorisée est un délit.
3
1 • Développement des 1.1 Introduction
systèmes de contrôle-commande
Bus CAN
Commande de ré-injection Capteur Calculateur
gaz échappement pression collecteur Communications
avec les autres
calculateurs
Capteur
Capteur
pollution en amont
température air
Capteur
Commande pollution en aval
injecteur essence
Commande
air
allumage
4
1 • Développement des 1.1 Introduction
systèmes de contrôle-commande
Temps
Contrôle
en chimie
1 heure
Contrôle
fabrication
1 minute
Contrôle
© Dunod – La photocopie non autorisée est un délit.
1 seconde stockage
10 ms
1 ms Robotique
Systèmes Systèmes
Systèmes
vocaux mesures
radar
1 µs scientifiques
100 ns
Application
5
1 • Développement des 1.1 Introduction
systèmes de contrôle-commande
6
1 • Développement des 1.2 Architecture des applications
systèmes de contrôle-commande de contrôle-commande
de contrôle-commande
1.2.1 Architecture logicielle des applications de contrôle-commande
m Architecture multitâche
7
1 • Développement des 1.2 Architecture des applications
systèmes de contrôle-commande de contrôle-commande
Réseaux
Programmation multitâche
Liaison
Liaison
Liaison
réseau
réseau
réseau
Consignes Mesures
Visualisation Commandes
Liaison
stockage
Récupération Sauvegarde
Unités de stockage
8
1 • Développement des 1.2 Architecture des applications
systèmes de contrôle-commande de contrôle-commande
– Tâches de communications : ces tâches sont destinées à gérer les messages envoyés
ou reçus à travers un ou plusieurs réseaux ou bus de terrain. Si ce type de tâches
existe, l’application est dite distribuée ou répartie.
– Tâches de sauvegarde : ces tâches permettent de stocker l’état du système à des
instants fixés. Cette sauvegarde peut être utilisée a posteriori pour analyser le
fonctionnement de l’application ou lors d’une reprise d’exécution à une étape
précédente.
Après l’analyse et la conception de l’application, nous obtenons un ensemble de
tâches ou activités qui coopèrent afin de réaliser le contrôle-commande du procédé
géré. Ces tâches appartiennent aux différents groupes listés précédemment : tâches
d’entrées/sorties, tâches de traitement, tâches de gestion de l’interface utilisateur,
tâches de communications et tâches de sauvegarde. Ce découpage purement fonc-
tionnel peut être modifié dans certains cas en utilisant une conception tournée
vers les entités ou « objets » à contrôler. Cet aspect de la conception et de la mise
en œuvre est présenté dans les chapitres suivants.
Les tâches obtenues, qui constituent l’application de contrôle-commande, ne sont
pas des entités d’exécution indépendantes. En effet, certaines tâches sont connectées
vers l’extérieur pour les entrées/sorties. De plus elles peuvent être liées par des rela-
tions de type (figure 1.7) :
– synchronisation : cela se traduit par une relation de précédence d’exécution entre
les tâches ;
– communications : à la notion de précédence, traduite par la synchronisation,
s’ajoute le transfert de données entre les tâches ;
– partage de ressources : les tâches utilisent des éléments mis en commun au niveau
du système comme des zones mémoire, des cartes d’entrées/sorties, cartes réseau,
etc. Certaines de ces ressources, comme par exemple les zones mémoire, ne sont
pas ou ne doivent pas être accessibles, pour avoir un fonctionnement correct,
par plus d’une tâche à la fois, elles sont dites ressources critiques.
Tâche 2
R Tâche 5
Tâche 3 Tâche 6
Tâche 4
9
1 • Développement des 1.2 Architecture des applications
systèmes de contrôle-commande de contrôle-commande
Cette architecture logicielle peut être vue comme un ensemble de tâches synchro-
nisées, communicantes et partageant des ressources critiques. Le rôle essentiel du
système informatique est donc de gérer l’enchaînement et la concurrence des tâches
en optimisant l’occupation du processeur, cette fonction est appelée l’ordonnance-
ment. Ce principe d’ordonnancement est un point crucial des systèmes de contrôle-
commande ; en effet l’ordonnancement va déterminer les caractéristiques tempo-
relles et être le garant du respect des contraintes de temps imposées à l’exécution de
l’application.
Nous pouvons distinguer deux modèles d’exécution de ces systèmes de contrôle-
commande : l’exécution dite synchrone et l’exécution asynchrone. Nous allons pré-
senter ces deux modèles d’exécution à l’aide d’un modèle d’application très simple.
Cette application, constituée d’un ensemble de tâches pour gérer le procédé, intègre
en particulier les deux tâches suivantes :
– Tâche de lecture des données entrées par l’opérateur à l’aide d’un clavier, appelée
« Lecture_consigne ». L’intervention humaine fait que cette tâche peut être longue.
– Tâche d’alarme qui se déclenche sur un événement d’alerte correspondant au
dépassement d’un paramètre critique, appelée « Alarme ». Celle-ci doit s’exécuter
au plus vite pour éviter l’endommagement du procédé.
Pour mettre en avant les différences entre les deux modèles d’exécution, nous allons
étudier la situation dans laquelle la tâche « Lecture_consigne » s’exécute et la tâche
« Alarme » demande son exécution alors que la tâche « Lecture_consigne » n’est
pas terminée.
Dans le modèle d’exécution synchrone, la perception de l’occurrence de tout évé-
nement par le système est différée du temps d’exécution de la tâche en cours. Dans
l’exemple proposé, nous pouvons constater que la prise en compte d’un signal d’alerte
n’est effective que lors de la fin de la tâche « Lecture_consigne » (figure 1.8). D’un
point de vue du procédé, la réaction est perçue comme différée, alors que du point
de vue du système informatique, elle est perçue comme immédiate. L’occurrence
Alarme
Application
Lecture_consigne
Occurrences
observées
par le système
Réaction perçue comme immédiate
Occurrences par le système
émises
par le procédé temps
Clavier Alerte
Réaction perçue comme différée par le procédé
10
1 • Développement des 1.2 Architecture des applications
systèmes de contrôle-commande de contrôle-commande
Alarme
Application
Lecture_consigne Lecture_consigne
© Dunod – La photocopie non autorisée est un délit.
Occurrences
observées
par le système
11
1 • Développement des 1.2 Architecture des applications
systèmes de contrôle-commande de contrôle-commande
Pour terminer cette section, nous allons rappeler trois définitions importantes que
nous avons utilisées et fixer le contexte de cet ouvrage. Nous avons ainsi défini :
– Tâche non préemptible ou préemptible :
• Une tâche non préemptible ne peut être interrompue qu’à des endroits spéci-
fiques et à la demande de la tâche elle-même : fin_de_tâche, attente_signal…
Dans ce cas, la programmation et plus simple et aucun mécanisme de partage
de ressources critiques n’est à prévoir. En revanche, des temps de réponse longs
peuvent se produire.
• Une tâche préemptible peut être interrompue à n’importe quel instant et le
processeur affecté à une autre tâche. Dans ce cas, les temps de réponse à un évé-
nement externe peuvent être très courts ; mais nous avons alors une program-
mation plus complexe avec un besoin de mécanisme de partage de ressources
critiques.
– Analyse de l’ordonnancement hors ligne ou en ligne :
• Une analyse de l’ordonnancement hors ligne correspond à la construction
d’une séquence d’exécution complète sur la base des paramètres temporels des
tâches en utilisant une modélisation (réseaux de Petri…) ou une simulation
(animation ou énumération du modèle). L’ordonnanceur nécessaire est minimal
puisque la séquence d’exécution est prédéfinie, il se réduit à un séquenceur.
En revanche, l’application ainsi figée est peu flexible.
• Une analyse de l’ordonnancement en ligne correspond à un choix dynamique
de la prochaine tâche à exécuter en fonction des paramètres de la tâche en utili-
sant une modélisation de l’algorithme d’ordonnancement et une simulation de
l’exécution. L’ordonnancement a un coût temporel non négligeable ; en revanche,
l’application peut réagir à des événements ou des situations non prévus.
– Exécution synchrone ou asynchrone :
• Une exécution est dite synchrone si les tâches sont non préemptibles et s’exé-
cutent les unes après les autres dans un ordre qui peut être défini par une analyse
hors ligne de l’ordonnancement.
• Une exécution est dite asynchrone si les tâches sont préemptibles et s’exécutent
selon l’ordonnancement. Une analyse de la séquence doit se faire obligatoire-
ment en ligne.
Dans la suite de cet ouvrage, nous nous intéressons plus particulièrement aux sys-
tèmes asynchrones composés de tâches préemptibles avec un ordonnancement en
ligne. Ainsi, l’architecture logicielle de l’application est composée de plusieurs tâches
réalisées par le concepteur et d’un environnement spécifique, le noyau temps réel,
que nous allons décrire. Le point essentiel de cet environnement est l’ordonnanceur
qui permet d’affecter à tout instant le processeur à une tâche afin de respecter
l’ensemble des contraintes temporelles attachées à la gestion du procédé.
12
1 • Développement des 1.2 Architecture des applications
systèmes de contrôle-commande de contrôle-commande
cution avec des paramètres temporels fixés (temps de prise en compte d’une inter-
ruption, changement de contexte entre deux tâches, etc.). Nous pouvons comparer
les différences au niveau des objectifs fixés pour le noyau d’exécution d’un système
informatique classique et d’un système informatique de contrôle-commande.
Un système classique n’a pas été conçu pour permettre de respecter des contraintes
temporelles, mais il suit les règles suivantes :
– politiques d’ordonnancement des activités basées sur le partage équitable du pro-
cesseur : affectation identique du temps processeur à tous les processus en cours ;
– gestion non optimisée des interruptions ;
– mécanismes de gestion mémoire (cache…) et de micro-exécution engendrant des
fluctuations temporelles (difficulté pour déterminer précisément les durées des
tâches) ;
– gestion des temporisateurs ou de l’horloge pas assez fine (plusieurs milli-
secondes) ;
– concurrence de l’application temps réel avec le système d’exploitation toujours
actif ;
– gestion globale basée sur l’optimisation d’utilisation des ressources et du temps
de réponse moyen des différents processus en cours.
Un système informatique de contrôle-commande s’attache aux caractéristiques
suivantes :
– efficacité de l’algorithme d’ordonnancement avec une complexité limitée ;
– respect des contraintes de temps (échéances…). Ces contraintes temporelles se
traduisent plus en termes de choix d’une activité à exécuter à un instant donné
plutôt que de rapidité d’exécution de toutes les activités ;
– prédictibilité (répétitivité des exécutions dans des contextes identiques) ;
– capacité à supporter les surcharges ;
– possibilité de certification pour les applications de certains domaines comme
l’avionique, l’automobile…
En général, contrairement à un noyau temps réel, les contraintes temporelles ne sont pas garanties
dans un système d’exploitation classique (Unix, Windows NT…).
© Dunod – La photocopie non autorisée est un délit.
Une application temps réel étant par définition un système multitâche, le rôle essentiel
du noyau temps réel est donc de gérer l’enchaînement et la concurrence des tâches en
optimisant l’occupation de l’unité centrale du système informatique. Les principales
fonctions d’un noyau temps réel peuvent être scindées en trois groupes :
1. gestion des entrées/sorties (gestion des interruptions, gestion des interfaces
d’entrées/sorties, gestion des réseaux de communications…) ;
2. ordonnancement des tâches (orchestration du fonctionnement normal, surveil-
lance, changements de mode, traitement des surcharges…) ;
3. relations entre les tâches (synchronisation, communication, accès à une ressource
critique en exclusion mutuelle, gestion du temps…).
13
1 • Développement des 1.2 Architecture des applications
systèmes de contrôle-commande de contrôle-commande
Il important de noter que les tâches sont les unités actives du système ; le noyau temps
réel n’est actif que lors de son appel. Une tâche activée peut appeler le noyau temps
réel par une requête. Les différentes requêtes sont servies par des modules du noyau
temps réel appelées primitives. Ensuite le noyau temps réel réactive une tâche de
l’application selon l’algorithme d’ordonnancement utilisé (figure 1.10). Ainsi, le
noyau temps réel centralise toutes les demandes d’activation des tâches et gère des
tables lui permettant de comparer les priorités (ou les urgences) et l’état de ces diverses
tâches, ainsi que l’état d’occupation des ressources. La décision d’activation d’une
tâche étant prise, le noyau temps réel lance les modules de programmes correspon-
dant à cette tâche et lui alloue les ressources disponibles. La tâche activée occupe le
processeur jusqu’à la fin de son exécution sous le respect des conditions suivantes :
– Elle ne réalise pas d’opérations d’entrées-sorties.
– Les ressources utilisées sont disponibles.
– Aucun événement extérieur ne revendique le déroulement d’une tâche plus prio-
ritaire.
Comme nous le verrons au cours de cet ouvrage, les langages de développement des
applications de contrôle-commande sont très divers. Mais, par rapport à l’environ-
nement d’exécution que nous venons de décrire (noyau temps réel avec les trois
14
1 • Développement des 1.2 Architecture des applications
systèmes de contrôle-commande de contrôle-commande
Application
Mesures Commandes
Tâche i Tâche j Tâche k Tâche x Tâche y
…
Requête Activation…
…
Gestion
…
des interruptions
Ordonnanceur
1 1 1
2 2
© Dunod – La photocopie non autorisée est un délit.
Langages
3 réactifs
Langages
Langages multitâches
standards (Langage Ada…)
(Langage C…) Autre
langage
15
1 • Développement des 1.2 Architecture des applications
systèmes de contrôle-commande de contrôle-commande
– langages réactifs (langages Lustre, Esterel, Signal…) : ces langages donnent non
seulement la possibilité de décrire les fonctionnalités du programme, mais aussi
l’enchaînement des différentes parties. Le noyau est donc limité à une couche
proche du matériel lié notamment à la gestion des interruptions. En revanche,
étant donné la possibilité très limitée d’expression de l’aspect fonctionnel, ils
sont souvent associés à un langage standard pour palier ce manque.
16
© Dunod – La photocopie non autorisée est un délit.
Boîtier
Les calculateurs situés dans des parties Mémoire
exposées sont placés dans des boîtiers Stocke les tâches utilisant
métalliques étanches et blindés. le microcontrôleur.
Ceux de l’habitacle sont en plastique.
Horloge
1 • Développement des
systèmes de contrôle-commande
Amplification
des informations d’entrée. Détermine la vitesse
d’exécution des opérations.
Elle se mesure en mégahertz.
(50 MHz).
Microcontrôleur de sécurité
ASIC Certains calculateurs (injection, ABS,
Un ASIC (Application Specific etc.) disposent d’un microcontrôleur
Integrated Circuit) est une puce de secours (redondance).
dédiée à une application qu’elle
exécute directement.
Alimentation
Alimente le calculateur en électricité
Microcontrôleur en diminuant la tension en provenance
Exécute les différentes tâches de la batterie.
stockées en mémoire.
17
1 • Développement des 1.3 Développement des applications
systèmes de contrôle-commande de contrôle-commande
18
1 • Développement des 1.3 Développement des applications
systèmes de contrôle-commande de contrôle-commande
modules logiciels utilisés dans le contexte d’Ariane IV. Les spécifications, attachées
à l’accélération, auraient dû être différentes en termes de limites afin d’éviter ce
dysfonctionnement.
Cette liste d’exemples de problèmes au niveau de l’exécution d’applications de
contrôle-commande montre la nécessité de mettre en place un cycle de développe-
ment encore plus rigoureux pour ces applications de gestion de procédé physique
dont les tests en exécution réelle ne sont pas toujours facilement accessibles.
Cahier Conceptions
Spécification
des préliminaire Logiciel
globale
charges et détaillée
19
1 • Développement des 1.3 Développement des applications
systèmes de contrôle-commande de contrôle-commande
Une présentation généralement plus adaptée est celle du cycle en « V » (figure 1.15).
Dans cette représentation, à chaque niveau d’analyse ou de conception correspond
un niveau de validation ; ainsi, nous avons :
– conception détaillée et tests unitaires ;
– conception préliminaire et tests d’intégration ;
– spécification et validation globale.
Il est évident que cette formalisation méthodologique du développement des appli-
cations informatiques a pour principaux objectifs : éviter les fautes logicielles, accroître
la maintenabilité, faciliter l’évolutivité… À chaque étape ou ensemble d’étapes cor-
respond une méthode qui est généralement supportée par un outil informatique
pour aider à sa mise en œuvre plus ou moins automatisée (CASE Tools : Computer
Aided Software Engineering Tools).
SPÉCIFICATION VALIDATION
CONCEPTION
INTÉGRATION
PRÉLIMINAIRE
CONCEPTION
TESTS UNITAIRES
DÉTAILLÉE
CODAGE
20
1 • Développement des 1.3 Développement des applications
systèmes de contrôle-commande de contrôle-commande
les fonctions à remplir par ce procédé (figure 1.16). Ainsi, la spécification de l’appli-
cation commence par une spécification système (matériel et logiciel). Une partie de
la conception préliminaire peut aussi intégrer les deux aspects puisque la réalisation
d’un module fonctionnelle peut être réalisée soit de manière matérielle (circuit
intégré spécifique – FPLA : Field Programmable Logic Array ou FPGA : Field Pro-
grammable Gate Array) soit d’une manière logicielle. Ensuite, le développement des
deux parties peut continuer de façon différenciée et classique avec la conception
détaillée, l’implémentation (réalisation ou codage) et les tests. À nouveau, les deux
aspects de l’application doivent se rejoindre pour passer à la phase d’intégration et
de validation de l’ensemble. La phase d’intégration est certainement l’étape la plus
importante et la plus difficile. En effet, une partie logicielle va être insérée dans un
environnement matériel très spécifique.
Bien que ce ne soit pas le propos de cet ouvrage, l’aspect matériel est abordé de façon
succincte dans le chapitre 4. Au niveau de la conception, il existe aussi de nom-
breuses méthodes permettant d’avoir des développements de qualité. Ainsi, le lan-
gage VHDL (VHSIC Hardware Description Language – VHSIC : Very High Speed
Integrated Circuit) de description des fonctions à réaliser d’un point de vue matériel
conduit à une conception formelle des circuits, c’est-à-dire autorisant une preuve
de la conception établie.
Prenons un exemple très répandu comme les consoles de jeux portables. Ces matériels
possèdent une interface utilisateur très spécifique comportant un ensemble de
Architecture
opérationnelle
Logiciel + Matériel
S P ÉCIFICATION
SYSTÈME
Architecture Architecture
logicielle CONCEPTION
P R É LIMINAIRE matérielle
CONCEPTION CONCEPTION
DÉTAILLÉE DÉTAILLÉE
CODAGE RÉALISATION
© Dunod – La photocopie non autorisée est un délit.
TEST TEST
I N T ÉGRATION
VALIDATION
21
1 • Développement des 1.3 Développement des applications
systèmes de contrôle-commande de contrôle-commande
Spécifications du système
• Aspect fonctionnel et aspect comportemental
• Autres contraintes de développement :
– matériel (processeur, architecture, taille, consommation…)
– noyau temps réel
– langage de développement
Environnement de développement
Logiciel
applicatif
Environnement d’exécution
Matériel Logiciel
Noyau Logiciel
temps réel
22
1 • Développement des 1.3 Développement des applications
systèmes de contrôle-commande de contrôle-commande
INTÉGRATION
INTÉGRATION
avec noyau
CONCEPTION
CONCEPTION
ADAPTÉE
© Dunod – La photocopie non autorisée est un délit.
TESTS TESTS
Développement Développement
en environnement « hôte » en environnement « cible »
23
1 • Développement des 1.3 Développement des applications
systèmes de contrôle-commande de contrôle-commande
24
1 • Développement des 1.3 Développement des applications
systèmes de contrôle-commande de contrôle-commande
SPÉCIFICATION VALIDATION
SA-RT
MSMC
StateCharts CONCEPTION
INTÉGRATION
UML PRÉLIMINAIRE
SA-RT
DARTS
CONCEPTION
MSMC TESTS UNITAIRES
DÉTAILLÉE
StateCharts
UM
HOOD
DARTS
StateCharts CODAGE
GRAFCET
HOOD
© Dunod – La photocopie non autorisée est un délit.
Comme cela a été justifié et expliqué dans l’avant-propos, les méthodes SA-RT et
DARTS, qui permettent de décrire complètement le cycle dans ses phases de spé-
cification et de conception, sont celles qui sont étudiées de façon détaillée dans cet
ouvrage. En effet, dans le cas d’applications embarquées de taille petite ou moyenne
qui sont implémentées dans une architecture multitâche, il semble plus pertinent
d’utiliser une analyse et une conception de type fonctionnel et structuré.
25
1 • Développement des 1.3 Développement des applications
systèmes de contrôle-commande de contrôle-commande
26
2 • SPÉCIFICATION
SELON LA MÉTHODE SA-RT
27
2 • Spécification 2.1 Introduction générale
selon la méthode SA-RT à la méthode SA-RT
données. Cette méthode très générale de description d’un système a été adaptée à
la spécification de logiciels avec la méthode très connue SA (Structured Analysis)
(figure 2.1).
Spécification
Structured Analysis
statique SA (E. Yourdon, T. Demarco, 1979)
d’un logiciel
Spécification
Structured Analysis Real Time
dynamique SA-RT (Ward/Mellor, 1985 ; Hatley/Pirbhai, 1986)
d’un logiciel
L’analyse structurée SA, définie par E. Yourdon et T. Demarco, est une méthode
descendante par affinages successifs des traitements, appelés « process ». Les différents
diagrammes sont donc ordonnés hiérarchiquement en faisant apparaître pour les
derniers niveaux des fonctions élémentaires, appelées primitives élémentaires ou
« process » primitifs. Les différents outils composant cette méthode sont :
– diagrammes de transformations de données ou diagramme de flots de données
(DFD) ;
– dictionnaire de données ;
– spécifications des « process » primitifs.
Les diagrammes de flots de données sont construits à partir de quatre éléments
graphiques : traitement (cercle), flot de données (flèche), unité de stockage (traits
parallèles) et entité externe (rectangle) (tableau 2.1). À partir de ces éléments de base,
il est possible de décrire l’aspect fonctionnel d’une application par un diagramme flots
de données. Un exemple, présenté sur la figure 2.2, montre l’analyse d’une applica-
tion très simple de régulation de température avec trois entités externes, deux process
et une unité de stockage.
Remarque
Il est intéressant de noter que cette description graphique fonctionnelle d’une application à l’aide
de la méthode SA sera presque entièrement reprise dans la méthode SA-RT, montrant bien ainsi sa
dépendance chronologique.
28
2 • Spécification 2.1 Introduction générale
selon la méthode SA-RT à la méthode SA-RT
Témoin
de chauffage
Température_consigne Commande_chauffage
Deux groupes élaborèrent la méthode SA-RT avec des différences notables en termes
de représentation : d’une part, la méthode établie par Ward et Mellor en 1985 qui
associe le fonctionnel et le contrôle dans un même diagramme et, d’autre part, la
méthode proposée par Hatley et Pirbhai en 1986 qui sépare le fonctionnel et le
contrôle. Mais ces deux vues de la même méthode restent très similaires en termes de
capacité d’expression de la spécification. Nous présentons dans cet ouvrage la
méthode SA-RT établie par Ward et Mellor en 1985.
Comme le montre la figure 2.1, la méthode SA-RT a continué à évoluer au sein
des entreprises en intégrant des besoins spécifiques à un domaine d’applications.
Ainsi, nous trouvons une méthode SA-RT, appelée ESML et utilisée dans l’avionique,
qui a été enrichie d’un point de vue flot de contrôle.
29
2 • Spécification 2.2 Présentation de la syntaxe graphique
selon la méthode SA-RT de la méthode SA-RT
La méthode SA-RT intègre les trois aspects fondamentaux d’une méthode de spéci-
fication en mettant l’accent sur les deux premiers qui sont des points essentiels dans
les applications de contrôle-commande :
– aspect fonctionnel (ou transformation de données) : représentation de la trans-
formation que le système opère sur les données et spécification des processus qui
transforment les données ;
– aspect événementiel (piloté par les événements) : représentation des événements
qui conditionnent l’évolution d’un système et spécification de la logique de
contrôle qui produit des actions et des événements en fonction d’événements en
entrée et fait changer le système d’état ;
– aspect informationnel (données) : spécification des données sur les flots ou dans
les stockages. Ce dernier aspect qui est en général assez négligé dans ce type d’appli-
cation peut faire l’objet d’une description spécifique choisie au sein d’une entre-
prise.
30
2 • Spécification 2.2 Présentation de la syntaxe graphique
selon la méthode SA-RT de la méthode SA-RT
Lecture Écriture
de données de données
Étiquette
Processus
N
Transformation
de données
Exemples
Signal Température
température Mesurer mesurée
température
1
Signal Allumage
interrupteur Commander lampe
lampe
2
Distance
parcourue
Affichage
Calculer vitesse
vitesse
3
Top horloge
31
2 • Spécification 2.2 Présentation de la syntaxe graphique
selon la méthode SA-RT de la méthode SA-RT
_2
ée
Décomposition
nn
Do
Donné e Donné e Do Donné e Do
nn nn
ée ée
_1 _1
ée
Donné e Donné e _2 Donné e
_1
_1
ée
nn
ée
nn
Do
Do
Le flot Donné e s ’est enrichi Le flot Donné e est construit avec
Création alternative de Donné e les flots Donn é e _ 1 et Donné e _ 2
de Donné e _ 1
Ces flots de données peuvent se décomposer ou au contraire se regrouper lors des liai-
sons entre les processus fonctionnels dans le diagramme flot de données (figure 2.5).
Pression
Température
Paramètres_moteur Paramètres_moteur
32
2 • Spécification 2.2 Présentation de la syntaxe graphique
selon la méthode SA-RT de la méthode SA-RT
Température_consigne*
Température_mesurée
Afficher Affichage
température
3
Température_consigne*
Remarque
Pour des besoins de clarté graphique, un stockage de données peut être visualisé plusieurs fois sur
un diagramme flot de données en notant cette duplication par une « * ».
33
2 • Spécification 2.2 Présentation de la syntaxe graphique
selon la méthode SA-RT de la méthode SA-RT
Signal Production
Capteur de données
Commande Consommation
Actionneur de données
Pilotage de l’exécution
Événement
Pilotage de l’exécution
Donnée_1 Donnée_1
Processus Processus
1 Donnée_3 1 Donnée_3
Donnée_2 Donnée_2
(a) (b)
34
2 • Spécification 2.2 Présentation de la syntaxe graphique
selon la méthode SA-RT de la méthode SA-RT
Température
Déclenchement
Vérifier Contrôler
température chauffage
1 2
Trop_chaud
Température_consigne
Les deux premiers événements sont utilisés ensemble « E/D » pour piloter un processus
fonctionnel de type « boucle sans fin » ou périodique, c’est-à-dire que le processus
de contrôle doit lancer l’exécution de ce processus avec l’événement « E » et ensuite
peut l’arrêter avec l’événement « D ». L’événement « T » est utilisé pour activer un
processus fonctionnel de type « début-fin » ou sporadique, c’est-à-dire que le pro-
cessus de contrôle doit lancer l’exécution de ce processus avec l’événement « T » et
ensuite le processus s’arrête à la fin de son exécution sans intervention du contrôle.
35
2 • Spécification 2.3 Les diagrammes flot de données
selon la méthode SA-RT
36
2 • Spécification 2.3 Les diagrammes flot de données
selon la méthode SA-RT
Opérateur 1
Consigne_1
Capteur 1 Donnée_E_1 Donnée_S_1 Actionneur 1
Écran
Conducteur
© Dunod – La photocopie non autorisée est un délit.
Commande_freinage
Système de freinage
Contrôler
Bouton activation ABS système
Activation_ABS freinage
0 Voyant ABS
Affichage_ABS
37
2 • Spécification 2.3 Les diagrammes flot de données
selon la méthode SA-RT
Donnée_lue
Capteur_1
Actionneur_1
Acquérir Traiter Donnée_calculée Commander
donnée donnée actionneur
1 2 3
Autre_Donnée_2
Le passage des données entre les processus fonctionnels peut être réalisé selon les
besoins avec les deux méthodes de base : flots de données direct (exemple entre les
processus 2 et 3) ou unité de stockage (cas des processus 1 et 2).
Ainsi, dans l’exemple simple d’un système de freinage automobile (§ 2.3.1), le dia-
gramme préliminaire est constitué de cinq processus fonctionnels (figure 2.15). Au
niveau de cet exemple simple, nous n’avons pas une décomposition fonctionnelle
aussi complexe que l’exemple générique présenté précédemment : seules les parties
38
2 • Spécification 2.3 Les diagrammes flot de données
selon la méthode SA-RT
Acquérir
demande Niveau_freinage
Demande_freinage freinage
1
Commande_freinage
Commander
Détecter État_glissement freinage
glissement 3
Glissement_roue de roue
2
Lire Afficher
État_bouton_ABS
bouton état bouton
ABS Affichage_ABS
Activation_ABS ABS
4
39
2 • Spécification 2.3 Les diagrammes flot de données
selon la méthode SA-RT
Diagramme de contexte
Capteur1 a
Contrôler c
système Actionneur
0
Capteur2 b
Diagramme préliminaire
Processus
c
a Processus 3 Légende
1 d e
Processus
2
: Processus
Processus
Stockage b à décomposer
a Processus
1.1 f d
Processus
1.2
Stockage
2.3.4 Conclusion
La décomposition hiérarchique fonctionnelle, explicitée dans ce paragraphe, est la
base de la méthode d’analyse SA-RT. Mais l’aspect temporel ou plus exactement le
contrôle de l’enchaînement dans l’exécution de ces différents processus fonctionnels
n’est pas décrit au travers de ces diagrammes flots de données. Ceci va donc faire
l’objet d’un complément au niveau de ces diagrammes par l’aspect « contrôle ».
40
2 • Spécification 2.4 L’aspect contrôle de la méthode SA-RT
selon la méthode SA-RT
Nous pouvons souligner qu’il ne peut pas exister de processus de contrôle au niveau
du diagramme de contexte puisqu’un seul diagramme fonctionnel est représenté.
En revanche, il est tout à fait possible d’avoir des flots de contrôle au niveau de ce
diagramme de contexte entre les terminaisons et le processus fonctionnel. Ces flots
de contrôle, correspondant à des signaux tout ou rien, doivent être réservés à des
signaux particuliers ne nécessitant aucun processus fonctionnel particulier (acqui-
sition, mise en forme…). Ainsi, nous pouvons citer les événements externes tels
que « frappe clavier », « click souris », « interrupteur de mise sous tension », etc.
Pour préciser de façon plus complète ces notions de signaux et d’événements, nous
pouvons considérer qu’un signal nécessite une acquisition (scrutation périodique
par exemple) et une mise en forme minimum. Le résultat de ce « prétraitement »
peut alors effectivement devenir un événement. La figure 2.17a illustre cette diffé-
rence signal/événement en considérant l’exemple d’un interrupteur. Le signal élec-
trique fourni par l’interrupteur tout ou rien peut être considéré comme un événement
Processus fonctionnel
Bord de modèle
Signal_interrupteur
Événement_interrupteur
Figure 2.17 – Méthodes de prise en compte d’un signal électrique afin de réaliser une trans-
formation données-événements : (a) processus fonctionnel dans le diagramme préliminaire,
(b) circuit électronique visualisé au niveau du diagramme de contexte.
41
2 • Spécification 2.4 L’aspect contrôle de la méthode SA-RT
selon la méthode SA-RT
Conducteur
Commande_freinage
Système de freinage
Contrôler
Bouton activation ABS système
Activation_ABS freinage
0 Voyant ABS
Affichage_ABS
Rappelons que l’ensemble des flots entrants et sortants de l’unique processus fonc-
tionnel du diagramme de contexte doit se retrouver dans le diagramme préliminaire
y compris les flots de contrôle s’ils existent.
42
2 • Spécification 2.4 L’aspect contrôle de la méthode SA-RT
selon la méthode SA-RT
Autre_Donnée_2
Traiter
donnée
Donnée_lue 2
Donnée_calculée
Traitement_terminé T
Capteur_1 Actionneur_1
T E/D
Acquérir Contrôler Commander
donnée application actionneur
1 6 3
Capteur_2
Actionneur_2
Acquisition_terminée
Mise_en_marche
minaire, représenté sur la figure 2.15, va être modifié pour intégrer un processus de
contrôle. En particulier, les deux flots de données « Etat_glissement » et « Etat_
bouton_ABS » de type booléen deviennent des événements envoyés respectivement
par les processus fonctionnels « Détecter glissement roue » et « Lire boutons ABS ».
Nous obtenons alors le diagramme préliminaire complet de la figure 2.20.
Cet exemple de diagramme préliminaire, présenté sur la figure 2.20, doit faire
l’objet de plusieurs remarques qui sont généralisables à la réalisation d’un diagramme
préliminaire quelconque. Ainsi, nous pouvons noter :
– Tous les différents processus fonctionnels sont liés au processus de contrôle par
des flots de contrôle de type « E/D » ou « T ». Le choix de l’un ou l’autre de ces
43
2 • Spécification 2.4 L’aspect contrôle de la méthode SA-RT
selon la méthode SA-RT
Acquérir
demande
Demande_freinage freinage Pa Niveau_freinage
s_
1 de
_f
re
in
Fr
ag Commande_freinage
ein
e Commander
ag
freinage
e
3
E/D
E/D
e_
Pas_d ent
m
Détecter glisse
glissement E/D Contrôler
Glissement_ de roue application
roue 2 G li s s 6
em ent E/D
vé
T
cti
Afficher
a
état bouton
S_
vé ABS
AB
cti Affichage_ABS
n _a 5
no
Lire S_
AB
bouton
Activation_ABS ABS Mise_en_marche
4
événements est fait, comme nous l’avons déjà vu, en fonction de la structure de
fonctionnement interne du processus fonctionnel : processus de type « boucle
sans fin » ou de type « début-fin ».
– Certains processus fonctionnels possèdent des retours d’exécution vers le processus
de contrôle. Dans ce cas, il est souhaitable d’expliciter les événements indiquant
le résultat du traitement, par exemple « ABS_activé ». Ces événements pour-
raient être caractérisés par des variables de type booléen ; ainsi l’absence d’occur-
rence de l’événement « ABS_activé » pourrait être interprétée comme NON
(« ABS_activé ») – NON étant l’opération booléenne. Mais, dans beaucoup de
cas, il est préférable pour des raisons de clarté opérationnelle, de faire apparaître
les deux événements possibles : « ABS_activé » et « ABS_non_activé ».
– Il ne faut pas oublier de connecter les flots de contrôle du diagramme précédent
réalisé dans l’analyse descendante, par exemple l’événement « Mise_en_marche »
du diagramme de contexte.
La mise en place du processus de contrôle au niveau d’un diagramme préliminaire
ou d’un diagramme de décomposition avait pour but d’exprimer l’exécution ou
l’enchaînement des processus fonctionnels. L’objectif n’est pas complètement atteint
puisque le diagramme flot de données ainsi complété ne reflète pas cette exécution.
Il est nécessaire d’ajouter cette information supplémentaire décrivant le fonction-
nement du processus de contrôle, cela se traduit généralement par un diagramme
état/transition que nous allons décrire dans le paragraphe suivant.
44
2 • Spécification 2.4 L’aspect contrôle de la méthode SA-RT
selon la méthode SA-RT
État courant
événement
action
État suivant
Pour illustrer cette description du processus de contrôle avec un exemple très géné-
rique, reprenons le diagramme préliminaire de la figure 2.19 qui représente la
© Dunod – La photocopie non autorisée est un délit.
45
2 • Spécification 2.4 L’aspect contrôle de la méthode SA-RT
selon la méthode SA-RT
État repos
Mise_en_marche
<E> 3
<T> 1
Phase d’acquisition
Acquisition_terminée
<T> 3
Phase de traitement
Traitement_terminé
<T> 1
– Les actions du processus de contrôle sur les processus fonctionnels sont notées
par « < E ou D ou T > + numéro ou nom du processus fonctionnel ». Il peut y avoir
plusieurs actions de ce type associées à une seule transition.
Mise en marche
<E> 1
<D> 5
Pas de freinage
demandé
Freinage
<T> 4
<E> 3
Test
Pas de freinage Pas de freinage Pas de freinage
ABS
<D> 3 <D> 3 <D> 3
<D> 2 <D> 2 ABS_activé ABS_non_activé
<E> 5 <D> 5
<E> 2
Freinage Freinage
avec ABS sans ABS
Pas de freinage
(ABS)
46
2 • Spécification 2.4 L’aspect contrôle de la méthode SA-RT
selon la méthode SA-RT
47
2 • Spécification 2.4 L’aspect contrôle de la méthode SA-RT
selon la méthode SA-RT
Mise en marche
<E> 1, <E> 4, <D> 5
État initial
et test ABS
ABS_activé ABS_non_activé
<E> 5 <D> 5
ABS_non_activé
<D> 5
ABS activé ABS non activé
pas de freinage pas de freinage
ABS_activé
<E> 5
Freinage Pas de freinage Freinage Pas de freinage
<E> 3, <E> 2 <D> 3, <D> 2 <E> 3 <D> 3
Freinage Freinage
avec ABS sans ABS
Pas de freinage
(ABS)
Contrôler Contrôler
Application Application
6 6
e
T E/D
vé
é
tiv
cti
ac
_a
ée ée
S_
S
v tiv
cti
AB
AB
c
_a _a
on on
S_n S_n
Lire AB Lire AB
bouton bouton
ABS ABS
4 4
Activation_ABS Activation_ABS
48
2 • Spécification 2.5 Spécification des processus primitifs
selon la méthode SA-RT
État 1
e1 ET e2 NON (e3)
<E> 3 <D> 1
État 2
e3 OU e4 e5 ET (e1 OU e3)
<E> 6 <D> 1
État 3
décline en 6 mots-clés :
– « E/ données : Nom_flots_de_données… » : liste des flots de données en entrée
du processus fonctionnel ;
– « E/ événements : Nom_flots_d’événements… » : liste des flots d’événements en
entrée du processus fonctionnel, c’est-à-dire en général « E/D » ou « T », ou
éventuellement les événements produits directement par les bords de modèle ;
– « S/ données : Nom_flots_de_données… » : liste des flots de données en sortie du
processus fonctionnel ;
– « S/ événements : Nom_flots_d’événements… » : liste des flots d’événements en
sortie du processus fonctionnel ;
49
2 • Spécification 2.5 Spécification des processus primitifs
selon la méthode SA-RT
Il est aussi possible de faire une spécification de type pré et postcondition sur le
modèle suivant :
– « Précondition : » : liste des flots de données ou d’événements en entrée du pro-
cessus fonctionnel ave les conditions associées.
– « Postcondition » : liste des flots de données ou d’événements en sortie du pro-
cessus fonctionnel ave les conditions associées.
Donc, dans le cas de l’exemple simple « système de freinage automobile », nous avons
le processus fonctionnel 1 « Acquérir demande de freinage » spécifié de la façon
suivante :
50
2 • Spécification 2.6 Spécification des données
selon la méthode SA-RT
Symbole Signification
=… Composé de…
*……* Commentaire
n{……}p Itération de n à p
(……) Optionnel
© Dunod – La photocopie non autorisée est un délit.
51
2 • Spécification 2.6 Spécification des données
selon la méthode SA-RT
52
2 • Spécification 2.6 Spécification des données
selon la méthode SA-RT
Séquence Ou exclusif
Pression Pression
Itération Itération
Pression avec bornes Pression
53
2 • Spécification 2.7 Organisation générale de la méthode SA-RT
selon la méthode SA-RT
54
2 • Spécification 2.7 Organisation générale de la méthode SA-RT
selon la méthode SA-RT
Diagramme de contexte
Diagramme X a
de contexte Système c
de Z
b contrôle
X
Diagramme préliminaire
Diagramme a h 4 g
préliminaire 1 3 c
d e
2 Diagramme E-T du 4
Diagramme E-T du 4
Spécification
b f des processus
de contrôle
Diagramme de décomposition
h
Diagramme de a k 1.4
décomposition 1.1
Spécif du 2
i 1.2 Spécif du 3
j d
1.3 Spécif du 1.1
Spécif du 1.2
Spécif du 1.3 Spécification
des processus
onna
ire des d
onné primitifs
Dicti es
Dictionnaire a = ..
...
des données
b = ..
...
55
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
Tableau 2.3 – Liaisons flots de données entre les entités du modèle SA-RT.
Tableau 2.4 – Liaisons flots de contrôle entre les entités du modèle SA-RT.
2.8 Exemples
Nous allons mettre en œuvre cette méthodologie d’analyse SA-RT pour quelques
exemples plus complexes que l’exemple décrit jusqu’à présent « système de freinage
automobile ». Toutefois, nous nous limitons à la description principale, c’est-à-dire :
diagramme de contexte, diagramme préliminaire avec un processus de contrôle et
diagramme état/transition du processus de contrôle.
En plus de la gestion automatisée d’une mine, l’aspect sécurité est le point essentiel
de la gestion d’une zone d’extraction de minerai, située en sous-sol, avec une pré-
sence humaine. Nous allons limiter notre étude au système de gestion de la sécurité
qui concerne principalement le contrôle des deux fluides :
– Eau : les eaux de ruissellement sont évacuées par un ensemble de conduites vers
un lieu unique, appelé puisard. Le niveau de ce puisard, qui récupère toutes les
56
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
eaux, est contrôlé en permanence avec une évacuation à l’aide de pompes afin
de la maintenir entre deux valeurs de niveaux.
– Air : la ventilation des galeries de la mine est effectuée en permanence. Lors de
l’extraction, il peut se produire un accès accidentel à une poche de gaz comme
du méthane. Fortement toxique, la meilleure protection consiste à procéder à
l’évacuation de la zone ou de la mine entière sachant que le volume de méthane
n’est, a priori, pas connu. D’autre part si le niveau de méthane est élevé, il faut
éviter toutes les productions d’étincelles (moteur…).
Nous allons considérer un système simple de gestion de la sécurité de la mine vis-
à-vis de ces deux facteurs. Le pilotage de cet ensemble comprend donc les éléments
suivants (figure 2.30) :
– un capteur analogique de niveau d’eau, appelé LS (Level Sensor), pour détecter
les deux niveaux limites de régulation, soit le niveau bas (LLS, Low Level Sensor)
et le niveau haut (HLS, High Level Sensor) ;
– une pompe à eau à débit réglable permettant l’évacuation du puisard ;
– un capteur analogique du taux de méthane contenu dans l’air, appelé MS
(Methane Sensor) ;
– une interface vers l’opérateur pour affichage de l’alarme.
Console
opérateur
Système
Commande de contrôle
pompe
MS LS
Pompe
© Dunod – La photocopie non autorisée est un délit.
HLS
Puisard LLS
57
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
La mine doit donc fonctionner tant que la sécurité maximale peut être maintenue.
Ainsi, les indications générales ou règles de fonctionnement sont les suivantes :
– Règle 1 : La pompe doit être mise en route si le niveau d’eau dépasse le niveau
maximum (LS > HLS). La pompe s’arrête dès que le niveau descend en dessous
de la valeur inférieure (LS < LLS).
– Règle 2 : Une alarme doit être lancée vers la console de l’opérateur dès que le
niveau limite du capteur MS est franchi afin de pouvoir opérer une évacuation
de la mine. Cette valeur limite est appelée MS_L1 (Methane Sensor Level 1).
– Règle 3 : La pompe ne doit pas fonctionner quand le niveau du capteur de
méthane (MS) est supérieur à une limite fixée MS_L2 (Methane Sensor Level 2)
avec la condition MS_L2 > MS_L1 afin d’éviter les risques d’explosion.
Dans cet exemple d’application simple, nous sommes donc en présence de deux lignes
de régulation : le contrôle du niveau de l’eau dans le puisard (règle 1) et le contrôle
du taux de méthane dans l’air (règle 2). L’interaction entre ces deux régulations se
situe au niveau de la commande de la pompe pour l’évacuation de l’eau du puisard
dont le fonctionnement est lié non seulement au niveau d’eau, mais aussi au taux
de méthane (règle 3).
m Analyse SA-RT
M Diagramme de contexte
Interrupteur
Console Mise_sous_tension
opérateur
Pompe
MS
Commande_pompe
Gérer
sécurité
mine
0
Capteur LS Alarme Console
niveau eau
opérateur
Figure 2.31 – Analyse SA-RT de l’application de la gestion de l’aspect sécurité d’une mine :
Diagramme de contexte.
58
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
Mise_sous_tension
MS_L1 dépassée
Afficher Alarme
Acquérir
MS alarme
capteur Consig
ne_resp E/D 4
ectée
méthane
1
E/D Contrôler
mine
5
Niveaux_ MS_L2 dépassée
Consignes_méthane
E/D
Niveaux_ Niveau_LLS
Consignes_eau E/D
Niveau_HLS Commande_pompe
Commander
pompe
3
LS Acquérir
capteur eau Vitesse_pompe
2
© Dunod – La photocopie non autorisée est un délit.
Figure 2.32 – Analyse SA-RT de l’application de la gestion de l’aspect sécurité d’une mine :
Diagramme préliminaire.
59
60
Mise_sous_tension
<E> Acquérir capteurs air
<E> Acquérir capteurs eau
Consigne respectée
<D> Afficher alarme
Fonctionnement
nominal
de la mine
selon la méthode SA-RT
2 • Spécification
État alerte
(consignes dépassées) Pompe
en marche
Niveau HLS ET NON (MS L2 dépasser) Chauffage_terminé Niveau HLS ET NON (MS L1 dépasser)
<E> Commander pompe <T> Analyser température <E> Commander pompe
<E> Afficher alarme
Figure 2.33 – Analyse SA-RT de l’application de la gestion de l’aspect sécurité d’une mine :
Diagramme état/transition.
2.8 Exemples
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
Le processus de contrôle est lié aux différents processus fonctionnels par des événe-
ments qui sont mis en place en même temps que la réalisation du diagramme état/
transition de la figure 2.33. Ce diagramme état/transition, description du fonc-
tionnement du processus de contrôle 5 (Contrôler mine), comprend quatre états :
– fonctionnement nominal de la mine (niveau d’eau inférieur à HLS et taux de
méthane inférieur à MS_L1) ;
– pompe en marche (niveau d’eau supérieur à LLS) ;
– état alerte (consigne MS_L1 dépassée et niveau d’eau inférieur à HLS, ou consigne
MS_L2 dépassée et niveau d’eau quelconque) ;
– état alerte (consigne MS_L1 dépassée) et pompe en marche.
Nous pouvons remarquer que, dans ce diagramme état/transition complexe, nous
avons utilisé dans certains cas des combinaisons logiques de deux événements pour
passer d’un état à l’autre, par exemple « MS_L2_dépassée OU Niveau_LLS » pour
passer de l’état « État alerte et pompe en marche » à l’état « État alerte ».
D’autre part, ce diagramme état/transition fait l’hypothèse pour la surveillance du
taux de méthane que les événements « MS_L1_dépassée » et « MS_L2_dépassée » se
produisent toujours dans l’ordre cité et, lors du retour à la situation normale, l’évé-
nement « Consigne_respectée » est émis.
61
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
Capteur
détection sable
Capteur de niveau
Commande sable du four
d’approvisionnement
Capteur
de température
Chauffage
verre
du four
m Analyse SA-RT
M Diagramme de contexte
La première étape d’analyse consiste à élaborer le diagramme de contexte de l’appli-
cation. Ce diagramme, représenté sur la figure 2.35, intègre les six bords de modèles
correspondant aux trois entrées ou capteurs (capteur de température – thermocouple,
capteur de niveau de sable, capteur tout ou rien de détection d’arrivée du sable) et
aux deux sorties ou actionneurs (approvisionnement en sable, commande du four de
chauffage). Le dernier bord de modèle correspond à la console opérateur qui fournit
les deux événements : « Arrêt » et « Marche ». Ces événements ne sont utilisés que
pour le démarrage de l’application et éventuellement son arrêt. Le processus fonc-
tionnel initial 0 « Piloter four à verre » constitue l’application à étudier. En résumé,
en plus des deux événements précédemment cités, nous avons cinq flots de données :
trois flots entrants (Température, Niveau_sable, Arrivée_sable) et deux flots sortants
(Sable, Chauffage). L’ensemble de ces flots doit se retrouver dans le diagramme pré-
liminaire : premier niveau d’analyse du processus fonctionnel 0.
62
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
Console opérateur
Capteur de température
Arrêt Marche
Commande approvisionnement
Température
Sable
Piloter
Capteur de niveau four à verre
Niveau_sable 0
Chauffage
Arrivée_sable
Capteur Commande chauffage
de détection arrivée sable
Le diagramme préliminaire, présenté sur la figure 2.36, donne une analyse ou décom-
position fonctionnelle du processus fonctionnel initial 0 « Piloter four à verre ».
Cette analyse fait apparaître six processus fonctionnels de base et un processus de
contrôle permettant de séquencer l’ensemble. Nous pouvons vérifier la cohérence des
flots de données ou d’événements entrants ou sortants par rapport au diagramme de
contexte.
Les différents processus fonctionnels correspondent aux deux chaînes de régulation :
température (processus fonctionnels 1 à 3) et approvisionnement en sable (processus
fonctionnels 4 à 6). La régulation de la température suit exactement la décomposi-
tion fonctionnelle générique que nous avons vue (figure 2.14). Ainsi, les trois pro-
cessus fonctionnels de base existent : acquisition (1 – Acquérir température), traite-
ment (2 – Analyser température) et commande (3 – Chauffer four). Les deux unités
de stockage sont utilisées dans les deux cas classiques : soit pour la mémorisation
d’une constante (Température_consigne) soit pour une donnée partagée (Température_
© Dunod – La photocopie non autorisée est un délit.
mesurée). Dans ce dernier cas, nous pouvons noter que l’unité de stockage est utilisée
deux fois dans le diagramme et donc comporte une « * ».
Dans le cas de la régulation du niveau du sable, les trois processus fonctionnels mis
en œuvre ne correspondent pas exactement au modèle de décomposition générique
précédent ; mais nous avons uniquement deux processus fonctionnels : acquisition
(5 – Acquérir niveau), traitement et commande (6 – Analyser besoin sable). Nous
pouvons remarquer que ce dernier processus utilise pour élaborer la décision de
commande trois données : Niveau_consigne (constante placée dans une unité de
stockage), Niveau_mesurée (donnée fournie directement par le processus 5) et
Température_mesurée (unité de stockage partagée avec la chaîne de régulation de la
63
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
Température_*
mesurée Niveau_
Température
Marche consigne
Arrêt
Aquérir
température
1 Analyser sable
E/D
E/D besoin
Température_* sable
Réguler 6
mesurée four
T 7
Niveau_mesuré
Analyser T
température
2 Trop_froid
Sable_
T E/D Analyser Niveau_sable
tombé
Commande_ niveau
5
chauffage
Chauffage_
Température_ terminé Détecter
consigne Chauffer arrivée
four sable
3 4
Chauffage Arrivée_sable
64
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
État repos
Marche Arrêt
<E> Acqérir température <D> Acqérir température
<T> Analyser température <D> Détecter arrivée sable
<E> Détecter arrivée sable
Fonctionnement
nominal
Régulation
Chauffage
niveau
du four
sable
65
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
niquent entre eux par un bus de terrain comme CAN (voir chapitre 4) afin de par-
tager des informations et gérer ainsi le véhicule de façon cohérente.
Cette application de commande d’un moteur à combustion est représentée schéma-
tiquement sur la figure 2.38. Le contrôle-commande de cette application est fait par
l’intermédiaire de sept capteurs (pédale accélérateur, température air, pression air,
température eau, rotation vilebrequin et deux capteurs de pollution) et de quatre
actionneurs (injection essence, allumage, admission air, réinjection gaz échappement
ou brulés). Le calculateur est donc aussi relié au bus de communication CAN.
Bus CAN
Commande de réinjection Capteur pression
Calculateur
gaz échappement collecteur air Communication
avec les autres
calculateurs
Commande
admission air (papillon)
Capteur pédale
accélérateur
Capteur
Commande
pollution
injecteur
en amont Capteur
pollution
en aval
Commande allumage
Excepté les deux capteurs de pollution amont et aval, la loi de régulation du moteur
à combustion prend en compte l’ensemble des données d’entrée et élabore les dif-
férentes sorties de commande. Nous n’analyserons pas cette loi de commande qui
présente une relative complexité et correspond à une spécificité constructeur pour
un type de moteur donné. Ainsi, l’élaboration de la loi de commande du moteur à
combustion est considérée comme une « boîte noire » fonctionnelle qui utilise un
ensemble de données en entrée (Paramètres_moteur_entrée) et qui fournit des don-
nées en sortie (Paramètres_moteur_sortie). Les données sur la pollution ne sont pas
utilisées dans ce calculateur ; mais elles sont fournies par l’intermédiaire du réseau
de communications à un autre calculateur, par exemple, pour affichage.
66
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
m Analyse SA-RT
M Diagramme de contexte
Le diagramme de contexte de l’application est représenté sur la figure 2.39. Il donne
les 11 bords de modèles correspondant aux sept entrées ou capteurs et aux quatre
sorties ou actionneurs, énumérés précédemment. Nous avons ajouté un bord de
modèle correspondant à la connexion au bus CAN et un bord de modèle représentant
l’action du conducteur. Le dernier bord de modèle fournit les deux événements :
« Arrêt » et « Marche ». Pour le bord de modèle du bus CAN, nous supposons que
les communications bidirectionnelles sont identifiées : en sortie (Com_S) et en entrée
(Com_E).
Capteur accélérateur
Conducteur
Ac
Bus CAN
cé
_S _E
ai
r
ge
u ma
Capteur température eau T_eau All
Piloter
moteur Commande injecteur
sse Injecteur
Vite 0
Capteur vitesse
E En
n_ tré
io e_a
ll ut En ir
S
Po
n_
z_
Po
67
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
Accélérateur
Com_S
Arrêt
Marche
Paramètres_* Com_E
moteur_entrée Acquérir
accélérateur
1 Communiquer
P_air bus CAN
E/D 9
T_air Acquérir
Messages*
paramères E/D E/D
moteur Contrôler
T_eau moteur
2 T Allumage
10 Commande
Paramètres_* E/D allumage
Moteur en entrée T 8
Acquérir
Paramètres_*
vitesse T moteur_sortie
E/D Commande_ T
3 Commander
s se prête
Vite
injection
Messages* 7
Lire Élaborer Injecteur
capteur commande
pollution moteur Commander
_E 4 5 Entrées gaz Entrée_air
on
lluti 6
Po
_S
moteur_entrée moteur_sortie
ut
ll
Po
68
2 • Spécification 2.8 Exemples
selon la méthode SA-RT
Une autre unité de stockage est dédiée aux messages (Messages). Cette unité de stoc-
kage est un peu particulière par rapport aux autres unités de stockage. En effet, en
entrée, nous avons une file gérée de façon FIFO ou à priorité et, en sortie, la file
est gérée de manière FIFO.
Remarquons que toutes ces unités de stockages sont dupliquées au niveau de ce
diagramme et donc notées avec une « * ».
Le processus de contrôle (10 – Contrôler moteur) est lié aux différents processus
fonctionnels par des événements qui sont mis en place en même temps que la
réalisation du diagramme état/transition de la figure 2.41. Nous avons simplifié le
fonctionnement de ce processus de contrôle en supposant que les processus fonc-
tionnels d’acquisition étaient lancés au début de l’exécution (1, 2, 3 et 4) et four-
nissaient les données de façon périodique ; en particulier le processus fonctionnel
« 1 – Acquérir accélérateur » fournit régulièrement l’événement État_accélérateur
qui déclenche l’élaboration de la loi de commande à partir de toutes les données
d’entrée acquises. Lorsque la commande est prête, le processus « 5 – Élaborer com-
mande moteur » déclenche l’ensemble des processus fonctionnels de commande
(6, 7 et 8). Enfin, le processus fonctionnel 9 de communication gère périodique-
ment les messages au niveau réception et émission.
État repos
marche arrêt
<E> 1 <D> 1
<E> 2 <D> 2
<E> 3 <D> 3
<E> 4 <D> 4
<E> 9 <D> 9
État attente
État_accélérateur Commande_prête
<T> 5 <T> 6
<T> 7
<T> 8
Élaboration
contrôle
moteur
Diagramme état/transition.
Nous pouvons remarquer que, dans ce diagramme état/transition très simple, nous
avons utilisé uniquement deux états actifs en plus de l’état repos : Un état attente
qui est périodiquement interrompu par l’événement « État_accélérateur » et un état
correspondant à l’élaboration de la loi de commande du moteur à combustion.
69
2 • Spécification 2.9 Extensions de la méthode SA-RT
selon la méthode SA-RT
Une syntaxe graphique plus complète permet de préciser les flots de données discrets
ou continus. Ainsi, l’analyse des flots de données permet et, donc oblige, de distin-
guer un flot de données discret (arc orienté simple comme précédemment) et un flot
de données continu (arc orienté avec une double flèche). Mais, dans ce cas, où la
richesse d’expression des données véhiculées par ces flots est augmentée, il est
nécessaire de préciser la sémantique attachée à cette nouvelle description. Or deux
types de sémantique peuvent être attachés aux flots de données (figure 2.42) :
– Sémantique 1 :
• Flot de donnée discret : valeur discrète de donnée (type booléen) ;
• Flot de donnée continu : valeur continue de la donnée (type entier ou réel).
– Sémantique 2 :
• Flot de donnée discret : donnée consommable ou lisible une fois (existence de
la donnée à des temps discrets) ;
Utilisateur
Distance_parcourue
Top_horloge Entrée_nom
Sémantique 1 Sémantique 2
70
2 • Spécification 2.9 Extensions de la méthode SA-RT
selon la méthode SA-RT
Comme pour les flots de données, une syntaxe graphique plus complète permet de
préciser les flots d’événements discrets ou continus. Ainsi, l’analyse des flots d’évé-
nements permet et, donc oblige, de distinguer un flot d’événements discret (arc
orienté simple comme précédemment) qui concerne des événements consommables
ou lisibles une seule fois (existence à des temps discrets). De même un flot d’évé-
nements continu (arc orienté avec une double flèche) décrit un événement conti-
nuellement disponible (existence permanente).
Il est important de noter que les événements prédéfinis « E, D, T » sont des événe-
ments discrets, envoyés à chaque fois pour activer ou arrêter un processus fonctionnel.
En revanche, un flot d’événements continu permet au processus de contrôle de tester
en permanence le résultat d’un processus fonctionnel qui est indépendant du pro-
cessus de contrôle (figure 2.43).
Température
T_consigne
71
2 • Spécification 2.9 Extensions de la méthode SA-RT
selon la méthode SA-RT
La méthode SA-RT, qui vient d’être présentée dans les sections précédentes, est
une méthode de spécification simple avec un langage graphique très compréhensible
par un utilisateur. Cette facilité d’expression a un inconvénient majeur, celui de
pouvoir conduire à des ambiguïtés. En effet, lors de l’analyse d’une application,
basée sur la méthode SA-RT, la réalisation des différents diagrammes flots de données
reflète l’idée du concepteur au travers d’un langage simple, limité et très abstrait.
Ainsi, deux utilisateurs peuvent avoir exprimé deux analyses différentes sous la
forme d’un même diagramme flots de données SA-RT. Pour lever cette ambiguïté,
il est nécessaire de disposer d’un outil formel qui offre un moyen d’expression précis
et qui peut être validé.
Ainsi, il est possible d’utiliser en complément ou en parallèle de cette méthodologie
SA-RT, un modèle formel comme des automates à états finis ou des réseaux de
Petri. Ces modèles présentent l’avantage d’être rigoureux au niveau de l’expression
et d’être analysable mathématiquement. En revanche, ces modèles sont en général
complexes et d’une lisibilité faible. Aussi le projet européen IPTES (Incremental
Prototyping Technology for Embedded Real-Time Systems), dont les résultats ont été
présentés en 1998, a voulu donner une sémantique à la méthode SA-RT basée sur les
réseaux de Petri. Nous pouvons schématiser cette association par le graphique de la
figure 2.45. Ainsi, la méthode SA-RT peut être vue comme l’interface de la méthode
d’analyse globale avec un langage graphique de haut niveau pour exprimer la spé-
cification, c’est l’interface utilisateur. Directement liée à ce modèle graphique, nous
trouvons la correspondance dans le modèle formel apporté par les réseaux de Petri
qui constituent le noyau fonctionnel, c’est-à-dire la partie permettant d’exécuter
ce modèle graphique représentant la spécification.
L’utilisateur décrit sa spécification avec la syntaxe graphique SA-RT et, de façon
automatique, le modèle formel, basé sur les réseaux de Petri, se construit. Cela permet
ensuite une analyse mathématique de la spécification : simulation et vérification.
Pour atteindre ce but, il est nécessaire d’avoir une traduction unique d’un modèle
vers l’autre.
Langage graphique
Langage machine
de haut niveau
pour l'analyse
pour la spécification
72
2 • Spécification 2.9 Extensions de la méthode SA-RT
selon la méthode SA-RT
m Principes généraux
Écrire_donnée_vide Vide
Écrire_donnée Lire_donnée
73
2 • Spécification 2.9 Extensions de la méthode SA-RT
selon la méthode SA-RT
Écrire_donnée_vide Vide
Écrire_donnée Lire_donnée
(cas flot continue) ou qui a été consommée (cas du flot discret). Cette place possède
un jeton à l’initialisation. Dans le cas du flot de données continu, à la première écri-
ture de la donnée la place « Vide » perd son jeton et la place « Valeur » sera ensuite
toujours marquée. En revanche, dans le cas du flot de données discret, le franchis-
sement de la transition « Lire_donnée » fait passer le jeton de la place « Valeur » à la
place « Vide ».
La modélisation de l’unité de stockage, représentée sur la figure 2.48, est plus simple
d’un point de vue réseau de Petri. Ainsi, nous avons une place « Stockage », intégrant
un jeton représentant la donnée, et les deux transitions correspondant à l’écriture
et à la lecture (Écrire_donnée et Lire_donnée). En revanche, dans le but d’obtenir
un modèle formel précis, une sémantique particulière est associée au jeton décrivant
l’état de la donnée selon la lecture ou non de cette donnée. L’enregistrement dans
l’unité de stockage comprend deux champs : la donnée elle-même et son état
(tableau 2.5). Après chaque écriture, la donnée a un état déclaré nouveau qui peut
donc être utilisé par le processus fonctionnel qui lit cette donnée.
Donnée État
74
2 • Spécification 2.9 Extensions de la méthode SA-RT
selon la méthode SA-RT
Do 2
nn d_
ée e_
_d é
_1 nn
Do 3
e_d_
Donnée_c_1 onné
Processus D
Donnée_c_2 A
1 Donnée_c_3
Donnée_s_1
Donnée_s_2 Donnée_s_3
Donnée_d_1
Donnée_d_2
Suppression
Exécution Donnée_d_3
Donnée_c_1 Fin
Donnée_c_3
Oisif
Donnée_c_2
© Dunod – La photocopie non autorisée est un délit.
Donnée_s_3
Lancement
Donnée_s_1
Donnée_s_2
75
2 • Spécification 2.9 Extensions de la méthode SA-RT
selon la méthode SA-RT
m Exemple simple
La difficulté de ce genre de modélisation par élément est le recollement des différents
modèles lors de l’analyse d’une application complète. Il n’est pas possible d’ajouter
ou de juxtaposer les modèles de chaque élément d’une application sans analyser
l’ajustement de deux modèles l’un à la suite de l’autre. En effet, dans notre cas, les
transitions ou les places situées aux limites des modèles doivent se lier avec le modèle
précédent ou suivant.
Nous allons étudier cet aspect sur un exemple simple composé de deux processus
fonctionnels (Processus A et Processus B) et d’une unité de stockage, appelée
« Donnée_s » (figure 2.50). Ces éléments sont liés par des flots de données discrets.
D’autre part des flots de données discrets arrivent sur les deux processus fonctionnels
(Donnée_a et Donnée_b) et un flot de données discret est émis (Donnée_c).
Donnée_s
76
© Dunod – La photocopie non autorisée est un délit.
T_3 Donnée_s
selon la méthode SA-RT
2 • Spécification
A_Oisif B_Oisif
Donnée_b_valeur
Figure 2.51 – Traduction du diagramme flots de données de la figure 2.50 en réseaux de Petri.
2.9 Extensions de la méthode SA-RT
77
2 • Spécification 2.9 Extensions de la méthode SA-RT
selon la méthode SA-RT
sus B. L’unité de stockage est modélisée par une seule place « Donnée_s » comme
le montre le modèle initial de la figure 2.48. En se basant sur le modèle générique
de la figure 2.46, les trois flots de données discrets sont représentés chacun par deux
places, soit, par exemple pour le flot de données « Donnée_a » du modèle SA-RT :
« Donnée_a_vide » et « Donnée_a_valeur ». En résumé nous pouvons noter que,
lors de la traduction d’un diagramme flots de données de SA-RT en un réseau de
Petri, il y aura toujours un nombre de places égal à la somme des places des modèles
génériques des différents éléments du diagramme SA-RT.
En revanche, nous pouvons constater que les transitions sont partagées entre les
différents éléments modélisés. Considérons le cas du raccordement du flot de don-
nées « Donnée_a » au processus fonctionnel A. La transition T_2 représente à la fois
la transition « Lire_donnée » du modèle du flot de données discret et la transition
« Exécution » du modèle d’un processus fonctionnel. De même la transition T_1
représente à la fois la transition « Écrire_donnée » du modèle du flot de données
discret et la transition « Suppression » du modèle d’un processus fonctionnel. La
dernière transition T_3, attachée à la modélisation du processus fonctionnel « Proces-
sus A », correspond en même temps à la transition « Fin » du modèle générique
d’un processus fonctionnel et à la transition « Écrire_donnée » du modèle de l’unité
de stockage « Donnée_s ». Nous pouvons répéter cette constatation pour l’ensemble
du réseau de Petri.
Le réseau de Petri, qui est l’exact modèle du diagramme flots de données SA-RT,
peut être utilisé pour vérifier certaines propriétés de la spécification comme la non
occurrence de l’exécution simultanée de deux processus fonctionnels, le chemine-
ment des données, etc. Une fonction de simulation peut être réalisée à l’aide du
modèle formel réseau de Petri sous-jacent aux diagrammes SA-RT. Dans ce cadre,
le réseau de Petri est analysé en traçant le graphe de marquages dans le cas de tran-
sition immédiate (modèle [0,0] de Merlin et Farber), c’est-à-dire l’évolution de la
position des jetons dans le réseau. Si le modèle des transitions temporisées est pris
compte l’analyse est réalisée à l’aide d’un graphe des classes. En parallèle avec cette
évolution des jetons correspondant à l’activation ou non de l’action associée à la place,
il est alors possible de visualiser l’activité au niveau du diagramme flots de données
SA-RT. Prenons l’exemple précédent, le marquage présenté sur la figure 2.51 cor-
respond à un moment de l’exécution où nous avons les éléments suivants :
– pas de donnée sur le flot de données discret « Donnée_a » entrant ;
– le processus fonctionnel « Processus A » en exécution ;
– une donnée présente dans l’unité de stockage « Donnée_s » ;
– une donnée présente sur le flot de données discret « Donnée_b » entrant ;
– le processus fonctionnel « Processus B » en arrêt ;
– pas de donnée sur le flot de données discret « Donnée_c » sortant.
Par conséquent, il est possible de visualiser ces différents états sur le diagramme
flots de données initial, représenté sur la figure 2.50. Cette visualisation peut être
animée et donc ainsi permettre au concepteur de voir le déroulement de l’exécution
de diagramme flots de données SA-RT. Cette visualisation est schématisée sur la
78
2 • Spécification 2.9 Extensions de la méthode SA-RT
selon la méthode SA-RT
figure 2.52 qui reprend la figure 2.50 avec une marque symbolisant l’activité d’un
processus fonctionnel ou la présence d’une donnée.
Donnée_s
79
3 • CONCEPTION
SELON LA MÉTHODE DARTS
3.1 Introduction
La méthode de conception va permettre de passer d’un modèle de spécification
aux programmes codés dans un langage exécutable. Étant donné l’analyse de type
fonctionnel et structuré qui a été réalisée avec la méthode SA-RT, nous pourrions
être tentés de traduire directement ces modules fonctionnels (processus fonctionnels
des diagrammes flots de données de SA-RT) en des entités de programmes. Cette
approche serait très néfaste pour deux raisons :
– d’une part, le découpage modulaire de l’application a été fait en se préoccupant
essentiellement des fonctions à réaliser et non pas de la structuration efficace en
termes de réalisation et d’exécution, c’est-à-dire que le nombre de tâches de
l’application ne correspond pas obligatoirement au nombre de processus fonc-
tionnels des diagrammes SA-RT ;
– d’autre part, les relations entre les processus fonctionnels des diagrammes flots
de données sont décrites de manière très abstraite (zone mémoire, passage de
données) sans se préoccuper de leurs implémentations possibles avec ou sans
synchronisation.
La question qui se pose concerne le choix de cette méthode. En effet, il serait tout
à fait envisageable, comme nous l’avons vu dans les exemples industriels du para-
graphe 1.3.4, d’associer une méthode d’analyse de type « flot de données » à une
méthode de conception de type « orienté objets ». En revanche, le choix de l’une
ou l’autre voie amène quasi obligatoirement un choix de langage. Ainsi, la figure 3.1
© Dunod – La photocopie non autorisée est un délit.
représente les choix possibles selon les deux groupes de méthodes de conception.
La méthode DARTS (Design Approach for Real-Time Systems – H. Gomaa, 1984)
trouve naturellement sa place entre la méthode d’analyse SA-RT et une implémen-
tation avec un langage de programmation de haut niveau exécuté dans un envi-
ronnement de type noyau temps réel, ou avec un langage multitâche (Ada) ou un
langage flots de données (LabVIEW).
81
3 • Conception 3.1 Introduction
selon la méthode DARTS
• Ada • Ada
•C • C++
• labVIEW
l’architecture donnée sur la figure 3.2, c’est-à-dire les différentes tâches, les relations
entre les tâches et la possibilité d’accès à des ressources critiques.
Ainsi, nous pouvons donc décliner les différentes caractéristiques à représenter
avec la méthode DARTS :
– les tâches : type d’activation, paramètres ou données en entrées ou en sorties ;
– les relations entre les tâches :
• synchronisation de type asynchrone ou synchrone (rendez-vous),
• communication avec des données qui transitent entre deux tâches ;
– le partage de ressources critiques.
Tâche 8 synchronisation
entrée
Tâche 1 sortie
Ressource Tâche 7
critique
synchronisation
communication
Tâche 2
R Tâche 5
entrée sortie
Tâche 3 communication Tâche 6
Tâche 4
synchronisation
82
3 • Conception 3.1 Introduction
selon la méthode DARTS
Demande
Tâche d'affichage Tâche
acquisition affichage
Capteur Données
ou externes associés à ces tâches matérielles. Les principaux types de signaux sont
(figure 3.4) :
– les signaux périodiques qui peuvent être caractérisés par une première date
d’occurrence et une période. Ces signaux (référencés 1 sur la figure 3.4) provien-
nent en général d’une horloge interne « HTR – Horloge Temps Réel ». ;
– les signaux apériodiques qui se produisent de façon répétitive mais sans période
fixée (référencés 2 sur la figure 3.4). Pour déterminer les caractéristiques des
tâches associées à ces signaux apériodiques, il est nécessaire de posséder des infor-
mations temporelles les concernant, par exemple la durée minimale entre deux
occurrences ;
83
3 • Conception 3.1 Introduction
selon la méthode DARTS
Système de contrôle
– les signaux sporadiques qui se produisent une seule fois, comme les signaux
d’alarme provenant du procédé.
Le point le plus complexe est la traduction des différentes relations entre les tâches.
En effet, cette relation traduit « un lien de dépendance entre les tâches », c’est-à-dire
qu’une ou plusieurs tâches ne commencent leur exécution uniquement que lors-
qu’une autre tâche s’est exécutée en partie ou en totalité. Cette synchronisation ou
communication peut être unilatérale ou asynchrone (seule la tâche en attente est
bloquée), ou bilatérale ou synchrone (les deux tâches ont un rendez-vous, exemple
du langage Ada). La figure 3.5 montre cette différence de relations de synchronisa-
tion entre deux tâches. Dans le premier cas I de la relation asynchrone, la tâche 1
bloque la tâche 2 qui doit attendre l’exécution d’une partie de la tâche 1 en un certain
point de son code ; en revanche, dans le second cas, les deux tâches doivent s’attendre
mutuellement.
Attachée à cette synchronisation de deux ou plusieurs tâches, un transfert de données
peut être associé, nous parlons alors de communication. Une communication est
caractérisée par la taille de cette zone d’échange de données (une ou plusieurs don-
nées) et le mode de gestion de cette zone de données (FIFO – First In Fisrt Out,
à priorité). Le critère principal, lié à cet échange de données, est l’aspect bloquant
ou non de l’interaction entre les tâches. Si la production et la consommation de
données sont synchronisées, c’est-à-dire que, si la zone de données est vide, la tâche
consommatrice ne peut s’exécuter et réciproquement, si la zone de données est
pleine, la tâche productrice ne peut s’exécuter. Nous avons dans ce cas une com-
munication mettant en jeu une vraie synchronisation entre les tâches. Dans le cas
contraire, la zone de données est dite « à écrasement » et nous avons un blocage
uniquement en lecture des données.
84
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
Synchronisation Synchronisation
unilatérale ou asynchrone bilatérale ou synchrone (rendez-vous)
Point de synchronisation
Tâche 1 t Tâche 1 t
Cas l
Cas l
Attente Attente
Tâche 2 t Tâche 2 t
Attente
Tâche 1 t Tâche 1 t
Cas II
Cas II
Pas d’attente Pas d’attente
Tâche 2 t Tâche 2 t
de conception DARTS est de type flots de données. Ainsi, les diagrammes flots de
données de la méthode SA-RT (diagrammes préliminaires ou diagrammes de
décomposition) sont traduits en diagramme flots de données DARTS représentant
l’architecture multitâche de l’application.
D’autre part, la méthode DARTS que nous présentons dans cet ouvrage permet de
décrire de nombreuses applications de type contrôle-commande. Mais afin d’obtenir
des applications dont le test ou la vérification peuvent être conduits de façon fiable,
nous avons volontairement limité la possibilité d’expression en se donnant des règles
de « bonne conception ». Ces règles correspondent en partie à un profil de program-
mation adopté dans le cadre de la programmation en langage Ada d’applications à
haut niveau de sécurité. La présentation détaillée de ce profil de programmation,
85
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
appelé Ravenscar profile (1997), est effectuée dans le chapitre 5. Dans ce contexte,
nous pouvons ainsi définir les règles suivantes :
– Nombre fixé de tâches au démarrage de l’application.
– Un seul événement de déclenchement par tâche : signal temporel, synchronisation
avec une autre tâche.
– Interaction entre les tâches qui doit être réalisée uniquement par données partagées
gérées de manière atomique.
– Possibilité d’analyse fonctionnelle des tâches de façon individuelle.
Il est à noter que d’autres règles concernant plus la partie implémentation et l’aspect
ordonnancement sont étudiées dans les chapitres concernés.
En premier lieu, nous trouvons la tâche qui représente l’entité de base de l’archi-
tecture multitâche. Nous pouvons avoir un ou plusieurs flots de données en entrées
et un ou plusieurs flots de données en sortie (figure 3.6). Les tâches sont modélisées
par un parallélogramme qui comporte une étiquette ou label explicite formé de :
Étiquette_Tâche = verbe (+ un ou plusieurs compléments d’objets)
Cette étiquette peut correspondre à celle donnée dans le cadre d’un diagramme
flots de données SA-RT s’il y a, par exemple, une correspondance entre une tâche
et un processus fonctionnel comme nous allons l’étudier dans la suite de ce chapitre.
Nous avons aussi un signal d’activation qui a une provenance et un type différents
selon que nous modélisons une tâche matérielle (activation de type événement
externe au programme) ou une tâche logicielle (activation de type événement interne
au programme : synchronisation ou communication). Ce signal d’activation doit
obligatoirement exister et doit être unique pour répondre au profil de conception fixé.
Nous avons aussi le module de traitement qui correspond à des programmes spé-
cifiques appelés par les tâches pour effectuer des calculs particuliers. Nous pouvons
avoir un ou plusieurs flots de données en entrées et un ou plusieurs flots de données
en sortie (figure 3.6). Les modules de traitement sont modélisés par un rectangle
qui comporte une étiquette ou label explicite formé de :
86
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
Synchronisation multiple
de type « OU »
© Dunod – La photocopie non autorisée est un délit.
Pour les communications, nous avons plusieurs modèles correspondant d’une part
à la taille de la zone de communication et d’autre part à la gestion de cette zone de
stockage des données (figure 3.8). D’une manière générale, ces modèles « boîtes
aux lettres – BAL » représentent le cheminement des données entre les tâches avec
un aspect relation de précédence pour certaines communications. Les communi-
cations sont donc représentées par un symbole orienté avec une étiquette ou label
explicite formé de :
87
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
BAL à priorité
88
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
Comme nous l’avons présenté dans le paragraphe 3.1.2, il existe deux types de
tâches : tâches matérielles et tâches logicielles. Dans les deux cas, l’activation est très
différente. Ainsi, pour les tâches matérielles qui sont déclenchées par des événements
ou signaux externes, nous distinguons trois types de signaux :
– Signal « Horloge temps réel – HTR ». Ce signal, qui provient d’une horloge maté-
rielle interne à l’ordinateur, correspond à un signal rigoureusement périodique.
– Signal « Interruption – IT ». Ce signal qui provient du procédé externe doit
toujours être considéré comme apériodique du fait de l’asynchronisme du monde
extérieur par rapport au cadencement de l’ordinateur.
– Signal « Chien de garde – CG ». Ce signal provient d’une horloge interne utilisée
comme un réveil. Son utilisation et son fonctionnement sont décrits de façon
détaillée dans la suite de cet ouvrage. En termes de signal, il est identique à l’hor-
loge temps réel (signal interne) ; mais il se produit de façon apériodique.
Les activations sont donc représentées par un symbole orienté (ligne brisée) avec
une étiquette ou label explicite formé de (figure 3.10) :
Étiquette_Activation_HTR = HTR (durée en ms)
Étiquette_Activation_IT = IT (+ nom interruption)
Étiquette_Activation_CG = CG (+ nom chien de garde)
Valeur de la période en ms
HTR (Val_per)
Tâche matérielle périodique
© Dunod – La photocopie non autorisée est un délit.
Acquérir
Entrée Sortie
température
IT (Nom_interruption),
CG (Nom_chien_de_garde)…
Tâche matérielle apériodique
Acquérir
Entrée Sortie
température
89
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
Comme nous l’avons déjà explicité, les tâches logicielles sont déclenchées par d’autres
tâches (matérielles ou logicielles) avec les mécanismes de synchronisation ou de
communication de type bloquant. Ainsi, le signal d’activation de ces tâches peut
être l’action des éléments suivants (figure 3.11) :
– boîtes aux lettres à écrasement ou non gérées selon une file FIFO ou FIFO à
priorité ;
– boîtes aux lettres à écrasement ou non à une seule place ;
– boîtes aux lettres à n places gérées selon la priorité.
Tâche logicielle
HTR (250 ms)
Ainsi, nous pouvons noter qu’une tâche logicielle ne peut pas être activée par un
élément de synchronisation et en même temps être connectée à une autre tâche en
amont par une boîte aux lettres à écrasement, c’est-à-dire bloquante en lecture
(figure 3.12). Ainsi, la tâche « Commander vanne » doit être synchronisée soit par
la tâche matérielle « Attendre mesures » qui est activée par une interruption, soit
par l’autre tâche matérielle « Acquérir niveau », dite à scrutation, qui est périodique
et activée par l’horloge temps réel dont la période est de 350 ms.
IT (déclenchement_mesures)
Evt_mesure
Attendre
mesures
?
Commander Sortie
HTR (350 ms) vanne
BAL_niveau
Acquérir
Entrée_niveau
niveau
90
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
Le dernier élément à modéliser est le module de données qui permet une protection
des accès à une unité de gestion de données en exclusion mutuelle par deux ou
plusieurs tâches. Les modules de données sont représentés par un rectangle associé
à des entrées permettant de réaliser une action sur les données : LIRE, ÉCRIRE, etc.
Ce symbole du module de données est représenté avec une étiquette ou label expli-
cite formé de (figure 3.13) :
Étiquette_module_données = nom (+ qualifiant)
Module_de_données
Tâche
1
LIRE
ÉCRIRE
Tâche
2
Module_de_données Module_de_données
LIRE
LIRE
ÉCRIRE
ÉCRIRE
INITIALISER
91
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
m Règles générales
Pour cette méthode de conception non formelle, nous allons définir des règles géné-
rales permettant de passer d’un diagramme flots de données de la méthode SA-RT
à un diagramme multitâche DARTS. Pour cela, nous devons réaliser la traduction
selon les quatre phases suivantes :
– Phase 1 : Création des tâches. Cela correspond à la traduction des processus
fonctionnels ou de contrôle en tâches.
– Phase 2 : Détermination du typage et de l’activation des tâches. Les tâches sont
déclarées matérielles ou logicielles. Dans le cas des tâches matérielles, le signal
d’activation doit être défini précisément (HTR, IT, CG). Pour les autres tâches
logicielles, il est nécessaire d’identifier les synchronisations permettant de les
activer. Celles-ci sont souvent déjà déclarées comme des événements traités par
le processus de contrôle.
– Phase 3 : Mise en place des synchronisations et des transferts de données.
Les relations par communications sont traduites par des boîtes aux lettres ou
par des modules de données.
– Phase 4 : Regroupement ou rassemblement des tâches. Afin d’améliorer et de
simplifier la première conception réalisée de façon semi-automatique en suivant
les trois règles de base que nous allons décrire, le diagramme multitâche est de
nouveau analysé, et les tâches sont regroupées selon un ensemble de critères qui
sont exposés dans la suite.
Pour la première phase de création des tâches, nous pouvons définir trois règles de
base qui servent de guide à cette traduction semi-automatisée :
– Règle 1.1 : Une tâche du modèle DARTS est créée pour chaque processus fonc-
tionnel du diagramme SA-RT.
– Règle 1.2 : Une tâche supplémentaire du modèle DARTS est associée au proces-
sus de contrôle du diagramme SA-RT si le diagramme état/transition, associé à
ce processus de contrôle, est complexe, c’est-à-dire qu’il possède au moins une
structure conditionnelle.
En ce qui concerne le typage et l’activation des tâches, nous pouvons énoncer les
règles suivantes :
– Règle 2.1 : Une tâche en entrée (acquisition de données) est obligatoirement de
type matériel déclenchée soit par l’horloge temps réel (ex. : tâche de scrutation
d’un paramètre physique), soit par une interruption provenant du procédé
(ex. : tâche d’alarme).
92
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
– Règle 2.2 : Les autres tâches sont déclarées soit logicielles (activation par syn-
chronisation ou communication avec les tâches matérielles), soit matérielles
déclenchée par un événement interne (horloge temps réel, chien de garde…).
– Règle 2.3 : Les événements importants du diagramme flots de données de SA-RT
peuvent être traduits par des synchronisations qui sont utilisées pour activer des
tâches logicielles.
Enfin, les communications entre les tâches de DARTS sont établies en se basant
sur les deux règles suivantes :
– Règle 3.1 : Les communications directes entre les processus fonctionnels (flot de
données d’un processus fonctionnel à un autre) sont traduites préférentiellement
par des boîtes aux lettres.
– Règle 3.2 : Les communications par une zone de stockage entre les processus
fonctionnels sont traduites préférentiellement par des modules de données, en
particulier si cette zone de stockage se trouve partagée par plus de deux tâches.
Les règles (ou critères de regroupement des tâches) concernant la dernière phase
sont étudiées dans la suite de cette section sur un exemple précis.
Cet ensemble de règles ne constitue pas une traduction automatique et formelle de
la spécification SA-RT en des diagrammes multitâches DARTS. En effet, la règle,
qui concerne les regroupements ou les scissions éventuelles des tâches créées en se
basant sur les deux premières règles 1.1 et 1.2, laisse un libre choix de la configura-
tion multitâche. De même les deux règles 3.1 et 3.2, concernant les communica-
tions entre les tâches, ne définissent en rien la gestion de ces boîtes aux lettres
(FIFO ou priorité, à écrasement ou non, à une ou plusieurs places). Ainsi, en partant
d’une même spécification SA-RT (diagramme flots de données), cette phase de
conception, basée sur DARTS, donne naturellement plusieurs solutions selon le
concepteur qui effectue la traduction.
Température_moteur
HTR (2 ms)
Tester Commande_moteur
état Commande_moteur
Moteur Température_moteur Contrôle
3 Vitesse_moteur moteur
Vitesse_moteur
93
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
Nous pouvons noter les noms des différents éléments qui sont conservés autant
que possible. Cette tâche matérielle est donc une tâche d’acquisition de données
dite à scrutation. La transmission de la donnée « Commande_moteur » est traduite
par une boîte aux lettres à n places conformément à la préconisation de la règle 3.1.
Dans l’exemple de la figure 3.16, nous avons aussi un processus fonctionnel cor-
respondant à l’acquisition de données provenant de deux capteurs (thermocouple
et capteur rotatif ). D’après la règle 2.1, ce processus fonctionnel se transforme en
une tâche matérielle d’entrée qui va être activée par une interruption (Dépassement_
température) qui est issue de la mesure du capteur de température. La tâche ainsi
créée est donc apériodique et s’active au rythme de l’interruption. Le processus
fonctionnel émet un événement « Afficher_alarme » vers le processus de contrôle
supposé. Dans ce cas, en appliquant la règle 3.2, nous traduisons cet événement
par une synchronisation nommée « Alarme ».
Température_moteur
IT (Dépassement_température)
Tester
Afficher_alarme Alarme
état
Tester
Moteur Vitesse_moteur
état moteur
3
Vitesse_moteur
De la même manière que pour la création des tâches à partir des diagrammes flots
de données de SA-RT, la traduction des transferts de données peut suivre les règles
affichées. Ainsi, la figure 3.17, qui présente un flot de données direct entre deux
processus fonctionnels, montre la traduction par un élément de communication
de type boîte aux lettres conformément à la règle 3.1. Nous pouvons remarquer
que la première tâche est de type matériel, déclenchée par une horloge temps réel,
et la deuxième tâche de type logiciel est activée par la synchronisation de la commu-
nication par boîte aux lettres.
HTR
Figure 3.17 – Traduction d’un flot de données direct entre deux processus fonctionnels
par une boîte aux lettres.
Le deuxième cas traité, illustré sur la figure 3.18, est celui du transfert de données
par une unité de stockage au niveau du diagramme flots de données SA-RT. Selon
94
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
la règle 3.2, il est naturel de traduire ce flot de données par un module de données ;
toutefois ce transfert étant limité à deux processus fonctionnels, aurait pu aussi être
traduit par un élément de type boîte aux lettres comme dans le cas de la figure 3.17.
En revanche, la première traduction par un module de données, qui n’est pas une
synchronisation, oblige à synchroniser aussi la deuxième tâche par une autre horloge
temps réel par exemple. Les tâches sont alors indépendantes au niveau de leur cadence
d’exécution.
HTR_1 HTR_2
Produire Consommer
données données
Produire Consommer
données données
1 2 Données
ÉCRIRE
Données
LIRE
Figure 3.18 – Traduction d’une unité de stockage entre deux processus fonctionnels
par un module de données.
Nous allons mettre en œuvre cette méthodologie DARTS pour l’exemple décrit
jusqu’à présent, c’est-à-dire le « système de freinage automobile ». Pour cela nous
allons considérer le dernier diagramme préliminaire défini, celui de la figure 2.20,
modifié avec la partie de diagramme représenté sur la figure 2.25. Ce diagramme
préliminaire, présenté sur la figure 3.19, sert de base pour la traduction en dia-
gramme multitâche DARTS.
La première phase à mettre en œuvre est la création des tâches. Ainsi, selon la règle 1.1,
nous allons créer cinq tâches correspondant aux cinq processus fonctionnels du
diagramme préliminaire. Le processus de contrôle de ce diagramme préliminaire a
un fonctionnement décrit par le diagramme état/transition relativement complexe
© Dunod – La photocopie non autorisée est un délit.
représenté sur la figure 2.24. Par conséquent, d’après la règle 1.2, nous devons dans
une première étape traduire ce processus de contrôle par une tâche. Ainsi, nous
obtenons un diagramme multitâche DARTS comportant six tâches ayant les mêmes
noms que les processus fonctionnels SA-RT du diagramme préliminaire de la
figure 3.19. Ce diagramme multitâche DARTS, représenté sur la figure 3.20, com-
porte de plus pour chacune des tâches les entrées/sorties des processus fonctionnels
correspondant avec des noms identiques. La nomination correspondante entre les
diagrammes SA-RT et DARTS n’est pas obligatoire, mais elle joue un rôle important
sur la traçabilité lors du passage de la spécification à la conception.
La deuxième phase concerne la détermination du type des tâches et de leur activation.
En suivant la règle 2.1, les tâches d’acquisition « Acquérir demande freinage »,
95
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
Acquérir
demande
Demande_freinage freinage
1 Pa Niveau_freinage
s_
de
_f
re
in
Fr
ag Commande_freinage
ein
e Commander
ag
freinage
e
E/D 3
E/D
e_
Pas_d ent
m
Détecter glisse
glissement E/D Contrôler
Glissement_ de roue application
roue 2 G li s s 6
em ent E/D
é
tiv
T Afficher
ac
état bouton
S_
é ABS
AB
tiv Affichage_ABS
_ ac 5
n on
Lire S_
AB
bouton
Activation_ABS ABS Mise_en_marche
4
« Détecter glissement » et « Lire boutons ABS » sont tout naturellement des tâches
matérielles de type scrutation (tâche périodique déclenchée par l’horloge temps réel).
Nous allons définir les périodes respectives de ces tâches : 100, 150 et 1 000 ms.
Les trois autres tâches peuvent être des tâches logicielles, si elles sont activées par
l’une des tâches précédentes, ou éventuellement une tâche matérielle. Nous pouvons
identifier sur le diagramme préliminaire de la figure 3.19 deux événements « Freinage »
et « ABS_activé » qui vont agir respectivement sur les tâches « Contrôler appli-
cation » et « Afficher état bouton ABS ». Ces deux événements peuvent donc être
traduits par des synchronisations (figure 3.20).
Nous arrivons maintenant à la troisième et dernière phase de traduction quasi-
automatique du diagramme SA-RT en diagramme DARTS, sachant que la phase
suivante correspond à la partie de la conception par reprise et amélioration du dia-
gramme DARTS obtenu après les trois premières phases. La mise en place des com-
munications se fait en suivant les règles 3.1 et 3.2. Nous avons donc une traduction
du flot de données direct entre les deux processus fonctionnels « Acquérir demande
freinage » et « Commander freinage » par une boîte aux lettres. Le type de boîte aux
lettres choisi est une BAL FIFO à écrasement. En effet, comme nous allons le voir,
la tâche « Commander freinage » peut s’exécuter moins souvent que la tâche de
scrutation du capteur « Acquérir demande freinage » du fait de l’arrêt du freinage
lors du glissement et du système ABS en fonctionnement. Toutefois il est nécessaire
de synchroniser cette tâche « Commander freinage » avec la tâche « Contrôler
96
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
État_glissement
Evt _freinage
LIRE Freinage
ÉCRIRE Contrôler
application
Détecter État_demande_ABS
Glissement_roue
glissement roue
LIRE
ÉCRIRE
Lire Afficher
Activation_ABS
bouton ABS état bouton ABS
ABS_activé Affichage_ABS
qui doivent se synchroniser et s’échanger une donnée partagée par d’autres tâches.
Cette liaison peut se faire de trois manières différentes. Les conditions initiales
sont une première tâche dont l’activation est définie (dans cet exemple, une horloge
temps réel), une deuxième tâche de type logiciel qu’il est nécessaire de synchroniser
et un module de données créé afin de distribuer une donnée vers d’autres tâches.
Ainsi, les différents cas, présentés sur la figure 3.21, sont les suivants :
– Cas I : ce cas est celui adopté dans la solution du diagramme DARTS de la
figure 3.20. La deuxième tâche est donc liée à la première par une synchronisation
et la transmission de la donnée entre ces deux tâches est faite au travers du module
de données.
97
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
État_demande_ABS
LIRE
ÉCRIRE
HTR (1 000 ms)
Cas l
Lire Afficher
Activation_ABS
bouton ABS état bouton ABS
ABS_activé Affichage_ABS
État_demande_ABS
LIRE
ÉCRIRE
HTR (1 000 ms)
État_demande_ABS
LIRE
ÉCRIRE
HTR (1 000 ms) HTR (1 000 ms)
– Cas II : la deuxième tâche récupère la donnée par une boîte aux lettres qui sert
en même temps d’activation. Ainsi, la lecture de la donnée par cette deuxième
tâche vers le module de données devient inutile.
– Cas III : tous les liens de synchronisation entre les deux tâches sont supprimés
et la deuxième tâche lit la donnée dans le module de données. Pour l’activation
de la deuxième tâche, il est nécessaire de mettre un événement de type « horloge
temps réel ». Les deux tâches ont donc des activations périodiques de même
période mais non synchronisées. Pour conserver la relation de dépendance entre
ces deux tâches, la seule solution réside dans l’utilisation des priorités : la première
tâche « Lire bouton ABS » ayant la priorité la plus grande.
Reprenons le travail de conception du diagramme multitâche réalisé (figure 3.20) par
la dernière phase, c’est-à-dire le regroupement ou le rassemblement de tâches. Pour
obtenir un programme multitâche plus simple à valider et à réaliser, nous devons
98
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
Figure 3.22 – Exemple d’un regroupement de tâches d’un diagramme multitâche DARTS
basé sur une cohésion temporelle.
IT (Capteur_roue)
Capteur_roue Déterminer Distance Déterminer Vitesse
distance distance Déterminer
1 2 vitesse
Vitesse
Taille_roue Temps
© Dunod – La photocopie non autorisée est un délit.
Figure 3.23 – Exemple d’un regroupement de tâches d’un diagramme multitâche DARTS
basé sur une cohésion séquentielle.
99
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
Afficher
nouvelle donnée
T valeur HTR
2
Gérer
Gérer
affichage
affichage
1
Effacer des données Commande_affichage
effacement
T ancienne
valeur
3
Figure 3.24 – Exemple d’un regroupement de tâches d’un diagramme multitâche DARTS
basé sur une cohésion de contrôle.
Donnée_capteur
HTR
Acquérir
Donnée_capteur Piloter
E/D capteur
Commander robot
robot 3
Commande_robot
1 Position_finale Position_initiale Programme_robot
LIRE
Programme_robot Commander
déplacements
2
ÉCRIRE
Commande_robot
Figure 3.25 – Exemple d’un regroupement de tâches d’un diagramme multitâche DARTS
basé sur une cohésion fonctionnelle.
Il est important de noter que certaines tâches remplissent parfois plusieurs de ces
critères à la fois et donc la justification du regroupement est encore plus évidente.
En appliquant ces critères de cohésion au diagramme DARTS de l’application « sys-
tème de freinage automobile » de la figure 3.20, nous obtenons un diagramme multi-
tâche plus compact, puisque nous passons d’une architecture six tâches à quatre tâches.
Le premier regroupement concerne les deux tâches « Commander freinage » et
« Contrôler application » qui remplissent à la fois le critère de cohésion de contrôle
et le critère de cohésion fonctionnelle. Nous obtenons ainsi une seule tâche qui est
appelée « Contrôler freinage ». Nous pouvons remarquer qu’il paraît important que
cette tâche, issue du regroupement de plusieurs tâches, ne porte pas le même nom
d’autant qu’elle ne correspond plus à un processus fonctionnel du diagramme SA-RT
initial. Notons que ce regroupement économise deux synchronisations ; en revanche,
la boîte aux lettres de type FIFO à écrasement a été remplacée par une boîte aux
lettres de type FIFO sans écrasement afin de réaliser une synchronisation forte.
100
3 • Conception 3.2 Présentation de la méthode DARTS
selon la méthode DARTS
Le deuxième regroupement effectué concerne les deux tâches « Lire bouton ABS »
et « Afficher état bouton ABS » qui remplissent à la fois le critère de cohésion tem-
porelle et le critère de cohésion fonctionnelle. Nous obtenons alors une seule tâche
qui est appelée « Lire et afficher demande ABS ». Dans ce cas aussi, le diagramme
fait l’économie d’une synchronisation.
Nous obtenons ainsi un diagramme DARTS plus simple d’un point de vue multi-
tâche. Cette clarification de l’architecture logicielle permet alors une compréhension
plus aisée qui peut conduire à une validation plus complète.
LIRE LIRE
ÉCRIRE ÉCRIRE
101
3 • Conception 3.3 Exemples de conception
selon la méthode DARTS avec la méthode DARTS
Acquérir Alarme
afficher
capteur méthane
MS alarme
Evt_alarme
Niveau_méthane
Contrôler
mine
Niveau_eau
LIRE Vitesse_pompe
ÉCRIRE
Commander
pompe
Commande_pompe
HTR (5 000 ms)
Acquérir
capteur eau
LS
102
3 • Conception 3.3 Exemples de conception
selon la méthode DARTS avec la méthode DARTS
dynamique des grandeurs physiques associées : 500 ms pour la mesure d’un taux
de gaz (variation du méthane dans l’air : évolution rapide) et 5 s pour la mesure d’un
niveau (eau dans un puisard : évolution lente). Cette différence de vitesse d’exécution
nécessite l’emploi d’un module de données « Niveau_eau » pour le passage de para-
mètres afin de désynchroniser ces tâches. La tâche la plus rapide va cadencer le pilo-
tage général de l’application en se synchronisant avec les tâches suivantes de régula-
tion : « Contrôler mine », « Commander pompe » et « Afficher alarme ».
Remarque
La tâche d’acquisition de données, dont la période est la plus petite, va dicter la cadence principale
d’exécution de l’application, c’est-à-dire synchroniser la régulation du procédé.
103
3 • Conception 3.3 Exemples de conception
selon la méthode DARTS avec la méthode DARTS
Température_mesurée
ÉCRIRE
LIRE
Analyser
besoin sable Sable
IT (Arrivée_sable) Niveau_mesuré
Sable_tombé
Détecter
arrivée sable Acquérir
niveau
Niveau
Figure 3.28 – Premier diagramme multitâche DARTS obtenu à partir du diagramme préliminaire
de l’application « pilotage d’un four à verre » de la figure 2.36.
Nous pouvons noter immédiatement le regroupement qui a été effectué entre les
processus fonctionnels « Analyser température » et « Chauffer four » pour donner une
seule tâche appelée « Réguler température » sur des critères de cohésion fonctionnelle
et séquentielle.
Deux des trois processus fonctionnels d’entrée de données ont été traduits par
deux tâches matérielles « Acquérir température » et « Détecter arrivée sable ». La
première tâche est une tâche périodique à scrutation déclenchée par l’horloge
temps réel de période 1 s. En revanche, la deuxième tâche est une tâche matérielle
déclenchée par une interruption « Arrivée_sable ». Cette tâche est utilisée pour
déclencher de façon logicielle, par une synchronisation « Sable_tombé », la tâche
d’acquisition « Acquérir niveau » traduction d’un processus fonctionnel d’entrée de
données. Nous avons ici un exemple rare de processus d’acquisition qui est traduite
par une tâche logicielle.
Nous avons une traduction très classique des transferts de données. Ainsi, le flot de
données direct « Niveau_mesuré » entre les processus fonctionnels « Acquérir niveau »
et « Analyser besoin sable » se retrouve sous la forme d’une boîte aux lettres du
même nom entre les deux tâches aussi de même nom conformément à la règle 3.1.
De la même manière, le transfert de la donnée « Température_mesurée » sous la forme
d’une zone de stockage, partagée par les trois processus fonctionnels « Acquérir tem-
pérature », « Analyser température » et « Analyser besoin sable », est traduit par un
module de données du même nom conformément à la règle 3.2 qui est accédé par
les trois tâches « Acquérir température », « Réguler température » et « Analyser besoin
sable ».
104
3 • Conception 3.3 Exemples de conception
selon la méthode DARTS avec la méthode DARTS
Nous pouvons noter la relation entre les deux tâches « Acquérir température », et
« Réguler température » qui correspond aux modèles du cas I de la figure 3.21.
Aussi, cette relation aurait pu être traduite par les autres modèles possibles (cas II
et III de la figure 3.21).
Pour terminer l’étude de cette conception, nous allons analyser le comportement de
cette architecture multitâche. Ainsi, l’exécution de la séquence des tâches « Détecter
arrivée sable », « Acquérir niveau » et « Analyser besoin sable » est conditionnée
par l’arrivée de l’interruption « Arrivée_sable » de déclenchement de la tâche ini-
tiale « Détecter arrivée sable ». Si aucun approvisionnement en sable ne s’effectue,
alors l’interruption ne peut se produire et cette chaîne d’exécution est bloquée, car
seule la tâche finale « Analyser besoin sable » peut modifier l’approvisionnement
en sable. Afin de pallier ce problème, une solution basée sur un chien de garde est
mise en place. La tâche « Analyser besoin sable » va créer un chien de garde destiné
à la réveiller dans le cas où elle resterait inactive pendant une durée supérieure à 5 s.
Si ce dysfonctionnement se produit, un événement lié à ce chien de garde est généré
et peut être utilisé pour résoudre le blocage. Cela explique la présence sur la
figure 3.29 de la tâche matérielle « Alarme » déclenchée par le chien de garde CG
de durée 5 s. Cette tâche ne correspond à aucun processus fonctionnel du diagramme
SA-RT. Lors de son exécution, la tâche « Alarme » va générer une synchronisation
identique à celle générée par la tâche « Détecter arrivée sable ». Ainsi, la séquence
va être débloquée, une nouvelle mesure du niveau du sable, qui aura diminué, con-
duira à une modification de la vitesse d’approvisionnement.
Température_mesurée
ÉCRIRE
LIRE
Analyser
CG (5 000 ms) besoin sable Sable
© Dunod – La photocopie non autorisée est un délit.
Alarme Niveau_mesuré
Sable_tombé
IT (Arrivée_sable)
Acquérir
niveau
Détecter
Niveau
arrivée sable
Figure 3.29 – Deuxième diagramme multitâche DARTS obtenu à partir du diagramme prélimi-
naire de l’application « pilotage d’un four à verre » de la figure 2.36 avec la présence d’une
tâche déclenchée par un chien de garde CG.
105
3 • Conception 3.3 Exemples de conception
selon la méthode DARTS avec la méthode DARTS
Nous obtenons ainsi un diagramme final de conception DARTS de six tâches dont
deux n’ont pas d’équivalent direct dans le diagramme flots de données SA-RT de
cette application. Une première tâche a été créée suite à un regroupement de tâche
et une deuxième tâche a été mise en place pour répondre à un blocage du système
par une solution de type chien de garde.
La modification du diagramme multitâche initial, issu d’une traduction automatique
du diagramme flots données SA-RT, a été dans cet exemple de la « pilotage d’un four
à verre » plus importante que dans l’exemple précédent « gestion de la sécurité
d’une mine ». Le dernier exemple plus complexe « Commande d’un moteur à com-
bustion » va montrer un travail de conception encore plus profond.
106
© Dunod – La photocopie non autorisée est un délit.
accélérateur Acquérir
accélérateur
Accélération
Synchro_allumage
ÉCRIRE Commander allumage
allumage
LIRE Élaborer
commande
HTR (5 ms)
moteur injecteur
selon la méthode DARTS
3 • Conception
P_air
Paramètres_moteur
Acquérir Messages
T_eau
paramètres LIRE
T_air moteur ÉCRIRE
ÉCRIRE
LIRE
Figure 3.30 – Diagramme multitâche DARTS obtenue à partir du diagramme préliminaire de l’application
« Commande d’un moteur à combustion » de la figure 2.40.
avec la méthode DARTS
3.3 Exemples de conception
107
3 • Conception 3.3 Exemples de conception
selon la méthode DARTS avec la méthode DARTS
pouvons noter que la tâche matérielle « Communiquer bus CAN » est à la fois une
tâche d’entrées et de sorties. Cette synchronisation forte est implémentée à l’aide
de deux boîtes aux lettres à une place « Vitesse_moteur » et « Mélange », et d’une
synchronisation « Synchro_allumage ».
Cette différence de vitesse d’exécution entre ces tâches d’acquisitions nécessite
l’emploi de quatre modules de données pour le passage de paramètres afin de désyn-
chroniser ces tâches : « Accélération », « Paramètres_moteur », « Paramètres_pollution »
et « Messages ».
En conclusion de cet exemple, il important de souligner que la méthode DARTS
est une méthode de conception non formelle qui permet d’exprimer sous une forme
graphique définie une architecture multitâche. Dans ce sens, les diagrammes multi-
tâches DARTS présentés en solution des exemples traités sont une des conceptions
possibles de l’application ; mais il existe évidemment de nombreuses autres solutions
possibles qui peuvent aussi évoluer en fonction de l’implémentation.
108
4 • ARCHITECTURES SYSTÈMES
m Processeur
109
4 • Architectures systèmes 4.1 Architecture matérielle
Un microcontrôleur est une unité de traitement optimisée pour les entrées sorties.
Généralement, un microcontrôleur est associé directement à plusieurs types d’entrées/
sorties, et il ne nécessite pas l’apport de cartes enfichables supplémentaires. Il est
de ce fait plus compact qu’un microprocesseur muni de cartes d’acquisition, mais
moins performant en terme de calculs.
Les unités de calculs sont caractérisées par une fréquence d’horloge en hertz (Hz).
Cette fréquence correspond au nombre de cycles par seconde effectués par l’unité
de calcul. Chaque instruction de bas niveau exécutée par l’unité de calcul nécessite
de un à quelques cycles d’horloge.
La fréquence d’horloge des microprocesseurs actuels se situe aux alentours de quel-
ques gigahertz (GHz), alors que les microcontrôleurs sont cadencés (ce terme vient
du fait que l’horloge interne donne la cadence) à quelques dizaines de mégahertz
(MHz).
Dans la suite, le terme générique processeur sera utilisé pour désigner un micro-
contrôleur ou un microprocesseur. Il est à noter que de plus en plus de processeurs
dupliquent certaines parties centrales afin d’être capables d’exécuter plusieurs ins-
tructions en parallèle. Cependant, afin de simplifier la présentation, on peut les voir
comme un regroupement de deux (ou plus) processeurs séquentiels.
m Mémoire
Les instructions d’un programme, ainsi que les données et le contexte d’exécution
du programme sont stockés dans la mémoire centrale (mémoire vive comme la
RAM pour Random Access Memory, ou mémoire FLASH plus lente que la RAM mais
rémanente).
Une mémoire se caractérise par sa taille, la taille des mots mémoires (taille de données
que le processeur et la mémoire échangent à chaque accès) et son temps d’accès.
Le temps d’accès est donné en fréquence, soit en nombre d’accès par seconde.
Généralement, la mémoire centrale est relativement lente par rapport au processeur.
Dans le cas des microcontrôleurs, la fréquence d’accès à la mémoire centrale est
souvent la même que la fréquence du microcontrôleur : pour charger ou déposer
un mot en mémoire, le processeur n’a pas à attendre plus d’un cycle. Dans le cas
des microprocesseurs, la mémoire centrale est souvent 5 à 10 fois plus lente que le
microprocesseur, cela implique qu’une communication avec la mémoire peut durer
5 à 10 cycles d’horloge du microprocesseur. Ainsi, lors du déroulement d’un pro-
gramme le chargement d’une instruction à partir de la mémoire centrale, ou l’accès
à une donnée, peut durer plusieurs cycles processeur. Cependant, comparée à la
mémoire de masse, comme le disque dur par exemple, avec un temps d’accès de
l’ordre de 10 millisecondes, une mémoire centrale, pouvant fonctionner à plusieurs
centaines de mégahertz (temps d’accès de l’ordre de quelques nanosecondes) fait
figure de mémoire extrêmement rapide.
Dans un système informatisé, la taille est caractérisée en octets. Un octet est une
entité composée de 8 bits (bit = binary digit, chiffre binaire). Toute information
(instruction, donnée, information sur l’état interne, etc.) est représentée en binaire sur
un certain nombre d’octets (voir § 4.1.3). L’octet est la plus petite entité adressable :
chaque octet possède une adresse en mémoire centrale.
110
4 • Architectures systèmes 4.1 Architecture matérielle
Les instructions d’un programme, mémorisées sous forme d’octets en mémoire cen-
trale, sont stockées séquentiellement. L’exécution d’un programme par un processeur
consiste donc, schématiquement, à charger une instruction à partir de la mémoire,
l’exécuter, aller chercher l’instruction suivante et ainsi de suite. Un programme est
donc caractérisé par un point d’entrée : l’adresse de sa première instruction.
Les instructions sont composées d’un certain nombre d’octets (variable en fonction
de l’instruction). Chaque octet est adressable et peut être obtenu grâce à son adresse
en mémoire.
Le transfert d’un mot entre la mémoire centrale et le processeur s’effectue à l’aide
du bus mémoire. La fréquence du bus correspond à la fréquence de la mémoire
(généralement, la fréquence maximale d’une mémoire centrale est supérieure ou
égale à celle du bus mémoire : c’est la fréquence du bus mémoire qui sert de réfé-
rence à la vitesse de la mémoire). Un bus mémoire est un bus parallèle : schémati-
quement, il est capable de faire passer en parallèle un nombre de bits correspon-
dant au mot mémoire. Le nombre maximal d’octets par seconde (débit maximal)
échangés entre le processeur et la mémoire correspond donc à fréquence du bus
mémoire × taille du mot mémoire en octets. Typiquement, la largeur d’un mot mémoire
est de 8, 16, 32 ou 64 bits.
Les registres sont des mémoires internes au processeur permettant de stocker les
données manipulées : les calculs ne sont pas effectués directement à partir du contenu
de la mémoire centrale, mais les données de la mémoire sont d’abord chargées dans
des registres, à partir desquels elles sont manipulées par l’unité de calcul. Le nombre,
le nom la fonction, et la taille des registres varient en fonction du processeur,
cependant certains registres ont des fonctions communes d’un processeur à un autre.
Ainsi, un registre particulier, nommé accumulateur, sert sur certains processeurs à
stocker le résultat de chaque calcul arithmétique ou logique. Sur d’autres processeurs,
divers registres généraux peuvent servir à cette fonction. La taille de ce ou ces registres
est importante car elle donne la taille maximale des données manipulées en une seule
instruction. Cette taille s’appelle le mot machine, et correspond généralement à la
taille du mot mémoire. Pour les microcontrôleurs, on trouve fréquemment des mots
machines de 8 ou 16 bits et rarement 32 bits, alors que pour les microprocesseurs,
on trouve plutôt des mots machine de l’ordre de 32 ou 64 bits.
Le processeur comprend notamment (figure 4.1) :
– un séquenceur de commandes, permettant d’initier et de contrôler le déroulement
© Dunod – La photocopie non autorisée est un délit.
111
4 • Architectures systèmes 4.1 Architecture matérielle
Processeur
112
4 • Architectures systèmes 4.1 Architecture matérielle
113
4 • Architectures systèmes 4.1 Architecture matérielle
114
4 • Architectures systèmes 4.1 Architecture matérielle
115
4 • Architectures systèmes 4.1 Architecture matérielle
informaticiens d’aujourd’hui. Son nom est devenu célèbre grâce à son algèbre et
au type booléen présent dans de nombreux langages de programmation.
Toute opération booléenne est définie dans le domaine {vrai, faux}. Cependant, étant
donné que le but de cette partie est de permettre de comprendre les différents
types de manipulation binaire, il est important que le lecteur sache que vrai est assi-
milé à 1 en binaire, et faux à 0.
On définit usuellement trois opérateurs (appelés connecteurs) de base : le complé-
ment (ou « non ») noté ¬, la conjonction (ou « et ») notée ∧, et la disjonction
(ou « ou ») notée ∨. Tous les connecteurs de l’algèbre booléenne peuvent être
obtenus à partir de ces trois connecteurs (il est aussi possible de la définir à l’aide
de deux connecteurs).
La valeur d’une formule booléenne, composée de connecteurs et de variables boo-
léennes (dont la valeur peut être vrai ou faux) peut être représentée à l’aide d’une
table de vérité. Chaque ligne de la table donne une valeur possible aux variables, et
la valeur obtenue de la formule étudiée. La table de vérité donne donc toutes les valeurs
possibles d’une formule en fonction des variables. Par exemple, pour la variable
a∈{vrai, faux}, la table de vérité de ¬a est donnée dans le tableau 4.2. Ce tableau
présente aussi les tables de vérités des principaux connecteurs : « et », « ou », « impli-
cation », « ou exclusif » appelé xor, « équivalence ». Cette table est présentée sous
la forme algébrique en utilisant les connecteurs booléens classiques, puis reprise sous
forme algébrique proche du binaire. En effet, le « et » booléen a des propriétés
similaires à la multiplication, puisque le « faux » est absorbant, de même que le 0
pour la multiplication. Le « vrai » est absorbant pour le « ou », ce qui fait penser à
une somme dans laquelle on ne considérerait que les résultat 0 ou différent de 0
(dans ce cas, 1). Le « ou exclusif » est vrai si un et un seul des opérandes est vrai,
cela signifie qu’il est vrai si ses deux opérandes sont différents l’un de l’autre (le
domaine n’ayant que deux valeurs possibles). Le « ou exclusif » correspond à l’usage
du mot « ou » dans le langage usuel. Ainsi, le « ou » correspondant à l’affirmation
« je me rendrai à la réunion en train ou en avion » est un ou exclusif. A contrario, le
« ou » logique classique, dit « ou inclusif », laisse la possibilité aux deux variables
d’être vraies. L’équivalence est vue comme l’égalité de ses deux opérandes.
On dit que deux formules sont équivalentes si elles possèdent la même table de vérité.
L’implication est donnée ici à titre indicatif. La formule a ⇒ b est équivalente à la
formule (¬a) ∨ b, ce qui signifie que le seul cas où elle est fausse correspond au cas
où a est vrai, et b est faux. Ainsi, l’affirmation « si j’avais des pouvoirs magiques,
alors je soulèverais la tour Eiffel », qui est une implication, ne peut pas être infirmée
tant que la personne qui prétend cela n’a pas effectivement de pouvoirs magiques.
Il suffit de considérer la façon dont on démontre une implication en mathématiques :
faire l’hypothèse que a est vrai, et démontrer, dans ce cas seulement, que b est vrai.
La comparaison entre les opérateurs algébriques somme et produit ne s’arrête pas à
une sémantique proche des opérateurs booléens « et » et « ou ». En effet, les opéra-
teurs booléens possèdent les mêmes propriétés de distributivité et d’associativité,
plus d’autres liées à la taille du domaine des booléens. Le tableau 4.3 donne quelques
propriétés algébriques, ainsi que les représentations graphiques des opérateurs logi-
ques dans la norme américaine (MIL STD 806) et la norme européenne (IEC 617).
116
4 • Architectures systèmes 4.1 Architecture matérielle
a ¬a
faux vrai
vrai faux
0 0 0 0 0 1 1
0 1 0 1 1 0 1
1 0 0 1 1 0 0
1 1 1 1 0 1 1
Les propriétés algébriques peuvent être vérifiées très simplement à l’aide de tables
de vérité (tableau 4.3, pages suivantes).
Les principales propriétés à retenir sont les propriétés absorbantes du « faux » sur
« et » (i.e. 0 . a = 0) et les propriétés absorbantes du « vrai » sur « ou » (i.e. 1 + a = 1).
En effet, pour les manipulations de nombres binaires, notamment la création de
masques binaires (voir § 4.1.4), très utile lors d’accès au matériel par des primitives
bas niveau, ces règles sont fondamentales.
© Dunod – La photocopie non autorisée est un délit.
Bien que les premières machines à calcul de l’ère électromécanique fussent en base
décimale, depuis l’ère de l’électronique, tous les ordinateurs utilisent la base binaire
pour effectuer des calculs. La raison en est simple : une tension à deux valeurs pos-
sibles, (0, n volts) ou bien (– n, + n volts), permet l’utilisation intensive de transistors
(portes électroniques s’ouvrant ou se fermant en fonction d’une faible tension
d’entrée). Les transistors sont les éléments de base des processeurs modernes, ils
permettent d’implémenter aisément toutes les fonctions d’algèbre booléenne, qui
servent aussi à effectuer des calculs en arithmétique binaire. En effet, chaque chiffre
117
4 • Architectures systèmes 4.1 Architecture matérielle
Commutativité
Associativité
Idempotence et complémentation
Le « ou » et le « et » a∨a⇔a a+a=a
sont idempotents. a∧a⇔a a.a=a
Règles de a ∨ ¬a ⇔ vrai a + ¬a = vrai
complémentation. a ∧ ¬a ⇔ faux a . ¬a = faux
Distributivité
118
4 • Architectures systèmes 4.1 Architecture matérielle
Lois de De Morgan
a∨a∧b⇔a a + ab = a
a ∧ (a ∨ b) ⇔ a a(a + b) = a
a ∨ ¬a ∧ b ⇔ a ∨ b a + (¬ab) = a + b
a ∧ (¬a ∨ b) ⇔ a ∧ b a(¬a + b) = a + b
a ⇒ b ⇔ ¬a ∨ b a ⇒ b = ¬a + b
1
IDENTITÉ a a a a
1
NON a ¬a a ¬a
a a &
ET ab ab
b b
a a ?1
OU a+b a+b
b b
a a =1
XOR (OU exclusif) a+b a+b
b b
a a
NAND ¬(ab) &
b ¬(ab)
b
a a ?1
NOR ¬(a+b) ¬(a+b)
b b
© Dunod – La photocopie non autorisée est un délit.
a a =1
EGAL ¬(a + b) ¬(a + b)
b b
119
4 • Architectures systèmes 4.1 Architecture matérielle
m Changements de base
Un nombre se représente dans une base b par une suite de chiffres bi compris entre
0 et b – 1. Tout nombre se décompose de façon unique en chiffres dans une base b :
bk · bk + bk–1 · bk–1 + … + b1 · b1 + b0 · b0. Le nombre s’écrit dans la base b : bkbk–1
… b1b0.
Ainsi, en système décimal, le nombre 3 412 correspond à 3 × 103 + 4 × 102 + 1 × 101
+ 2 × 100. Tout nombre x, que l’on a l’habitude de manipuler sous sa forme décimale,
correspond à une quantité, qui se décompose de façon unique dans toute base b
x
sous la forme d’une suite de chiffres bkbk–1 … b1b0, avec b i = ----i ≡ b où a
b
est la partie entière inférieure de a et a ≡ b est a modulo b (i.e. reste de la division
de a par b). Ainsi, cette quantité peut se décomposer en base 1 en 3 412 « bâtons ».
Pour passer une quantité d’une base à une autre, il suffit d’utiliser la division eucli-
dienne « a = bq + r » : lorsque l’on divise un nombre par une base b, on obtient
comme reste le chiffre des unités. Ainsi, si l’on souhaite effectuer des divisions eucli-
diennes successives de 3 412 par la base 10, on obtient 3 412 = 341 × 10 + 2. Le
chiffre des unités est donc 2, et il y a 341 dizaines. 341 = 34 × 10 + 1, il y a donc
une dizaine et 34 centaines. 34 = 3 × 10 + 4, il y a donc 4 centaines et 3 milliers.
3 = 0 × 10 + 3, il n’y a donc que 3 milliers. 3 412 se décompose donc en base 10
par 3 milliers, 4 centaines, 1 dizaine, et 2 unités, ce qui est évident.
Si l’on souhaite convertir 3 412 dans une base b > 1 quelconque, il suffit donc
d’effectuer des divisions euclidiennes successives, la première donne le chiffre des
unités, la seconde celui des « b-zaines », …, la nième celui des « bn–1-zaines ». La
figure 4.2 représente les conversions de 3 412 en base 10, 2, 8 et 16.
Par convention, dans la suite, les nombres en binaire seront préfixés par la lettre b
(sauf non ambiguïté), les nombres en octal seront préfixés par la lettre o, les nom-
bres en hexadécimal seront préfixés par « 0x », alors que les nombres en décimal ne
seront pas préfixés du tout.
Certains changements de base se voient simplifiés lorsque l’on passe d’une base b à
une puissance de b et vice-versa. Les bases fréquemment utilisées en informatique,
en plus du binaire qui est l’unité physique utilisée, sont l’octal (base 8) et l’hexa-
décimal (base 16). En effet, un nombre représenté en binaire est très long, et diffi-
cilement lisible. Or 8 = 23 et 16 = 24. Chaque chiffre octal correspond donc à
3 chiffres binaires, et chaque chiffre hexadécimal correspond à 4 chiffres binaires.
Pour passer de la base 2 à la base 8, il suffit donc de regrouper les bits par 3 (en par-
tant bien entendu des bits de poids faible !!!), alors que pour passer de la base 8 à la
base 2, il suffit de représenter chaque chiffre octal par 3 bits. Il en va de même
pour la base 16, sauf que les regroupements sont de 4 bits. La figure 4.3 donne un
exemple de conversion binaire-octal et binaire-hexadécimal.
L’une des illustrations du changement de base par division euclidienne peut être faite
à l’aide d’un changement de base direct entre deux bases non usuelles : par exemple,
passage de la base 8 à la base 16 (figure 4.4). Cela peut sembler troublant, car la
division euclidienne se fait directement en base 8 (les règles d’addition et de sous-
traction sont différentes de celles de la base 10 que chacun a l’habitude de manipuler,
120
4 • Architectures systèmes 4.1 Architecture matérielle
3412 10
-3410
341 10
3412 2 2
- 340
-3412 2*1 34 10
1706 2 0 0 1
0 + 1 - 30
-1706 *10 1 3 10
853 2 4
0*2 0 0 + 4 - 0
+ 0 - 852 *10 2 0
426 2 3
*2 1 1 + 3
+ 1 - 426 *10 3
*2 2 213 2
0
+ - 212
0*2 3 106 2
3412 8 1
+ - 106
-3408 1*2 4 53 2
426 8 0
4 + - 52 2
- 424 0*2 5 26
4*8 0 53 8 1
2 + 1 - 26 13 2
+ 2 - 48 *2 6
*8 1 6 8 0 - 12
5 + 0 6 2
+ 5 - 0 *2 7 1
*8 2 0 - 6
6 + 1 3 2
+ *2 8 0
6*8 3 - 2
3412 16 + 0 1 2
*2 9 1
-3408 - 0
213 16 + 1 0
4 *2 10 1
4*1 - 208 + 1
60 13 16 *2 11
5
+ 5 - 0
*16 1 0
13
+ 1
3*1
62
3412 = b110101010100 = o6524 = 0xD54
1425 =b 10110010001
2 6 2 1 1425 =o2621
1425 =b 10110010001
5 9 1 1425 =0x591
o2621 o20=16
-o20
o131 o20
o62
© Dunod – La photocopie non autorisée est un délit.
-o120
-o60 o5 o20
o11
o21 - o0
-o20 o0
o5
o1*
o20o1
0
+ o
11*
o2 1
+ o
5*o
20 2
121
4 • Architectures systèmes 4.1 Architecture matérielle
1 1 1
57 b111001
+24 +b011000
81 b1010001
122
4 • Architectures systèmes 4.1 Architecture matérielle
Des exemples d’additions ont été présentés dans la section précédente. La multipli-
© Dunod – La photocopie non autorisée est un délit.
cation utilise les mêmes règles qu’en numération décimale. La division, quant à elle,
est une division entière et utilise les mêmes règles qu’en numération décimale.
Il est à noter que dans toute base b, une multiplication par la base correspond à
décaler les chiffres vers la gauche, afin d’introduire un 0 comme chiffre de poids
faible. Ainsi, 32 × 10 = 320. Il en est de même pour le binaire : multiplier par 2
consiste à décaler à gauche. La division entière par la base consiste à enlever le chiffre
de poids faible : 321/10 = 32. Cela correspond à un décalage des chiffres vers la
droite.
Lorsque l’on doit programmer à bas niveau, il arrive fréquemment que l’on ait à créer
des octets de toute pièce (par exemple créer un octet dont le i ème bit et le j ème bit
sont à 1, les autres étant à 0 afin de configurer une carte d’acquisition) ou bien à
123
Tableau 4.5 – Quelques repères en binaire et hexadécimale.
124
Propriété algébrique Utilisations type Exemples
16 = 24 Passage aisé binaire ↔ hexadécimal Octet dont le bit 5 (6e bit) est à 1 :
b0010 0000 = 0x20
Octet dont tous les bits sont à 1, sauf le bit 3 :
b1111 0111 = 0xF7
« 0 » absorbant et « 1 » neutre sur le « et » Vérifier qu’un bit est à 1 dans un octet Le bit 2 (3e bit) de l’octet k est à 1 si et seulement si :
k et 0x04 ≠ 0
Le bit de poids fort de l’octet k est à 1 si et seulement si :
4 • Architectures systèmes
k et 0x80 ≠ 0
Le bit i de l’octet k est à 1 si et seulement si :
k et (1 décalé à gauche de i) ≠ 0
Vérifier qu’un bit est à 0 dans un octet Le bit 2 de l’octet k est à 0 si et seulement si :
k et 0x04 = 0
Le bit de poids fort de l’octet k est à 0 si et seulement si :
k et 0x80 = 0
Le bit i de l’octet k est à 0 si et seulement si :
k et (1 décalé à gauche de i) = 0
Accéder à une partie d’un nombre Obtenir le 2e octet d’un nombre entier k codé sur 32 bits :
(k décalé à gauche de 8) et 0xFF
« 1 » absorbant et « 0 » neutre sur le « ou » Mettre à 1 un bit dans un nombre Mettre à 1 le bit 5 de l’octet k :
k := k ou 0x20
Mettre à 1 le bit i de l’octet k :
4.1 Architecture matérielle
k := k ou (1 décalé à gauche de i)
4 • Architectures systèmes 4.1 Architecture matérielle
tester certains bits (celui-ci est-il à 1, celui-là est-il à 0 ?). Pour cela, on utilise les
opérateurs binaires qui sont directement issus de l’algèbre booléenne. Il est donc bon
d’avoir quelques repères binaires et booléens (voir tableau 4.5). Rappelons (voir
§ 4.1.2) que le 0 est absorbant pour le « et », et neutre pour le « ou », et que le 1 est
absorbant pour le « ou » et neutre pour le « et ». Rappelons de plus que la repré-
sentation binaire est peu lisible, et que dans un programme, il est généralement
plus pratique d’exprimer les valeurs binaires en hexadécimal (1 chiffre hexadécimal
= 4 bits).
Enfin, voici une petite technique qui permet de trouver rapidement la représentation
binaire d’un nombre, à condition que celui-ci ne soit pas trop grand. Pour cela, il
suffit de connaître les puissances de 2, et de faire mentalement quelques soustrac-
tions. Soit un nombre décimal, par exemple 155, que l’on veut représenter en binaire
(noter qu’au minimum, ce sera un octet non signé, car 155 > 127). On choisit la
plus grande puissance de 2 inférieure ou égale à 155, soit 128. On place un 1 qui
correspond à 128 (point n’est nécessaire de préciser qu’il s’agit de 27), on retranche
128 à 155, il reste donc 27 à exprimer en binaire. On passe alors toutes les puis-
sances de 2 de façon décroissante : à la suite du 1 correspondant à 128, on note 0
pour 64, 0 pour 32, 1 pour 16 (reste 11), 1 pour 8 (reste 3), 0 pour 4, 1 pour 2
(reste 1), et 1 pour 1. On obtient donc la représentation binaire b10011011.
Dans tous les systèmes informatisés, toute information, qu’elle soit instruction ou
bien donnée, est représentée sous forme binaire. Il en va de même pour les caractères :
les caractères sont représentés en mémoire sous la forme d’un code numérique. Ce
code numérique dérive de la table de caractères normalisée par l’ANSI, qui a
défini, sur 7 bits, 128 caractères de base non accentués. Cette table adoptée dans les
années 1960, appelée ASCII (American Standard Code for Information Interchange),
a eu plusieurs dérivés, ainsi, les codes ASCII étendus ajoutent un bit à la représen-
tation ASCII, ce qui permet d’obtenir 256 caractères. Cela est loin d’être suffisant
pour représenter tous les caractères internationaux, donc les 128 caractères ajoutés
(codes 128 à 255) dépendent d’une table chargée en fonction du pays. Le même
principe est repris dans l’implémentation Unicode de la norme ISO/IEC 10646.
Cette implémentation, reprise dans la plupart des logiciels actuels, propose des tables
de caractères sur 8, 16 et même 32 bits. Lorsque sous MS Windows® on lance une
application console et que les accents ne sont pas bien affichés, cela est souvent dû
© Dunod – La photocopie non autorisée est un délit.
au fait que l’application ne prend pas en compte la bonne page d’ASCII étendu.
Lorsque l’on navigue sur internet et qu’une page asiatique n’est pas convenablement
affichée, cela est dû au fait que la plupart des navigateurs européens se basent sur
la page Unicode Latin-1, qui regroupe les caractères d’Europe de l’ouest en utilisant
une représentation sur 16 bits.
Les informations en mémoire peuvent correspondre, en fonction du contexte, à des
instructions/opérandes, ou données quel que soit leur type. La figure 4.7 présente
un récapitulatif des différents formats utilisés pour représenter les données de base.
125
4 • Architectures systèmes 4.1 Architecture matérielle
ASCII
BCD
Données
EBCDIC
non numériques
ANSI
Unicode
Données
entières
Signe, valeur absolue
Données ≤ 0 et ≥ 0 Complément à 1
Complément à 2
Données
numériques
m Interruptions matérielles
Lorsque le processeur doit lire des informations de l’extérieur, il peut le faire de deux
façons. La plus simple, mais la moins efficace, consiste à faire de la scrutation
(aussi appelée attente active ou polling) : le processeur lit en boucle ce qui arrive
sur une entrée, sans savoir avant de lire s’il y a des données « intéressantes ». La plus
efficace, mais qui n’est pas toujours possible, consiste à utiliser les interruptions.
Le processeur peut être en train d’effectuer un calcul quelconque, mais lorsque des
données sont prêtes, le processeur est interrompu dans son traitement, et un trai-
tement (généralement très court) appelé routine de traitement d’interruption
(en anglais ISR pour Interrupt Service Routine) est effectué. Ainsi, le processeur ne
gaspille pas de temps à vérifier la présence d’informations intéressantes : il en est
prévenu par interruption. Généralement, c’est un dispositif physique dédié, appelé
contrôleur de bus d’entrées/sorties, qui est chargé de générer des interruptions.
Il en va de même pour l’envoi de données vers un bus d’entrées/sorties : le débit
autorisé par le bus pour le transfert d’informations est tellement faible par rapport
à la vitesse du processeur qu’il serait obligatoire d’attendre de nombreux cycles
126
4 • Architectures systèmes 4.1 Architecture matérielle
processeurs entre l’envoi de chaque mot mémoire. Grâce aux interruptions, on utilise
généralement un système de buffer (zone de stockage d’informations) de données
à émettre : le contrôleur d’entrées/sorties se charge donc d’émettre les données à la
vitesse du bus, et prévient par interruption le processeur lorsque le buffer est vide
ou quasi-vide afin que le processeur fournisse de nouvelles données à émettre.
Ainsi, le processeur n’a pas à attendre que le bus d’entrées/sorties soit libre, et il peut
se consacrer à d’autres traitements.
Étant donné que le traitement effectué par un processeur est séquentiel, comment
une interruption peut-elle être traitée ? L’état d’un programme est totalement carac-
térisé par l’état des registres (notamment du compteur ordinal) et bien entendu sa
mémoire propre en mémoire centrale (instructions, données…). Si l’ISR n’affecte
pas la mémoire propre du programme en mémoire centrale, et si les registres sont
sauvegardés, il est possible de changer sur interruption la valeur du compteur ordinal
pour la « brancher » sur l’adresse de début des instructions de l’ISR, la routine s’exé-
cute alors, et à la fin de la routine, l’état du processeur est restauré. Le programme
en cours d’exécution est donc interrompu, mais à la fin du traitement de l’interrup-
tion, le processeur est remis dans le même état qu’au moment de l’interruption :
il continue donc son exécution comme s’il n’avait pas été interrompu (figure 4.8).
Interruption
Sauvegarde du registre d’état
et du compteur ordinal
Le programme reprend
comme si rien ne s’était passé
127
4 • Architectures systèmes 4.1 Architecture matérielle
Il peut être possible d’ignorer complètement une interruption : cela s’appelle désarmer
une interruption. L’interruption pourra à nouveau être prise en compte lorsqu’elle
sera armée.
Pour retarder le traitement d’une interruption (par exemple lors du traitement d’une
autre interruption), on peut masquer une interruption. Dans ce cas, les requêtes
d’interruptions sont si possible mémorisées : ces requêtes seront traitées lorsque
l’interruption sera démasquée.
m Bus d’entrées/sorties
Au niveau logiciel, la possibilité d’utiliser les interruptions pour éviter l’attente active
se traduit par la possibilité d’effectuer des lectures bloquantes. De plus, la plupart
du temps, les envois sur les bus d’entrées/sorties sont bufferisés et suspensifs pour
le programme les émettant. Cela signifie qu’un programme qui envoie des données
sur un bus d’entrées/sorties (par exemple, un programme qui effectue un affichage
sur un terminal alphanumérique) envoie des données dans le buffer, puis se suspend
(le processeur peut être utilisé à autre chose) jusqu’à ce qu’il y ait à nouveau de la
place dans le buffer, etc., jusqu’à épuisement des données à émettre. Généralement,
les entrées/sorties utilisant des bus sont suspensives pour l’émission de données et
bloquantes pour la réception, grâce à l’utilisation des interruptions matérielles.
Le nombre des interruptions matérielles est limité sur un système informatisé (par
exemple, il y en a 16 sur les PC). Les interruptions sont utilisées par les bus d’entrées/
sorties et autres éléments d’entrées/sorties :
– bus série (norme RS-232) : l’un des bus externes (permettant de brancher un
élément externe au système informatisé) les plus utilisés encore aujourd’hui pour
communiquer avec du matériel d’acquisition/commande « intelligent ». La com-
munication série (transfert bit à bit) est très répandue aussi bien au niveau des
microprocesseurs, qui cohabitent le plus souvent avec des puces capables de gérer
des entrées/sorties série, qu’au niveau des microcontrôleurs, qui intègrent très
souvent ce type de communication. Ce bus est très souvent utilisé avec un pro-
tocole bidirectionnel de type ASCII (ce sont des caractères qui sont échangés),
permettant au processeur d’envoyer des commandes ou des informations de con-
figuration, et de recevoir des informations de statut ou des données d’acquisition.
Le type de matériel utilisant la communication série est très large : centrales
d’acquisition de températures sur thermocouples, capteurs GPS, centrales iner-
tielles intégrant accéléromètres, gyromètres et magnétomètres, écrans alphanumé-
riques, etc. C’est un bus relativement simple à programmer, car il existe de nom-
breuses bibliothèques logicielles permettant de tirer parti de ce bus capable de
transporter des flots de données jusqu’à 128 000 bits/s ;
– bus parallèle : presque obsolète, ce bus externe permet une transmission de 8 bits
en parallèle à des débits de 3 Mo/s. Il y a quelques années, c’était un bus très
utilisé pour communiquer avec des imprimantes ;
– bus USB (Universal Serial Bus) : ce bus externe héritier du bus série permet,
dans sa version 1.0, des débits de l’ordre de 1,5 Mo/s et dans sa version 2.0 des
débits aux alentours de 60 Mo/s. Dans le monde de la micro-informatique, ce
bus a supplanté les bus série et parallèle pour permettre la communication avec
128
4 • Architectures systèmes 4.1 Architecture matérielle
des éléments variés qui peuvent être relativement gourmands en débits de données
(souris, clavier, webcams, imprimantes, scanners, modems, capteur GPS…). Il a
l’avantage de pouvoir transporter l’alimentation vers le matériel lorsque celui-ci
consomme peu d’énergie. Cependant, bien qu’il soit possible qu’il s’impose dans
quelques années sur le marché des éléments temps réel, il n’a pas encore fait de réelle
apparition dans ce domaine, et peu de microcontrôleurs intègrent un bus USB ;
– bus FireWire (norme IEEE 1394) : ce bus série externe concurrence l’USB 2.0
dans sa version a, offrant des débits de l’ordre de 50 Mo/s. Ce bus est très utilisé
pour l’acquisition vidéo numérique (le format de compression vidéo utilisé, le for-
mat DV, est peu compressé pour éviter les pertes de qualité). Dans sa norme b,
offrant des débits de l’ordre de 400 Mo/s, il concurrence le bus SCSI pour le
branchement de disques durs/lecteurs DVD ou CD externes. Il est pour l’instant
peu présent sur les microcontrôleurs et dans le monde des systèmes temps réel ;
– bus SCSI (Small Computer System Interface) : en perpétuelle amélioration, ce bus
externe parallèle fournissant des débits allant de 5 Mo/s (SCSI-1) à 320 Mo/s
(Ultra-4-SCSI), permet notamment la connexion d’un ordinateur à des périphé-
riques de stockage externe (disques durs externes, etc.) ;
– bus PCMCIA (Personal Computer Memory Card International Association) : ce bus
externe parallèle permettant des débits de l’ordre de 130 Mo/s, présent surtout
sur les ordinateurs portables, permet l’utilisation de périphériques compacts (équi-
valent des périphériques PCI des ordinateurs de bureau). De format presque
identique (modulo un petit adaptateur), le format Compact Flash est de plus en
plus utilisé pour des éléments compacts facilement embarquables (comme des
GPS pour PC de poche) ;
– bus ISA (Industry Standard Architecture) : presque obsolète, ce bus interne (lors-
qu’il est présent, se trouve sur la carte mère des ordinateurs utilisant un micro-
processeur) permet de connecter différents types de cartes internes (vieille carte son,
vieille carte d’acquisition…) peu gourmandes en débit de données ;
– bus PCI (Peripheral Component Interconnect) : très utilisé pour brancher des cartes
internes (cartes d’acquisition, cartes son, etc.) dans les ordinateurs, ce bus interne
parallèle fournit des débits de l’ordre de 1 Go/s ;
– bus AGP (Advanced Graphic Port) : utilisé exclusivement pour connecter des
cartes vidéo (carte se chargeant de l’affichage graphique), ce bus interne parallèle
est l’un des bus d’entrées/sorties les plus rapides avec des débits qui augmentent
© Dunod – La photocopie non autorisée est un délit.
129
4 • Architectures systèmes 4.1 Architecture matérielle
bus externes
bus internes
130
4 • Architectures systèmes 4.1 Architecture matérielle
l’envoi de plusieurs données par cycle d’horloge. C’est d’ailleurs le cas pour les bus
spécialisés dans le transfert de données entre le processeur et la mémoire centrale.
Finalement, le bus d’entrées/sorties le plus utilisé avec des éléments d’acquisition
externes est le bus série. Quel que soit le bus employé, il est important de conserver
en mémoire que le fait d’accéder à un périphérique via un bus d’entrées/sorties
(que ce soit en lecture ou en écriture de données) est suspensif pour un programme.
Le tableau 4.7 donne quelques mesures communément utilisées dans les systèmes
informatisés, permettant de mieux appréhender le tableau 4.6.
Mesures de fréquence
THz 1012 cycles/s Fréquence ondulatoire des ondes lumineuses (370 à 750 THz)
Mesures de capacité
Sur les microcontrôleurs, on trouve des interfaces d’entrées/sorties que l’on peut
connecter presque (modulo des boîtiers de conditionnement et des relais en puis-
sance) directement à du matériel d’acquisition ou commande (capteurs ou action-
neurs). On trouve le même type d’interface pour les ordinateurs : elles se présentent
le plus souvent sous la forme de cartes d’extension (notamment au format ISA, PCI
ou PCMCIA) nommées cartes d’acquisition. Deux types de signaux peuvent être
manipulés par ce genre d’interface : les signaux numériques et les signaux analo-
giques.
131
4 • Architectures systèmes 4.1 Architecture matérielle
M Entrées/sorties numériques
Les entrées/sorties numériques sont de type tout ou rien (2 états possibles qui cor-
respondent électriquement par exemple à – 5 V, + 5 V). Ce type d’entrée peut être
branché (via une adaptation de puissance et ou tension) à des capteurs ou action-
neurs de type tout ou rien (TOR) (électrovanne TOR, capteur de présence, alimen-
tation…). On appelle ligne une entrée ou sortie numérique (physiquement corres-
pondant à deux connecteurs : la masse numérique et la porteuse). L’état d’une
ligne est caractérisé par un bit (0 ou 1). Comme la plus petite entité adressable en
mémoire est l’octet, les lignes sont regroupées par 8, formant ainsi un octet appelé
port numérique. Il est possible de définir des ports de taille multiple de 8 (ports
de 16 ou 32 bits par exemple). Chaque ligne d’un port peut être configurée de façon
logicielle en entrée (lecture sur un capteur) ou en sortie (écriture vers un actionneur).
Généralement, l’utilisation d’entrées/sorties numériques se fait de la façon suivante
(figure 4.9) :
– Configuration du port numérique à l’aide d’un octet de configuration de direction
des lignes. Le plus souvent, le bit i (i = 0..taille du port-1) permet de configurer
la ligne i (i = 0..taille du port-1). Par exemple, en fonction de la carte d’acquisi-
tion ou du microcontrôleur, un bit à 1 peut correspondre à mettre la ligne cor-
respondante en sortie, alors qu’un bit à 0 configure la ligne en entrée. Dans ce
cas, un octet de configuration valant 0x73 (b01110011) configurerait les lignes
6,5,4,1,0 en sortie et les lignes 7,3,2 en entrée. L’octet de configuration 0x0
placerait tout le port en entrée, alors que 0xFF (ou bien -1 grâce à la représentation
en complément à 2 de ce nombre, qui n’est constituée que de 1 en binaire) confi-
gurerait le port en sortie. La configuration a généralement lieu une seule fois en
début de programme.
– Lecture d’un port : les lignes se lisent toujours par port (d’au moins un octet donc).
Par exemple, si le port est d’un octet, la lecture renvoie un octet, dont la repré-
sentation binaire correspond à l’état des lignes. Ainsi, si on lit l’octet 0xA3
(b10100011) sur un port préalablement configuré en entrée, cela signifie que
les lignes 7,5,1,0 sont à 1 et que les lignes 6,4,3,2 sont à 0. Si le port n’est pas
totalement configuré en entrée, les bits lus correspondant aux lignes configurées
en sorties ont des valeurs non significatives.
– Écriture sur un port : de même que les lectures, les écritures concernent toujours
un port entier. Il existe cependant une différence à noter. En effet, supposons qu’un
port, configuré totalement en sortie, ait toutes les lignes reliées à des actionneurs.
La sortie est maintenue de façon matérielle à une certaine valeur (1 ou 0), qui
est la dernière valeur écrite. Supposons alors que l’on veuille modifier l’état d’un
seul actionneur, donc d’une seule ligne. Il faut alors connaître l’état complet du
port, modifier le bit correspondant à l’actionneur, puis écrire cet octet sur le port
de sortie. Ce fonctionnement, bien que possible, n’est pas très pratique : en effet,
les applications de contrôle-commande sont souvent multitâches, et il arrive fré-
quemment que le composant logiciel commandant un actionneur ne sache pas
du tout dans quel état se trouvent les autres actionneurs. L’idée généralement
retenue pour éviter d’avoir à s’occuper de ce problème est d’utiliser un masque.
Le principe est assez proche du ou des octets de configuration de port. Supposons
132
4 • Architectures systèmes 4.1 Architecture matérielle
que le port soit d’un octet, on crée un octet masque : pour chaque bit à 1, on
considère qu’il y a un trou dans le masque et que les bits correspondants à un trou
dans le masque ont une action physique sur le port. Si le bit est à 0, on considère
que le masque est opaque et que la ligne correspondante ne peut pas être affectée.
L’écriture sur un port se base donc sur deux paramètres, chacun de la même largeur
que le port : un masque, et une commande. Tout se passe comme si seuls les bits
de la commande correspondant à un trou (bit à 1) dans le masque pouvaient
arriver jusqu’aux lignes de sortie numérique. Par exemple (figure 4.10), si un port
© Dunod – La photocopie non autorisée est un délit.
d’un octet est configuré en écriture, et si l’on veut mettre à 1 la ligne numéro 6
sans toucher aux autres lignes, il suffit de créer un masque valant 0x40 (1 décalé
de 6 à gauche), et d’appliquer une commande valant 0x40, ou même 0xFF (octet
composé de 8 bits à 1). Grâce au masque, seule la ligne 6 est affectée, et mise à 1.
Si l’on souhaite mettre la ligne 6 à 0, on utilise le même masque (0x40), et on
applique la valeur 0, seule la ligne 6 est affectée et mise à 0.
Le dispositif de masque est généralement purement logiciel. En fonction du niveau
de programmation disponible : soit via une bibliothèque fournie, soit par adressage
direct à la main, il peut être nécessaire d’implémenter soi-même la technique du
masque.
133
4 • Architectures systèmes 4.1 Architecture matérielle
Il est à noter que le fait d’écrire sur une ligne configurée en entrée n’a généralement
aucun effet, et que le fait de lire sur une ligne configurée en sortie donne des valeurs
non significatives.
La communication logicielle avec les ports d’entrées/sorties s’effectue au plus bas
niveau par utilisation des registres de la carte d’acquisition. Une carte d’acquisition
plaque ses registres sur des adresses spécifiques dites d’entrées/sorties. Ces adresses
sont caractérisées par une adresse de base, et une taille des registres plaqués sur la
mémoire. Par exemple si une carte d’acquisition utilise les adresses 0x210 à 0x213,
cela signifie que son adresse de base est 0x210, et qu’elle plaque 4 octets de ses
registres à partir de l’adresse da base. Ainsi, il est possible que le registre plaqué sur
l’adresse 0x210 corresponde aux 8 lignes du port 0, 0x211 aux 8 lignes du port 1,
0x212 aux 8 lignes du port 2, et 0x213 à un octet de configuration de la carte. Le
fait d’écrire sur un de ces octets a pour effet de modifier l’état de la carte. Le fait de
lire un de ces octets a pour effet d’interroger la carte. Ces actions sont suspensives
pour un programme : dans ce cas, qu’il cherche à lire ou écrire sur une adresse
d’entrées/sorties, il peut être suspendu jusqu’à la fin de l’entrée/sortie. La lecture
n’est pas bloquante : cela signifie que même si aucune valeur n’a changé depuis la
dernière lecture sur la carte, la lecture a lieu. L’utilisation basique qui est faite des
entrées numériques est donc de type scrutation : il est nécessaire de lire les entrées
en boucle.
Il existe cependant des types d’entrées capables de générer une interruption sur
front montant ou descendant (changement d’état 0 à 1 ou 1 à 0) : cela permet de
faire des lectures bloquantes, c’est-à-dire de ne pas consommer de temps processeur
hors traitement des événements qui changent l’état des entrées. Il en est ainsi des
entrées dites de type trigger, des entrées de type compteur, ou bien entrées suppor-
tant le pattern matching (capables de générer une interruption sur une différence
d’entrée).
134
4 • Architectures systèmes 4.1 Architecture matérielle
Les entrées de type trigger sont généralement utilisées pour synchroniser les acqui-
sitions sur une source externe, par exemple un trigger (qui se traduit par un signal
carré, soit passage de 0 à 1 puis passage de 1 à 0) périodique, ou bien un trigger
déclenché lorsqu’un événement extérieur survient.
Les entrées/sorties de type compteur permettent de compter des fronts (montants
et ou descendants) de façon bloquante (utilisation des interruptions).
Les entrées supportant le pattern matching (permettant de déclencher une inter-
ruption lorsque les entrées changent), se trouvent sur du matériel assez coûteux.
Elles sont utilisées lorsque le temps de prise en compte des événements extérieurs
doit être très court. En effet, l’un des inconvénients du polling est qu’il est difficile de
garantir la prise en compte d’un événement en dessous d’une période de scrutation,
au minimum de l’ordre de la milliseconde.
M Entrées/sorties analogiques
Lorsqu’un système doit lire des valeurs externes pouvant varier dans le domaine
continu (thermocouple, capteur de position axiale…) ou bien pouvoir commander
des actionneurs comme des électrovannes réglables ou bien l’accélération variable d’un
moteur, il lui faut utiliser des entrées ou sorties analogiques. Les entrées/sorties
analogiques permettent de passer de la continuité du monde réel à l’univers discret
des systèmes informatisés et réciproquement.
Une entrée analogique permet de brancher un élément externe pouvant délivrer
une tension (généralement, les entrées analogiques permettent de lire des tensions de
l’ordre de 0-10 V ou – 10 V-+ 10 V). Un circuit spécifique, nommé Convertisseur
Analogique Numérique (CAN) est utilisé pour convertir la tension d’entrée en valeur
numérique. La précision de cette valeur numérique dépend de plusieurs facteurs :
– la résolution, c’est-à-dire le nombre de bits utilisés pour représenter la tension
lue sous forme numérique (en binaire). Pour n bits de résolutions, 2n valeurs de
tension différentes peuvent être fournies par une entrée analogique. Si le domaine
est – 10 V-+ 10 V, alors 2n valeurs sont utilisées pour représenter une gamme de
20 V. La finesse de la lecture est alors, en plus de la précision physique de la
carte, de 20 V/2n. Typiquement, la résolution est de l’ordre de 12, 16, ou 24 bits.
Avec une résolution de 12 bits, deux valeurs successives représentables ont un écart
de 4,88 mV. En 16 bits, on obtient 300 µV alors qu’en 24 bits, deux valeurs
successives sont séparées de 1 µV. Généralement, il est possible d’ajuster de façon
© Dunod – La photocopie non autorisée est un délit.
logicielle le domaine d’entrée, ainsi, si l’on sait que le signal lu se situe entre 0 et
5 V, la précision de la conversion sera multipliée par 4 par rapport à – 10 V-
+ 10 V (car deux valeurs successives sont séparées de 5 V/2n) ;
– la gamme, caractérisé par la tension minimale et maximale pouvant être lue,
typiquement – 10 V-+ 10 V ;
– le gain permet d’amplifier le signal d’entrée : avec un gain de 10 et une gamme
de 0-10V, le domaine de lecture peut se situer entre 0 et 1 V, ce qui décuple la
précision de la lecture par rapport à un gain de 1 ;
– le bruit, pouvant provenir de différents facteurs extérieurs.
135
4 • Architectures systèmes 4.1 Architecture matérielle
136
© Dunod – La photocopie non autorisée est un délit.
Sélection
32 entrées/sorties
Circuits compteurs/temporisateurs
137
4 • Architectures systèmes 4.2 Architecture logicielle
Applications
Système d’exploitation
Matériel
Ce chapitre n’a pas pour objectif de présenter de façon exhaustive les rôles d’un
système d’exploitation. Il s’attache à décrire quelques éléments indispensables pour
la bonne compréhension des problèmes liés au multitâche. Il présente donc la notion
de processus, d’ordonnancement, et de synchronisation de processus.
4.2.1 Processus
L’un des rôles primordiaux des systèmes d’exploitation multitâches (c’est le cas
aujourd’hui de la quasi-totalité des systèmes d’exploitation) est d’assurer l’exécution
de plusieurs programmes en parallèle.
138
4 • Architectures systèmes 4.2 Architecture logicielle
Terminaison
Attente d’une durée
Exécuté
Endormi
Préemption
Réveil
Suppression
Suppression
Suppression Existant
Inexistant
on peut plonger un processus, jusqu’à sa reprise qui le remet dans l’état prêt, ainsi
que l’état endormi, dans lequel un processus peut se trouver lorsqu’il décide d’attendre
pendant un certain temps.
Techniquement, la préemption se déroule de la façon suivante (figure 4.14) : une
interruption particulière, l’horloge temps réel (HTR), est utilisée par le système
d’exploitation. Périodiquement (la durée d’une période s’appelle un quantum),
l’interruption horloge a lieu. L’ISR qui a lieu fait partie du système d’exploitation :
elle sauvegarde alors dans le bloc de contrôle de processus l’état d’exécution du
processus (l’état des registres), puis restaure dans le processeur l’état des registres de
l’un des processus prêt que le système d’exploitation choisit. Ce qui caractérise un
processus en exécution s’appelle un contexte. La préemption consiste donc en un
changement de contexte. La commutation de contexte est réalisée par une routine
spécifique du système d’exploitation appelée dispatcher. Souvent, dans les micro-
processeurs, le dispatcher est implémenté de façon matérielle.
© Dunod – La photocopie non autorisée est un délit.
139
4 • Architectures systèmes 4.2 Architecture logicielle
Déroulement du processus P1
Déroulement du processus P2
Déroulement du processus P1
Temps
SE
P1 Temps
P1
m Ordonnancement de processus
La stratégie utilisée pour choisir parmi les processus prêts le prochain processus à
exécuter s’appelle l’ordonnancement. L’ordonnancement consiste simplement à
ordonner les processus par priorité. Le dispatcher se charge de placer le contexte du
processus le plus prioritaire sur le processeur. Il existe plusieurs stratégies d’ordonnan-
cement spécifiques pour le temps réel qui seront abordées par la suite. Dans ce cha-
pitre, nous présentons quelques stratégies de bases, utilisées par les systèmes d’exploi-
tation.
Grâce à la préemption et à l’ordonnancement, tout se passe comme si les processus
pouvaient s’exécuter en parallèle. Dans la réalité, le système d’exploitation réalise
un entrelacement temporel des processus.
Notons que les exemples d’ordonnancement présentés ci-après sont très académiques,
puisque les processus sont censés ne pas s’endormir, se suspendre, ou même effectuer
d’actions suspensives ou bloquantes. En effet, lorsqu’un processus exécute une instruc-
140
4 • Architectures systèmes 4.2 Architecture logicielle
P1
P2
P3
P4
P1
P2
© Dunod – La photocopie non autorisée est un délit.
P3
P4
141
4 • Architectures systèmes 4.2 Architecture logicielle
Les critères d’évaluation des algorithmes d’ordonnancement utilisés dans les systèmes
d’exploitation classiques sont les suivants :
– l’équité caractérise le fait que le processeur est réparti de façon équitable entre
les processus. Ainsi, l’algorithme d’ordonnancement FIFO n’est pas équitable,
car si un processus a une durée très longue, les processus lancés après devront
attendre longtemps avant de pouvoir s’exécuter. Si l’on se place dans le contexte
d’un système d’exploitation classique, cela signifie que l’utilisateur ne peut pas exé-
cuter de nouveau processus tant qu’un processus n’est pas terminé. Au contraire,
le tourniquet paraît très équitable puisqu’il partage le processeur de façon égale
entre les processus. Enfin, les algorithmes à priorité ne sont pas équitables pour
les processus peu prioritaires au regard des processus prioritaires ;
– le temps d’attente moyen/maximal représente la durée moyenne/maximale
pendant laquelle un processus reste dans l’état prêt (en attente du processeur). Il
est évident que FIFO et les algorithmes à priorité n’étant pas équitables, ils ne
proposeront pas un temps d’attente maximal très intéressant pour un grand
nombre de cas (il est bien sûr possible de générer des cas particuliers en choisis-
sant les dates de réveil et les durées des processus de sorte à observer un bon temps
d’attente moyen ou maximal). Pour le tourniquet, le temps d’attente maximal
d’un processus est borné : si l’on nomme q la durée d’un quantum, n le nombre
maximal de processus prêts à un instant, et d la durée d’une préemption par le
système d’exploitation, le pire temps d’attente d’un processus est (n – 1)q + n · d
(figure 4.16). Pour les algorithmes FIFO et à priorités, le temps d’attente de
chaque tâche dépend des arrivées et/ou priorités des autres tâches ;
P1
Temps d’attente maximal
P2
P3 Temps
Durée d’un quantum
P4
SE
Durée du surcoût
142
4 • Architectures systèmes 4.2 Architecture logicielle
P1 durée = 6 u.t.
Temps de réponse (P1) = 6 u.t.
P1 durée = 4 u.t.
Temps de réponse (P2) = 9 u.t.
P3 durée = 3 u.t.
Temps de réponse (P3) = 11 u.t.
P1 durée = 6 u.t.
Temps de réponse (P1) = 13 u.t.
P1 durée = 4 u.t.
Temps de réponse (P2) = 10 u.t.
P3 durée = 3 u.t.
Temps de réponse (P3) = 7 u.t.
143
4 • Architectures systèmes 4.2 Architecture logicielle
m Introduction
Des processus doivent pouvoir partager des ressources (matériel, variables, etc.) et
s’échanger des données. Par conséquent, le système d’exploitation permet la com-
munication et la synchronisation de processus. Pour cela il offre des primitives de
communication et de synchronisation, souvent conformes ou proches de la norme
POSIX (voir chapitre 6).
Le fait que les processus puissent s’exécuter en parallèle entraîne des problèmes
appelés problèmes de la concurrence. Considérons pour illustrer cela un exemple :
l’accès concurrent à une variable partagée par deux processus.
Deux processus (figure 4.18) partagent une variable x. Leur action est d’incrémenter
x, puis de se terminer. Ils ont le même code très simple, que l’on peut exprimer par
x := x+1.
Processus A x 0 Processus B
x:=x+1 x:=x+1
Élection de A
Transférer X dans registre R
R 0
Préemption par B (sauvegarde du contexte de A dans son BCP)
BCPA
… Transférer X dans registre R
R 0
R=0
CO Incrémenter le registre R
R 1
Transférer le registre R dans X
x 1
R 0
Incrémenter le registre R
R 1
Transférer le registre R dans X
x 1
144
4 • Architectures systèmes 4.2 Architecture logicielle
Initialement, la variable x vaut 0. Lorsque les deux processus ont terminé leur exécu-
tion, sa valeur peut être 1 ou 2 (cela est déterminé uniquement par les préemptions).
On imagine facilement comment le résultat final de x peut valoir 2, voyons com-
ment il est possible d’obtenir 1. L’incrémentation d’une variable se décompose au
minimum en trois instructions : copie de la variable dans un registre du processeur,
modification du registre, puis recopie du registre à l’adresse mémoire de la variable.
Supposons que le processus P1 ait commencé son exécution : il charge x dans un
registre du processeur, sa valeur est donc 0. Il est alors préempté par P2, les registres
de P1, y compris le registre contenant la valeur 0, sont sauvegardés dans le contexte
de P1. P2 s’exécute alors entièrement, c’est-à-dire qu’il copie la valeur de x (donc 0)
dans un registre, incrémente le registre, et termine après avoir copié la valeur 1 du
registre à l’adresse de x. P1 peut alors poursuivre son exécution. Ses registres sont
restaurés : le registre utilisé pour le calcul vaut donc 0, et le compteur ordinal cor-
respond à l’instruction d’incrémentation du registre. Le registre passe donc à 1,
valeur qui est copiée à l’adresse de x.
Dans cet exemple, la variable x est appelée une ressource critique : élément partagé
par plusieurs processus. Afin de garantir que le résultat vaut toujours 2, il faut garantir
que les processus ne peuvent pas s’interrompre mutuellement entre la lecture de x
et son écriture (ce qui n’empêche pas un autre processus n’utilisant pas x de les
interrompre). La portion de code allant de la lecture à l’écriture de x s’appelle une
section critique. Le fait de garantir que les sections critiques de deux processus
accédant à la même ressource critique ne se préemptent pas s’appelle garantir le respect
de l’exclusion mutuelle. On dit que les deux sections critiques doivent être en exclu-
sion mutuelle. Ainsi, si l’on respecte l’exclusion mutuelle entre les sections critiques
de l’exemple de la figure 4.18, la valeur finale de x est forcément 2.
m Exclusion mutuelle
si la ressource est libre : par exemple 1 pour libre, 0 pour pris. Le code des processus
voulant entrer en section critique serait alors :
Tant que libre≠1 attendre
libre :=0
-- Entrée en section critique
Le problème n’est que repoussé : en effet, un processus peut être interrompu par un
autre entre le moment où il a constaté que Libre était à 1 et le moment où il le met
à 0. On peut alors se retrouver avec plusieurs processus en section critique, car un
second processus peut lui aussi penser que la ressource est libre. C’est exactement
le même type de problème que sur la figure 4.18. Il n’y a aucune solution purement
145
4 • Architectures systèmes 4.2 Architecture logicielle
M Le sémaphore
L’un des outils logiciels (utilisant le matériel) les plus utilisés est le sémaphore. Un
sémaphore peut être vu comme un verrou, qu’il est possible de « prendre » et de rendre
(on dit « vendre »). Prendre un sémaphore est une action bloquante : si le sémaphore
est libre, il devient pris, par contre s’il est déjà pris, le processus voulant le prendre
passe dans l’état bloqué jusqu’à ce qu’il lui soit possible de prendre le sémaphore.
Techniquement, le fait de tenter de prendre un sémaphore qui n’est pas libre a pour
effet de passer le processus dans l’état bloqué, et de placer le numéro du processus
dans la file d’attente du sémaphore. Un sémaphore est donc composé d’un entier
et d’une file d’attente. Les actions prendre et vendre peuvent se dérouler de la façon
suivante (cas d’un sémaphore binaire, c’est-à-dire à deux états) :
Un sémaphore S consiste en un entier S.val, et une file d’attente
d’identificateurs de processus S.file.
Procédure Prendre(S: in out sémaphore)
Début
Masquer les interruptions
-- La procédure prendre ne doit pas être interrompue
Si S.val=0 alors
Ajouter le n° de processus à S.file
Démasquer les interruptions
Passer dans l’état bloqué et appeler le dispatcher
Sinon
S.val ←0
Démasquer les interruptions
FinSi
Fin
Procédure Vendre(S:in out sémaphore)
Début
Masquer les interruptions
Si il existe un processus dans S.file alors
Enlever un processus de la file
Mettre ce processus dans l’état prêt
Sinon
S.val←1
FinSi
Démasquer les interruptions
Appel au dispatcher
Fin
À l’intérieur des primitives, les interruptions sont masquées afin de les rendre non
préemptibles : ces primitives sont atomiques, dans le sens où elles sont non inter-
ruptibles.
Il est important de bien comprendre que ce sont les processus qui exécutent les
primitives Prendre et Vendre. Ces primitives, fournies par le système d’exploita-
tion ou présentes dans les langages de programmation, utilisent le masquage des
interruptions afin d’exécuter des parties critiques (test de la valeur du sémaphore,
mise à jour de la valeur et de la file d’attente). Un processus qui exécute Prendre se
bloque si le sémaphore n’est pas libre, un processus qui exécute Vendre réveille lui-
146
4 • Architectures systèmes 4.2 Architecture logicielle
même un processus bloqué. Il n’y a donc pas d’arbitre, et les processus eux-mêmes
gèrent grâce à Prendre et Vendre le passage de l’état prêt à bloqué et de bloqué à prêt.
Afin d’illustrer le fonctionnement du sémaphore, supposons que trois processus A,
B, et C exécutent chacun le code suivant en parallèle :
S : Semaphore := créer_sémaphore("mutex_x",1)
-- S est un sémaphore partagé par tous les processus nommé mutex_x
-- Ce sémaphore commun protège les accès à la variable x partagée par
-- les processus
Prendre(S)
x := x+1
Vendre(S)
Notons que le terme mutex (pour mutual exclusion) est souvent utilisé pour dénommer
les sémaphores permettant d’assurer l’exclusion mutuelle. Ici, « mutex_x » est un
sémaphore du système d’exploitation utilisable par tous les processus : le premier
processus demandant à créer ce sémaphore crée effectivement le sémaphore, avec
une valeur initiale de 1. La fonction créer_sémaphore renvoie alors une référence à
ce sémaphore, qui est utilisée en paramètre des fonctions Prendre et Vendre. Après
que ce sémaphore ait été créé, il est repéré par son nom dans le système. Les appels
suivants à créer_sémaphore("mutex_x",1) se contentent alors de renvoyer la réfé-
rence du sémaphore déjà existant. C’est donc le même sémaphore du système qui
sera utilisé par tous les processus utilisant un sémaphore du même nom.
Supposons que le processus A débute son exécution : il prend le sémaphore (sa valeur
passe donc à 0) et entre en section critique. Le processus B est créé et préempte le
processus A pendant sa section critique. Au moment où le processus B tente de
prendre le sémaphore, il passe alors dans l’état bloqué et son numéro est stocké
dans la file d’attente du sémaphore. Supposons que C soit alors créé : de la même
façon que le processus précédent, il passe dans l’état bloqué au moment où il tente
de prendre le sémaphore et son numéro est stocké dans la file d’attente du sémaphore.
Le processus A reprend alors la main. Lorsqu’il sort de sa section critique, il vend
le sémaphore. Cela se traduit par le réveil du premier processus en attente (si l’on
considère que la file d’attente est gérée en FIFO), qui peut alors entrer en section
critique. La valeur du sémaphore ne change pas, puisque cela reviendrait à l’incré-
menter puis le décrémenter. Il est important de remarquer que ce processus ne peut
pas savoir qu’il s’est bloqué, tout se passe comme s’il s’était endormi en attendant
d’entrer en section critique, sans s’en apercevoir. A termine alors. Lorsque B termine
© Dunod – La photocopie non autorisée est un délit.
sa section critique, il passe C dans l’état prêt : celui-ci peut à son tour entrer en sec-
tion critique. C, lorsqu’il vend le sémaphore à la fin de sa section critique, incré-
mente sa valeur puisqu’il n’y a plus aucun processus en attente.
Que se passerait-il si la ressource critique était telle que deux processus, mais pas
plus, pouvaient y accéder en même temps ? Il suffirait simplement d’initialiser le
sémaphore à la valeur 2. Nous aurions alors un sémaphore que l’on qualifierait de
sémaphore à compte, à la différence du sémaphore binaire (prenant les valeurs 0
ou 1).
Lorsque l’on utilise un sémaphore à compte, il est possible pour un processus de
demander à Prendre ou Vendre plusieurs instances d’un sémaphore, les primitives
147
4 • Architectures systèmes 4.2 Architecture logicielle
Prendre et Vendre sont alors modifiées pour prendre en compte un paramètre qui
est le nombre d’instances concernées.
L’utilisation d’un sémaphore pour garantir l’exclusion mutuelle est donc relativement
simple, puisqu’elle consiste à entourer une section critique par la prise et la vente d’un
sémaphore utilisé pour protéger l’accès à la ressource. L’avantage est que le respect
de l’exclusion mutuelle se fait à coût processeur faible, puisque les processus en attente
d’entrée en section critique n’utilisent pas le processeur (passage dans l’état bloqué ).
L’inconvénient de cette approche est le risque d’oublier de protéger l’accès à une
ressource par un sémaphore, d’oublier de le vendre, etc. Nous verrons dans la suite
que le sémaphore est un outil important, très utilisé en programmation parallèle
dans les langages de programmation à base de langage C.
Parfois, il est intéressant de nuancer le type d’accès à une ressource : accès en lecture
seule, ou accès en écriture. Par exemple, il n’est pas gênant que des processus faisant
un accès en lecture à une base de données se préemptent mutuellement pendant
un accès en lecture à la base. Par contre, il faut garantir qu’un processus faisant un
accès en écriture (modification d’une ligne d’une table par exemple) soit le seul en
section critique : aucun « lecteur » (processus faisant un accès en lecture), et bien sûr
aucun autre « écrivain » (processus modifiant la base de données) ne doit pouvoir
avoir accès à la base pendant sa modification (pendant qu’un écrivain est en section
critique). Ce problème est appelé problème du lecteur/écrivain. Sa solution est très
simple lorsque le nombre de lecteurs n est connu et borné :
Sémaphore_bdd : sémaphore(n)
Processus lecteuri,i=1..n :
Début
Faire toujours
Prendre(sémaphore_bdd)
Lire la base de données
Vendre(sémaphore_bdd)
Fait
Fin
Processus écrivaini,i=1..m :
Début
Faire toujours
Prendre(sémaphore_bdd,n)
-- on prend les n instances du sémaphore
Modifier la base de données
Vendre(sémaphore_bdd,n)
Fait
Fin
148
4 • Architectures systèmes 4.2 Architecture logicielle
Producteur Consommateur
Début Tampon Début
Prendre(Libre) Prendre(Plein)
Produire Prendre dans tampon
Déposer dans tampon Consommer
Vendre(Plein) Libre ← taille_tampon Vendre(Vide)
Fin Plein ← 0 Fin
Sémaphores
On voit que si l’un des processus détient une ressource, et que l’autre processus
détient l’autre ressource, alors les deux processus sont bloqués indéfiniment : c’est un
interblocage. Il existe plusieurs solutions au problème d’interblocage : par exemple, la
détection, l’évitement, la reprise après interblocage. On peut se faire une idée de la
complexité de la détection d’un interblocage sur un exemple classique : le dîner
des philosophes (figure 4.20). Plusieurs philosophes sont assis autour d’une table,
pensent, et mangent de temps en temps en utilisant des baguettes. Mais les facétieux
149
4 • Architectures systèmes 4.2 Architecture logicielle
cuisiniers n’ont disposé à table qu’une seule baguette par philosophe : tout se passe
bien, jusqu’à ce que chaque philosophe, ayant faim au même moment, ne saisisse
une baguette. Les processus sont alors en interblocage.
Philosophe 0
Baguette 0 Baguette 1
Philosophe 1
Philosophe 3
Baguette 3 Baguette 2
Philosophei,i=0..3:
Faire toujours
Philosophe 2 Penser
Prendre(baguettei)
Prendre(baguettei-1 mod 4)
Manger
Vendre(baguettei-1 mod 4)
Vendre(baguettei)
Fait
Une solution simple d’évitement de l’interblocage consiste, lorsque cela est pos-
sible, à prendre les sémaphores dans un ordre déterminé. Ainsi, chaque sémaphore
peut être numéroté, et un processus ayant besoin de plusieurs ressources pour entrer
en section critique, ne peut demander à prendre des sémaphores que dans l’ordre
croissant de leur numéro. Sur la figure 4.20, si l’on numérote les baguettes de 0 à 3
dans le sens des aiguilles d’une montre, alors trois des philosophes prendront la
baguette à leur droite, puis la baguette à leur gauche, et le quatrième prendra la
gauche avant la droite, rompant ainsi le cycle fatal. Nous verrons qu’il existe une
autre solution basée sur l’ordonnancement des processus.
M Le moniteur
Nous avons vu que l’inconvénient des sémaphores était le risque d’oubli : une res-
source critique (par exemple une variable partagée par plusieurs processus) est protégée
par un sémaphore. Il faut donc normalement prendre le sémaphore avant tout
accès, et le vendre après. Cependant, sur une application relativement importante,
150
4 • Architectures systèmes 4.2 Architecture logicielle
Incrémenter
File d’attente de processus bloqués
Décrémenter
Lire
Attendre_valeur_positive
© Dunod – La photocopie non autorisée est un délit.
Moniteur
Un seul processus peut être en cours
d’exécution d’une primitive du moniteur
151
4 • Architectures systèmes 4.2 Architecture logicielle
Cette solution n’est pas satisfaisante. En effet, d’une part, même si elle pouvait fonc-
tionner, elle contiendrait de l’attente active. Mais surtout, elle ne peut pas fonc-
tionner, étant donné que lorsqu’un processus se trouve dans la boucle « tant que »,
il bloque l’accès au moniteur, et empêche alors l’autre processus de modifier nombre
_pris. Un processus entrant dans la boucle « tant que » reste donc indéfiniment
dans cette boucle et bloque l’accès au moniteur. Le moniteur propose donc deux
procédures : wait et signal. La procédure wait a pour effet de bloquer le proces-
sus appelant, libérant ainsi le moniteur, jusqu’à l’appel de la procédure signal. À
l’appel de cette procédure, les processus bloqués sur wait seront remis dans l’état
152
4 • Architectures systèmes 4.2 Architecture logicielle
prêt et poursuivront leur exécution. Ce sont ces procédures (voir code ci-après) qui
sont utilisées lorsqu’un processus doit être bloqué sous certaines conditions à l’inté-
rieur du moniteur, jusqu’à ce qu’un autre processus vienne le réveiller. La solution
correcte au problème producteur/consommateur est donc :
Moniteur prod_cons
taille:entier -- taille du tampon
T:tampon(taille)
nombre_pris:entier:=0 -- nombre de cases prises dans le tampon
Procédure produire(e:élément)
Début
Tant que nombre_pris=taille faire
-- le tampon est plein
wait -– le processus s’endort jusqu’au prochain
-– signal
Fait
Mettre e dans T
nombre_pris:=nombre_pris+1
signal
-- réveille un éventuel consommateur bloqué
Fin
Fonction consommer renvoie élément
Début
Tant que nombre_pris=0 faire
-- le tampon est vide
wait
Fait
nombre_pris:=nombre_pris-1
Enlever et retourner un élément de T
signal
-- réveille un éventuel producteur bloqué
Fin
Fin du moniteur
Lorsqu’un processus doit produire (resp. consommer) une donnée, il lui suffit donc
d’appeler prod_cons.Produire (resp. prod_cons.Consommer), et il n’y a pas à se
soucier de faire une utilisation correcte des sémaphores comme dans la solution à
base de sémaphores.
uns des autres. Lorsque des processus partagent une ou des ressources, ils peuvent
passer dans l’état bloqué afin de permettre la garantie d’exclusion mutuelle, lorsqu’ils
tentent d’accéder à un sémaphore ou bien à un moniteur. Il est très intéressant
d’observer l’impact des sections critiques sur les algorithmes à priorité, qui sont
très utilisés dans les systèmes temps réel et dans les systèmes de contrôle-commande,
ainsi que dans l’algorithme MLF utilisé par nombre de systèmes d’exploitation géné-
ralistes.
Observons un effet possible de l’exclusion mutuelle sur des processus (figure 4.22).
Trois processus s’exécutent à des niveaux de priorité différents. Le processus de priorité
haute partage une ressource avec le processus de priorité basse, et un processus de
priorité intermédiaire s’exécute indépendamment des deux autres.
153
4 • Architectures systèmes 4.2 Architecture logicielle
Prendre(s) Vendre(s)
Algorithme d’ordonnancement à priorités
P1
Priorité faible
Vendre(s)
P2 P2 est bloqué
Priorité forte
Prendre(s)
P3
Inversion de priorité
Priorité moyenne
Section critique
m Segmentation de la mémoire
Chaque processus possède sa mémoire propre : ceci s’explique par le fait qu’un sys-
tème d’exploitation est plus robuste si chaque processus est cantonné dans un espace
mémoire clos. Au lancement d’un processus, le système d’exploitation lui alloue de
la mémoire dans la mémoire centrale qu’il segmente en trois parties distinctes : le
segment de code, dans lequel il place les instructions du processus (voir tableau 4.1),
alloue un segment de pile (appelé stack), et un segment de données (appelé tas,
154
4 • Architectures systèmes 4.2 Architecture logicielle
Pile d’appel
Pile ou stack Variables locales aux fonctions/procédures
(segment de pile) Paramètres, résultat et adresses de retour
des sous-programmes
Le segment de données sert à stocker les variables globales, les variables rémanentes
des sous-programmes (par exemple, en langage C, celles déclarées avec le mot clé
static), ainsi que les variables allouées dynamiquement (pointeurs).
© Dunod – La photocopie non autorisée est un délit.
155
156
Procédure f1(a: entier)
Procédure f2(a:entier)
Procédure f3(a,b: entier)
vi signifie valeur de la variable i État de la pile
vj signifie valeur de la variable j
Processus ∅
…
f1(3) Adresse
3
de retour 1
Exécution f1(3)
Adresse
4 • Architectures systèmes
i, j:entier 3 vi vj
…. de retour 1
f2(i) Adresse Adresse
3 vi vj vi
de retour 1 de retour 2
Exécution f2(vi)
….
fin
Adresse
Adresse de retour 2 3 vi vj
de retour 1
…
f3(i,4) Adresse Adresse
3 vi vj 4 vi
de retour 1 de retour 3
Exécution f3(vi,4)
….
fin
Adresse
Adresse de retour 3 3 vi vj
de retour 1
…
fin
Adresse de
retour de 1 ∅
…
Si l’on observe les adresses des instructions et variables du tableau 4.1, on trouve
© Dunod – La photocopie non autorisée est un délit.
des valeurs de l’ordre de 0x400000, soit des adresses aux alentours de 4 Mo. Si l’on
compare les adresses utilisées par plusieurs processus s’exécutant en parallèle, on pourra
trouver des adresses identiques, qui ne correspondent pas aux mêmes adresses phy-
siques. En effet, le système d’exploitation permet aux processus d’utiliser un adressage
virtuel : pour un processus, tout se passe comme s’il possédait les adresses 0 à x,
sachant que le système d’exploitation se charge de faire une conversion de l’adresse
virtuelle en adresse physique.
Le but de la pagination est le suivant : un très grand espace mémoire peut être alloué
à chaque processus, tel que la somme soit supérieure à la taille de la RAM. La
mémoire centrale est découpée en cadres de taille identique (par exemple 4 Ko).
La mémoire d’un processus chargée en mémoire centrale réside alors dans un certain
157
4 • Architectures systèmes 4.2 Architecture logicielle
158
4 • Architectures systèmes 4.2 Architecture logicielle
Seule l’adresse physique du début de la page doit être retrouvée. Il faut alors une
sorte d’annuaire par processus, appelée table des pages, qui fait la correspondance
entre numéro de page logique, sur 20 bits, et adresse physique du cadre contenant
la page. Si la page n’est pas en mémoire centrale, la MMU se charge de l’y placer
avant de trouver l’adresse physique du cadre correspondant. Donc la MMU se charge
d’effectuer la correspondance entre les 20 bits de l’adresse logique d’une page d’un
processus et les 20 bits de l’adresse physique du cadre contenant la page (en effec-
tuant si nécessaire un swap pour placer la page en mémoire centrale).
Cependant, quand on fait les comptes, on s’aperçoit qu’une page de 4 Ko peut con-
tenir 212 octets, ce qui n’est pas suffisant pour contenir les éventuelles 220 adresses
physiques des pages. Il y a donc souvent un index des pages à deux niveaux, avec
un certain nombre de pages indexées directement, les autres nécessitant l’accès à des
tables des pages de second niveau. En fonction des processus en cours d’exécution,
le temps d’exécution d’un processus peut varier de façon significative à cause de la
mémoire virtuelle : en effet un accès physique à la mémoire centrale coûte généra-
d y d
x
0 Adresse
Adresse
logique 1 physique
...
x y
z d
x1 x2 d
Adresse 0 Adresse
© Dunod – La photocopie non autorisée est un délit.
logique 1 physique
... 0
x1 y 1
...
x2 z
Table des pages
externe
Table des pages
Deux niveaux de table située dans la page y
159
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
lement 5 à 10 cycles machine, et il peut falloir 3 accès pour accéder à une variable
(accès à la table des pages de premier niveau, accès à la table de second niveau, pour
obtenir l’adresse physique du cadre contenant la variable), avec, suivant l’utilisation
de la mémoire physique, possibilité d’avoir à swapper pour utiliser ces 3 pages.
Dans le pire des cas, l’accès à une variable peut donc se compter en dizaines de milli-
secondes. Dans le meilleur des cas, les trois pages sont en mémoire centrale, et même,
les valeurs nécessaires sont dans la mémoire cache (accès en un cycle machine) et
l’accès à la variable ne coûte que trois cycles machine.
Sur un système d’exploitation utilisant la mémoire cache et la mémoire virtuelle,
l’accès à une variable peut nécessiter un temps pouvant aller de moins d’une nano-
seconde à plusieurs dizaines de millisecondes en fonction de l’utilisation de la mémoire
cache et de la mémoire virtuelle par l’ensemble des processus.
160
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
Arbre Maillage
Anneau Étoile
Pour le réseau internet, ces deux types de communication sont mélangés : des réseaux
locaux utilisent généralement de la diffusion, et un nœud particulier appelé routeur
est connecté en point à point avec un autre routeur, lui-même souvent point d’entrée
d’un autre réseau local. Les connections point à point sont souvent hiérarchiques,
161
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
ce qui permet à n’importe quel nœud d’un réseau local de communiquer avec
n’importe quel nœud d’un autre réseau local (figure 4.28), comme nous le verrons
avec le protocole TCP/IP.
Routeur
ou passerelle
Routeur
ou passerelle
Réseau local
Réseau local
m Architecture en couches
162
© Dunod – La photocopie non autorisée est un délit.
Application Données
Recherche adresse IP de la
prochaine machine à atteindre En-tête En-tête
Couche IP Réassemblage éventuel Données
pour transmettre les données IP TCP
4 • Architectures systèmes
Internet Protocol
à destination (routage),
Datagramme
fragmentation éventuelle
Couche ARP
Obtention adresse MAC à partir d’une adresse IP, diffusion de son
Address Resolution
adresse IP et son adresse MAC
Protocol
Préparation éventuelle de la mise Peut s’occuper du séquencement
en oeuvre d’une connexion des trames, vérifier les erreurs, En-tête En-tête En-tête
Couche LLC Données
logique, séquencement de trames, envoi éventuel d’accusé LLC IP TCP
Logical Link Control
peut ajouter contrôles d’erreur, de réception
PDU (Protocol Data Unit)
possibilité de ré-émission si erreur
Ajout adresse physique de Vérification d’adresse MAC,
Couche MAC En-tête En-tête En-tête En-tête Queue
destination, gestion d’accès vérification cohérence trame Données
Medium Access Control MAC LLC IP TCP MAC
au médium reçue
Trame
Transfert des bits après encodage Lecture et décodage des bits
Couche Physique sur le medium de communication circulant sur le médium 1011101100110011001010010111…
de communication Bits
Médium de communication :
paire torsadée, câble coaxial, fibre optique, support immatériel…
Signal
163
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
données d’application de façon fiable. Afin de dialoguer avec la couche TCP du nœud
destination, TCP ajoute un en-tête qui sera lu et enlevé du message lors de sa
réception par la couche TCP du nœud destination.
Chaque couche ajoute ses propres données (sous forme d’un en-tête et/ou queue)
aux données transmises par la couche du dessus lors de l’envoi de données. Lors de
la réception de données, chaque couche se sert de l’en-tête et/ou queue placée par
la couche correspondante du nœud émetteur afin de traiter les données reçues.
Elle enlève ensuite ces informations qui sont spécifiques à son niveau, pour passer
les données à la couche de niveau supérieur. L’avantage d’une telle approche est que,
quel que soit le support physique sous-jacent (câble, fibre optique, air, vide…), les
couches à partir de LLC peuvent fonctionner de la même façon. De plus, pour un
programmeur, le support de transmission employé est totalement transparent.
L’ISO (International Organization for Standardization), l’AFNOR (Association
Française de Normalisation) et le CCITT (Comité Consultatif International Télé-
phonique et Télégraphique, devenu depuis ITU pour International Communication
Union) ont normalisé le modèle OSI (Open Systems Interconnection) en couche,
qui est utilisé par des réseaux définis ultérieurement à TCP/IP (comme par exemple
le protocole X.25 qui définit comment une communication par modem et ligne
téléphonique se déroule en point à point). Ce modèle est illustré sur la figure 4.30.
Les couches diffèrent dans leur découpage, mais on peut réaliser les associations sui-
vantes : couche 1 = couche physique, couche 2 = MAC + LLC + 1/2 ARP, couche 3
Système A Système B
protocole
Couche 7 Application Application
Application
protocole de la communication
Couche 6 Présentation Présentation
Gestion
protocole
Couche 5 Session Session
Session
protocole
Couche 4 Transport Transport
Transport
protocole
Couche 3 Réseau Réseau
de la transmission
Réseau
protocole
Gestion
164
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
M Couche 1 (physique)
Les bits peuvent être transmis sous la forme de signaux en bande de base (2 ou 3 états
électriques différents servent à passer les bits), ou bien en modulation (amplitude,
fréquence et/ou phase). Les différents codages utilisés sont étudiés afin d’éviter
© Dunod – La photocopie non autorisée est un délit.
165
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
Aléatoire Contrôlé
Centralisé Distribué
Anneau à jeton
ARINC 629 (CSMA/CA) CAN (CSMA/CA) CSMA/CD (Ethernet) FIP
FDDI
(Avionique) (Automobile) CSMA/DCR DQDB
TDMA
ARINC 629 : réseau avionique civile (1994)
CAN : Controller Area Network (1994)
CSMA/CD (Ethernet) : Carrier Sense Multiple Access/Collision Detection (1985)
CSMA/DCR : Carrier Sense Multiple Access/Deterministic Collision Resolution (1990)
CSMA/CA : Carrier Sense Multiple Access/Collision Avoidance (~ 1990)
FIP : Factory Instrumentation Protocol (1990)
DQDB : Dual Queue Dual Bus (1985)
FDDI : Fibber Distributed data Interface (~ 1990)
TDMA : Time Division Multiple Access (~ 1990)
Ethernet
La norme Ethernet (normalisée IEEE 802.3) est la norme de niveau MAC la plus
répandue à l’heure actuelle sur les réseaux locaux. Cette norme fait son apparition
sur le marché industriel et concurrence désormais les bus de terrain, car bien que
théoriquement moins robuste, elle a un coût financier très faible, des débits moyens
très élevés, et des temps moyens d’accès au médium très bas. Au niveau physique,
Ethernet utilise généralement de la transmission numérique sur paires torsadées
ou câbles coaxiaux. Les débits sont importants, puisque suivant le matériel utilisé,
on peut échanger des données à 10 Mbits/s, 100 Mbits/s, 1 Gbits/s, etc. La commu-
nication est de type diffusion (le plus souvent, sur un bus), et chaque nœud est muni
d’une adresse (sur 2 octets, ou plus, typiquement sur 6 octets) dite adresse MAC
ou adresse Ethernet.
Lorsqu’un nœud émet une trame (figure 4.32) à destination d’un autre nœud, tous
les nœuds du réseau la reçoivent, mais seul le nœud possédant l’adresse destination
doit lire la trame.
En réalité, les bus Ethernet ont une forme d’étoile ramifiée (figure 4.33). Dans le
protocole originel, toute trame émise par un nœud est diffusée à tous les nœuds
166
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
1500 o ≥ Taille ≥ 46 o
Taille ≥ 64 o
Switch
Extérieur
Serveur
important Routeur
Switch
Hub
Hub
b a
Figure 4.33 – Un « bus » Ethernet ressemble davantage à une étoile ramifiée qu’à un bus.
que vers les branches concernées. Un switch est en fait une passerelle de niveau MAC.
Ethernet arbitre l’accès au medium suivant le protocole d’accès décentralisé non déter-
ministe CSMA/CD (Carrier Sense Multiple Access with Collision Detection) :
lorsqu’un nœud souhaite émettre des données, il attend que le médium soit libre
(pas d’émission en cours) et émet une trame contenant ses données. Tout en conti-
nuant à émettre, il observe le réseau pendant 2 fois la durée maximale de propagation
d’un message (temps d’aller/retour maximal d’un signal entre les deux nœuds les
plus éloignés du réseau). Pendant cette durée appelée tranche canal, il vérifie que
ce qu’il lit sur le réseau correspond bien à ce qu’il écrit (i.e. il vérifie qu’aucun autre
nœud n’a pris l’initiative d’émettre presque en même temps que lui). S’il s’aperçoit
qu’il y a collision, il continue à émettre sa trame avec des bits de bourrage afin d’être
167
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
certain que tous les nœuds concernés s’aperçoivent aussi de la collision (il y a une
taille minimale de trame de 64 octets afin d’être certain que deux stations entrant
en collision s’aperçoivent de la collision, et que les nœuds destination des trames
s’aperçoivent aussi de la collision). Les deux nœuds entrés en collision tirent alors
un nombre aléatoire entre [0 et 2n × tranche canal-1] avec n le nombre de tentatives
d’émission de la trame. Au bout d’un certain temps, non déterministe, si le réseau
n’est pas surchargé, un nœud pourra émettre sa trame. Si le réseau possède trop de
nœuds et que le nombre de collisions est important, on parle d’écroulement du
réseau.
La longueur maximale d’un réseau Ethernet est fonction de la tranche canal, et de
la longueur minimale d’une trame.
Afin de limiter les problèmes d’écroulement lorsque le nombre de nœuds sur un
réseau Ethernet est important, l’Ethernet commuté est apparu : il se base sur des
répéteurs actifs appelés switchs. Leur rôle est de collecter les adresses Ethernet des
nœuds connectés à chacune de ses branches, ainsi, lorsqu’une trame arrive sur un
nœud à destination d’une adresse, le switch ne relaie la trame que sur la branche
menant au nœud destination. Les switchs permettent de réduire les espaces de
collision (un espace de collision est un espace dans lequel tout message est vu par
tous les nœuds).
Enfin, grâce aux paires torsadées, la communication entre les switchs ou hubs et les
nœuds peut utiliser le full-duplex, c’est-à-dire émettre et recevoir en même temps
des trames.
Le protocole Ethernet est extrêmement flexible, performant, et bon marché mais
non déterministe.
Wi-Fi
Une version sans fil d’Ethernet a été normalisée sous les normes IEEE 802.11,
appelées Wi-Fi (Wireless Fidelity). Ce protocole, utilisant une transmission ana-
logique à base de déphasage (quadrature de phase), permet des débits relativement
importants (55 Mbits/s pour la norme 802.11g). Les ondes radio utilisées sont sur
la bande des 2,4 GHz (ou 5 GHz pour la norme 802.11a). Le mode d’accès ne peut
pas être de type CSMA/CD car deux nœuds émettant vers un nœud tiers peuvent
168
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
169
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
Arbitrage centralisé
Des protocoles à accès contrôlé de façon centralisée ont été définis, mais ils sont
relativement peu utilisés pour les réseaux généralistes : étant donné que l’arbitre ne
peut pas savoir à l’avance quelle station voudra émettre, et de quelle bande passante
elle a besoin, une bonne partie de la bande passante est perdue par le mécanisme
d’interrogation des nœuds. L’arbitre interroge chaque nœud, soit chacun leur tour
(scrutation) soit avec un message destiné à tous (test) afin de savoir qui veut émettre.
Il alloue alors le médium aux nœuds, chacun leur tour.
Citons le protocole TDMA (Time Division Multiple Access) qui accorde un certain
temps à chaque nœud, qu’il veuille émettre ou non : ce mode déterministe et pério-
dique gaspille énormément de bande passante.
M Couche 3 (réseau)
Quelles que soient les couches de niveau 1 et 2, elles assurent une transmission entre
deux ou n nœuds à vue directe, c’est-à-dire que, pour la couche 2, deux nœuds peu-
vent communiquer s’ils partagent le même médium de communication. La couche 3,
s’appuyant sur la couche 2, a pour objet le routage des informations entre des
nœuds pouvant appartenir à différents réseaux locaux. Le protocole de couche 3 le
plus utilisé est le protocole IP (Internet Protocol). Chaque nœud visible sur un
réseau possède une adresse IP (en plus bien sûr d’une adresse de niveau inférieur,
comme une adresse Ethernet par exemple). Deux entités de couche 3 peuvent com-
muniquer à partir du moment où il existe un chemin entre eux à travers des nœuds
appelés routeurs. La communication physique passe de nœud en nœud à vue directe,
mais la couche 3 se charge de choisir un chemin afin d’acheminer des données
entre deux nœuds qui peuvent se trouver sur différents réseaux locaux.
Une adresse IP (dans la version 4) est une adresse constituée de 32 bits (figure 4.34)
notée sous la forme de 4 chiffres compris entre 0 et 255, qui sera supplantée par
une adresse sur 64 bits dans la norme IP version 6. À part les adresses spécifiques
privées, IP version 4 définit 5 classes d’adresses IP.
Une adresse de classe A est composée d’un octet définissant un réseau (par exemple,
11.0.0.0 définit un réseau de classe A), et de 3 octets définissant les adresses des
nœuds du réseau (un nœud du réseau peut avoir l’adresse 11.241.23.195), ce qui
permet aux gestionnaires d’un réseau de classe A d’adresser plus de 16 millions de
nœuds (224-2 machine, le suffixe .0.0.0 étant réservé pour adresser le réseau entier,
et le suffixe .255.255.255 à diffuser à chaque nœud du réseau).
Une adresse de classe B est composée de deux octets définissant le réseau (par
exemple, 130.15.0.0 est un réseau de classe B) et de deux octets pour définir l’adresse
d’un nœud du réseau (par exemple, 130.15.65.123), ce qui permet d’adresser 216-2
machine.
Enfin, les réseaux de classe C sont définis sur 3 octets (par exemple, 193.55.198.0
est un réseau de classe C), ce qui laisse la possibilité d’adresser 254 nœuds (par
exemple, 193.55.198.56).
Un organisme mondial, l’ICANN (Internet Corporation for Assigned Names
and Numbers) attribue des classes d’adresses IP à différents organismes continentaux,
170
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
comme le RIPE (Réseaux IP Européens) qui alloue alors des classes d’adresses aux
administrateurs de réseaux.
Généralement, toutes les machines d’un même réseau local utilisent une adresse
de la même classe d’adresse. En théorie, une classe d’adresse peut correspondre
physiquement à un seul réseau local. Cependant, il est clair qu’il est impensable de
mettre 16 millions de nœuds (cas des réseaux de classe A), voire même 65 000
(réseaux de classe B), sur un même réseau Ethernet : celui-ci s’écroulerait immédia-
tement sous le nombre de collisions. En pratique, une zone de collision d’un réseau
Ethernet (zone de diffusion totale par hub par exemple) ne dépasse pas quelques
dizaines ou petites centaines de nœuds (pas plus de 1 024 d’après la norme Ethernet).
Donc, les réseaux de classe A et B sont souvent découpés en sous-réseaux. Pour ce
découpage, on utilise un masque de sous réseau. Ce masque, de la même taille en
octets que l’adresse IP, est utilisé avec un « et » binaire sur l’adresse d’un nœud. Ainsi,
si un nœud possède une adresse a1.a2.a3.a4, et que le masque est m1.m2.m3.m4,
alors toute machine d’adresse a’1.a’2.a’3.a’4 telle que a1.a2.a3.a4 et m1.m2.m3.m4
= a’1.a’2.a’3.a’4 et m1.m2.m3.m4 est censée se trouver sur le même réseau local, c’est-
à-dire à vue directe. La première tâche de la couche IP est donc de déterminer si le
© Dunod – La photocopie non autorisée est un délit.
nœud destination est à vue directe. Pour cela, IP connaît l’adresse du nœud d’émis-
sion, le masque de sous-réseau du réseau local, et l’adresse destination. Il suffit
alors d’utiliser la formule « mon adresse et masque = adresse destination et masque »
afin de déterminer si le nœud destination est directement accessible : si c’est le cas,
IP obtient grâce à ARP (Address Resolution Protocol) l’adresse MAC du nœud
destination, il lui suffit alors d’utiliser les services de la couche 2 pour émettre ses
données, appelées datagramme. Si le nœud ne se trouve pas sur le même réseau,
alors le datagramme est envoyé vers une machine particulière du réseau, identifiée
comme passerelle ou routeur (figure 4.35), qui se charge de trouver une route jusqu’au
réseau du nœud destination (généralement, le routeur est connecté à un ou plusieurs
autres routeurs, le datagramme va donc traverser un certain nombre de routeurs,
171
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
Noeuda1
Hôte a 2
Routeur r1 Routeur r 2
(Passerelle par défaut) (Passerelle par défaut)
du réseau a 1 & m1 du réseau a2 & m2
Application Application
TCP TCP
Au cours du trajet suivi par les données, différents supports physiques sont traversés :
chacun a des contraintes sur la taille maximale des datagrammes transportés. Le
datagramme original pourra donc être fragmenté pendant le voyage, puis réassemblé
à l’arrivée. Il est à noter que la route empruntée par les datagrammes, ainsi que
leur ordre d’arrivée et le fait même qu’ils arrivent à destination, est indéterministe.
En fonction des couches physiques choisies de machine à machine, il est possible que
de tel à tel nœud, la transmission soit fiable, mais que de tel à tel autre nœud, la trans-
mission soit non fiable. Dans tous les cas, IP est un protocole réseau non fiable,
puisqu’il n’y a aucune garantie d’acheminement, et généralement pas de garantie
de qualité de service (vitesse de transmission…) bien que la norme IP version 6
tente d’y apporter des améliorations.
IP permet donc d’assurer le routage de datagrammes entre plusieurs réseaux hétéro-
gènes, ceci sans aucune garantie de fiabilité. C’est pourtant à ce jour le protocole
de couche réseau le plus utilisé.
172
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
M Couche 4 (transport)
La couche transport s’appuie sur la couche réseau : la couche réseau fournit des
services permettant une transmission non fiable d’un nœud à un autre, quels que
soient les réseaux locaux auxquels ils appartiennent. Les deux protocoles de couche
transport les plus utilisés sont TCP (Transmission Control Protocol) et UDP
(User Datagram Protocol).
Dans la pile TCP/IP, la couche 4 est la dernière couche avant l’application : les ser-
vices de la couche transport sont donc souvent directement utilisés par les processus.
Sur un nœud, plusieurs processus peuvent utiliser le réseau (figure 4.36) : par exemple,
un navigateur internet, un logiciel de lecture de courrier, etc. Il faut donc distinguer
les données entrantes afin de les communiquer à un processus particulier. En effet,
ce sont des processus qui s’exécutent sur un nœud et qui communiquent à travers
le réseau avec d’autres processus distants. L’idée est alors la suivante : il y a une adresse
de niveau MAC utilisée pour la transmission physique à vue directe, une adresse IP
pour la transmission de nœud à nœud, et finalement une adresse de couche transport
permettant d’adresser un processus particulier sur un nœud. Pour les protocoles
TCP et UDP, cette adresse est un nombre sur 16 bits appelé port. Un port est donc
une valeur allant de 0 à 65 535, qui sert à discriminer les processus disposés à recevoir
des données sur le réseau. Pour un processus, travaillant directement sur la couche
TCP ou la couche UDP, le moyen de communiquer avec un processus est de con-
naître l’adresse IP du nœud sur lequel ce processus s’exécute, et le port utilisé par le
processus. Ces informations caractérisent totalement une adresse de communication
entre deux processus.
Le protocole TCP est un protocole fiable connecté, empêchant la congestion du
réseau. Sa fiabilité est assurée par un système d’accusés de réception. Une connexion
© Dunod – La photocopie non autorisée est un délit.
logique est constituée avant chaque échange de données. Ce protocole est asymétrique :
l’un des processus se définit sur un nœud comme prêt à accepter une connexion sur
un certain port : ce processus est appelé un serveur. Au lancement du processus,
celui-ci prévient la couche TCP du système d’exploitation qu’il est à l’écoute d’un
port (par exemple, c’est généralement le port 80 qui est utilisé par un serveur web).
L’autre processus prendra l’initiative de demander à ouvrir une connexion avec le
serveur : ce processus est alors un client. Sur TCP, la communication est donc de
type client/serveur.
Ainsi, par exemple lorsqu’un processus de type navigateur internet est exécuté sur
un nœud du réseau, et que l’utilisateur souhaite se connecter à un serveur web, le
processus charge la couche TCP d’effectuer une connexion au port 80 du nœud
173
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
Correspondance
avec le modèle OSI
communication fiable
5, 6, 7
UDP
TCP (Transfert Control Protocol) 4
(User Datagram Protocol)
IP (Internet Protocol)
3
ARP (Address Resolution Protocol)
Physique 1
174
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
175
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
sans avoir encore reçu d’accusés de réception. Par exemple, avec une fenêtre équiva-
lente à 4 segments, la couche TCP transmet sur la connexion 4 segments, dont elle
attend les accusés de réception. Dès qu’un accusé de réception arrive, elle n’attend
plus que 3 accusés de réception, elle peut alors émettre un segment supplémentaire,
ce qui la met en attente de 4 accusés de réception.
Pour une application, le protocole TCP/IP est donc extrêmement intéressant, car
il repose sur une transmission fiable, bien que non déterministe en temps. Sa pro-
grammation est très simple, car tous les langages de programmation proposent des
bibliothèques de fonctions pour programmer TCP/IP :
– l’application serveur prévient la couche TCP qu’elle désire écouter un certain port,
puis se met en attente sur ce port, l’application se retrouve alors dans l’état bloqué,
et repassera dans l’état prêt lorsqu’une connexion aura été établie à l’initiative
d’un client ;
– l’application client doit demander à ouvrir une connexion TCP en fournissant
l’adresse IP du serveur (abus de langage signifiant l’adresse IP du nœud sur lequel
l’application serveur est susceptible de s’exécuter), ainsi que le port utilisé par
celui-ci ;
– une connexion TCP est alors ouverte. On peut y écrire des données, comme on
pourrait les écrire à l’écran ou dans un fichier : les données écrites sont transférées
à la couche TCP qui se charge de les faire parvenir à destination. Notons que l’écri-
ture est doublement suspensive, d’une part, localement, comme toute entrée/
sortie, mais aussi à cause du fait que l’écriture de données est considérée comme
effectuée lorsque les accusés de réception ont été reçus. On peut lire des données
sur une connexion comme on pourrait lire des données en provenance d’un clavier :
lire des données consiste soit à piocher des données dans une zone tampon des
données reçues sur la connexion si des données sont présentes, soit à les attendre
si elles ne sont pas arrivées, dans ce cas, la lecture est bloquante ;
– un protocole d’application doit bien sûr avoir été préalablement défini, permet-
tant d’établir comment les applications pourront se comprendre (voir un exemple
au chapitre 7).
On peut remarquer qu’un seul processus peut utiliser un port, ce qui fait que pour
éviter d’empiéter sur les ports utilisés par certaines applications serveur standard
(serveur HTTP web écoutant par défaut le port 80, serveur de fichiers FTP écoutant
par défaut le port 21, serveur d’envoi de courrier électronique SMTP écoutant le
port 25, etc.), les applications utilisateurs choisiront des numéros de ports élevés.
Le protocole UDP, quant à lui, est nettement plus simple, puisque c’est une version
non fiable et non connectée d’un protocole de couche transport. Cependant, il est
beaucoup moins utile aux applications de contrôle-commande, car il est gênant de
ne pas savoir si les données transmises ont bien été reçues.
m Réseaux de terrain
176
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
– les volumes de données transférés sont moins importants (donnés sur l’état du
procédé, informations provenant de capteurs ou commandes vers des actionneurs,
commande de supervision…) ;
– les contraintes de temps dues aux aspects temps réel du contrôle nécessitent des
modes d’accès déterministes au médium ;
– certains messages étant plus importants que d’autres, des mécanismes de priorité
doivent être introduits ;
– face à un environnement de terrain, le matériel utilisé doit être robuste ;
– étant donnée la criticité des données transmises, le contrôle d’erreur doit être
important ;
– les nœuds connectés par un réseau de terrain sont souvent des calculateurs ayant
moins de puissance de calcul qu’un ordinateur, il faut donc des protocoles rela-
tivement simples ;
– généralement, un réseau de terrain est dédié à une et une seule application (une
application de contrôle-commande et ou de supervision/suivi de production…).
Il est alors inutile de mettre en œuvre les couches hautes du modèle OSI (éta-
blissement d’une connexion TCP entre deux processus par exemple), connexion
logicielle TCP… Cependant, il est intéressant d’utiliser des connexions de bas
niveau (par exemple LLC) avec un contrôle d’erreurs de bas niveau, et pourquoi
pas un mécanisme d’accusés de réception de niveau LLC ;
– en général, les nœuds devant communiquer sont à vue directe sur le réseau, la
plupart des protocoles de réseaux locaux s’arrêtent donc souvent à la couche 2.
Cependant il existe pour la plupart d’entre eux des nœuds routeurs capables de
faire le lien entre différents réseaux de terrain et des réseaux généralistes utilisant
TCP/IP.
M Le protocole CAN
Le protocole CAN (Controller Area Network), est un réseau de terrain très utilisé
en milieu industriel notamment dans le monde automobile (figure 4.37). De nom-
breux microcontrôleurs du marché intègrent une interface de communication CAN.
Ce protocole est à 2 couches (physique, MAC/LLC) et se base sur de la diffusion
(qu’elle soit sur bus, ondes radio ou lumineuses) de bits en série (comme le protocole
Ethernet), mais de façon synchrone. La seule contrainte sur le médium est qu’il
© Dunod – La photocopie non autorisée est un délit.
permette un « et » logique matériel sur les bits transmis : si un nœud émet 0, et qu’un
autre émet simultanément 1, alors le médium doit transporter 0. Si tous les nœuds
émettent 1, le médium transporte 1.
L’émission synchrone est fondamentalement différente de l’émission asynchrone
proposée par la plupart des réseaux généralistes : un signal de synchronisation passe
périodiquement sur le réseau, un top qui indique aux nœuds qu’ils peuvent, s’ils le
désirent, émettre des données. Le synchronisme est très pratique, puisqu’il existe
alors un temps commun à tous les nœuds. Cependant, il a un coût en bande passante
important : en effet, chaque bit a une durée égale à deux fois la durée de propagation
d’un signal de bout en bout sur le médium : cela correspond à la tranche canal
présentée pour CSMA/CD.
177
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
Il n’y a pas d’adresse de nœud pour CAN, et la trame CAN ne transporte pas d’infor-
mations sur la source ou la destination d’un message. Par contre, un message est
caractérisé par un identifiant (ID) : un ID est un nombre codé sur 11 bits (CAN 2.0A)
ou 29 bits (CAN 2.0B) permettant de transporter en même temps l’identifiant et
la priorité du message. Plus le nombre est faible, plus la priorité est élevée. Ainsi, le
concepteur d’une application utilisant CAN attribue une priorité aux messages, qui
vont typiquement encapsuler des données capteur ou actionneur, et un nœud obser-
vant un message passer sur le réseau ne le lit que si l’ID l’intéresse. C’est une philo-
sophie totalement différente de la philosophie client/serveur : en effet, étant donné
qu’une application utilisant un réseau de terrain est déterminée à l’avance, le concep-
teur sait quels types de messages chaque nœud doit recevoir.
En théorie, le principe est le suivant : les nœuds synchronisent leur horloge sur les
messages émis. Lorsque le médium est disponible, une station commence par émettre
un bit Start Of Frame à 0 suivi de l’ID (priorité) du message en commençant par les
bits de poids fort. À chaque bit émis, elle écoute le réseau (elle doit donc attendre
un temps équivalent à la tranche canal), afin de vérifier que ce qu’elle lit est ce qu’elle
a émis. À chaque bit de l’ID ainsi émis, la station écoute le médium afin de déter-
miner si elle peut continuer à émettre : si la station a émis un 0 (bit dominant), elle
est sûre de pouvoir continuer à émettre, cependant, elle ne peut pas savoir s’il existe un
autre nœud ayant commencé en même temps qu’elle à émettre (à une durée de pro-
pagation près sur le réseau) qui émet elle aussi un 0. Par contre, si le nœud émet
un 1 (bit récessif ), et lit un zéro, cela signifie qu’un autre nœud ayant commencé à
transmettre au même moment a un numéro d’ID plus petit, donc plus prioritaire
(le médium assure un « et » physique entre les bits transmis). Dans ce cas, le nœud
stoppe sa transmission et tentera de ré-émettre son message à la fin de la transmission.
Site monoprocesseur
Bus de terrain VAN Affichage OS : OSEK/VDX
Confort
conducteur
Bus de terrain
CAN
Contrôle Contrôle Interface
moteur freinage réseaux Climatisation
Dans le cas où un nœud a passé entièrement son ID, il peut alors continuer à émettre
sa trame (figure 4.38). Pour augmenter la robustesse du protocole, celui-ci intègre
au niveau 2 un mécanisme d’accusés de réception, et les nœuds qui prennent en
compte un message envoient un accusé de réception : cela consiste à positionner le
1er bit du champ ACK à 0. Étant donné que la transmission est synchrone, le nœud
178
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
SOF Identifier (ID) RTR IDE r0 DLC Données CRC ACK End of Frame
1 bit 11 bits 1 bit 1 bit 1 bit 4 bits 0 à 8 octets 15 bits 2 bits 7 bits
Trame CAN 2.0A
SOF Identifier (ID) SRR IDE Identifier long RTR r1 r0 DLC Données CRC ACK End of Frame
1 bit 11 bits 1 bit 1 bit 18 bits 1 bit 1 bit 1 bit 4 bits 0 à 8 octets 15 bits 2 bits 7 bits
Trame CAN 2.0B
émettant la trame se rend compte si sa trame a été reçue par au moins un nœud,
sans identifier lequel.
Ce protocole entre dans la catégorie des CSMA/CA, car il évite les collisions. Ce
protocole est très intéressant pour les applications temps réel, puisque les messages
les plus prioritaires (donc généralement les plus critiques) sont certains de pouvoir
passer prioritairement par rapport aux autres messages sur le réseau : au pire lorsqu’un
nœud veut émettre un message, il doit attendre la fin du message courant, et les
éventuels messages plus prioritaires.
De plus, le protocole CAN est très résistant aux fautes : différents niveaux de contrôle
d’erreur ont été introduits :
– contrôle CRC (Cyclic Redundancy Check) à la fin de chaque trame qui permet
de vérifier les données ;
– contrôle de la forme de la trame au niveau physique ;
– accusé de réception de chaque trame reçue.
L’avantage principal de CAN dans les applications temps réel est qu’il est possible
d’étudier a priori l’ordonnancement des messages sur le réseau : en effet, le réseau
peut être vu comme un processeur géré de façon non préemptive (un message ne peut
pas être interrompu) mais utilisant un ordonnancement basé sur la priorité des mes-
© Dunod – La photocopie non autorisée est un délit.
sages, puisqu’à la fin d’un traitement, c’est le message le plus prioritaire qui est émis.
La limitation principale de CAN est liée à son mode synchrone d’émission limitant
fortement les débits ou longueurs de médiums de communication. En effet, sur un
bus, un signal se propage à environ 200 000 km/s. Ce qui signifie que si le médium
mesure n mètres, il faudra attendre 2n m/2 × 109 m/s = n/109 secondes, soit n × 10–9
secondes. Ce qui donne un débit théorique maximal de 109/n bits/s sur un bus de
n mètres. Pour un bus de 100 m, le débit maximal pourrait être de 1 Mbits/s.
Cependant, en pratique, ce débit est atteint pour des bus d’une longueur d’environ
40 m.
179
4 • Architectures systèmes 4.3 Réseaux et bus de terrain
M Le protocole FIP
WorldFIP est une autre illustration de réseau de terrain très utilisé dans l’industrie.
Comme CAN, il permet de faire communiquer différents nœuds, certains nœuds
étant spécialisés dans des acquisitions de capteurs ou dans des commandes d’action-
neurs. Le protocole a une philosophie très particulière : son but, comme celui de
CAN, est de faire communiquer des nœuds participant à la même application.
Cependant il se base sur 3 types de messages : variables transmises périodiquement
(rafraîchissement périodique), variables transmises de façon apériodique, et messages
transmis de façon apériodique. Le protocole se base sur un arbitre gérant l’accès au
médium (la redondance des arbitres permet d’augmenter la robustesse) en se basant
sur un cycle (20 ms par exemple). L’arbitre sait à quel moment les variables pério-
diques doivent être émises sur le réseau, il envoie donc une demande pour chaque
variable périodique à intervalle régulier. Un seul nœud étant producteur pour une
variable, ce nœud doit alors envoyer la valeur de la variable, qui pourra être lue par
les nœuds consommateurs de cette variable. Le reste du temps est accordé au trafic
apériodique (valeurs de variables, messages, ou informations de configuration). Les
débits atteints par les réseaux de terrain WorldFIP peuvent atteindre 25 Mbits/s
sur de faibles distances (quelques dizaines de mètres), et de nombreux équipements
existent. Il est ainsi possible de relier par un routeur World/FIP-Ethernet un réseau
de terrain à un réseau généraliste (qui bien sûr n’est pas déterministe).
Le protocole FIP est composé des couches 1, 2 et 7. La couche application permet
notamment aux applications de s’assurer de la cohérence temporelle et spatiale des
variables reçues via le réseau. La cohérence temporelle consiste à s’assurer qu’une
variable est fraîche, c’est-à-dire qu’elle a été lue dans le même cycle temporel. La
cohérence spatiale permet à un nœud de savoir si les autres nœuds consommant
les mêmes variables que lui ont des valeurs identiques pour ces variables.
180
5 • EXÉCUTIFS TEMPS RÉEL
Un exécutif temps réel peut être employé à la place d’un système d’exploitation
généraliste par un système de contrôle-commande lorsqu’il est soumis à des contraintes
de temps, ou bien lorsqu’il doit être embarqué sur un microcontrôleur.
5.1 Introduction
5.1.1 Limitations des systèmes d’exploitation généralistes
Lorsqu’une application de contrôle-commande est soumise à de fortes contraintes
de temps, l’utilisation d’un système d’exploitation généraliste est inadaptée, car leurs
objectifs sont les suivants :
– garantir l’indépendance des processus et les protéger les uns vis-à-vis des autres
notamment grâce à la MMU (Memory Management Unit ), ce qui implique une
mise en œuvre lourde des communications entre les processus ;
– augmenter la vitesse moyenne de traitement à l’aide d’optimisations locales :
utilisation d’un cache disque permettant un accès asynchrone, mais non prédictible,
aux mémoires de masse ; utilisation des optimisations des processeurs tels les
pipelines et la mémoire cache nuisant à la prédictibilité du temps d’exécution
(les optimisations processeur peuvent être utilisées par les noyaux temps réel aussi) ;
– limiter le surcoût processeur dû au système d’exploitation, ce qui conduit souvent
à une gestion grossière du temps : l’unité de temps typique est la milliseconde
ou la dizaine de millisecondes ;
– favoriser l’équité dans l’ordonnancement au détriment du temps de réponse,
© Dunod – La photocopie non autorisée est un délit.
181
5 • Exécutifs temps réel 5.1 Introduction
Processus utilisateur
Modules
Noyau
Noyau
Matériel Matériel
182
5 • Exécutifs temps réel 5.1 Introduction
183
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
matériel, et d’un ensemble d’outils et librairies spécifiques à une cible. Cet ensemble
d’outils est regroupé sous la forme d’un ensemble de pilotes de périphériques, de
fonctions spécifiques (gestion de la mémoire, accès aux matériels spécifiques, etc.)
nommé BSP (Board Support Package).
En général, les exécutifs temps réel sont moins ouverts, moins flexibles, et optimisent
moins la vitesse moyenne des traitements que les systèmes d’exploitation généralistes,
mais ils favorisent la prédictibilité temporelle.
184
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
Terminaison
Attente d’une durée
ou d’une date
Exécutée
Endormie
Suppression
Préemption Réveil
Attente Élection
bloquante Suppression
Prête
Événement
attendu arrivé
Suppression
Bloquée Initialisation
Existante
Créée
Existante
Création mais non initialisée
Inexistant
m Éléments de base
M Sémaphores
Nous avons présenté au chapitre 4 l’outil sémaphore : un sémaphore est une variable
soit binaire (deux états sont possibles : libre et pris), soit n-aire. Dans le cas des séma-
© Dunod – La photocopie non autorisée est un délit.
185
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
(a) (b)
(a) (b)
Notons enfin qu’il existe une variante spécifique du sémaphore dans la norme POSIX
(voir § 5.3.1, p. 219) : le rwlock (sémaphore d’accès de type lecteur/écrivain), qui
sera abordé dans le cadre de la description de la norme.
M Moniteurs
Nous avons vu au chapitre 4 que le moniteur était un outil strictement plus puissant
que le sémaphore. Il existe deux types de moniteur :
– Le moniteur classique, appelé moniteur de Hoare, dans lequel la non réentrance
des primitives est couplée avec un mécanisme d’attente (wait) et de réveil (signal)
permettant de mettre en attente ou réveiller une tâche accédant au moniteur sous
certaines conditions.
– Le moniteur à la Ada (objet protégé) permet de mettre une garde (barrière
logique) à l’entrée de chaque primitive du moniteur, ce qui permet une gestion
186
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
plus fine et plus transparente des conditions d’accès que dans le cas du moniteur
de Hoare. De plus, cette gestion est plus efficace dans le cas où il y a plusieurs
conditions de réveil : seule la tâche concernée est réveillée par le changement d’état
du moniteur, contrairement au cas du moniteur de Hoare dans lequel toutes les
tâches en attente sur wait sont réveillées sur un signal et doivent alors évaluer
leur condition afin de se remettre en attente, ou de poursuivre leur exécution.
De plus, le moniteur à la Ada possède une distinction native accès en lecture/
accès en écriture, ce qui permet à plusieurs tâches d’accéder au moniteur en lecture
en même temps.
Afin d’illustrer la différence existant entre les deux types de moniteur, considérons
un problème simple : l’implémentation d’un double sémaphore (ensemble de deux
sémaphores, pouvant être requis simultanément, par exemple « réseau » et « port
série »). Comme cela a été dit au chapitre 4, ce type d’exemple ne doit être vu que
comme un exemple purement académique.
L’implémentation illustrative proposée possède 6 primitives :
– prendre_réseau prend le sémaphore « réseau » ;
– vendre_réseau vend le sémaphore « réseau » ;
– prendre_rs232 prend le sémaphore « série » ;
– vendre_rs232 vend le sémaphore « série » ;
– prendre_tout prend les deux sémaphores ;
– vendre_tout vend les deux sémaphores.
Deux variables booléennes internes (réseau_libre et rs232_libre) donnent l’état des
sémaphores.
L’implémentation d’un tel moniteur à la Hoare est donnée ci-dessous :
Moniteur Communication :
réseau_libre, rs232_libre : booléen := vrai
Procédure prendre_réseau
Début
tant que réseau_libre = faux faire
–- on attend que le réseau soit libre
wait
fait
réseau_libre := faux
© Dunod – La photocopie non autorisée est un délit.
Fin
Procédure vendre_réseau
Début
réseau_libre := vrai
signal
Fin
Procédure prendre_rs232
Début
tant que rs232_libre = faux faire
–- on attend que le port série soit libre
wait
fait
rs232_libre := faux
Fin
187
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
Procédure vendre_rs232
Début
rs232_libre := vrai
signal
Fin
Procédure prendre_tout
Début
tant que réseau_libre = faux faire
–- on attend que le réseau soit libre
wait
fait
tant que rs232_libre = faux faire
–- on attend que le port série soit libre
wait
fait
réseau_libre := faux
rs232_libre := faux
Fin
Procédure vendre_tout
Début
réseau_libre := vrai
rs232_libre := vrai
signal
Fin
Nous pouvons remarquer que lorsque par exemple une tâche libère le réseau, les
éventuelles tâches en attente sur le port série sont réveillées pour rien. Le moniteur
à la Ada évite cet écueil en introduisant des gardes (conditions d’entrée) à l’entrée
des primitives :
Moniteur Communication :
réseau_libre, rs232_libre : booléen := vrai
Procédure prendre_réseau quand réseau_libre
-- Lorsqu’une tâche entre ici, le réseau est forcément libre
Début
réseau_libre := faux
Fin
Procédure vendre_réseau
Début
réseau_libre := vrai
Fin
Procédure prendre_rs232 quand rs232_libre
-- Lorsqu’une tâche entre ici, le port série est forcément libre
Début
rs232_libre := faux
Fin
Procédure vendre_rs232
Début
rs232_libre := vrai
Fin
Procédure prendre_tout quand réseau_libre et rs232_libre
-- Lorsqu’une tâche entre ici, toutes les ressources sont libres
Début
réseau_libre := faux
rs232_libre := faux
Fin
188
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
Procédure vendre_tout
Début
réseau_libre := vrai
rs232_libre := vrai
Fin
Verrou
Variables File(s) d’attente Verrou Verrou ∀ primitive: file(s) Priorité Variables
internes de tâches sur wait de lecture d’écriture d’attente de tâches plafond internes
(a) (b)
Figure 5.5 – Caractérisation (a) d’un moniteur de Hoare, (b) d’un moniteur à la Ada.
M Variables conditionnelles
Variable conditionnelle
Verrou File d’attente
du moniteur de tâches
189
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
Les variables conditionnelles sont des outils de base conjointement utilisés avec des
sémaphores d’exclusion mutuelle pour la construction de moniteurs de Hoare, comme
le montre ci-dessous l’implémentation de l’exemple présenté au paragraphe 5.2.2,
p. 186.
190
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
fait
réseau_libre := faux
rs232_libre := faux
vendre(verrou)
Fin
Procédure vendre_tout
Début
prendre(verrou)
réseau_libre := vrai
rs232_libre := vrai
signal(condition_réseau)
signal(condition_rs232)
vendre(verrou)
Fin
rence du signal synchrone associé a lieu. À chaque signal est associée une action par
défaut. Typiquement, l’action par défaut consiste à terminer la tâche, ou bien le pro-
cessus père, ou encore à ne rien faire. Une tâche peut définir une action spécifique à
effectuer lorsqu’elle reçoit un signal synchrone. De même, elle peut définir l’ensemble
des signaux qu’elle veut bloquer (il y a certains signaux qui ne peuvent pas être
bloqués).
Signaux asynchrones
Les signaux asynchrones sont des signaux provenant d’une source externe à l’adresse
d’une tâche ou un processus (signal privé), ou bien à plusieurs tâches et/ou plusieurs
processus (signal public).
191
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
Occurrence Occurrence
non prise prise
en compte en compte
émettrice du signal
signalé
État
non
signalé
signal signal
exécutée
Tâche
prête
wait wait
exécutée
Tâche sensible
au signal
prête
bloquée
en attente
du signal
Prise
en compte
du signal
192
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
– signal mémorisé : si elle ne peut être prise en compte, l’occurrence est mémorisée.
Si une autre occurrence du même événement a lieu d’ici à sa prise en compte, elle
est ignorée (figure 5.8). Ce type de mémorisation suppose qu’une tâche (typique-
ment tâche recevant le signal) réinitialise l’occurrence ;
signalé
État
non
signalé
signal signal signal
exécutée
Tâche
prête
wait wait wait
exécutée
Tâche sensible
au signal
prête
bloquée
en attente
du signal
Prise
en compte
du signal
– tube ;
– socket ;
– rendez-vous ;
– tableau noir ;
– …
Chacun de ces outils a ses spécificités propres qui sont présentées dans les paragraphes
suivants.
193
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
émettrice du signal
État signalé 1 2 1
non
signalé
signal signal signal
exécutée
Tâche
prête
wait wait wait
exécutée
Tâche sensible
au signal
prête
bloquée
en attente
du signal
La boîte aux lettres permet une communication asynchrone puisque l’analogie exis-
tant entre communication asynchrone et communication par boîte postale permet
d’appréhender très simplement le concept.
Une boîte aux lettres (figure 5.10) est constituée d’une zone d’échange tampon
(buffer) dans laquelle une tâche dite émettrice peut déposer des données. La taille
de la zone d’échange est donnée par le nombre de messages maximum multiplié
par la taille de chaque message.
Message Message
Émettrice Réceptrice
2 1
File de messages
de la boîte aux lettres²
Les données de la boîte aux lettres sont gérées en FIFO (i.e. premier déposé/premier
retiré) ou bien avec une file FIFO par niveau de priorité. Une tâche dite réceptrice,
retire les données dans l’ordre d’arrivée ou de priorité. En fonction du langage support
et de l’implémentation, la priorité peut être :
194
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
– orientée message (figure 5.11) : le message est muni d’une priorité influençant
l’ordre dans lequel il va être lu ;
Message Message
2 1
File de messages normaux
Émettrice Réceptrice
– orientée tâche (figure 5.12) : la priorité du message est liée à la priorité de la tâche
émettrice, ce qui n’a de sens que si plusieurs tâches sont à même d’émettre des
messages dans la même boîte aux lettres.
Émettrice Message
Réceptrice
de priorité moyenne 1
Émettrice Message
Messages lus en dernier
de priorité faible 3
dans l’état bloquée jusqu’à ce que des données aient été déposées dans la boîte ;
– une boîte aux lettres a une taille bornée, et lorsqu’une tâche doit déposer des
données dans une boîte pleine, en fonction de l’implémentation choisie, soit le
message le plus ancien est écrasé par le nouveau, on parle alors de boîte aux lettres
à écrasement (ou bien de RT FIFO), soit la tâche passe dans l’état bloquée
jusqu’à ce que des données aient été retirées de la boîte, on parle alors de boîte
aux lettres sans écrasement. Dans le cas général, le terme boîte aux lettres désignera
une boîte sans écrasement, et on précisera « avec écrasement » dans le cas contraire.
Remarquons que le concept de boîte aux lettres (sans écrasement) est identique
au problème producteur/consommateur présenté au chapitre 4.
195
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
Une boîte aux lettres est généralement utilisée pour une communication entre une
émettrice et une réceptrice ou bien n émettrices et une réceptrice. Bien que cela soit
possible, il est assez rare que l’on utilise une boîte aux lettres pour faire communiquer
n émettrices et m réceptrices.
Les boîtes aux lettres peuvent être à attente bornée aussi bien pour l’émission que
pour la réception : en cas d’attente dépassant un délai spécifié, une émission ou récep-
tion de message est avortée, la primitive renvoyant alors une erreur.
Une boîte aux lettres (figure 5.13) peut donc être représentée par un tampon de
messages qui peut être soit une file, soit un ensemble de files classées par priorités,
une file d’attente de tâches désirant émettre (cas où la file est pleine et sans écrasement),
et une file d’attente de tâches en attente de message (cas où la file est vide). Comme
pour les messages, ces files d’attente peuvent être gérées en FIFO ou bien gérées sous
forme de files de priorités.
Lorsque le langage utilisé ne propose pas le type de boîte aux lettres désiré, il est
possible d’en implémenter simplement à partir de sémaphores, en utilisant la tech-
nique du producteur/consommateur présentée au chapitre 4, ou bien de manière
plus élégante en utilisant des moniteurs (voir § 5.2.2, p. 186). Nous présentons
quelques implémentations de boîtes aux lettres à base de moniteurs dans le
chapitre 6.
M Le tube
Un tube (ou pipe) permet comme la boîte aux lettres une communication unidirec-
tionnelle par passage de messages. Cependant, la philosophie du tube repose sur le
concept de flots d’octets (comme tout périphérique Unix), exactement comme dans
un fichier : une tâche transmettant des données à travers un tube les envoie comme
un flot d’octets dans un fichier (figure 5.14). Les données insérées les unes à la suite
des autres dans un tube, bien que pouvant tout à fait être stockées sur disque, sont
destinées à être lues en FIFO par une autre tâche. Le tube utilise un concept assez
proche du producteur/consommateur. Un producteur place n octets dans le tube à
chaque fois qu’il veut transmettre un message vers le consommateur, qui prélèvera
Émettrice Réceptrice
Message2 Message1
196
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
les messages n octets par n octets. Le tube est bloquant en lecture, mais non bloquant
en écriture : dans le cas où le tube est plein, l’écriture ne se bloque pas et renvoie une
erreur au producteur. En effet, théoriquement, un tube peut être stocké sur disque
et donc utiliser un tampon limité seulement par la taille du disque.
Un tube peut donc être vu comme une boîte aux lettres avec perte de messages dans
le cas où il est plein. Sa structure (figure 5.15) est limitée à une file d’octets pouvant
être stockée sur disque, et une file d’attente de réceptrice(s).
Tube
Tampon = file File d’attente
d’octets de réceptrices
Cet outil de communication étant à même d’utiliser des fichiers physiques, il n’offre
aucune garantie de délai de communication.
M Le socket
Le socket est un outil de communication bidirectionnelle (figure 5.16) par message
implémenté sur un protocole réseau (typiquement TCP ou UDP). Il permet à deux
processus, ou tâches, de communiquer après établissement d’une connexion de type
client/serveur (voir § 4.3.2, p. 173).
Flot d’octets
Serveur
Client
Flot d’octets
Les messages sont des flots d’octets, comme pour le tube. Chaque élément commu-
niquant est muni de deux tampons : l’un pour les messages à envoyer (par exemple,
© Dunod – La photocopie non autorisée est un délit.
le protocole TCP, utilisant la technique du fenêtrage TCP, peut être amené à attendre
un accusé de réception avant de continuer l’émission de segments TCP supplémen-
taires, la bande passante étant limitée, des données peuvent tout simplement être
en attente d’accès au médium de communication), et l’autre pour les messages reçus
mais non encore lus.
Le socket permet une communication entre 2 tâches ou processus (socket TCP ou
UDP classique), ou bien entre n tâches ou processus (cas de la multidiffusion implé-
mentée sur UDP).
Un socket se caractérise au niveau de chaque élément communiquant (figure 5.17) par
deux tampons bornés : les données arrivées mais non lues, et les données en attente
197
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
d’envoi, et une file d’attente par tampon pouvant contenir la tâche ou processus
communiquant sur le socket.
Socket
File d’attente Tampon de sortie Tampon d’entrée File d’attente
d’émission = file d’octets = file d’octets de réception
Figure 5.17 – Caractérisation d’un socket au niveau d’un processus ou d’une tâche.
M Le rendez-vous
Un rendez-vous n’a lieu que lorsque la tâche acceptante est sur une instruction accept
et que la tâche appelante demande l’obtention du rendez-vous : la tâche appelante
peut passer des données à la tâche acceptante, puis est bloquée jusqu’à la fin du rendez-
vous exécuté par la tâche acceptante. Des données peuvent alors être passées de la
tâche acceptante à la tâche appelante. Les chronogrammes d’exécution possibles
d’un rendez-vous sont donnés sur la figure 5.19.
L’acceptation de rendez-vous, de même que la demande de rendez-vous, sont des ins-
tructions bloquantes, un rendez-vous peut donc être caractérisé (figure 5.20) par
un pointeur vers la tâche acceptante, son état (en attente de rendez-vous ou non),
et une file d’attente gérée en FIFO ou bien un ensemble de files d’attente FIFO
gérées par niveau de priorité des tâches appelantes.
Le rendez-vous peut se ramener à l’utilisation de deux boîtes aux lettres (que nous
nommerons dans l’exemple qui suit bal_demande_rdz et bal_fin_rdz).
198
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
Demande Demande
de rendez-vous de rendez-vous
Tâche appelante … … … …
Temps
(a) (b)
Rendez-vous
199
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
On peut noter que dans cette implémentation, la boîte aux lettres de demande de
rendez-vous est nécessairement publique, car toute tâche peut demander un rendez-
vous, alors que la boîte utilisée pour signifier la fin du rendez-vous est spécifique à
chaque tâche demandant le rendez-vous.
Remarque : il est possible, notamment en Ada, d’utiliser un mécanisme d’héritage de
priorité lorsqu’une tâche prioritaire est en attente d’une autre tâche moins prioritaire.
M Le tableau noir
Le tableau noir (figure 5.21) est conceptuellement le plus simple des moyens de
communication asynchrone : il utilise une zone de mémoire commune pouvant
contenir un message. L’écriture d’un message écrase le message précédent, et la lecture
est non bloquante et non destructive (une valeur déjà lue peut être relue tant qu’elle
n’a pas été écrasée par une écriture).
Tableau noir
Figure 5.21 – Communication par tableau noir : la lecture est non destructive.
Tableau noir
Mécanisme(s)
Valeur actuelle
d’exclusion
mutuelle
200
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
Exclusion mutuelle
Files(s)
Ressource
d’attente
Comme dans le cas du tableau noir, pour lequel la valeur (stockée à un emplacement
mémoire) est une ressource critique, le mécanisme d’exclusion mutuelle utilisé est
© Dunod – La photocopie non autorisée est un délit.
201
202
Communication par message
Locale
Locale ou distante
Une synchronisation de tâche peut être binaire ou à compte : lorsqu’une tâche est
en attente sur une synchronisation binaire, si un déclenchement n’a pas été pris en
compte et qu’un second déclenchement a lieu, il écrase le dernier déclenchement et
la tâche en attente ne sera déclenchée qu’une fois. Dans le cas d’une synchronisation
à compte, le nombre de déclenchements non pris en compte est mémorisé, et la
tâche en attente de synchronisation sera déclenchée autant de fois que la synchro-
nisation est déclenchée. Ces deux sémantiques sont similaires aux sémantiques
associées aux signaux mémorisés et signaux à compte (voir § 5.2.2, p. 191).
Une synchronisation à compte (figure 5.25a) peut être caractérisée par une file
d’attente (qui contiendra au plus une tâche en attente) et à un entier contenant le
nombre de déclenchement non encore pris en compte.
Une synchronisation binaire (figure 5.25b) peut être caractérisée par un booléen
traduisant l’état de la synchronisation (déclenchée ou non), et une file d’attente
contenant au plus une tâche.
(a) (b)
Figure 5.25 – Caractérisation d’une synchronisation n/1 (a) à compte, (b) binaire.
vendre(synchro)
-- déclenchement de la tâche en attente
…
Tâche en attente
…
prendre(synchro)
-- attente de déclenchement, le sémaphore n’est
-- disponible qu’à la suite d’un déclenchement
…
203
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
M Synchronisation à diffusion
La synchronisation à diffusion est une extension à n producteurs, et m tâches en
attente de déclenchement de la synchronisation n /1. Si le nombre de tâches en attente
est connu, cela revient à utiliser m synchronisations n /1. Cependant, dans ce cas,
un répartiteur de synchronisation doit être utilisé. L’ordre de prise en compte des
déclenchements doit être cohérent avec les priorités des tâches déclenchées (déclen-
chement de la tâche la plus prioritaire d’abord), ce qui implique que soit le réparti-
teur connaît les priorités des tâches, soit il est plus prioritaire que toutes les tâches
en attente de synchronisation.
Le principe est assez semblable à celui des signaux asynchrones publics vus au para-
graphe 5.2.2, p. 191. Comme pour les synchronisations n /1, la synchronisation à
diffusion peut être binaire (correspond au signal mémorisé, voir figure 5.8) ou à
compte (correspond au signal à compte, voir figure 5.9).
On peut implémenter une synchronisation à diffusion à l’aide de signaux ou bien
de sémaphores (sur le principe des synchronisations n /1) couplés à un répartiteur.
Ce type de synchronisation est très rarement utilisé dans les systèmes temps réel et
les systèmes de contrôle-commande. En cas de besoin, on préférera le déterminisme
de plusieurs synchronisations n /1 avec répartiteur (voir exemple d’implémentation
ci-après).
M Rendez-vous synchronisé
Dans les rares cas où l’on souhaite que n tâches soient en même temps à un empla-
cement spécifique de leur exécution, on y définit l’attente d’un rendez-vous synchro-
nisé à n tâches. Un rendez-vous synchronisé est caractérisé par un nombre de tâches
attendues au rendez-vous, et une file d’attente de tâches arrivées au rendez-vous
(figure 5.26).
204
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
Rendez-vous synchronisé
Une tâche exécutant une attente de rendez-vous est insérée dans la file d’attente et
passe dans l’état bloquée sauf si elle est la dernière tâche attendue au rendez-vous.
Lorsque le nombre de tâches attendues est arrivé au rendez-vous, toutes les tâches
de la file d’attente passent dans l’état prête.
Lorsque le rendez-vous synchronisé n’est pas disponible, il peut être implémenté à
l’aide d’un signal public mémorisé (ou bien de sémaphores et d’un répartiteur) et
d’un sémaphore protégeant l’entier comptant le nombre de tâches arrivées au rendez-
vous.
On pourrait penser qu’un signal fugace conviendrait, car on peut penser que lorsque
les tâches du rendez-vous sont toutes arrivées, elles sont forcément toutes en attente
du signal, qui serait donc pris en compte par toutes les tâches du rendez-vous.
Le problème est qu’il est possible, la tâche devant vendre le sémaphore avant de se
mettre en attente du signal, que l’avant-dernière tâche arrivant au rendez-vous soit
préemptée par la dernière tâche arrivant au rendez-vous entre la libération du séma-
phore et l’attente du signal. Dans ce cas, le signal ne réveillerait pas l’avant-dernière
tâche.
205
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
m Interruptions logicielles
Les interruptions logicielles sont déclenchées par une tâche (ou l’exécutif lui-même)
lors de son fonctionnement : par exemple débordement lors d’un calcul, erreur de
segmentation, défaut de page sur pagination, division par zéro, etc. Dans ce cas,
c’est à la tâche de traiter elle-même l’interruption. Enfin, certaines interruptions
logicielles auront pour effet de réinitialiser le système (erreur de parité lors d’un
transfert entre la mémoire centrale et le processeur, erreur du bus de données, etc.).
Une interruption logicielle peut se traduire sous la forme de signal synchrone (voir
§ 5.2.2, p. 191).
m Interruptions matérielles
Les interruptions matérielles sont générées par une source externe au processeur,
comme par exemple un contrôleur de dispositif d’entrées/sorties (clavier, périphérique
de stockage…) ou bien une horloge qui peut être programmée afin de déclencher
une interruption au bout d’un certain temps ou à une certaine date. Dans ce cas,
il faut qu’un traitement associé au noyau ait lieu, son rôle principal sera de relayer
l’interruption vers la tâche de traitement adéquate.
Physiquement, une interruption matérielle est apportée par une ligne d’interruption.
Le nombre d’interruptions possibles est limité par le matériel, ainsi, sur une archi-
tecture de type x86, il y a 16 interruptions matérielles. Les interruptions matérielles
sont souvent appelées IRQ (Interrupt ReQuest ).
Une table nommée vecteur d’interruption, contient l’adresse de début du traitement
logiciel à exécuter pour chaque interruption. Ce traitement logiciel est exécuté
dans le contexte du noyau (en utilisant sa pile, et en interrompant le programme en
cours d’exécution) ou bien avec une pile spécifique aux traitements d’interruptions.
La table contient aussi un bit par interruption signifiant si une interruption a eu lieu.
Le traitement s’appelle une routine de traitement d’interruption (ISR) : ce traite-
ment commence éventuellement par prévenir l’élément matériel qui a signalé l’inter-
ruption que celle-ci a été prise en compte. Certaines architectures matérielles prévoient
en effet qu’un élément matériel doive attendre d’obtenir un acquiescement de leur
interruption avant d’en signaler d’autres : cela permet d’être certain qu’aucune inter-
ruption n’est perdue. Ce processus doit être très rapide, cela explique en partie qu’il
n’est en aucun cas possible d’effectuer une opération bloquante dans une routine de
traitement d’interruption.
Une routine de traitement d’interruption ne doit en aucun cas être réentrante (le
traitement d’une interruption ne doit pas être interrompu par le traitement de cette
même interruption). Lors du traitement d’une interruption, on masque donc géné-
ralement l’interruption traitée elle-même. Si elle survient à nouveau, le bit informant
de l’occurrence de l’interruption est mis à 1 dans le vecteur d’interruptions, mais
celle-ci devra attendre avant d’être traitée.
206
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
Interruption
ISR
(Routine de traitement DSR
d’interruption) (Tâche de traitement
… de l’interruption)
Vendre(synchro) Faire toujours
© Dunod – La photocopie non autorisée est un délit.
Prendre(synchro)
…
Fait
207
5 • Exécutifs temps réel 5.2 Concepts des exécutifs temps réel
208
5 • Exécutifs temps réel 5.3 Principales normes temps réel
Tâche une_tâche_périodique
Début
Faire toujours
-- des actions
Attendre une durée égale à une période
Fait
Fin
Soit t l’instant où la tâche est activée, il lui faut un temps non nul pour effectuer
les actions, d’autant que d’autres tâches peuvent utiliser le processeur et la retarder.
L’instruction d’attente d’un délai a donc lieu à une date t + e1, la tâche n’est donc
réveillée qu’au plus tôt à la date t + période + e1. Il lui faut ensuite un temps e2
avant d’arriver à l’instruction d’attente, ce qui décale son prochain réveil au plus tôt
à la date t + 2période + e1 + e2. Et ainsi de suite à chaque période. Ce phénomène
s’appelle la dérive des horloges. Par conséquent, une tâche périodique se doit de
reposer sur un réveil par date (on peut calculer les dates de réveil désirées en fonc-
tion d’un instant initial) et non par délai.
m Introduction
209
5 • Exécutifs temps réel 5.3 Principales normes temps réel
1003.1h Tolérance aux fautes (Fault Tolerance) Dernière version 2000 – Devenu 1003.25.
1003.1i Corrections aux extensions temps réel Dernière version 1995, intégrée dans
(Fixes to 1003.1b) 1003.1 depuis 2001.
210
5 • Exécutifs temps réel 5.3 Principales normes temps réel
211
5 • Exécutifs temps réel 5.3 Principales normes temps réel
– ordonnancement ;
– horloges haute précision ;
– mémoire non virtuelle.
212
5 • Exécutifs temps réel 5.3 Principales normes temps réel
De même, une interface XSI alternative existe. L’interface XSI augmente les possibi-
lités des sémaphores POSIX en permettant de manipuler des tableaux de sémaphores.
Cet outil est intéressant puisqu’il évite les risques d’interblocage : lorsqu’un processus
doit prendre plusieurs sémaphores, s’il utilise les primitives de prise de tableau de
sémaphores, le cycle menant à l’interblocage est impossible, car les sémaphores
sont pris dans un ordre déterminé (voir chapitre 4).
Notons qu’aucun mécanisme de gestion de ressource (protocole à priorité héritée
ou plafond) n’est disponible pour les sémaphores nommés.
Tableaux noirs
La mémoire des processus étant protégée, le mécanisme de tableau noir n’est possible
qu’en utilisant des zones mémoire spécifiquement partagées. Celles-ci portent le nom
de shared memory, et sont créées ou ouvertes par la fonction shm_open. Après
avoir ouvert/créé une zone de mémoire partagée, on peut la calquer sur une variable
du processus avec la fonction mmap : tout accès à la variable est alors un accès à la
zone de mémoire partagée, et non pas l’accès à une variable interne au processus.
Ce mécanisme, de même que le mécanisme de tableau noir se couple généralement
à un sémaphore afin de garantir l’exclusion mutuelle des accès.
Comme pour les autres IPC, les tableaux noirs peuvent être accédés à partir d’une
interface XSI.
un processus utilisant les signaux temps réel commence par masquer l’ensemble des
signaux (fonction sigemptyset), puis à définir une action pour chacun des signaux
qu’il souhaite traiter (fonction sigaction). Le traitement a lieu soit par l’appel d’une
fonction exécutée dans le contexte initial du processus (préemption du traitement en
cours), soit par la création d’une tâche qui exécutera une fonction dans son contexte
propre.
Il est possible d’attendre explicitement un ou plusieurs signaux (fonction sigwait).
Notons que certaines fonctions bas niveau masquent les signaux pendant leur exécu-
tion (comme la plupart des fonctions manipulant les IPC, les fonctions manipulant
les signaux elles-mêmes, etc.). Si plusieurs signaux arrivent et ne peuvent être traités,
213
5 • Exécutifs temps réel 5.3 Principales normes temps réel
ils sont, si possible, placés dans des files d’attente. Lorsque le processus est prêt à
traiter un signal, il commence par traiter les signaux de plus petit numéro.
Ordonnancement
POSIX offre deux niveaux de programmation concurrente : processus et tâches
(threads). L’ordonnancement peut donc se faire à deux niveaux : local (figure 5.28)
et global (figure 5.29), avec une possibilité de mixer les deux niveaux (figure 5.30).
Tâchej,n Tâchej,r
Ordonnanceur
Tâchej,p
Processus i
Tâche l,q
214
5 • Exécutifs temps réel 5.3 Principales normes temps réel
Le problème du modèle local est que lorsqu’une tâche fait une action bloquante,
par exemple une entrée/sortie, le processus entier est bloqué. De plus, les différentes
tâches d’un processus ne peuvent être ordonnancées simultanément si plusieurs
processeurs sont à disposition.
Tâchej,r
Ordonnanceur
Processus i
Tâche j,p
Dans un ordonnancement global (figure 5.29), chaque élément, qu’il soit tâche
ou processus, est ordonnancé au même niveau. L’inconvénient est qu’il est nécessaire
d’effectuer un appel système, plus lourd qu’un appel de fonction interne au pro-
cessus, afin de modifier un paramètre influençant l’ordonnancement. Cependant,
les tâches d’un même processus ne se bloquent pas lorsque l’une des tâches du proces-
sus fait une entrée/sortie bloquante, et elles peuvent être exécutées simultanément
sur plusieurs processeurs.
Dans un ordonnancement mixte, on peut choisir pour chaque tâche si elle doit
être ordonnancée localement (à l’intérieur de son processus père) ou globalement.
Ainsi, sur la figure 5.30, la tâche Tâchel,q est ordonnancée au niveau local, alors que
© Dunod – La photocopie non autorisée est un délit.
les autres tâches sont ordonnancées au niveau global. Avec un choix judicieux des
tâches locales et globales, les avantages des deux approches peuvent être cumulés.
POSIX impose au minimum 32 niveaux de priorité, et propose quatre politiques
d’ordonnancement :
– SCHED_FIFO : la tâche prête ou le processus prêt ayant la plus forte priorité
au niveau global se voit attribuer le processeur. En cas d’égalité, SCHED_FIFO
exécute les tâches ou processus dans leur ordre d’arrivée dans la file des tâches
prêtes.
215
5 • Exécutifs temps réel 5.3 Principales normes temps réel
Tâche j,r
Ordonnanceur
Processus i
Tâche l,q
Tâche j,p
Horloges
Deux améliorations sont apportées aux horloges temps réel par rapport aux horloges
classiques : la résolution d’horloge et la gestion du temps absolu (afin d’éviter la
dérive des horloges, notamment pour les tâches périodiques).
Toutes les opérations proposées peuvent utiliser une horloge, appelée CLOCK_
REALTIME. Normalement, cette horloge représente le temps écoulé depuis le
1er janvier 1970 à 00 h 00. Cette horloge a une résolution au pire de 20 ms, mais,
216
5 • Exécutifs temps réel 5.3 Principales normes temps réel
Gestion de la mémoire
Afin d’éviter les indéterminismes liés à l’utilisation de la mémoire virtuelle, la norme
POSIX.1b définit le concept de mémoire bloquée. Il est possible, à l’aide des fonc-
tions mlock et mlockall de forcer toute ou partie de la mémoire d’un processus à
résider en mémoire centrale. La mémoire bloquée ne peut pas faire l’objet de swap
(voir § 4.2.3, p. 157).
De même, un fichier peut être placé en mémoire centrale avec la fonction mmap,
ce qui permet des temps d’accès presque déterministes.
217
5 • Exécutifs temps réel 5.3 Principales normes temps réel
clock_getres Renvoie la résolution d’une horloge, soit l’écart séparant deux ticks. La
résolution est définie en fonction de l’architecture sous-jacente et ne
peut normalement pas être modifiée par programme. Elle donne la
précision maximale des dates ou durées utilisées par les timers.
timer_create Crée un timer dans un processus, tel qu’il envoie un signal asynchrone à
expiration(s).
timer_settime Déclenche un timer soit en mode one shot, soit en mode périodique,
avec des délais ou des dates absolues. Le signal choisi lors de la création
est envoyé au processus à expiration(s) du timer.
nanosleep Endort le processus (ou la tâche) appelant pendant une certaine durée
ou jusqu’à une date absolue (utilise l’horloge CLOCK_REALTIME).
clock_nanosleep Identique à nanosleep sauf que l’horloge utilisée peut être différente
de CLOCK_REALTIME.
Tâches POSIX
Une tâche est créée par la fonction pthread_create. Ses arguments principaux sont
des attributs de tâches, un pointeur de fonction (cette fonction est le code de la tâche)
et un pointeur pouvant contenir le ou les arguments à passer à la fonction.
Les attributs d’une tâche ne sont pas portables et ne sont pas définis par la norme
POSIX, ils peuvent définir notamment :
– le type d’ordonnancement à appliquer à la tâche (local ou global) ;
– la taille de la pile sur les architectures matérielles qui le nécessitent ;
– une priorité ou bien la définition d’un serveur sporadique associé à la tâche
(voir § 5.3.1, p. 214) ;
– l’état détaché ou attaché de la tâche. En effet, on peut exécuter une tâche de façon
synchrone (join), dans ce cas, le créateur de la tâche attend la terminaison de celle-ci
avant de poursuivre sont exécution. Ou bien les tâches peuvent être détachées
(detach state), ce qui les rend indépendantes de leur créateur. Typiquement, si les
tâches sont créées par la procédure principale d’un processus, et que l’on veut éviter
218
5 • Exécutifs temps réel 5.3 Principales normes temps réel
écriture.
Variables conditionnelles
Comme nous l’avons vu au paragraphe 5.2.2, p. 189, les variables conditionnelles
représentent un outil puissant, permettant de créer des moniteurs. POSIX.1c pro-
pose cet outil, nommé pthread_cond. Après sa création (pthread_cond_init), une
variable conditionnelle peut servir, conjointement avec un mutex, à synchroniser des
tâches.
Il faut cependant être attentif au fait que si plusieurs tâches sont susceptibles d’être
bloquées simultanément sur la même variable conditionnelle, un appel à l’équivalent
de signal (pthread_cond_signal) ne réveille qu’une seule tâche bloquée sur la variable
219
5 • Exécutifs temps réel 5.3 Principales normes temps réel
conditionnelle. Dans le cas où plusieurs sont susceptibles d’être bloquées sur une
variable conditionnelle, on peut être amené à utiliser la fonction (pthread_cond_broad-
cast) qui réveille toutes les tâches bloquées sur une variable conditionnelle.
Il est à noter qu’il est possible d’effectuer une attente bornée sur une variable condi-
tionnelle. L’attente bornée sur une variable conditionnelle n’étant jamais signalée
est une technique utilisée pour rendre une tâche périodique, comme nous le verrons
au chapitre 6.
Rendez-vous
L’amendement 1003.1j introduit les rendez-vous synchronisés (voir § 5.2.2, p. 204)
sous le nom de pthread_barrier. Après création d’un rendez-vous synchronisé
(pthread_barrier_init) pour un certain nombre de tâches, des tâches peuvent
s’attendre mutuellement sur une instruction pthread_barrier_wait. Lorsque le
nombre de tâches requises est arrivé au rendez-vous, celles-ci sont réveillées et peu-
vent poursuivre leur exécution.
Notons qu’une attente de rendez-vous ne peut pas être bornée dans le temps.
SpinLocks
L’amendement 1003.1j introduit les spinlocks, aussi appelés Test and Set Lock. Cet
outil de synchronisation, antérieur au sémaphore, fonctionne de la façon suivante :
un verrou est défini par un entier valant 0 lorsqu’il est ouvert, et 1 lorsqu’il est fermé.
Verrouiller un spinlock (instruction pthread_spin_init) consiste, de façon atomique,
à lire la valeur actuelle du verrou et à mettre celui-ci à 1. Après avoir verrouillé un
spinlock, on peut donc être sûr que le verrou est mis. On peut donc alors observer
la valeur lue avant la fermeture du verrou : si elle valait 0, le verrou était ouvert, et
la tâche est responsable de sa fermeture. Dans le cas contraire, si le verrou était déjà
fermé (valeur 1), alors une autre tâche avait déjà mis le verrou.
L’utilisation de spinlocks est très risquée, car l’utilisation de la fonction pthread_spin_
lock effectue une attente active sur le verrou (boucle testant le verrou jusqu’à ce qu’il
soit disponible). Ce mécanisme est cependant extrêmement rapide et peut être utilisé
notamment dans le cas multiprocesseur.
220
5 • Exécutifs temps réel 5.3 Principales normes temps réel
PSE 51 Profil de système Définit un système multitâche à un seul processus, sans sys-
temps réel minimaliste tème de fichiers (les seuls fichiers utilisés sont les fichiers spé-
(Minimal Realtime ciaux, moyens d’accéder aux interfaces d’entrées/sorties).
System Profile) Nécessite un processeur (ou microcontrôleur) et de la mémoire.
Ne nécessite pas de support matériel pour la pagination
(MMU), ni de périphériques standard (console, clavier, péri-
phériques de stockage…).
PSE 52 Profil de système Extension du profil PSE 51 avec prise en compte d’un système
de contrôle temps réel de fichiers supportant un accès asynchrone. Ne nécessite pas
(Realtime Controller de MMU.
System Profile)
PSE 53 Profil de système Extension multiprocessus de PSE 51, avec un système de fichier
temps réel dédié simpliste (pas de hiérarchie comme les répertoires).
(Dedicated Realtime
System Profile)
PSE 54 Profil de système Multiprocessus, avec système de fichiers. Autorise les inter-
temps réel généraliste actions avec un utilisateur. Implémente POSIX.1, 1b, 1c et ou
(Multi-Purpose Realtime 5b, 2 et 2a. PSE 54 définit un système d’exploitation minimal,
System Profile) avec périphériques d’entrées/sorties, périphériques de stoc-
kage, pagination de la mémoire, support réseau… Supporte
la présence de tâches temps réel et non temps réel.
plus importante. Il est typiquement utilisé sur une architecture de type PC ou autre
micro-ordinateur.
221
5 • Exécutifs temps réel 5.3 Principales normes temps réel
M Les tâches
Afin de permettre un traitement optimisé des tâches simples par le noyau, OSEK/
VDX distingue deux types de tâches : les tâches basiques, et les tâches étendues.
222
5 • Exécutifs temps réel 5.3 Principales normes temps réel
Générateur
Source C ou binaire
des pilotes
Compilateur
Fichiers
de configuration
traduits en C
Éditeur de liens
croisé
pour microcontrôleur
cible
Exécutable
pour la cible
Figure 5.31 – Mode de développement typique d’un système s’exécutant sur OSEK/VDX.
Les tâches basiques ne peuvent pas se bloquer (attente d’événement, message, res-
source…) entre deux activations successives. Elles ne possèdent donc pas d’état
bloquée (figure 5.32). De plus, une tâche est créée de façon statique, il n’y a donc
pas d’état inexistante contrairement à la figure 5.2. L’initialisation, quant à elle, est
effectuée automatiquement au démarrage de l’exécutif. Notons qu’une tâche ne se
termine jamais (i.e. son contexte, la mémoire utilisée, etc. ne sont jamais libérés) et
que le mot terminer signifie « se mettre en attente d’activation » dans la terminologie
OSEK/VDX. Ici, le terme endormie correspond à suspendue.
Les tâches étendues peuvent se bloquer durant leur exécution (figure 5.33). Typique-
ment, une tâche se bloque en attendant une synchronisation ou une ressource.
© Dunod – La photocopie non autorisée est un délit.
Comme dans la norme POSIX, une tâche est définie par une fonction. Cependant,
contrairement à celle-ci, une tâche n’est pas créée dynamiquement, mais est définie
statiquement dans le fichier OIL, ainsi que tous les objets de synchronisation et de
communication.
Une tâche basique ne peut donc pas se bloquer pendant son exécution, ce modèle
peut donc typiquement implémenter une tâche périodique (endormie ente chaque
activation) ou encore une tâche en attente de message, de synchronisation, ou d’inter-
ruption à chacune de ses occurrences. Cependant, il faut noter que dans ce cas,
une telle tâche ne peut pas accéder à une ressource critique, puisque le mécanisme
d’exclusion est potentiellement bloquant.
223
5 • Exécutifs temps réel 5.3 Principales normes temps réel
Exécutée
terminer
activer
Prête
Exécutée
attendre terminer
élection
Bloquée Endormie
préemption
relâcher activer
Prête
Dans le cas où une tâche peut attendre un message, une interruption, synchronisation
ou encore accéder à une ressource pendant son exécution, il conviendra d’utiliser
une tâche étendue.
De façon générale, l’accent est mis sur la prédictibilité du système, par conséquent,
plusieurs restrictions sont appliquées. La terminaison d’une tâche ne peut provenir
que de son propre appel à TerminateTask.
L’exécutif propose un certain nombre de services pour gérer les activations de tâches :
– ActivateTask permet d’activer une tâche endormie ;
– ChainTask permet à une tâche de se terminer en activant une autre tâche.
224
5 • Exécutifs temps réel 5.3 Principales normes temps réel
Exclusion mutuelle
L’exclusion mutuelle est assurée par la définition de ressources (de façon statique dans
le langage OIL), qui correspondent à des sémaphores binaires d’exclusion mutuelle,
gérés avec le protocole à priorité plafond immédiat, afin d’éviter l’inversion de
priorité et de borner la durée de blocage d’une tâche en attente d’une ressource.
OSEK/VDX fournit deux primitives, GetResource et ReleaseResource pour prendre
et vendre une ressource. Tenter de prendre une ressource non disponible a pour
effet de placer une tâche dans l’état bloquée.
Notons ici une spécificité d’OSEK/VDX : il existe des ressources internes qui sont
prises implicitement par les tâches liées à cette ressource dès qu’elles s’exécutent. Si
plusieurs tâches sont liées à la même ressource interne, on parle alors d’un groupe
de tâches. Lorsqu’une tâche du groupe s’exécute, elle hérite automatiquement de
la priorité plafond de la ressource interne (d’après le protocole à priorité plafond
immédiat), c’est-à-dire de la plus haute priorité des tâches du groupe. Il en résulte
qu’une tâche du groupe ne peut être préemptée que par une tâche de priorité supé-
rieure à la priorité de la tâche la plus prioritaire du groupe. Une extension naturelle
de la notion de ressource interne est le mode non préemptible des tâches : si toutes
les tâches sont liées à une même ressource interne, alors une tâche prête ne peut pas
être préemptée par une autre tâche, étant donné qu’elle hérite pendant son exécution
de la priorité plafond de la ressource, c’est-à-dire de la priorité la plus forte des tâches.
Le langage OIL propose cependant un moyen plus simple de rendre des tâches non
préemptibles, en proposant tout simplement la préemptibilité comme attribut de
tâche au moment de sa définition.
à plusieurs événements.
OSEK/VDX propose une primitive de déclenchement (SetEvent), une primitive
d’attente d’événement (WaitEvent) qui est distinguée de l’effacement de l’événement
(ClearEvent). Seule la tâche propriétaire d’un événement a le droit de l’attendre et de
l’effacer.
225
5 • Exécutifs temps réel 5.3 Principales normes temps réel
nications entre les tâches se calquent naturellement sur ce type d’architecture. Pour
le programmeur, le fait qu’une communication ait lieu entre deux tâches du même
calculateur ou bien entre deux tâches situées sur des calculateurs distincts se doit
d’être le plus transparent possible, tout en utilisant au mieux les mediums de com-
munication.
Les communications par message sont de type communication n/m (n émettrices,
et m réceptrices possibles pour un message). Dans le cas d’une communication de
type boîte aux lettres, si un message a m réceptrices, chacune d’entre elles est munie
d’une file d’attente de messages gérée en FIFO, et consomme ses messages indépen-
damment des autres réceptrices (i.e. chaque message envoyé est déposé dans chacune
des files d’attente de réceptrices de ce message).
Le type de communication peut aussi être de type tableau noir (i.e. la lecture est non
bloquante et non destructive, et le message lu est le message le plus récemment écrit).
Généralement, la taille des messages est statique, mais il est possible, modulo quel-
ques restrictions, de définir des messages de taille dynamique. Des messages vides
peuvent aussi être transmis, dans ce cas, ils servent de synchronisation entre tâches
pouvant se trouver sur des calculateurs distincts. Par conséquent, l’exécutif fournit
trois primitives d’envoi de message : SendMessage (envoi d’un message de taille fixe),
SendDynamicMessage (envoi d’un message de taille dynamique), et SendZeroMessage
(envoi d’un message vide).
Un même message peut être simultanément transmis de façon interne et sur un
medium de communication, si par exemple une réceptrice se trouve sur le même cal-
culateur que l’émettrice, et qu’une autre réceptrice se trouve sur un autre calculateur.
Afin d’optimiser l’utilisation du medium, plusieurs messages envoyés sur le medium
de communication peuvent être regroupés dans le même paquet (typiquement, sa
taille est fonction du protocole de communication sous-jacent). Ce paquet, nommé
I-PDU (Interaction layer Processor Data Unit), constitué d’un ou plusieurs messages,
pourra alors être émis physiquement à destination d’un ou plusieurs calculateurs
distants. Notons que le fait que la communication soit orientée messages facilite
l’utilisation d’un protocole de type CAN (voir § 4.3.2, p. 177).
La figure 5.34, extraite de la norme OSEK/VDX, montre le schéma simplifié de
communication par message sur un calculateur muni de l’exécutif OSEK/VDX.
Notons la présence de filtrage possible à l’émission vers un calculateur distant (typi-
quement, un filtre pourra éviter les envois de valeurs identiques d’un même message
sur le réseau).
De même, il est possible d’effectuer un filtrage à la réception de message, de sorte
à ne prendre en compte que les messages jugés intéressants (par exemple, message
différent des précédents).
Enfin, il existe différentes notifications permettant aux tâches d’être averties, soit de
l’arrivée d’un message (ce qui est logique), soit du départ d’un I-PDU, ou encore
d’une erreur, ou d’une violation d’échéance.
OSEK/VDX permet de définir des messages périodiques, qui seront automatique-
ment envoyés à chaque période. Chaque message peut être muni d’une échéance :
si le message n’est pas reçu dans le délai imparti, une notification de violation
d’échéance est envoyée.
226
5 • Exécutifs temps réel 5.3 Principales normes temps réel
Message arrivé
Erreur
Violation d’échéance
Envoi effectué
τ1 τ2 τ3 τ4 τ5
Application
SendMessage ReceiveMessage
Notification
Communication interne
Filtrage
Communication interne Filtrage
Filtrage Filtrage Couche d’interaction
Communication externe Extraction de message(s)
Communication externe
I-PDU
I-PDU
à
reçu
transmettre
Requête de transmission Indication de réception
Couches réseau
227
5 • Exécutifs temps réel 5.3 Principales normes temps réel
M Horloges
M Ordonnancement
Chaque tâche est munie d’une priorité, ne pouvant être modifiée que temporaire-
ment lors d’un héritage de priorité dû au protocole à priorité plafond immédiat.
De même, chaque interruption se voit munie d’une priorité (normalement, les inter-
ruptions sont plus prioritaires que les tâches, et que l’ordonnanceur lui-même).
L’ordonnancement de base est de type SCHED_FIFO (voir § 5.3.1, p. 214), c’est-
à-dire que les tâches prêtes sont placées dans des files d’attente FIFO gérées par
priorité. L’une des spécificités OSEK/VDX est que l’ordonnancement peut être
préemptif, non préemptif, ou mixte.
Si elle est préemptible, la tâche en exécution peut être préemptée en vue de l’élection
d’une autre tâche lorsque :
– une tâche plus prioritaire est activée (ActivateTask) ;
– une tâche plus prioritaire devient prête suite à l’occurrence d’un événement, la
libération d’une ressource, l’arrivée d’un message, etc. ;
– une tâche devient plus prioritaire (suite à la diminution de la priorité de la tâche
exécutée due à la fin d’un héritage de priorité) ;
– sur une instruction exécutée par la tâche en exécution : libération explicite du
processeur grâce à la primitive Schedule (dans ce cas, la tâche est mise à la fin de
la file FIFO de son niveau de priorité), libération d’une ressource, appel d’une
primitive bloquante, comme accès à une ressource non disponible, attente d’un
événement non déclenché, etc. ;
– sur la fin d’une routine de traitement d’interruption.
Remarque
Lorsqu’une tâche est préemptée, elle reste en tête de la file d’attente de son niveau de priorité.
Une tâche non préemptible ne peut être préemptée en vue de l’élection d’une autre
tâche qu’à sa propre initiative, c’est-à-dire à l’appel d’une primitive bloquante, ou
bien à un appel explicite de Schedule.
Enfin, nous avons abordé le concept de groupes de tâches au paragraphe 5.3.2,
p. 225, pour lesquels les tâches d’un groupe sont rendues non préemptibles les
unes par rapport aux autres à l’aide de l’utilisation d’une ressource interne.
228
5 • Exécutifs temps réel 5.3 Principales normes temps réel
M Particularités d’OSEK/VDX
Crochets
Afin de pouvoir modifier le comportement standard du système lors d’événement
particulier (erreurs, activation et terminaison de tâche, lancement et terminaison
du système…), il est possible de définir des crochets (hook). Un crochet est une
fonction exécutée avec une priorité plus haute que celle des tâches, non interruptible
par les ISR de niveau 2.
Modes de fonctionnement
Beaucoup d’architectures, notamment les microcontrôleurs, supportent différents
types d’exécution : programme chargé en RAM, en ROM, ou dans une mémoire
FLASH, etc. De plus, pendant la phase de développement, le débogage doit absolu-
ment être actif, alors que lors de la mise en fonctionnement, il est rare de consacrer
de la mémoire afin d’autoriser le débogage.
Par conséquent, OSEK/VDX prévoit la définition de différents modes de fonction-
nement dans les fichiers OIL.
Profils
© Dunod – La photocopie non autorisée est un délit.
229
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
Les classes de conformité étendue permettent l’utilisation des tâches étendues. Il est
à noter que les exécutifs ECC1 ne mémorisent pas, à l’instar de BCC1, les activations
multiples de tâches (alors que pour ECC2, les activations multiples de tâches basiques
sont possibles). De plus, les exécutifs ECC1 n’autorisent qu’une seule tâche active
par niveau de priorité.
Notons enfin que la norme impose seulement 8 niveaux de priorités de tâches dis-
tincts pour les exécutifs BCC, et 16 pour les ECC.
De même, 4 profils de communication sont définis (Com Conformance Class). Les
profils CCCA et CCCB ne proposent que les communications internes. CCCA est
limité aux messages sans file d’attente. Les profils CCC0 et CCC1 correspondent
à CCCA et CCCB avec un support des communications externes.
16
14
12
10
8
6
4
2
0
op ks
T- s
C x
9
TX
TX
do OS
µC l
er re
S
SE
TX
ire
T- X
e
nu
ru
u
S-
N
C
N
M
O
/O
rn
t
or
le
ta
Au
VR
R
VR
O
ho
Q
W yn x
S
iR
Li
s
ke
s
uc
W
ié
T-
w
p
w
do
Vx
N
N
R
N
yp
Pr
in
in
s
H
W
do
do
in
in
W
W
Figure 5.35 – Utilisation des exécutifs temps réel par les internautes
inscrits sur le site Open Group, sondage effectué en 2001.
230
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
Il est assez difficile d’évaluer un exécutif temps réel, car les besoins des concepteurs
d’applications de contrôle-commande peuvent être radicalement différents. Il en
résulte que certains noyaux ou exécutifs (offrant des possibilités similaires à celle
d’OSEK/VDX, ou au profil 51 POSIX) ciblent principalement des microcontrôleurs,
alors que les systèmes d’exploitation offrant une interface POSIX complète ou quasi
complète ciblent principalement les micro-ordinateurs.
Le tableau 5.4 présente quelques critères à prendre en compte.
231
Tableau 5.4 – Critères d’évaluation d’un exécutif temps réel.
232
Interfaces de programmation La plupart des exécutifs proposent plusieurs interfaces de programmation, compatibles avec telle ou telle norme
normalisées temps réel. Ces interfaces ont-elles été certifiées ?
Peut-on programmer en POSIX, si oui, sur quel ensemble de normes, sur quel profil ? Peut-on programmer en
OSEK/VDX, si oui, sur quel profil ? Peut-on programmer en ITRON ? Si oui, sur quel profil ?
Richesse des bibliothèques Y a-t-il un support des calculs à virgules flottantes ? Est-il logiciel ou matériel ? Y a-t-il une communauté de
de programmation développeurs mettant en commun leur savoir-faire en partageant du code ?
Langages de programmation Au minimum, l’assembleur est supporté, la plupart des exécutifs sont accompagnés de compilateurs permet-
supportés tant de programmer en C, voire en C++ ou encore en Ada.
5 • Exécutifs temps réel
Nombre de niveaux de priorités Typiquement, cela va de 8 au minimum pour certains noyaux à 256 ou plus pour d’autres. Comment l’ordon-
nanceur gère-t-il les tâches de même niveau de priorité (FIFO, tourniquet) ?
Nombre maximal de tâches Typiquement, cela va de 8 au minimum pour certains noyaux à 256 ou plus pour d’autres.
Support mémoire Empreinte mémoire (taille du noyau lui-même, et des différents modules à intégrer). Taille maximale de la
mémoire (pile) allouée à une tâche ou à un processus (code, pile et tas). Protection de la mémoire si multipro-
cessus. Allocation dynamique de mémoire. Mémoire virtuelle.
Gestion des interruptions Interruptions hiérarchisées ou non, possibilité ou non pour une ISR d’être interrompue, flexibilité de pro-
grammation.
Gestion des horloges Nombre et précision des horloges, granularité temporelle, possibilité de travailler en durées et en dates.
5 • Exécutifs temps réel
233
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
m Gestion du temps
La résolution d’horloge est limitée par la façon dont VxWorks® gère le temps.
L’unité de base est le tick : la fréquence d’occurrence des ticks peut être programmée,
ce qui a pour effet de programmer le matériel de sorte à générer une interruption
234
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
temps
tick et exécution
de l’ordonnanceur
Bien que cela soit en général possible, il est déconseillé d’augmenter la fréquence des
ticks au-delà d’1 kHz (ce qui offre une résolution d’1 milliseconde) car cela impli-
querait un surcoût processeur important. Le principal problème dû à cette implé-
mentation est que la granularité du système est relativement grossière par rapport aux
noyaux dirigés par les événements.
L’interface de programmation VxWorks® est relativement simple à comprendre et
à manipuler. Lors de la phase de développement, on utilise généralement une tâche
tShell fournissant un shell (invite de commande permettant à l’utilisateur de dialo-
guer avec l’exécutif ) qui permet, via une console contrôlée par la cible ou bien par
l’hôte, de lancer directement des fonctions C chargées sur la cible, et de gérer les
tâches (obtenir des informations, tuer les tâches…). Il est aussi très facile, via le sup-
port réseau très étendu, d’accéder au système de fichiers de l’hôte ou d’une autre
machine du réseau.
Différents choix très pratiques ont été effectués : ainsi, la tâche tLogTask, permettant
d’enregistrer une trace d’exécution (log) peut être utilisée à l’aide de la fonction logMsg
afin d’afficher ou enregistrer du texte dans un fichier, ce qui facilite le suivi du fonc-
tionnement de l’application sans appeler directement des fonctions d’affichage ou
d’écriture dans un fichier (fprintf ), c’est-à-dire sans ajouter d’appel suspensif pertur-
bant l’ordonnancement de l’application.
Les états possibles d’une tâche VxWorks®, ainsi que les noms de primitives natives
© Dunod – La photocopie non autorisée est un délit.
impliquant les transitions entre ces états, sont donnés sur la figure 5.38.
La mémoire est partagée par l’exécutif et toutes les tâches, comme cela est présenté sur
la figure 5.39. Chaque tâche VxWorks®, comme chaque tâche utilisateur, est caracté-
risée uniquement pas sa pile : elle contient donc les variables locales à la tâche ainsi
que son contexte d’exécution (voir § 4.2.3, p. 154). Notons que les interruptions
sont traitées en utilisant une pile prédéfinie.
Les tâches sont créées (taskInit et taskActivate ou taskSpawn qui est la combinaison
des deux actions) et détruites (taskDelete) dynamiquement. Lors de la création, étant
donnée la gestion mémoire employée, il est nécessaire de spécifier explicitement la
taille de la pile à allouer à une tâche, ainsi que des paramètres (10 en tout) à passer
à la tâche. Il est aussi nécessaire de spécifier explicitement si la tâche utilise les registres
235
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
Exécutée
Préemption
Election
semGive
msgQSend taskDelay
En attente Prête Endormie
semTake Délai écoulé
msgQReceive
taskSuspend
taskResume
taskActivate
ta
sk e
Re m
ta
sk su esu
m R nd
Su e sk pe
sp ta s
en Su
d sk
ta
Suspendue
taskInit
Données
Pile tâche 3
Pile tâche 2
Pile tâche 1
Pile tExcTask
Code
236
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
Ce petit programme, lorsqu’il est chargé sur la cible, permet de lancer, par exemple
à l’aide du shell, la fonction LancerTache. Là il y a de très fortes chances que les entiers
affichés ne correspondent pas du tout aux entiers attendus. En effet, si LancerTache
est exécutée dans le contexte de la tâche tShell, la variable tableau est allouée sur la
pile de cette tâche. Lorsque la fonction se termine, cette variable se voit écrasée par
© Dunod – La photocopie non autorisée est un délit.
237
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
En plus de l’interface POSIX 1003.1b et 1003.1c, sur laquelle nous ne revenons pas
ici, VxWorks® propose ses propres outils de communication et de synchronisation,
qui sont plus rapides d’après la documentation.
Trois types de sémaphores sont proposés : le sémaphore binaire (créé par semBCreate),
le sémaphore compteur (créé par semCCreate) et le sémaphore d’exclusion mutuelle
(créé par semMCreate) sont pris ou vendus par les mêmes primitives semTake et
semGive. La spécificité du sémaphore d’exclusion mutuelle est qu’il est possible de
lui adjoindre le protocole de gestion de ressources à priorité héritée.
Notons qu’il est possible lors de la prise de sémaphore de borner l’attente maximale
de celui-ci.
La communication par message est assurée à l’aide de boîtes aux lettres FIFO sans
écrasement, gérant deux niveaux de priorité de messages (normal et urgent). Une
boîte aux lettres est créée avec msgQCreate qui se voit donner la taille de la file, la taille
maximale d’un message, et un paramètre disant si les tâches en attente doivent être
traitées en FIFO ou suivant leur niveau de priorité. L’envoi de message (msgQSend ) et
l’attente de message (msgQReceive) peuvent être bornés dans leur temps d’attente
maximal.
m Avantages et inconvénients
238
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
5.4.2 OSEKturbo
OSEKturbo est un exécutif temps réel conforme à OSEK/VDX, profils ECC1 et com-
munication CCCB (communication interne uniquement) dans la version de base.
Comme la norme, celui-ci, extrêmement léger et rapide, est principalement déve-
loppé pour des microcontrôleurs (8, 16 ou 32 bits).
– Nom : OSEKturbo.
– Type : micro noyau.
– Société : Metrowerks.
– Noyau : OSEKturbo.
– Environnement de développement : CodeWarrior.
– Systèmes hôtes : Win32 (Windows95/98/NT/2000/XP…).
– Cibles : plusieurs microcontrôleurs PowerPC, ARM, NEC V850, C16x…
– Support multiprocesseur : non.
– Interface native de programmation : OSEK/VDX.
– Interface POSIX : non.
– Interface OSEK/VDX : BCC1, ECC1, communication CCCA, CCCB.
– Interface ITRON : non.
© Dunod – La photocopie non autorisée est un délit.
239
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
5.4.3 RTEMS
RTEMS (Real-Time Executive for Multiprocessor Systems) version 4.6 est l’un des exé-
cutifs temps réel gratuits et open source les plus riches que l’on puisse trouver. Issu
initialement d’un projet militaire (il a notamment été développé pour le contrôle
de missiles), cet exécutif s’est enrichi au fil des années et des versions.
– Nom : RTEMS.
– Type : noyau et tâches de service.
– Société : OAR Corporation.
– Noyau : RTEID/ORKID.
– Environnement de développement : divers outils en ligne de commande.
– Systèmes hôtes : Unix, Linux, Win32 (sous l’environnement Cygwin, portage des
outils Unix sur système Win32).
– Cibles : Chaque utilisateur étant à même de développer un BSP, la plupart des
processeurs et microcontrôleurs sont supportés (de façon officielle ou non). Il faut
cependant s’attendre, lorsque l’on utilise un BSP non officiel, à être confronté à
quelques bogues.
– Support multiprocesseur : oui.
– Interface native de programmation : interface spécifique.
– Interface POSIX : limitée au profil 52, implémentation presque complète de
POSIX 1003.1b (limitée au fait qu’il n’y a pas de support multiprocessus),
1003.1c et 1003.1h.
240
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
5.4.4 RTLinux
RTLinux est l’un des systèmes d’exploitation temps réel basé sur Linux. Ses principales
caractéristiques sont :
– Nom : RTLinuxPro/RTLinuxFree.
– Type : micro noyau accompagné d’un système d’exploitation Linux.
– Société : FSMLabs/communauté de développeurs.
– Noyau : RTCore.
241
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
242
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
Interruption
Matériel Machine réelle
RTLinux est l’une des adaptations de Linux au temps réel. Différentes techniques
sont utilisées par les systèmes d’exploitation temps réel basés sur Linux pour réduire
la latence (voir § 5.1.1) du noyau.
La première consiste à modifier les primitives du noyau afin d’insérer des points de
préemption ou de diminuer au maximum les parties non préemptibles de celui-ci
(Patch Low Latency, Preempt Kernel ).
La seconde, utilisée par RTLinux et le système d’exploitation RTAI, resté quant à lui
logiciel libre, consiste à utiliser un micro noyau temps réel fournissant une machine
virtuelle à un noyau Linux. Celui-ci est légèrement modifié de sorte à l’empêcher
de masquer les interruptions ou de les détourner directement (rôle du gestionnaire
d’interruptions du noyau RTCore), et les horloges qu’il utilise sont en fait simulées
par le noyau RTCore. Ainsi, la granularité temporelle de RTLinux peut descendre
jusqu’à la microseconde sans surcoût processeur, puisque seules les interruptions
© Dunod – La photocopie non autorisée est un délit.
243
5 • Exécutifs temps réel 5.4 Exemples d’exécutifs temps réel
un outil de communication par messages : les files temps réel (RT FIFO) et la
mémoire partagée (mbuff ) entre processus non temps réel et tâches temps réel.
Une file temps réel est vue comme un fichier de caractères du côté du processus non
temps réel l’utilisant (ouverture, lecture et écriture bloquantes ou non, fermeture), et
comme un tube (voir § 2.2.2, p. 196) non bloquant en lecture du côté de la tâche
temps réel l’utilisant.
Afin de palier le manque de boîte aux lettres dans la norme POSIX 1003.1c, RTLinux
propose aussi un mécanisme de boîte aux lettres spécifique (rt_mq).
Les avantages de RTLinux sont liés au fait qu’il est conforme au profil 51 de POSIX,
à sa gestion du temps, et surtout à sa cohabitation avec Linux. Son inconvénient
majeur est lié à l’empreinte mémoire énorme prise par Linux, d’où une limitation
des cibles potentielles.
244
6 • PROGRAMMATION
DES SYSTÈMES MULTITÂCHES
Après une brève présentation des langages C, Ada, et LabVIEW, ce chapitre présente
des règles de passage d’une conception DARTS à une application de contrôle-
commande.
Nous supposons que le lecteur est familier avec au moins un langage de program-
mation impératif ou bien flots de données. Par conséquent, la présentation des trois
langages de programmation supports est faite en commun, de sorte qu’avec la con-
naissance d’un des trois langages (ou d’un langage proche), le lecteur puisse rapide-
ment se faire une idée des deux autres.
Ce chapitre n’a pas pour objectif de présenter exhaustivement les normes et langages
sus-cités, mais de les présenter dans le but d’implémenter une conception DARTS.
m Le langage C
Le langage C a vu le jour à la fin des années 60. Son rôle initial était de permettre
la portabilité de la majeure partie du code d’un système d’exploitation d’une archi-
tecture matérielle à une autre. En 1973, il est utilisé avec succès pour écrire un sys-
tème d’exploitation. Depuis, son utilisation n’a cessé de croître et il est le langage
utilisé dans l’implémentation de la majeure partie des systèmes d’exploitation. C’est
aujourd’hui encore l’un des langages de programmation les plus utilisés.
245
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Le langage C est un langage de type impératif (en opposition aux langages décla-
ratifs, fonctionnels, flots de données ou orientés objets) compilé. Il est faiblement typé,
à compilation séparée. Le langage C est sensible à la casse (majuscules/minuscules).
Le langage C dispose d’une bibliothèque exceptionnelle de composants logiciels, et la
quasi-totalité des langages de programmation peuvent s’interfacer avec le langage C.
Le langage C est fait pour le multiprocessus, mais pas pour le multitâche, ce qui
explique l’émergence de différentes normes temps réel dont POSIX (chapitre 5)
est le représentant le plus répandu.
Le langage C est assez proche de la machine, comparé aux langages modernes. Cet
inconvénient est cependant un atout indéniable lorsque l’on doit faire de la program-
mation bas niveau, comme dans la programmation des entrées/sorties spécifiques
aux applications de contrôle-commande.
Le langage C est un langage de programmation normalisé. Aujourd’hui, des centaines
de compilateurs C et environnements de développement associés existent, aussi
bien en logiciel libre (le plus connu étant GNU C Compiler, GCC ) qu’en logiciel
commercial.
m Le langage Ada
L’idée du langage Ada naît au milieu des années 70 sous l’impulsion du département
de la défense américain du constat suivant : il n’y avait pas de langage « universel »
pour la programmation des systèmes embarqués. La diversité des langages utilisés
sur les différents projets coûtait cher en validation, formation, maintenance, etc.
Un concours a alors été lancé et parmi les quatre propositions de langages finalistes
(représentés anonymement par 4 couleurs), le langage green a été choisi. Il porte le
nom d’Ada en mémoire du premier programmeur au monde : Augusta Ada Byron.
Ada est un langage impératif à typage fort, compilé, modulaire, à compilation séparée.
Il permet le traitement des exceptions, et la généricité. Tout a été mis en œuvre pour
qu’il soit le plus sûr possible. De plus, il est nativement multitâche : en langage
Ada, une tâche est un type que l’on peut instancier. Ada est insensible à la casse
(majuscules/minuscules).
Sa première version a été normalisée en 1983 : elle présentait des lacunes notamment
au niveau des facilités de communication entre les tâches (seul le rendez-vous avec
données existait). La seconde version, Ada 95, permet la programmation objet et
surtout toute forme de communication asynchrone et synchronisation grâce à l’intro-
duction de moniteurs (objets protégés).
Ada est un langage normalisé. Plusieurs compilateurs Ada existent sur la plupart des
plateformes. Le plus connu des compilateurs libres est GNAT.
m Le langage LabVIEW
Le langage LabVIEW est un des rares représentants des langages flots de données.
C’est un langage graphique, typé, modulaire, et en tant que langage flots de données,
il est naturellement parallèle.
Contrairement à C et Ada, LabVIEW est un environnement de développement
propriétaire, développé par la société National Instruments. LabVIEW se base sur le
langage G (il semble que cela soit la seule implémentation de ce langage).
246
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
LabVIEW est un langage relativement récent, puisqu’il a fait son apparition sur
Macintosh au milieu des années 80. Initialement, il était dédié à la programmation
d’instruments virtuels utilisant des cartes d’acquisition de la société National Ins-
truments. L’exemple typique d’un instrument virtuel, souvent repris pour présenter
l’intérêt initial de LabVIEW, est un oscilloscope logiciel se basant sur une carte
d’acquisition multifonctions : l’oscilloscope est composé d’un certain nombre de
fonctionnalités internes, et d’une interface utilisateur. De même, tout programme ou
sous-programme LabVIEW est représenté par une interface graphique, nommée
face avant et une description du fonctionnement interne sous forme de flots de
données, nommée diagramme.
Depuis, le langage LabVIEW a évolué et s’est élargi au fil des versions successives,
jusqu’à devenir un langage complet de programmation, plaçant LabVIEW parmi
les langages les plus agréables à utiliser pour les applications de contrôle-commande.
M Langage C
Le tableau 6.1 présente les types de base du langage C.
Notons en particulier le fait que le type int, très employé, est souvent représenté par
la taille d’un registre de calcul. De nombreuses personnes assument de plus qu’une
variable de type int a la taille suffisante pour contenir une adresse (un pointeur).
Le fait que différents types aient une longueur dépendante de l’architecture sous-
jacente proscrit l’utilisation de valeurs constantes lorsque l’on manipule des tailles de
données. Un opérateur, nommé sizeof, est toujours utilisé lorsque l’on doit connaître
la taille en octets d’une variable, d’une constante ou bien d’un type. Ainsi, sur une
architecture 32 bits classique, sizeof(int) vaut 4.
Notons l’absence du type booléen : en langage C, les tests peuvent s’effectuer sur
n’importe quel type de valeur : si la valeur est nulle, le test est faux, si la valeur est
non nulle, le test est vrai.
Toute variable doit être définie avant son utilisation et avant toute instruction du
même bloc (un bloc est défini entre accolades et correspond à un groupe de définitions
et instructions).
int i; /* Déclaration d’un entier i non initialisé */
float d=1.51E+2f; /* Déclaration d’un flottant double précision d
© Dunod – La photocopie non autorisée est un délit.
initialisé à 151 */
unsigned a,b; /* Déclaration de deux entiers non signés */
char c=’a’ + 1 ; /* Déclaration d’un caractère valant ’b’ (code ASCII
suivant celui de ’a’) */
247
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Taille
Type Description Domaine Exemples de littéraux
(en bits)
248
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Noter l’emploi obligatoire du mot clé struct lors de l’utilisation du type. Pour éviter
cette lourdeur syntaxique, on peut utiliser le mot clé typedef qui permet de nommer
un type. Dans l’exemple ci-après, le même type enregistrement est déclaré de façon
anonyme, mais défini par le nom Complexe :
typedef struct {
float reel;
float imaginaire;
} Complexe;
Complexe C; /* Déclaration d’une variable de type Complexe */
Complexe C2={1,3.5}; /* C2 vaut 1+3.5i */
Un type énuméré, rarement utilisé en langage C, se définit avec le mot clé enum :
enum couleurs {rouge, vert, bleu};
/* En fait, rouge vaut 0, vert vaut 1 et bleu vaut 2 */
enum couleurs couleur=vert; /* définition de la variable couleur,
valant vert */
Afin d’éviter la lourdeur de répétition du mot clé enum, on utilise souvent le nom-
mage de type, comme pour les types enregistrement :
typedef enum {rouge, vert, bleu} couleurs;
/* Ici, enum {rouge, vert, bleu} est nommé couleurs */
couleurs couleur=vert; /* définition de la variable couleur, valant
vert */
Rappelons que C a un typage faible. Ainsi, l’exemple suivant passe parfaitement sur
la plupart des compilateurs.
m.c = ’a’+3+vert; /* en introduction sur C, nous parlons de typage
© Dunod – La photocopie non autorisée est un délit.
M Langage Ada
Le tableau 6.2 présente les types de base du langage Ada.
À l’instar du langage C, la norme Ada n’impose pas de taille pour les types numé-
riques. Généralement, c’est la taille d’un registre de calcul.
La norme prévoit, mais n’impose pas, la présence de types numériques courts,
longs, ou très longs : ainsi, Long_Integer correspond à un entier long, Short_Natural
correspond à un entier non signé court, Long_Long_Float correspond à un flottant
très long, etc.
249
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Le fait que différents types aient une longueur dépendante de l’architecture sous-
jacente proscrit l’utilisation de valeurs constantes lorsque l’on manipule des tailles de
données. Le langage Ada définit des attributs permettant d’obtenir diverses infor-
mations notamment sur les types et sur les variables. Ainsi, par exemple, l’expression
Integer’Size donne la taille des entiers, Integer’First donne le plus petit entier et
Integer’Last le plus grand.
Notons que Ada propose un constructeur de types point fixe (delta).
Toute variable doit être définie avant son utilisation.
i: integer; -- Déclaration d’un entier i non initialisé
d:float :=1.51E+2; -- Déclaration d’un flottant initialisé à 151
a, b : natural; -- Déclaration de deux entiers non signés
c: character := character’succ(’a’);
-- Déclaration d’un caractère valant ’b’ (caractère successeur de ’a’)
Noter l’emploi obligatoire du mot clé type lors de la déclaration d’un nouveau type.
Un type énuméré se définit de la façon suivante :
250
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Cependant, comme pour tout type discret, l’utilisation d’attributs permet d’opérer
une conversion avec les entiers ou bien les chaînes de caractères :
Coul := Couleur’Val(1); -- Couleur’Val(1) vaut Carreau
Coul := Couleur’Value("Trefle"); -- Couleur’Value("Trefle") vaut
Trefle
I := Couleur’Pos(Coul); -- I vaut 0, position de Trefle
M Langage LabVIEW
LabVIEW étant un langage graphique, c’est de façon graphique que l’on choisit le
type d’une variable. Dans ce langage, tout est flot. L’origine d’un flot a un type, qui
définit le type d’un flot. L’origine d’un flot peut être une « variable d’entrée »
nommée commande, et la fin d’un flot, une « variable de sortie » du flot nommée
indicateur.
Chaque programme, ou sous-programme LabVIEW s’appelle un instrument virtuel
(virtual instrument ou vi ). La philosophie est de définir un instrument par son
interface graphique, ou face avant, et son programme ou diagramme. La figure 6.1
montre un vi prenant en paramètre un entier a, lui appliquant un calcul (a + 1)/2
afin d’afficher le résultat b. a en tant que paramètre d’entrée s’appelle une commande
(pour commande utilisateur, modifiable via l’interface graphique) et b en tant que
paramètre de sortie visible par l’utilisateur, s’appelle un indicateur. Au début du
programme, les constantes et les commandes produisent une valeur sur chacun des
flots (fils) auxquels ils sont connectés. Dès qu’une valeur est disponible sur chacun
des flots en entrée, le vi devient exécutable. Il est alors exécuté et produit ses valeurs
© Dunod – La photocopie non autorisée est un délit.
en sortie, qui à leur tour rendent d’autre vi exécutables, etc. Dès qu’un flot est dis-
ponible en entrée d’un indicateur, la valeur est affichée sur l’élément correspondant
de la face avant.
La figure 6.2 montre qu’un numérique, s’il est entier, peut être signé ou non (préfixe
I pour signé, U pour non signé), sur 8, 16, ou 32 bits. Les nombres entiers peuvent
donc être de type I8, I16, I32, U8, U16, ou U32. Les nombres réels, peuvent être
représentés suivant la norme IEEE 754 par un flottant de 32, 64 ou 80 à 128 bits
selon la plateforme.
Les littéraux numériques se voient graphiquement imposer un type, et peuvent être
représentés en décimal, octal, hexadécimal ou binaire, etc. La couleur des flots des
éléments graphiques a une sémantique. Ainsi, graphiquement, les flots de données
251
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Commande numérique
Flottants
Entiers signés
Entiers non signés
Complexes
d’entiers (signés ou non, quelle que soit leur taille) sont représentés en bleu. Les flots
de données de réels sont représentés en orange.
Les booléens, normalement implémentés sur 8 bits, sont représentés graphiquement
en vert. Les chaînes de caractères sont distinguées des tableaux en langage LabVIEW.
Représentées en rose, celles-ci sont des types de base du langage.
La figure 6.3 montre une commande de type enregistrement (cluster en langage
LabVIEW), et la façon dont on y accède.
252
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Les flots de données de type cluster peuvent être de deux couleurs : marron ou rose.
Ils sont marron lorsque tous les champs sont numériques, il est alors possible de
réaliser des opérations arithmétiques champs à champs (sur la figure 6.3, on peut
additionner deux clusters rectangles, et obtenir un cluster rectangle de hauteur la
somme des hauteurs, et de largeur la somme des largeurs). Lorsqu’au moins un des
champs est non numérique, le fil est de couleur rose, et n’autorise pas d’effectuer
Énumération
© Dunod – La photocopie non autorisée est un délit.
253
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
des opérations arithmétiques directement sur les clusters. La figure 6.4 montre une
énumération.
Comme on le voit sur la figure 6.5, le typage est faible par défaut, comme en
langage C, cependant les coercions (changements de types) implicites sont visibles
sur le diagramme (point gris).
Division euclidienne
M Langage C
Le langage C fait très peu de distinction entre pointeur et tableau. Un tableau est
une zone mémoire contiguë permettant de stocker un certain nombre de valeurs du
même type.
Un tableau est construit de la façon suivante :
int t1[3]; /* t1 est un tableau de 3 entiers non initialisés */
long T[5] = {2512,-15,42,56.32}; /* T est un tableau de 5 entiers
longs initialisé */
Complexe c, tc[5]; /* c est un complexe, tc est un tableau de 5
complexes */
254
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
sizeof (T) = 20
sizeof (T) = 20
T2
0x22FF48
255
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
L’opérateur « & » utilisé avant une variable est un opérateur de référencement (&i
donne l’adresse de i). L’opérateur * placé avant une expression (ici *pt) est un opé-
rateur de déréférencement, c’est-à-dire qu’il permet d’accéder à l’adresse donnée par
l’expression (ici, l’adresse de i).
long **pt2 ; /* ici pt2 est un pointeur de pointeur d’entier long */
pt2 = &pt; /* pt2 contient l’adresse de pt */
*(*pt2) = 4 ; /* i vaut maintenant 4 */
Pointeurs et tableaux sont extrêmement proches, puisqu’ils contiennent tous les deux
une adresse. La différence fondamentale entre les deux est qu’un tableau voit sa
mémoire allouée au moment de sa déclaration, ainsi, 20 octets sont alloués au
tableau, dont l’adresse est le début de ces 20 octets lors de la déclaration de :
long T[5];
La taille d’un tableau est donc fixe. Les pointeurs se substituent aux tableaux lorsque
l’on doit utiliser un tableau de taille dynamique. Pour cela, on alloue à la main
l’espace nécessaire grâce aux fonctions malloc ou calloc :
long *pt;
pt = (long*)malloc(sizeof(long)); /* pt pointe vers 4 octets alloués*/
/* du code */
free(pt); /* libération de l’espace mémoire alloué à pt */
pt = (long*)calloc(10,sizeof(long)); /* pt pointe vers un tableau de
10 entiers longs initialisés à 0 */
pt[1]=3; /* la seconde case du tableau reçoit 3 */
/* du code */
free(pt); /* libération de l’espace mémoire alloué à pt */
On peut noter qu’en langage C, toute mémoire allouée doit être rendue (grâce à la
fonction free). Notons que la différence entre calloc et malloc, mis à part le nombre
de paramètres, est que calloc initialise chaque octet de mémoire allouée à 0.
Notons aussi qu’il est nécessaire de faire une coercion explicite de l’espace mémoire
créé par malloc et calloc, de type void * (void est le type muet), en long*.
Les chaînes de caractères sont des tableaux de caractères. Un caractère spécial, ’\0’,
de code ASCII 0, signifie la fin d’une chaîne de caractères. Ainsi, une chaîne de
caractères de n caractères occupe au moins n + 1 octets : les n caractères plus le
caractère de fin de chaîne.
char ch[]="une chaine"; /* Déclaration d’une chaîne de caractères */
char *ch2 = "abc"; /* Pointeurs et tableaux peuvent presque
indifféremment être utilisés */
char *ch3=ch2; /* ch3 pointe sur la même chaîne que ch2 */
La figure 6.8 montre comment ces chaînes de caractères sont gérées en mémoire.
Les chaînes de caractères étant fondamentalement des tableaux, l’affectation d’une
chaîne à une autre copie l’adresse de la chaîne, mais pas le contenu de la chaîne
elle-même.
int taille_ch;
char ch[512], *copie_ch; /* Déclaration d’une chaîne de caractères de
512 caractères ch, et d’un pointeur */
printf("Entrez une chaîne"); /* Affichage de "Entrez une chaîne" */
scanf("%s",ch); /* lecture d’une chaîne au clavier */
256
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
0x23FF45 'u' 'n' 'e' ' ' 'c' 'h' 'a' 'i' 'n' 'e' 0
ch
0x23FF45
Caractère de fin de chaîne,
nommé aussi '\0'
Les fonctions spécifiques aux chaînes de caractères parcourent la ou les chaînes jus-
qu’au caractère de fin de chaîne. Ainsi, strcpy copie les caractères un à un en incluant
le caractère de fin de chaîne et termine. De même, la comparaison de chaînes, effec-
tuée par strcmp, s’arrête au premier caractère de fin de chaîne rencontré.
M Langage Ada
Ada gère de façon fondamentalement différente pointeurs et tableaux, afin de per-
mettre un suivi sûr du déroulement des programmes.
Un type tableau se définit à l’aide du mot clé array :
type Tab is array (1..5) of Integer;
-- type tableau indicé de 1 à 5
T1: Tab := (1, 3, 5, 7, 9);
-- Déclaration de tableau initialisé
© Dunod – La photocopie non autorisée est un délit.
T2 : Tab := (others=>4);
-- Déclaration de tableau initialisé par agrégat
-- toutes les valeurs valent ainsi 4
257
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Notons qu’en langage Ada, les indices de tableau sont dépendants de la déclaration
du type. Notons aussi que tout type discret peut servir à indicer un tableau. Ainsi,
le code suivant est correct :
type Bus_Externe is (RS232, Parallele, USB, FireWire, SCSI);
type Tableau_Debit is array (RS232..SCSI) of Integer;
-- Type tableau indicé par un type énuméré
Debits : Tableau_Debit;
Notons qu’il est possible d’affecter une valeur au pointeur au moment de l’allocation :
P:= new Integer’(3); -- allocation dynamique et initialisation
Ada, qui est un langage sûr, ne permet pas de référencer une variable quelconque.
Il est nécessaire pour cela de dire explicitement que la variable peut être référencée
par pointeur grâce au mot clé aliased, et de permettre au type pointeur le référen-
cement (mot clé access all ) :
type Pt_Entier is access all Integer;
I : aliased Integer;
P : Pt_Entier;
Notons que contrairement à C, Ada est muni d’un ramasse miettes (garbage col-
lector) chargé de récupérer la mémoire dynamique n’étant plus utilisée. Il n’est donc
pas nécessaire, sauf cas exceptionnel, de libérer explicitement la mémoire allouée.
M Langage LabVIEW
LabVIEW permet de créer des tableaux de façon graphique (figure 6.9), mais n’a
pas la notion explicite de pointeur. En effet, la notion de pointeur n’est pas com-
patible avec la philosophie flot de donnée. Les pointeurs sont cependant accessibles
258
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
pour certains objets graphiques via des références, ou des variables locales de façon
à simplifier certains types de programmation.
LabVIEW propose divers outils fonctionnant sur les tableaux (indexation, calcul
de la taille, construction, concaténation…). Les tableaux sont indicés de 0 à n – 1
comme en langage C. Notons que les flots de données tableaux sont repérés par
un trait épais conservant la couleur du type contenu dans le tableau. Il est ainsi
possible, comme sur les clusters, d’effectuer directement des opérations arithmétiques
sur des tableaux entiers.
Noter la différence
d’épaisseur des traits
© Dunod – La photocopie non autorisée est un délit.
259
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
m Structures de contrôle
M Langages C et Ada
La différence syntaxique entre structures de contrôle C et Ada réside surtout dans
le fait qu’en langage C, une structure de contrôle contient une seule instruction ou
bien un bloc (entre accolades) d’instructions, alors qu’en langage Ada, un bloc
contient des instructions et se termine au(x) mot(s) clé(s) terminateurs de blocs
(par exemple if, end id ).
C Ada
260
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Nous pouvons noter que les indices de boucle ne sont pas déclarés en langage Ada,
et que les indices de tableaux ne commencent pas nécessairement à 0. Notons aussi
l’emploi obligatoire de break à la fin de chaque alternative d’une structure de choix
en langage C.
Notons aussi qu’en langage Ada, il est plus élégant de parcourir un tableau en uti-
lisant les attributs de tableau : ainsi, l’expression T’range vaut T’first..T’last, c’est-
à-dire 1..10 pour l’exemple donné sur le tableau 6.3. Donc, de façon plus élégante,
la boucle « pour » du tableau 6.3 peut s’écrire :
for i in T’range loop
T(i)=i-1;
end loop;
Notons aussi une subtilité lors de l’évaluation des booléens, ayant un impact sur
les structures de choix : en langage C l’évaluation des booléens est une évaluation
paresseuse, ainsi le code C suivant est correct :
char ch[512]; /* Ch est indicé de 0 à 511 */
scanf("%s",ch); /* Lecture au clavier de ch d’au plus 10 caractères */
int i=0;
/* Recherche du caractère ’.’ */
while (i<sizeof(ch) && ch[i]!=’.’ && ch[i]!=0) {
/* Tant que i est dans la chaîne et ch[i] différent de ’.’ et pas fin
de chaîne */
i++;
}
if (i<sizeof(ch) && ch[i]==’.’) {
/* Si on n’a pas dépassé la fin de la chaîne et que i est l’indice d’un
caractère ’.’ */
printf("Il y a un ’.’ au caractère %d",i+1);
} else {
printf("Il n’y a pas de point");
}
I: Integer := 0;
Longueur: Integer;
…
Get_Line(Ch,Longueur); -- Lecture de Ch au clavier,
-- Longueur vaut la longueur de la chaîne lue
-- Recherche du caractère ’.’
while (i<=Longueur and Ch(i)/=’.’) loop
-- Tant que i est dans la chaîne et ch(i) différent de ’.’
i:=i+1;
end loop;
if (i<Longueur and ch(i)=’.’) then
-- Si on n’a pas dépassé la fin de la chaîne
-- et que i est l’indice d’un caractère ’.’
Put("Il y a un ’.’ au caractère "&Integer’Image(I+1));
261
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Ada propose les opérateurs and then et or else permettant une évaluation paresseuse
d’une expression booléenne.
M Langage LabVIEW
LabVIEW propose presque les mêmes structures de contrôle que C et Ada : la boucle
« pour » (figures 6.11 et 6.12), la boucle « tant que » (figure 6.13), la structure
conditionnelle (figure 6.14) qui ne se distingue de la structure de choix (figure 6.15)
que par la condition – si c’est un booléen, c’est une structure conditionnelle, si
c’est un autre type de base (entier, type énuméré, chaîne de caractère), alors c’est
une structure de choix.
Indicateurs visibles
sur la face avant
Notons sur la figure 6.11 la présence d’un registre à décalage, outil très souvent
utilisé dans les boucles. En effet, d’après la philosophie du flot de données : un nœud
ne peut s’exécuter que lorsque tous ses flots de données en entrée sont présents, il
ne peut pas y avoir de cycle. Par conséquent, il ne serait pas possible de prendre un
fil en sortie de boucle (ici, la moyenne des températures lues) et de le remettre en
entrée de boucle. C’est pour cela que l’on utilise le registre à décalage.
Notons aussi qu’afin de faciliter l’utilisation de boucles « pour » sur les tableaux,
LabVIEW fournit un mécanisme d’indexation/désindexation automatique de tableau.
Ainsi, sur la figure 6.12, le tableau en entrée est indexé automatiquement (noter la
présence de petits crochets sur le bord de la boucle) : la valeur prise dans le tableau
262
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Indexation automatique
Indexation automatique
Une boucle « tant que » est caractérisée par une condition d’arrêt, prenant un para-
mètre booléen : sur la figure 6.13, ce paramètre est en bas à droite, et sort de la boucle
à la fin de la première itération pour laquelle le paramètre envoyé à la condition d’arrêt
est vrai. En fait, la boucle « tant que » LabVIEW est une boucle « faire jusqu’à »
lorsque la condition d’arrêt est affublée d’un point rouge, et une boucle « faire tant
que » lorsque la condition d’arrêt ne possède pas de point rouge.
Noter sur la figure 6.14 que toute structure de contrôle est considérée comme un
nœud dans LabVIEW : si un flot sort dans un cas (ici une chaîne de caractères), alors
il doit sortir dans tous les cas.
263
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
264
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
m Sous-programmes
M Langage C
En C, tout sous-programme est une fonction, prenant des paramètres en entrée
uniquement et renvoyant une valeur. Le prototype d’une fonction C à deux arguments
est :
type_de_retour nom_fonction (type1 argument1, type2 argument2) ;
Par exemple, la fonction calculant le plus grand commun diviseur de deux entiers
a pour prototype :
int pgcd(int a, int b);
/*
Entrée: a,b deux entiers
Retourne: le plus grand commun diviseur de a et b
Convention: pgcd(0,b) = pgcd(a,0) = 1
*/
Étant donné que tous les arguments sont passés par valeur (les valeurs des arguments
sont recopiées directement sur la pile au moment de l’appel), afin de passer un
paramètre en sortie ou en entrée sortie, de sorte à le modifier dans la fonction, on
passe son adresse. Voici le prototype d’une fonction transformant un mot en son
mot miroir (rappelons qu’un tableau est fondamentalement un pointeur) :
void miroir(char * ch);
© Dunod – La photocopie non autorisée est un délit.
/*
Entrée/Sortie: ch
Entraîne: ch’ = miroir(’ch)
*/
Le problème est qu’il est alors difficile de distinguer, typiquement lorsqu’on passe
un tableau ou un pointeur en paramètre à une fonction, si celui-ci est un paramètre
d’entrée, ou bien d’entrée/sortie. Pour distinguer cela, observons sur le prototype
de la fonction de recopie de chaîne de caractères l’emploi du mot clé const (constant) :
char *strcpy(char *destination, const char *source)
/*
Entrée: source
265
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Sortie: destination
Entraîne: copie de source dans destination
Nécessite: sizeof(destination)>=strlen(source)+1
Retourne: l’adresse de destination
*/
Remarque
Généralement, les types de retour de fonction sont des types scalaires. L’utilisation de tableaux comme
types de retour est à proscrire, car rappelons que l’affectation de tableaux ne fait qu’une copie
d’adresse et pas de contenu. Ainsi, un tableau créé à l’intérieur d’une fonction est perdu dès la fin
de celle-ci. En effet, en tant que variable locale, un tableau est créé dans la pile lors de l’appel d’une
fonction, et la pile est libérée au retour de celle-ci (§ 4.2.3, p. 154).
M Langage Ada
En Ada, il existe deux types de sous-programmes : les fonctions qui renvoient une et
une seule valeur, et ne prenant que des paramètres en entrée (IN), et les procédures,
ne renvoyant pas de valeur mais pouvant prendre des paramètres en entrée ou en
266
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
R,A,B : Integer;
begin
A:=abs(I); -- Travail en valeur absolue
B:=abs(J);
if B>A then
return Pgcd(B,A);
else
R := A mod B; -- Reste de la division entière
if R=0 then
return B;
else
return Pgcd(B,R);
end if;
end if;
267
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
M Langage LabVIEW
Le langage LabVIEW est inégalable pour les tests unitaires de sous-programmes
car chaque programme est potentiellement un sous-programme. En effet, lorsqu’on
conçoit un vi, les commandes sont les entrées utilisateur, et les indicateurs les sorties
utilisateur. Le principe de l’encapsulation LabVIEW permet de passer les commandes
d’un vi par un autre vi, qui devient ainsi vi appelant, et récupère les indicateurs
comme paramètres de sortie.
Afin d’illustrer ce concept, considérons le vi donné sur la figure 6.15 qui transforme
une température donnée dans une unité quelconque en degrés Celsius. Il peut être
exécuté pendant la phase de développement afin de le tester. Ensuite, il est prêt à être
utilisé en tant que sous-programme. Pour cela, il suffit de se placer sur sa face avant
et de visualiser les connecteurs sur l’icône du vi (figure 6.16). Là, il ne reste plus qu’à
identifier le fait que le vi possède un paramètre d’entrée et un paramètre de sortie
pour le transformer en sous-programme.
Identification
des connecteurs
Documentation
Afin de rendre le vi plus simple à reconnaître dans les vi qui l’utilisent, il reste à
dessiner un icône personnalisée, et à le documenter (propriétés du vi, documentation).
268
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Le flot ne sort
Dans le cas faux,
qu’à la fin de la boucle,
les fils sont simplement inversés
Division euclidienne donc lorsque B divise A
Valeur absolue
Si R≠0, pgcd(A,B)=pgcd(B,R),
de plus, par construction, B≥R≥0
m Modularité et visibilité
M Langage C
La figure 6.18 montre une hiérarchie typique liée à l’élaboration d’un programme C.
© Dunod – La photocopie non autorisée est un délit.
269
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Librairie précompilée
libm.a
=
math.o
En-tête de module +…
Objet
compilation
main.c main.o
En-tête de module cc-c main.c
stdio.h Corps de module
(programme principal)
string.h
… libstd.a
=
stdio.o
dépend de +string.o
+mem.o
produit
+…
Librairie précompilée
270
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
ment précis du processus afin de comprendre et d’éviter les erreurs dues à une hiérar-
chie de compilation mal construite.
La compilation d’un module s’effectue en deux phases : un préprocesseur interprète
un langage macro afin de remplacer toutes les macros par leur valeur. Le fichier ainsi
« préprocessé », est compilé en un objet (figure 6.19). Le langage macro commence
chacune de ses instructions par le caractère #. Ainsi, l’instruction #define permet
d’associer un nom logique à une suite d’instructions. Typiquement, ce type de
définition est utilisé pour les constantes de programme et les macros. Ainsi, sur la
figure 6.19, les constantes MAX_TEMP et MIN_TEMP sont substituées par leur
valeur par le préprocesseur.
temperature.h
typedef structef {
int p,t;
}t_pression_temperature;
…
temperature.c
typedef structef { temperature.o
#include "temperature.h" int p,t;
#define MAX_TEMP 90 }t_pression_temperature;
#define MIN_TEMP 13 …
Préprocesseur … Compilation Code objet
…
if (t>MAX_TEMP) if (t>90)
… …
C’est aussi une instruction préprocesseur qui permet de faire référence à des
fichiers d’en-têtes. Le préprocesseur se contente d’insérer leur contenu au fichier
préprocessé. Lors de la compilation du fichier ainsi obtenu, le compilateur ne voit
donc aucune différence entre corps de module (le fichier .c) et en-têtes de module.
Il en résulte que l’édition de lien du code suivant génère une erreur :
/* Fichier temperature.h
Module contenant des définitions de fonctions de gestion de la
© Dunod – La photocopie non autorisée est un délit.
température courante */
float temperature_courante;
/* Variable globale
température courante en °C */
float temperature_K();
/* retourne la température courante en °K */
float temperature_F();
/* retourne la température courante en °F */
void mettre_a_jour_temperature();
/* Met à jour la température courante en °C */
271
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
/* Fichier temperature.c
Corps de module contenant l’implémentation des fonctions de gestion de
la température courante */
#include "temperature.h"
/* implementation des fonctions déclarées dans
l’en-tête de module */
float temperature_K()
{
...
}
...
float temperature_K();
/* retourne la température courante en °K */
float temperature_F();
/* retourne la température courante en °F */
void mettre_a_jour_temperature();
/* Met à jour la temperature courante en °C */
#endif
/* Fin du fichier d’en-tête */
272
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
/* Fichier temperature.c
Corps de module contenant l’implémentation des fonctions de gestion de
la température courante */
#include "temperature.h"
/* implementation des fonctions déclarées dans
l’en-tête de module */
float temperature_courante;
/* déclaration effective de la variable globale */
float temperature_K()
{
...
}
...
M Langage Ada
Contrairement aux compilateurs C, les compilateurs Ada différencient corps de
module et en-tête de module. Un en-tête de module, ou spécification de module, est
un fichier d’extension .ads (pour Ada specification). Un corps de module est implé-
menté dans un fichier d’extension .adb (pour Ada body). Un module Ada est appelé
paquetage ou package. Il y a trois sortes d’unités de compilation en langage Ada :
les spécifications de paquetages, les corps de paquetage et les procédures ou fonc-
tions pouvant se trouver directement dans une unité de compilation (extension .adb).
Typiquement dans un programme modulaire, on trouve un certain nombre de
paquetages et une unité de compilation contenant une procédure : le programme
principal.
La figure 6.20 présente une hiérarchie de programme modulaire en langage Ada.
Objet
Test_pgcd.adb Test_pgcd.o
compilation
Corps de module
(programme principal)
Modules précompilés
Ada.Integer_Text_io dépend de
Ada.Text_io produit
Figure 6.20 – Hiérarchie lors de l’élaboration d’un programme écrit en langage Ada.
273
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Sur cette figure, nous distinguons un module pgcd (paquetage pgcd) décomposé
en un corps (fichier pgcd.adb) et une spécification (fichier pgcd.ads) dont le contenu
serait le suivant :
package Pgcd is
function Calcul_Pgcd (I : Integer; J : Integer) return Integer;
-- Entrée: I,J deux entiers
-- Retourne: le plus grand commun diviseur de I et J
-- Convention: pgcd(0,J) = J, pgcd(I,0) = I
end Pgcd;
On peut noter l’emploi du mot clé package permettant de définir une unité de
compilation de type spécification de module.
Le code source du corps de paquetage, défini par les mots clés package body, est
donné ci-après :
package body Pgcd is
-- Noter que contrairement à C, il est inutile de préciser
-- que le corps de paquetage utilise la spécification: cela est
-- implicite
function Calcul_Pgcd (I : Integer; J : Integer) return Integer is
-- Entrée: I,J deux entiers
-- Retourne: le plus grand commun diviseur de I et J
-- Convention: pgcd(0,J) = J, pgcd(I,0) = I
R,A,B : Integer;
begin
A:=abs(I); -- Travail en valeur absolue
B:=abs(J);
if B>A then
return Calcul_Pgcd(B,A);
else
R := A mod B; -- Reste de la division entière
if R=0 then
return B;
else
return Calcul_Pgcd(B,R);
end if;
end if;
end; -- Fin de la fonction Calcul_Pgcd
end Pgcd; -- Fin du paquetage
274
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
L’emploi des mots clés with et use, placés généralement en début d’unité de com-
pilation, permet de faire référence aux spécifications de paquetage concernées.
L’emploi de la clause with M donne à une unité de compilation la visibilité des
types, variables, sous-programmes, etc. définis dans la spécification du module M.
Cependant, l’emploi de ces éléments doit alors être préfixé par le nom du module.
Ainsi, pour utiliser la fonction F définie dans le module M, il faut écrire son nom
complet, soit M.F. On peut utiliser la clause use M, qui permet d’obtenir une visi-
bilité directe, ce qui permet d’appeler la fonction F sans préfixer son nom par M.
Ada définit en fait des espaces de noms liés aux modules. Ainsi, toute ambiguïté
peut être levée si une unité de compilation utilise plusieurs modules définissant
des éléments ayant le même nom.
Notons enfin que dans tous les cas, il est fortement recommandé d’utiliser un nom
de fichier identique au nom du paquetage ou du sous-programme contenu (i.e. si un
paquetage s’appelle temperature, alors les fichiers l’implémentant s’appellent tempe-
rature.ads et temperature.adb).
De nombreux concepts permettant de gérer finement la visibilité des types et sous-
programmes contenus dans les paquetages, la généricité, les modules imbriqués, etc.,
ne sont pas explicités ici.
M Langage LabVIEW
Le langage LabVIEW permet une modularité par sous-programme (vi) : en effet,
lorsqu’un sous-vi est créé, il peut être intégré dans un autre vi directement par
nom de fichier (par glisser/déposer du fichier sur le diagramme du vi par exemple).
Généralement, on organise les vi par types dans des dossiers du système de fichiers,
en les regroupant par thème.
M Langage C
La gestion des erreurs en langage C est relativement basique : une partie des fonctions
susceptibles de renvoyer une erreur (accès à un fichier, au réseau, conversions chaîne
de caractères/nombre, etc.) renvoient un entier représentant le statut d’erreur.
Généralement, une valeur de retour nulle est renvoyée s’il n’y a pas eu d’erreur, et -1
ou un code d’erreur est renvoyé en cas d’erreur. Dans le cas où le code d’erreur lui-
même n’est pas renvoyé par la fonction, celle-ci modifie une variable globale, nommée
© Dunod – La photocopie non autorisée est un délit.
275
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
#include <stdio.h>
/* Inclusion de la librairie système d’entrées/sorties standard
Noter l’emploi de <nomfichier> au lieu de "nomfichier"*/
#include <errno.h>
int main () {
FILE *f; /* f est un descripteur de fichier, destiné à pointer
sur une structure représentant un fichier */
int retval;/* variable utilisé lorsque l’on veut conserver une
valeur de retour */
if (!(f=fopen("monfichier","r"))) {/* Ouverture d’un fichier en
lecture. */
/* Si fopen renvoie null */
/* Noter l’emploi des doubles parenthèses :
en langage C, l’affectation renvoie une valeur, ainsi, l’expression
(a=b), en plus d’affecter b à a, renvoie la valeur de b. Cette valeur
peut alors être testée : est-elle nulle ? */
printf("Erreur %d lors de l’ouverture du fichier",errno);
/* affichage du numéro d’erreur */
return(errno); /* Terminaison du programme en renvoyant le
code d’erreur */
}
/* Traitements divers */
if ((retval=fclose(f))) { /* Fermeture du fichier. */
/* Si fclose renvoie une valeur non nulle, c’est un code d’erreur */
printf("Erreur %d lors de l’ouverture du fichier",retval);
/* affichage du numéro d’erreur */
return(retval); /* Terminaison du programme en renvoyant
le code d’erreur */
}
return 0; /* Normalement, un programme se terminant sans erreur
devrait renvoyer la valeur 0 */
}
Cet exemple met en évidence l’un des écueils liés à l’utilisation du C : le traitement
des erreurs rend le code difficile à lire.
Notons enfin que le mécanisme utilisant errno n’est pas compatible avec le multi-
tâche : en effet, rien n’empêche une autre tâche de modifier le contenu de la variable
errno entre le moment où l’erreur survient dans une tâche et le moment où celle-ci
lit la valeur du code d’erreur. Par conséquent, certains exécutifs, comme VxWorks®
par exemple, définissent une variable errno par tâche.
M Langage Ada
Ada gère les erreurs suivant un mécanisme d’exception. Le concept d’exception
est proche du concept d’interruption logicielle présenté au chapitre 5. Chaque
bloc d’instructions peut être muni d’une partie « traite exception ». Si une erreur a
lieu, une exception correspondant au type d’erreur est levée. L’exécution du bloc en
cours est alors interrompue. Si le bloc contient un traitement pour cette exception,
alors ce traitement est exécuté, le bloc se termine, et le programme continue au niveau
du bloc appelant ou englobant. Si le bloc ne contient pas de traitement pour cette
exception, alors l’exception est propagée au bloc appelant ou englobant. Si une excep-
tion n’est pas traitée, elle arrive au bloc de plus haut niveau (programme principal,
ou bien tâche) et ce bloc arrête son exécution. Si c’est dans le programme principal
276
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
lui-même que l’exception est propagée, alors le programme s’arrête (un message
d’erreur est alors affiché).
Ce mécanisme permet de gérer les erreurs sans nuire à la lisibilité d’un programme,
contrairement à la gestion des erreurs en langage C. Ainsi, l’exemple traité en langage
C au paragraphe 6.1.2, p. 276 est repris ci-après en langage Ada :
with Ada.Text_Io;
use Ada.Text_Io;
-- Paquetage standard permettant les entrées/sorties
procedure Test_Fichier is
F: File_Type;
-- Descripteur de fichier
begin
Open(F,In_File,"monfichier");
-- Ouverture d’un fichier en lecture
-- Actions diverses sur le fichier
Close(F);
-- Fermeture du fichier
exception
when Name_Error => Put_Line("Erreur de nom de fichier");
-- Traitement spécifique de l’exception Name_Error
when others => Put_Line("Erreur sur le fichier");
-- Traitement pour toute exception
end;
M Langage LabVIEW
La figure 6.21 présente un exemple de gestion d’erreur en langage LabVIEW.
Descripteur de fichier
© Dunod – La photocopie non autorisée est un délit.
Cluster d’erreur
Un vi pouvant lever une erreur possède une sortie de type cluster d’erreur (figure 6.22)
généralement nommée error out. La plupart des vi susceptibles de lever des erreurs
possèdent une entrée et une sortie d’erreur. En cas d’erreur dans un vi, sa sortie
277
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
d’erreur contient les informations d’erreur. Un vi ayant une erreur sur son entrée
d’erreur ne fait rien, et se contente de relayer l’erreur sur sa propre sortie d’erreur.
Ainsi, si une chaîne de traitement voit se lever une erreur, les vi s’exécutent sans
rien faire, si ce n’est relayer l’erreur au reste de la chaîne. Généralement, on traite
donc l’erreur a posteriori, en vérifiant l’état de la dernière sortie d’erreur d’une chaîne
de traitement.
Sur la figure 6.21, le traitement d’erreur consiste à afficher une boîte de dialogue
décrivant celle-ci. Notons que depuis la version 7 de LabVIEW, une erreur non traitée
entraîne l’affichage d’une boîte de dialogue de description de l’erreur (ce mode de
fonctionnement peut être inhibé afin de garantir une compatibilité ascendante au
niveau des versions). Ce type de traitement se rapproche alors de l’exception.
M Introduction
Sur les microcontrôleurs et parfois sur les microprocesseurs, il est nécessaire de
programmer au niveau des registres en particulier, à cause de l’absence de pilotes de
périphérique pour certains systèmes d’exploitation ou exécutifs. La programmation
bas niveau est généralement effectuée en langage C, bien qu’Ada le permette (§ 6.1.2,
p. 282). LabVIEW, quant à lui, ne propose que très peu d’outils de très bas niveau,
mais il possède un grand nombre de pilotes de périphériques. Si un périphérique
n’est pas supporté par LabVIEW, un pilote peut être écrit en langage C et facile-
ment interfacé avec LabVIEW.
Dans cette section, nous considérons l’exemple suivant : la carte National Instruments
PCI-DIO-24/PnP est une carte d’acquisition très simple au format PCI, avec 3 ports
de 8 entrées/sorties numériques, capable de déclencher des interruptions. Supposons
qu’il n’existe pas de pilote de périphérique pour l’exécutif utilisé avec cette carte. Il
est alors nécessaire de programmer directement la carte au niveau registres.
Les registres permettant à l’unité centrale de communiquer avec un périphérique
d’entrées/sorties sont généralement liés à une adresse, dite d’entrée/sortie, en mémoire.
278
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
7 6 5 4 3 2 1 0
Il est généralement possible de configurer l’adresse de base liée aux registres d’un
dispositif d’entrées/sorties : ainsi, par exemple, en fonction de la configuration maté-
rielle, les adresses des registres de la carte pourront se trouver en 0x210-0x213.
Dans ce cas, le premier registre, dont les 8 bits correspondent au port A de la carte,
se trouve en 0x210, le registre correspondant au port B en 0x211, le registre corres-
pondant au port C en 0x212 et le registre de configuration en 0x213.
M Langage C
Dans le premier cas, si les ports doivent être configurés de sorte à ce que le port A
et les lignes 0 à 3 du port C soient en sortie, et le reste en entrée, on peut créer
l’octet de configuration : en binaire b00001010, soit en hexadécimal 0x0A. L’initia-
lisation de la carte consisterait donc à écrire 0x0A à l’adresse 0x213 (par exemple
en exécutant une instruction d’écriture à une adresse d’entrée/sortie comme
OutByte(0x213,0x0A)). Cette solution est simple mais il est nécessaire de relire
plusieurs pages de documentation pour chaque changement de configuration.
La seconde solution, plus élégante, consiste à créer un pilote de périphérique (driver)
simplifié. Pour cela, C permet de définir assez finement un type facilitant les opé-
rations bit à bit sur le registre de configuration :
279
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
#ifndef BYTE
#define BYTE unsigned char /* Octet non signé */
#endif
typedef union { /* Correspond au registre de configuration : on peut y
accéder indifféremment par octet (champ config) ou par bits */
BYTE config; /* Octet complet */
struct { /* Définit chaque champ (sur 1 ou 2 bits en fonction du
champ) */
BYTE DirCL:1;
BYTE DirB: 1;
BYTE ModeB:1;
BYTE DirCH:1;
BYTE DirA:1;
BYTE ModeA:2;
BYTE Flag:1;
} ;
} t_DIO24_config;
280
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
outByte(adresse_base+CONFIG,(*DIO24).config.config); /* Ecriture
de la configuration à l’adresse du port de contrôle */
}
(*DIO24).valeurs[port]=(valeur&masque)|(~masque&(*DIO24).valeurs[port
]); /* Calcul de la nouvelle valeur en fonction de l’ancienne et du
masque */
outByte((*DIO24).base+port,(*DIO24).valeurs[port]); /* Ecriture à
l’adresse d’entrée/sortie correspondant au port */
}
Remarquons la façon dont le masque binaire est géré pour l’écriture d’un octet
(figures 4.9 et 4.10). Une erreur classique est d’utiliser le « non » logique (point
d’exclamation) à la place du complément binaire (tilde). Notons aussi que pour
simplifier, les erreurs (tentative d’écriture sur un port configuré en lecture, etc.) ne
sont pas gérées.
281
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
M Langage Ada
Comme C, Ada permet de gérer très finement les représentations binaires associées
à un type. Par exemple, la définition d’un type octet peut s’effectuer de la façon
suivante :
type Octet is mod 256; -- Intervalle de représentation du type
-- correspondant à un octet
for Octet’Size use 8; -- Taille en bits utilisée pour la représentation
La syntaxe utilisée pour les clauses de représentation, bien que plus flexible que celle
du langage C, n’en demeure pas moins plus lourde.
Afin de représenter un port configuré comme cela est effectué en langage C dans la
section précédente, le type enregistrement suivant peut être défini :
type T_Port_DIO24 is (A, B, C); -- Valeurs des ports, noter que la
-- position de A vaut 0, celle de B 1
-- et celle de C 2
type T_Valeurs_De_Port is array(A..C) of Octet;
type T_Dio_24 is record -- Type utilisé pour représenter la carte
-- DIO24 (en mode simple)
282
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
La lecture et l’écriture des ports utilisent le même principe. Noter que ce sont les
mêmes opérateurs que pour les booléens qui opèrent en mode binaire lorsque les
types manipulés sont de type mod comme le type octet.
procedure Ecrire_Port(Dio24: in out T_Dio_24; Port: T_Port_Dio24;
Valeur, Masque: Octet) is
-- Modifie un port configuré en sortie : utilise la technique du
masque binaire comme vu sur la figure 4.10
-- Nécessite: DIO24 préalablement configuré par Config_DIO24
© Dunod – La photocopie non autorisée est un délit.
283
6 • Programmation 6.1 Programmation
des systèmes multitâches C, Ada et LabVIEW
Étant donné qu’Ada est fortement typé, l’utilisation du pilote de périphérique ainsi
programmé nécessite l’emploi de la fonction de conversion d’entier en adresse, située
dans le paquetage System.Storage_Elements.
Dio24 : T_Dio_24;
begin
Config_Dio24(To_Address(16#210#),Sortie, Entree, Sortie,
Entree,Dio24);
Ecrire_Ligne(Dio24,A,0,True);
Ecrire_Port(Dio24,A,16#8F#, 16#F0#);
end;
Cet exemple montre qu’Ada permet la programmation bas niveau. Il faut cependant
jongler avec les types et les conversions, tout en ayant conscience de la représentation
binaire de ce que l’on manipule.
Dans les faits, la programmation bas niveau est souvent effectuée en langage C.
Il est donc très fréquent qu’un pilote de périphérique soit disponible en langage C,
mais pas en langage Ada. Dans ce cas, plutôt que de réécrire totalement le pilote,
un binding (interface entre deux langages) Ada est créé.
284
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
L’interface multitâche (pthread) définie par la norme POSIX se trouve dans les
amendements 1003.1c (intégrée dans 1003.1 depuis 2001) et 1003.1j. L’apport
principal de l’amendement 1003.1j est l’ajout de la possibilité de spécifier l’utilisa-
tion de différentes horloges (notamment CLOCK_MONOTONIC).
M Tâches
Une tâche (pthread) est représentée par une fonction pouvant posséder au plus un
paramètre de type void *. Ce paramètre peut être utilisé pour passer une donnée
de la taille d’un pointeur (généralement, on peut y passer un entier, ou bien n’importe
quel pointeur).
Chaque tâche se lance dynamiquement grâce à la fonction pthread_create. La
signature de cette fonction est :
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void*), void *arg);
285
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
Enfin, la valeur de retour est 0 si la tâche a bien été créée, un code d’erreur sinon.
Il est possible de créer une hiérarchie de tâches, mais dans la méthode DARTS, que
nous implémentons, toute tâche est fille directe du programme principal. Par consé-
quent, au lancement de la tâche, son masque de signaux est le même que celui du
programme principal. Il pourra ensuite être modifié.
Un exemple très simple de programme multitâche POSIX est donné ci-après :
#include <pthread.h> /* Librairie POSIX threads */
#include <stdio.h> /* Entrées/Sorties (printf) */
void main() {
pthread_t tache1, tache2;/* identificateurs de tâches */
pthread_create(&tache1, 0,f, (void *)1);
/* Création et lancement d’une instance de la tâche f. Observer
l’utilisation du paramètre pour passer un entier. Noter qu’en passant
la valeur nulle au paramètre d’attribut, les attributs par défaut sont
choisis, et notamment, la tâche peut être attachée */
usleep(2000000); /* Attente d’au moins 2 secondes */
pthread_create(&tache2, 0,f, (void *)2); /*Création de la seconde
tâche, implémentée par la même fonction*/
pthread_join(tache1,NULL); /* Le programme principal s’attache à
la tâche 1. Il ne se terminera que lorsque cette tâche se terminera */
}
Cet exemple montre le lancement de deux tâches implémentées par la même fonc-
tion. Il illustre le fonctionnement en mode attaché/détaché de la façon suivante :
seule la tâche 1 est attachée au programme principal. Dès qu’elle se termine, et que
le programme principal a la main, celui-ci arrive sur la dernière instruction et se
termine avant que la tâche 2, créée au moins deux secondes après la tâche 1, n’ait
le temps de se terminer. Le résultat de cette exécution peut être :
Tache 1 : iteration 0
Tache 1 : iteration 1
Tache 2 : iteration 0
Tache 1 : iteration 2
Tache 2 : iteration 1
Tache 1 : iteration 3
Tache 2 : iteration 2
Tache 1 : iteration 4
Tache 2 : iteration 3
286
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
Si l’on souhaite qu’une tâche fonctionne de façon spécifique, il faut lui donner des
attributs spécifiques. Ainsi, dans le code ci-après, la tâche 1 se voit ordonnancée
(si le système le supporte) de façon globale, alors que la tâche 2 l’est de façon locale.
On obtient donc un ordonnancement mixte.
pthread_attr_t attributs;
/* Attribut utilisé lors de la création des tâches */
pthread_attr_init(&attributs);
/*Initialisation des attributs aux valeurs par défaut*/
pthread_attr_setscope(&attributs, PTHREAD_SCOPE_SYSTEM);
/* L’ordonnancement des tâches créées avec cet attribut s’effectue
au niveau global */
pthread_create(&tache1, &attributs,f, (void *)1);
/* La tâche 1 est ordonnancée au niveau global */
usleep(2000000);
pthread_attr_setscope(&attributs, PTHREAD_SCOPE_PROCESS);
/* L’ordonnancement des tâches créées avec cet attribut s’effectue
au niveau local */
pthread_create(&tache2, &attributs,f, (void *)2);
/*Création de la seconde tâche, ordonnancée localement*/
pthread_attr_destroy(&attributs);
pthread_join(tache1,NULL);
/* Le programme principal s’attache à la tâche 1. Il ne poursuivra
son exécution jusqu’à sa terminaison que lorsque cette tâche se
terminera */
Nous pouvons noter que toutes les fonctions manipulant les attributs de tâches
commencent par pthread_attr suivi du nom de l’attribut à modifier. Il est possi-
ble de modifier (respectivement interroger) la plupart des attributs d’une tâche
pendant son exécution avec des fonctions dont le préfixe est pthread_set_ (respecti-
vement pthread_get_).
Il faut souligner que plusieurs compilateurs présents sur des systèmes d’exploitation
généralistes proposent une interface POSIX 1003.1c, mais que celle-ci est malheureu-
sement souvent incomplète (impossibilité d’attacher une tâche, gestion des priorités
et de l’ordonnancement absents). Par conséquent, il convient de tester les valeurs
de retour des fonctions POSIX afin d’y déceler des erreurs (notamment « fonction-
nalité non implémentée »).
M Ordonnancement et priorités
Les attributs de tâches permettent de leur affecter une priorité dès leur lancement.
© Dunod – La photocopie non autorisée est un délit.
Il est aussi possible de modifier la priorité d’une tâche au cours de son exécution
grâce à la fonction pthread_set_schedparam.
#include <pthread.h>
void f(int no) {
int i;
for (i=0;i<5;i++) {
printf("Tache %d : iteration %d\n",no,i);
usleep(1000000);
}
}
void main() {
287
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
schedparam.sched_priority = 30;
pthread_setschedparam(pthread_self(),SCHED_RR,&schedparam); /*
Modifie l’ordonnancement du programme principal, et passe sa priorité
à 30 */
pthread_attr_init(&attributs); /*Initialisation des attributs aux
valeurs par défaut*/
pthread_attr_setinheritsched(&attributs, PTHREAD_EXPLICIT_SCHED);
/* Force la prise en compte des paramètres d’ordonnancement de
l’attribut: sans cela, ils seraient hérités du programme principal */
pthread_attr_setschedpolicy(&attributs, SCHED_RR); /*
Ordonnancement à tourniquets par niveaux de priorités */
for (i=0;i<5;i++) {
schedparam.sched_priority = 2+i; /* Priorité de la prochaine
tâche à créer */
pthread_attr_setschedparam(&attributs, &schedparam); /* La
priorité est placée dans les attributs */
pthread_create(&(taches[i]), &attributs,f, (void *)i);
}
pthread_attr_destroy(&attributs);
for (i=0;i<5;i++) {
pthread_join(taches[i],NULL); /* Attente de terminaison des
tâches */
}
}
M Modules de données
Les modules de données DARTS peuvent être implémentés à l’aide d’un sémaphore
d’exclusion mutuelle (mutex). Ainsi, une partie du code correspondant à la figure 6.24
est donnée ci-après.
MDD1
T1
LIRE
ÉCRIRE
T2
#include <pthread.h>
288
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
void T1() {
/* Tâche 1 lisant le module de données */
t_Temperature_Pression TP;
while (1) { /* Faire toujours */
/* Exclusion mutuelle sur le module de données */
pthread_mutex_lock(&s_MDD1);
/* Lecture du MDD */
TP=MDD1;
pthread_mutex_unlock(&s_MDD1);
/* etc. */
}
}
void T2() {
/* Tâche 2 modifiant le module de données */
float T,P;
while (1) { /* Faire toujours */
/* Lecture des capteurs en dehors de la section critique */
P=Lire_Capteur_Pression();
T=Lire_Capteur_Temperature();
/* Exclusion mutuelle sur le module de données */
pthread_mutex_lock(&s_MDD1);
/* Modification du MDD */
MDD1.Temperature=T;
MDD1.Pression=P;
pthread_mutex_unlock(&s_MDD1);
/* etc. */
}
}
void main() {
pthread_t t1,t2; /* Identificateurs de tâches */
pthread_mutex_init(&s_MDD1,NULL); /*Initialisation du mutex de MDD1
*/
/* Initialisation de MDD1 */
MDD1.Temperature=Lire_Capteur_Temperature();
MDD1.Pression=Lire_Capteur_Pression();
/* Lancement des tâches */
pthread_create(&t1, NULL,T1,NULL);
pthread_create(&t2, NULL,T2,NULL);
/* Attente de terminaison */
© Dunod – La photocopie non autorisée est un délit.
pthread_join(t1,NULL);
pthread_join(t2,NULL);
}
On peut remarquer que le sémaphore est déclaré de façon globale, ce qui le rend
visible aux deux tâches concernées. Notons qu’il est créé et initialisé avant le lance-
ment des tâches, par exemple dans le programme principal.
Il est nécessaire de réduire la durée des sections critiques, par conséquent, sur cet
exemple, l’accès au matériel via les fonctions de lecture de capteurs, très longues, doit
s’effectuer à l’extérieur de la section critique d’accès au module de données.
Si l’on souhaite s’occuper des priorités, afin de pouvoir effectuer une étude d’ordon-
nançabilité, il convient de définir des priorités pour les tâches, de définir une prio-
289
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
rité plafond pour le sémaphore (la plus forte des priorités des tâches l’utilisant), et
de lui affecter le protocole à priorité plafond.
Ainsi, le programme principal devient :
void main() {
pthread_t t1,t2; /* Identificateurs de tâches */
pthread_attr_t task_attr; /* Attributs de tâches */
pthread_mutexattr_t mutex_attr; /* Attributs de mutex */
struct sched_param schedparam; /* Paramètre servant à affecter une
priorité aux tâches */
290
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
291
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
Renvoie: taille_element */
void bal_ecr_delete(bal_ecr bal);
/* Supprime la BaL b
Nécessite: b initialisée par bal_ecr_init
Entraine: b est supprimée */
L’implémentation des primitives manipulant les boîtes aux lettres à écrasement est
donnée ci-après :
bal_ecr bal_ecr_init(const unsigned taille_element) {
bal_ecr bal;
if (!(bal=(bal_ecr)malloc(sizeof(struct s_bal_ecr)))) return
0;/* Allocation de la structure */
if (!((*bal).buf=(char *)malloc(taille_element))) return 0;/*
Allocation du buffer contenant un message */
(*bal).vide=1;/* Initialement la bal est vide */
pthread_mutex_init(&((*bal).mutex),0);/* Création du sémaphore
garantissant l’exclusion mutuelle des accès à la structure */
(*bal).taille_element=taille_element;
pthread_cond_init(&((*bal).pas_vide), 0);/* Variable conditionnelle
qui sera déclenchée lorsqu’un message est ajouté */
return bal;
}
292
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
Notons la façon dont on utilise la variable conditionnelle : une tâche se met en attente
en appelant pthread_cond_wait(&(*bal).pas_vide, &(*bal).mutex) . Cela a
pour effet de libérer le mutex, et d’attendre un signal sur la variable conditionnelle
&(*bal).pas_vide. La tâche déposant un message signale cette variable afin de
réveiller les tâches en attente, qui peuvent alors vérifier si un message est présent.
Lorsqu’au maximum une tâche est en attente, le signal de la variable s’effectue avec
pthread_cond_signal, alors que lorsque plusieurs tâches sont susceptibles d’être
en attente, le signal s’effectue à l’aide de la fonction pthread_cond_broadcast.
T1 T2
B1
Figure 6.25 – Communication par boîte aux lettres de taille 1 avec écrasement.
L’implémentation partielle du schéma DARTS donné sur la figure 6.25 est donc :
#include <pthread.h>
/* Inclusion du module de boîtes aux lettres */
#include "BaLs.h"
void T1() {
/* Tâche envoyant des messages dans B1 */
t_Temperature_Pression TP;
while (1) { /* Faire toujours */
/* Lecture des capteurs en dehors de la section critique */
TP.Pression=Lire_Capteur_Pression();
TP.Temperature=Lire_Capteur_Temperature();
bal_ecr_envoyer(B1,(char*)&TP); /* Envoi du message */
/* Noter la perte de l’information de type lors du passage dans
la boîte aux lettres */
/* etc. */
© Dunod – La photocopie non autorisée est un délit.
}
}
void T2() {
/* Tâche en attente de messages dans B1 */
t_Temperature_Pression TP;
while (1) { /* Faire toujours */
bal_ecr_recevoir(B1,(char*)&TP); /* Attente du message */
/* Noter la coercion dans le type attendu */
/* etc. */
}
}
void main() {
293
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
T1 T2
B1
Figure 6.26 – Communication par boîte aux lettres de taille 1 sans écrasement.
294
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
295
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
return (*b).taille_element;
}
void bal_delete(bal b) {
pthread_cond_destroy(&(*b).pas_vide);
pthread_cond_destroy(&(*b).pas_plein);
pthread_mutex_lock(&(*b).mutex);
free((*b).buf);
pthread_mutex_unlock(&(*b).mutex);
free(b);
}
L’implémentation de tâches communiquant par boîtes aux lettres est presque iden-
tique à celle présentée pour l’utilisation de boîtes aux lettres avec écrasement. La
seule différence réside dans les noms de fonctions utilisés. Notons qu’il aurait été
possible de surcharger les fonctions, c’est-à-dire de créer des fonctions de même
nom (bal_init, bal_envoyer, bal_recevoir et bal_delete) manipulant des
types de boîtes aux lettres différents. Il en a été décidé autrement afin de faciliter la
lisibilité du code, et parce que l’implémentation suit normalement la conception,
pendant laquelle le type de boîte aux lettres utilisé a été choisi.
T1 T2
B1
T3 T4
B2
M Synchronisation
Généralement, on implémente la synchronisation (figure 6.28) à l’aide de sémaphores
à compte (§ 5.2.2, p. 201). Cependant, nous avons vu au chapitre 6 que les amen-
dements 1003.1c et 1003.1j ne proposent pas cet outil de synchronisation. Les
sémaphores à compte sont définis dans la partie 1003.1b orientée processus.
Cependant, il est possible, à la création d’un sémaphore à compte 1003.1b, de forcer
celui-ci à ne pas être partagé par plusieurs processus.
296
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
T1 T2
S1
#include <pthread.h>
#include <semaphore.h>
void T1() {
/* Tâche déclenchant une synchronisation S1 */
while (1) { /* Faire toujours */
/*etc. */
sem_post(&S1); /* Déclenchement de la synchronisation */
/* etc. */
}
}
void T2() {
/* Tâche en attente sur la synchronisation S1 */
while (1) { /* Faire toujours */
sem_wait(&S1); /* Attente de synchronisation */
/* etc. */
}
}
void main() {
pthread_t t1,t2; /* Identificateurs de tâches */
sem_init(&S1,0,0); /* Sémaphore non partagé par des processus
initialisés à 0 */
/* Lancement des tâches */
pthread_create(&t1, NULL,T1,NULL);
pthread_create(&t2, NULL,T2,NULL);
/* Attente de terminaison */
pthread_join(t1,NULL);
pthread_join(t2,NULL);
}
M Tâches périodiques
Il n’est pas trivial de rendre une tâche périodique en POSIX. Certains systèmes
© Dunod – La photocopie non autorisée est un délit.
proposent des primitives spécifiques non POSIX (suffixe _np au nom de la fonction)
permettant de gérer la périodicité stricte des tâches. Ainsi, RTLinux fournit une fonc-
tion pthread_make_periodic_np permettant de rendre une tâche périodique.
Cependant, la non portabilité de cette fonction est gênante. Il existe aussi des solu-
tions lourdes, basées sur la programmation d’horloges générant des signaux récupérés
par les tâches périodiques. Le problème est que différentes implémentations partielles
de POSIX ne les supportent pas. Une autre solution, non satisfaisante, consisterait
à endormir une tâche pendant la durée de sa période (fonctions nanosleep ou
usleep), mais elle expose la tâche au problème de la dérive des horloges (§ 5.2.4).
La solution retenue ici, fonctionnant sur la plupart des implémentations POSIX,
même partielles, consiste à utiliser la fonction pthread_cond_timedwait, attente
297
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
bornée sur une variable conditionnelle, car cette fonction utilise une date absolue
de timeout. L’idée est de créer une variable conditionnelle jamais signalée dans chaque
tâche périodique : l’attente de la prochaine date d’activation s’effectue par le biais
du timeout de pthread_cond_timedwait.
Ainsi, l’implémentation des tâches de la figure 6.29 est donnée ci-après.
Périodique 1 Périodique 2
#include <pthread.h>
ajouter_microsecondes(&horloge,periode_us);/* Calcul de la
date du prochain reveil */
pthread_mutex_lock(&sReveil);
pthread_cond_timedwait(&Reveil, &sReveil, &horloge);
/* Cette variable n’étant pas signalée, c’est au timeout que
cette instruction se termine */
}
}
298
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
void main() {
pthread_t periodique1,periodique2;
pthread_create(&periodique1, NULL,Periodique, (void*)1000); /*
Lancement de la tâche avec une période d’1 ms */
pthread_create(&periodique2, NULL,Periodique, (void*)1500); /*
Lancement de la tâche avec une période d’1,5 ms */
pthread_join(periodique1,NULL); /* Attente de terminaison */
pthread_join(periodique2,NULL); /* Attente de terminaison */
}
Une solution proche, plus simple, consiste à utiliser les sémaphores de l’amendement
1003.1, qui intègrent aussi une primitive d’attente bornée dans le temps par une
date. L’implémentation de la figure 6.29 deviendrait alors :
#include <pthread.h>
#include <semaphore.h>
ajouter_microsecondes(&horloge,periode_us);/* Calcul de la
date du prochain réveil */
sem_timedwait(&Reveil,&horloge); /* Le sémaphore étant
© Dunod – La photocopie non autorisée est un délit.
void main() {
pthread_t periodique1,periodique2;
pthread_create(&periodique1, NULL,Periodique, (void*)1000); /*
Lancement de la tâche avec une période d’1 ms */
pthread_create(&periodique2, NULL,Periodique, (void*)1500); /*
Lancement de la tâche avec une période d’1,5 ms */
pthread_join(periodique1,NULL); /* Attente de terminaison */
pthread_join(periodique2,NULL); /* Attente de terminaison */
}
299
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
IT
Tâche
matérielle
void Tache_Materielle() {
/* Tâche déclenchée sur interruption */
while (1) {
sem_wait(&declenche_dsr); /* Attente d’interruption */
/* Traitement d’une interruption */
/* etc. */
}
}
void main() {
pthread_t tache_materielle;
sem_init(declenche_dsr,0,0); /* Initialisation du sémaphore de
synchronisation ISR-]-DSR */
/* Appel d’une primitive système branchant l’ISR à l’interruption
traitée */
/* Démasquage de l’interruption traitée */
pthread_create(&tache_materielle, NULL,Tache_Materielle, NULL);
pthread_join(tache_materielle,NULL); /* Attente de terminaison */
}
300
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
m Exemple
/* Définition de constantes */
#define LLS 5.3
/* Valeur sous laquelle le niveau est considéré trop bas */
#define HLS 17.8
/* Valeur au-dessus de laquelle le niveau est considéré trop haut */
#define MS_L1 128
#define MS_L2 180
/* Seuils d’alerte de méthane*/
void Acquerir_Niveau_Methane() {
/* Tâche périodique d’acquisition */
struct timespec horloge; /* Date du prochain réveil */
sem_t Reveil; /* Sémaphore utilisé pour le réveil périodique */
float niveau_methane;
sem_init(&Reveil,0,0);
clock_gettime(CLOCK_REALTIME, &horloge); /* heure courante de
l’horloge au démarrage de la tâche */
while (1) {
301
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
/* Périodicité */
ajouter_microsecondes(&horloge,500000);/* Calcul de la
prochaine date de réveil (période de 500 ms) */
sem_timedwait(&Reveil,&horloge); /* Attente de la prochaine
date de réveil */
}
}
void Acquerir_Capteur_Eau() {
/* Tâche périodique d’acquisition */
struct timespec horloge; /* Date du prochain réveil */
sem_t Reveil; /* Sémaphore utilisé pour le réveil périodique */
float niveau_eau;
sem_init(&Reveil,0,0);
clock_gettime(CLOCK_REALTIME, &horloge); /* heure courante de
l’horloge au démarrage de la tâche */
while (1) {
niveau_eau=Lire_Capteur_Eau(); /* Lecture du capteur */
/* Périodicité */
ajouter_microsecondes(&horloge,5000000);/* Calcul de la
prochaine date de réveil (période de 5 s) */
sem_timedwait(&Reveil,&horloge); /* Attente de la prochaine
date de réveil */
}
}
void Afficher_Alarme () {
unsigned char etat_alarme=0; /* Etat courant de l’alarme (0
éteinte, 1 allumée) */
while (1) {
sem_wait(&Evt_Alarme); /* Attente de synchronisation */
etat_alarme=1-etat_alarme;
Piloter_Alarme(etat_alarme); /* Fonction actionnant ou coupant
l’alarme */
}
}
void Commander_Pompe() {
float vitesse;
while(1) {
bal_ecr_recevoir(Vitesse_Pompe,(char *)&vitesse); /* Attente
de message */
Actionner_Pompe(vitesse); /* Fonction de pilotage de la pompe
*/
}
}
302
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
void Controler_Mine() {
float niveau_methane;
float vitesse_pompe;
float niveau_eau;
typedef enum {nominal, pompe, alerte_et_pompe, alerte}
t_etat_controle;
t_etat_controle etat=nominal;
while (1) {
bal_ecr_recevoir(Niveau_Methane,(char *)&niveau_methane); /*
Attente sur boîte aux lettres */
/* Lecture du module de données */
pthread_mutex_lock(&sNiveau_Eau);
niveau_eau=Niveau_Eau;
pthread_mutex_unlock(&sNiveau_Eau);
/* Implémentation du diagramme Etats/Transitions donné sur la
figure 2.33 */
switch (etat) {
case nominal:
if (niveau_methane >= MS_L1) {
/* Seuil d’alerte */
sem_post(&Evt_Alarme); /* Alarme */
etat = alerte;
} else if (niveau_eau >= HLS) {
/* Allumage de la pompe, pour simplifier, nous supposons
que nous la pilotons de façon proportionnelle */
vitesse_pompe = niveau_eau-LLS;
bal_ecr_envoyer(Vitesse_Pompe,(char*)&vitesse_pompe);
etat = pompe;
}
break;
case pompe:
if (niveau_methane >= MS_L1) {
/* Seuil d’alerte */
sem_post(&Evt_Alarme); /* Alarme */
etat = alerte_et_pompe;
} else if (niveau_eau <= LLS) {
vitesse_pompe = 0;
bal_ecr_envoyer(Vitesse_Pompe,(char*)&vitesse_pompe);
etat = nominal;
} else {
/* Pilotage proportionnel */
vitesse_pompe = niveau_eau-LLS;
bal_ecr_envoyer(Vitesse_Pompe,(char*)&vitesse_pompe);
}
break;
© Dunod – La photocopie non autorisée est un délit.
case alerte:
if (niveau_methane < MS_L1) {
/* Seuil d’alerte */
sem_post(&Evt_Alarme); /* Extinction de l’alarme */
etat = nominal;
} else if (niveau_methane < MS_L2 && niveau_eau > HLS) {
/* Allumage de la pompe, pour simplifier, nous supposons
que nous la pilotons de façon proportionnelle */
vitesse_pompe = niveau_eau-LLS;
bal_ecr_envoyer(Vitesse_Pompe,(char*)&vitesse_pompe);
etat = alerte_et_pompe;
}
break;
case alerte_et_pompe:
303
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
void main() {
pthread_t acquerir_niveau_methane, afficher_alarme,
acquerir_capteur_eau, commander_pompe, controler_mine;
/* Initialisation du matériel (capteurs, actionneurs) */
/* etc. */
pthread_create(&acquerir_niveau_methane,NULL,Acquerir_Niveau_Methane,
NULL);
pthread_create(&acquerir_capteur_eau,NULL,Acquerir_Capteur_Eau,NULL);
pthread_create(&afficher_alarme,NULL,Afficher_Alarme,NULL);
pthread_create(&commander_pompe,NULL,Commander_Pompe,NULL);
pthread_create(&controler_mine,NULL,Controler_Mine,NULL);
pthread_join(acquerir_niveau_methane,NULL);
pthread_join(acquerir_capteur_eau,NULL);
pthread_join(afficher_alarme,NULL);
pthread_join(commander_pompe,NULL);
pthread_join(controler_mine,NULL);
}
304
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
VxWorks® est l’exécutif temps réel le plus répandu sur le marché. Dans ses versions
5.x, il implémente une grande partie des interfaces POSIX 1003.1b et 1003.1c. Il
est donc possible de programmer un système VxWorks® comme un système POSIX.
Cependant, afin de donner un autre exemple de programmation d’applications de
contrôle-commande en langage C, nous présentons ici la programmation basée
sur l’interface propriétaire de VxWorks®.
M Tâches
Une tâche (task) est représentée par une fonction pouvant posséder au plus dix
paramètres de type int. Un paramètre peut être utilisé pour passer une donnée de
la taille d’un entier, on peut donc y passer typiquement un entier, ou un pointeur.
Chaque tâche se lance dynamiquement grâce à la fonction taskSpawn ou bien à la
combinaison des deux fonctions taskInit et taskResume. La signature de cette
fonction est :
int taskSpawn(char *nom, int priority, int options, int
stackSize,FUNCPTR entryPoint,int arg1,…,int arg10);
La valeur de retour de cette fonction est l’identificateur de la tâche créée, nom est le
nom alphanumérique de la tâche, priority est sa priorité (attention, par rapport à
POSIX, l’ordre est inversé, et 0 est la priorité la plus forte), stackSize est la taille
de sa pile, entryPoint est la fonction implémentant la tâche, et les paramètres arg1 à
arg10 sont les paramètres passés à cette fonction.
Enfin, le paramètre options permet de spécifier différentes particularités au sys-
tème, comme l’utilisation ou non des calculs à virgule flottante afin de déterminer
quels registres doivent être sauvegardés lors d’un changement de contexte.
Les tâches s’exécutent en mode détaché (§ 5.4.1, p. 235). Notons qu’il n’y a pas de
hiérarchie de tâches.
Un exemple très simple de programme multitâche VxWorks® est donné ci-après :
#include <taskLib.h> /* Librairie tâches */
#include <stdio.h> /* Entrées/Sorties (printf) */
© Dunod – La photocopie non autorisée est un délit.
void LancerTaches() {
taskSpawn("tache1",90,0,20000,f,1,0,0,0,0,0,0,0,0,0); /* Lancement de
la tâche 1, de priorité 90, avec 20000 octets de pile, implémentée par
la fonction f, avec le 1er paramètre valant 1 */
305
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
taskSpawn("tache2",80,0,10000,f,2,0,0,0,0,0,0,0,0,0); /* Lancement de
la tâche 2, de priorité 80, avec 10000 octets de pile, implémentée par
la fonction f, avec le 1er paramètre valant 1 */
}
Nous pouvons noter que le système se base sur les ticks pour spécifier le temps.
D’autre part, l’une des particularités des exécutifs embarqués est que le programmeur
ne définit pas de programme principal : lorsque le code est compilé et chargé sur la
cible, la fonction LancerTaches peut être lancée à l’aide d’un shell.
M Modules de données
Les modules de données DARTS peuvent être implémentés à l’aide d’un sémaphore
d’exclusion mutuelle. Ainsi, une partie du code correspondant à la figure 6.24 est
donnée ci-après.
#include <taskLib.h>
#include <semLib.h> /* Module définissant les sémaphores d’exclusion
mutuelle */
void T1() {
/* Tâche 1 lisant le module de données */
t_Temperature_Pression TP;
while (1) { /* Faire toujours */
/* Exclusion mutuelle sur le module de données */
semTake(s_MDD1,WAIT_FOREVER);
/* Lecture du MDD */
TP=MDD1;
semGive(s_MDD1);
/* etc. */
}
}
void T2() {
/* Tâche 2 modifiant le module de données */
float T,P;
while (1) { /* Faire toujours */
/* Lecture des capteurs en dehors de la section critique */
P=Lire_Capteur_Pression();
T=Lire_Capteur_Temperature();
/* Exclusion mutuelle sur le module de données */
semTake(s_MDD1,WAIT_FOREVER);
/* Modification du MDD */
MDD1.Temperature=T;
MDD1.Pression=P;
semGive(s_MDD1);
/* etc. */
}
}
306
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
void LancerTaches() {
s_MDD1=semMCreate(SEM_Q_PRIORITY|SEM_INVERSION_SAFE); /
*Initialisation du mutex de MDD1 : La file d’attente des tâches est
gérée par niveaux de priorité, et le protocole à priorité héritée est
utilisé */
/* Initialisation de MDD1 */
MDD1.Temperature=Lire_Capteur_Temperature();
MDD1.Pression=Lire_Capteur_Pression();
/* Lancement des tâches */
taskSpawn("T1",90,0,20000,T1,0,0,0,0,0,0,0,0,0,0);
taskSpawn("T2",100,0,20000,T2,0,0,0,0,0,0,0,0,0,0);
}
On peut remarquer que le sémaphore est déclaré de façon globale, ce qui le rend
visible aux deux tâches concernées. Notons qu’il est créé et initialisé avant le lance-
ment des tâches, par exemple dans le programme de lancement des tâches.
Il est nécessaire de réduire la durée des sections critiques, par conséquent, sur cet
exemple, l’accès au matériel via les fonctions de lecture de capteurs, très longues,
doit s’effectuer à l’extérieur de la section critique d’accès au module de données.
Rappelons que VxWorks® ne propose pas le protocole à priorité plafond.
void T1() {
/* Tâche 1 en attente de messages sur la boîte aux lettres */
t_Temperature_Pression TP;
while (1) { /* Faire toujours */
msgQReceive(B1,(char*)&TP,sizeof(TP),WAIT_FOREVER);
© Dunod – La photocopie non autorisée est un délit.
/* etc. */
}
}
void T2() {
/* Tâche 2 envoyant des messages */
t_Temperature_Pression TP;
while (1) { /* Faire toujours */
/* Lecture des capteurs en dehors de la section critique*/
TP.Pression=Lire_Capteur_Pression();TP.Temperatu
re=Lire_Capteur_Temperature();
msgQSend(B1, (char*)&TP, sizeof(TP), WAIT_FOREVER,
MSG_PRI_NORMAL); /* Envoi du message en priorité normale, si la boîte
est pleine, cette instruction est bloquante */
307
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
/* etc. */
}
}
void LancerTaches() {
B1=msgQCreate(1,sizeof(t_Temperature_Pression),MSG_Q_PRIORITY);
/*Initialisation de la boîte aux lettres B1 : tampon de 1 message. Les
tâches en attente sont gérées par niveau priorité */
/* Lancement des tâches */
taskSpawn("T1",90,0,20000,T1,0,0,0,0,0,0,0,0,0,0);
taskSpawn("T2",100,0,20000,T2,0,0,0,0,0,0,0,0,0,0);
}
M Synchronisation
Comme en POSIX, la synchronisation peut être implémentée à l’aide d’un sémaphore
à compte. Ainsi, l’implémentation du diagramme DARTS donné sur la figure 6.28
est donnée ci-après.
#include <taskLib.h>
#include <semLib.h> /* Module définissant les sémaphores à compte */
void T1() {
/* Tâche déclenchant la synchronisation */
while (1) { /* Faire toujours */
/* etc. */
semGive(S1); /* Déclenchement de la synchronisation */
/* etc. */
}
}
void T2() {
/* Tâche en attente de synchronisation */
while (1) { /* Faire toujours */
semTake(S1,WAIT_FOREVER); /* Attente de synchronisation
*/
/* etc. */
}
}
void LancerTaches() {
S1=semCCreate(SEM_Q_FIFO,0); /*Initialisation du sémaphore de
synchronisation à 0, la file d’attente des tâches est FIFO pure (il
n’est pas nécessaire de gérer les priorités puisqu’il ne doit y avoir
qu’une tâche en attente de cette synchronisation) */
/* Lancement des tâches */
taskSpawn("T1",90,0,20000,T1,0,0,0,0,0,0,0,0,0,0);
taskSpawn("T2",100,0,20000,T2,0,0,0,0,0,0,0,0,0,0);
}
308
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
M Tâches périodiques
Comme nous l’avons vu précédemment, la base de temps de VxWorks est le tick.
Le nombre de ticks par seconde est donné par la fonction sysClkRateGet, et peut
être modifié à l’aide de sysClkRateSet. Un intervalle de temps est donc forcément
multiple de 1/sysClkRateGet(). On pourrait donc être tenté d’augmenter la fré-
quence des ticks afin d’obtenir une meilleure granularité temporelle. Cependant,
étant donné que VxWorks® est un exécutif dirigé par le temps, le système prend la
main à chaque tick. Par conséquent, augmenter la fréquence des ticks augmente
l’overhead (surcoût d’utilisation processeur due à l’exécutif ). Il est donc conseillé de
ne pas descendre l’intervalle entre deux ticks en dessous d’une milliseconde, bien qu’il
soit possible de diminuer cet intervalle aux alentours de quelques dizaines de micro-
secondes (ce qui entraîne naturellement un overhead important).
L’inconvénient majeur de l’interface de programmation native de VxWorks® est qu’il
n’existe aucune primitive permettant d’attendre jusqu’à une certaine date (en ticks).
Par conséquent, le seul moyen purement logiciel de déclencher périodiquement une
tâche consiste à attendre un certain nombre de ticks, ce qui entraîne une dérive des
horloges (§ 5.3.1, p. 216). Ainsi, l’implémentation des tâches de la figure 6.29 est
donnée ci-après.
#include <taskLib.h>
void LancerTaches() {
/* Lancement des tâches */
taskSpawn("Periodique1",90,0,20000,Periodique,sysClkRateGet()/
10,0,0,0,0,0,0,0,0,0); /* Periode de 100 ms */
taskSpawn("Periodique2",100,0,20000,Periodique1,sysClkRateGet()/
18,0,0,0,0,0,0,0,0,0); /* Periode de 55.555… ms */
}
Notons que la période est arrondie au tick inférieur, ainsi par exemple, si la fréquence
© Dunod – La photocopie non autorisée est un délit.
des ticks est de 300 ticks par seconde (un tick dure 3,33… millisecondes), la période
de la tâche Periodique1 est de 10 ticks, soit 33,33 millisecondes, et celle de
Periodique2 est de 18 ticks, soit 60 millisecondes.
309
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
rielle présente sur la figure 6.30 s’implémente simplement par synchronisation sur
sémaphore.
#include <semLib.h>
#include <taskLib.h>
void TacheMaterielle () {
/* Tâche de traitement de l’interruption */
while (1) {
semTake(declencheT1,WAIT_FOREVER); /* Attente de
synchronisation */
…
}
}
void LancerTaches() {
declencheT1 = semCCreate(SEM_Q_FIFO,0); /* Initialisation du
sémaphore de synchronisation */
intConnect(INUM_TO_IVEC(7),traiteIntr,0); /* La fonction
traiteIntr est appelée sur l’interruption IRQ7 */
taskSpawn("TacheMaterielle",90,0,20000,TacheMaterielle,0,0,0,0,
0,0,0,0,0,0);
}
m Exemple
La mise en œuvre des différents outils présentés pour l’implémentation d’éléments
DARTS sur VxWorks® avec la librairie native est faite sur l’exemple de la « gestion
de la sécurité d’une mine » dont le diagramme DARTS est donné sur la figure 3.27.
Afin d’en faciliter la lecture, le système de commande est présenté à plat, sans uti-
lisation de modules spécifiques.
#include <taskLib.h>
#include <msgQLib.h>
#include <semLib.h>
#include "procede.h" /* Fonctions d’accès aux capteurs et actionneurs
*/
/* Définition de constantes */
#define LLS 5.3
/* Valeur sous laquelle le niveau est considéré trop bas */
#define HLS 17.8
/* Valeur au-dessus de laquelle le niveau est considéré trop haut */
#define MS_L1 128
#define MS_L2 180
/* Seuils d’alerte de méthane*/
310
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
void Acquerir_Niveau_Methane() {
/* Tâche périodique d’acquisition */
float niveau_methane;
while (1) {
niveau_methane=Lire_Capteur_Methane(); /* Lecture du capteur
*/
msgQSend(Niveau_Methane, (char*)&niveau_methane,
sizeof(niveau_methane), WAIT_FOREVER, MSG_PRI_NORMAL);/* Envoi du
message */
taskDelay(sysClkRateGet()/2); /* Periode de 500 ms avec
dérive des horloges */
}
}
void Acquerir_Capteur_Eau() {
/* Tâche périodique d’acquisition */
float niveau_eau;
while (1) {
niveau_eau=Lire_Capteur_Eau(); /* Lecture du capteur */
void Afficher_Alarme () {
unsigned char etat_alarme=0; /* Etat courant de l’alarme (0
éteinte, 1 allumée) */
while (1) {
semTake(Evt_Alarme); /* Attente de synchronisation */
etat_alarme=1-etat_alarme;
Piloter_Alarme(etat_alarme); /* Fonction actionnant ou coupant
l’alarme */
}
© Dunod – La photocopie non autorisée est un délit.
void Commander_Pompe() {
float vitesse;
while(1) {
msgQReceive(Vitesse_Pompe,(char*)&vitesse,sizeof(vitesse),WAIT_FOREVER
); /* Attente de message */
Actionner_Pompe(vitesse); /* Fonction de pilotage de la pompe */
}
}
void Controler_Mine() {
float niveau_methane;
float vitesse_pompe;
311
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
float niveau_eau;
typedef enum {nominal, pompe, alerte_et_pompe, alerte}
t_etat_controle;
t_etat_controle etat=nominal;
while (1) {
msgQReceive(Niveau_Methane,(char
*)&niveau_methane,sizeof(niveau_methane),WAIT_FOREVER); /* Attente
sur boîte aux lettres */
/* Lecture du module de données */
semTake(sNiveau_Eau);
niveau_eau=Niveau_Eau;
semGive(sNiveau_Eau);
/* Implémentation du diagramme Etats/Transitions donné sur la
figure 2.33 */
switch (etat) {
case nominal:
if (niveau_methane >= MS_L1) {
/* Seuil d’alerte */
semGive(Evt_Alarme); /* Alarme */
etat = alerte;
} else if (niveau_eau >= HLS) {
/* Allumage de la pompe, pour simplifier, nous supposons
que nous la pilotons de façon proportionnelle */
vitesse_pompe = niveau_eau-LLS;
msgQSend(Vitesse_Pompe,(char*)&vitesse_pompe,sizeof(vitesse_pompe),WA
IT_FOREVER, MSG_PRI_NORMAL);
etat = pompe;
}
break;
case pompe:
if (niveau_methane >= MS_L1) {
/* Seuil d’alerte */
semGive(Evt_Alarme); /* Alarme */
etat = alerte_et_pompe;
} else if (niveau_eau <= LLS) {
vitesse_pompe = 0;
msgQSend(Vitesse_Pompe,(char*)&vitesse_pompe,
sizeof(vitesse_pompe), WAIT_FOREVER, MSG_PRI_NORMAL);
etat = nominal;
} else {
/* Pilotage proportionnel de la pompe */
vitesse_pompe = niveau_eau-LLS;
msgQSend(Vitesse_Pompe,(char*)&vitesse_pompe,
sizeof(vitesse_pompe), WAIT_FOREVER, MSG_PRI_NORMAL);
}
break;
case alerte:
if (niveau_methane < MS_L1) {
/* Seuil d’alerte */
semGive(Evt_Alarme); /* Extinction de l’alarme */
etat = nominal;
} else if (niveau_methane < MS_L2 && niveau_eau > HLS) {
/* Allumage de la pompe, pour simplifier, nous supposons
que nous la pilotons de façon proportionnelle */
vitesse_pompe = niveau_eau-LLS;
msgQSend(Vitesse_Pompe,(char*)&vitesse_pompe,
sizeof(vitesse_pompe),WAIT_FOREVER, MSG_PRI_NORMAL);
etat = alerte_et_pompe;
312
6 • Programmation 6.2 Programmation multitâche
des systèmes multitâches en langage C
}
break;
case alerte_et_pompe:
if (niveau_methane >= MS_L2 || niveau_eau <= LLS) {
/* Alerte MS_L2 ou niveau bas => extinction de la pompe */
vitesse_pompe = 0;
msgQSend(Vitesse_Pompe,(char*)&vitesse_pompe,
sizeof(vitesse_pompe),WAIT_FOREVER, MSG_PRI_NORMAL);
etat = alerte;
} else if (niveau_methane < MS_L1) {
/* Niveau de méthane sous le seuil d’alerte */
semGive(Evt_Alarme); /* Extinction de l’alarme */
etat = pompe;
} else {
/* Pilotage proportionnel */
vitesse_pompe = niveau_eau-LLS;
msgQSend(Vitesse_Pompe,(char*)&vitesse_pompe,
sizeof(vitesse_pompe),WAIT_FOREVER, MSG_PRI_NORMAL);
}
break;
};
}
}
void LancerTaches() {
/* Initialisation matérielle du système (capteurs, actionneurs) */
/* etc. */
sNiveau_Eau=semMCreate(SEM_Q_PRIORITY|SEM_INVERSION_SAFE|SEM_DELETE_S
AFE); /* Constater l’ajout de SEM_DELETE_SAFE */
/* Initialisation des outils de communication et synchronisation
*/
© Dunod – La photocopie non autorisée est un délit.
Niveau_Methane=msgQCreate(1,sizeof(float),MSG_Q_FIFO); /* Noter
que la gestion des files d’attente d’envoie et de réception est FIFO,
car une seule tâche émet, une seule tâche lit */
Vitesse_Pompe=msgQCreate(1,sizeof(float), MSG_Q_FIFO);
Evt_Alarme=semCCreate(SEM_Q_FIFO,0); /* Sémaphore de
synchronisation */
acquerir_niveau_methane=taskSpawn("AcqMethane",70,0,20000,
Acquerir_Niveau_Methane,0,0,0,0,0,0,0,0,0,0);
acquerir_capteur_eau=taskSpawn("AcqEau",71,0,20000,
Acquerir_Capteur_Eau,0,0,0,0,0,0,0,0,0,0);
afficher_alarme=taskSpawn("Alarme",40,0,20000,
Afficher_Alarme,0,0,0,0,0,0,0,0,0,0);
commander_pompe=taskSpawn("CmdPompe",45,0,20000,
Commander_Pompe,0,0,0,0,0,0,0,0,0,0);
313
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
controler_mine=taskSpawn("CtrlMine",48,0,20000,
Controler_Mine,0,0,0,0,0,0,0,0,0,0);
}
void StopperTaches() {
/* Stoppe toutes les tâches et détruit les outils de communication/
synchronisation, ce qui permet pendant la phase de mise au point, sans
redémarrer la cible, de stopper les tâches, modifier leur code source,
le recharger sur la cible, et de lancer à nouveau les tâches.
L’éventualité d’un arrêt des tâches explique le fait que les
sémaphores d’exclusion mutuelle soient créés avec l’option
SEM_DELETE_SAFE */
taskDelete(acquerir_niveau_methane);
taskDelete(acquerir_capteur_eau);
taskDelete(afficher_alarme);
taskDelete(commander_pompe);
taskDelete(controler_mine);
semDelete(sNiveau_Eau);
semDelete(Evt_Alarme);
msgQDelete(Niveau_Methane);
msgQDelete(Vitesse_Pompe);
}
314
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
pragma Priority(Default_Priority);
end T_Matache;
task body T_Matache is
begin
for I in 1..10 loop
Put_Line("Iteration de tache");
end loop;
end;
Matache : T_Matache; -- Instanciation
begin -- La tâche instanciée est lancée
null;
end; -- ne se termine que lorsque la tâche est terminée
315
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
Le code ci-après est équivalent, et définit une tâche de type anonyme : noter l’absence
du mot clé « type » dans la spécification.
with Ada.Text_Io;use Ada.Text_Io;
with System;use System;
procedure une_tache_anonyme is
task Matache is
-- Noter l’absence du mot clé "type" dans la déclaration
pragma Priority(Default_Priority);
end Matache;
task body Matache is
begin
for I in 1..10 loop
Put_Line("Iteration de tache");
end loop;
end;
-- La tâche Matache a été instanciée en même temps qu’elle a été
déclarée
begin -- La tâche instanciée est lancée
null;
end; -- ne se termine que lorsque la tâche est terminée
Les attributs d’un type tâche, comme sa priorité par exemple, sont placés dans la
spécification du type. On peut passer des paramètres à une tâche lors de son instan-
ciation par la technique des discriminants. Le type d’un discriminant est forcément
un type discret (entier, caractère, type énuméré, etc.) ou un pointeur. Ainsi, le code
ci-après crée un type de tâche ayant un numéro et une priorité passés en paramètre.
Notons qu’il est possible de changer la priorité d’une tâche au cours de son exécution
grâce à la fonction Set_Priority.
with Ada.Text_Io; use Ada.Text_Io;
with System;use System; -- Pour l’utilisation du type Priority
procedure Deux_Taches_Discriminant is
task type T_Matache(Priorite: Integer;Numero:Integer) is
-- Le discriminant permet de passer des paramètres scalaires
lors de l’instanciation
pragma Priority(Priorite); -- Définit la priorité de la tâche
end T_Matache;
task body T_Matache is
begin
for I in 1..10 loop
Put_Line("Iteration "&Integer’Image(I)&" de la tache
"&Integer’Image(Numero));
-- Affichage du numéro d’itération et du numéro de la tâche
-- Noter qu’il provient du discriminant
end loop;
end; -- Fin de la tâche
T1 : T_Matache(Priority’First+10,1);
-- T1 a une priorité 10 niveaux au-dessus du niveau minimal, et a
le numéro 1
T2 : T_Matache(Priority’Last-10,2);
-- T1 a une priorité 10 niveaux au-dessous du niveau maximal, et a
le numéro 2
begin -- Démarrage du programme principal, T1 et T2 sont
automatiquement lancées
316
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
m Communication et synchronisation
Dans sa première version, en 1983, Ada n’intégrait que le rendez-vous (§ 5.2.2,
p. 198). Divers inconvénients apparaissaient à l’utilisation, comme l’obligation de
créer des tâches pour implémenter des moniteurs (qui sont des objets passifs),
l’impossibilité de communiquer de façon asynchrone, et la difficulté d’étudier le
comportement temporel de ces outils de communications. Par conséquent, cet
outil n’est pas utilisé par la suite.
Depuis Ada95, un deuxième outil a été introduit : le moniteur de haut niveau (objet
protégé). De par sa nature, le moniteur Ada permet d’implémenter tout type de syn-
chronisation et de communication, synchrone et asynchrone, comme cela est montré
par la suite.
Comme pour les tâches, il existe un constructeur de types d’objet protégé (pro-
tected type) et il est possible de définir des objets protégés anonymes (protec-
ted).
La spécification d’un objet protégé contient une partie privée, correspondant aux
variables internes au moniteur, et un ensemble de primitives. Il y a trois types de
primitives :
© Dunod – La photocopie non autorisée est un délit.
– les procédures (procedure) sont des primitives non réentrantes par rapport aux
autres primitives de l’objet protégé (i.e. une procédure ne peut pas avoir lieu
tant qu’une autre primitive de l’objet protégé est exécutée). Cependant, elles sont
non bloquantes, c’est-à-dire qu’elles n’intègrent pas la possibilité d’attendre un
événement du moniteur (de faire l’équivalent d’une instruction wait sur le
moniteur, voir § 5.2.2, p. 186). Les procédures peuvent avoir des paramètres en
entrée ou en sortie ;
– les procédures gardées (entry) sont des procédures dont l’accès est conditionné
(gardé), c’est-à-dire que tout se passe comme si un code tant que la garde n’est
pas vraie faire wait se trouvait au début de la primitive ;
317
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
– les fonctions (function) sont des primitives en lecture seule sur l’objet protégé :
elles ne peuvent pas modifier les variables internes de l’objet. En revanche, les
fonctions sont réentrantes les unes par rapport aux autres (une fonction peut
s’exécuter même si une fonction du même objet protégé est en cours d’exécution).
Ces primitives permettent d’affiner les accès aux objets protégés en leur donnant
des primitives de type lecteur/écrivain.
Par exemple, le code ci-après définit un sémaphore à l’aide d’un objet protégé.
protected type Semaphore(Valeur_Initiale: Positive:=1) is
-- Utilisation d’un discriminant donnant la valeur initiale
-- Noter l’emploi du mot clé "type" permettant de définir un type
-- d’objet protégé
entry Prendre; -- Procédure gardée (il n’est possible de prendre
-- le sémaphore que lorsque sa valeur est > 0)
procedure Vendre;
function Valeur return Natural; -- Renvoie la valeur courante du
compte du sémaphore
private -- Variables internes
Valeur: Natural:=Valeur_Initiale; -- Compte du sémaphore
end Semaphore;
protected body Semaphore is
entry Prendre when Valeur > 0 is
-- Tant que le sémaphore est <=0, les tâches appelant cette
-- entrée sont bloquées et mises en attente dans la file
-- d’attente de l’entrée
begin
Valeur:=Valeur-1;
end Prendre;
procedure Vendre is
begin
Valeur:=Valeur+1;
end Vendre;
function Valeur return Natural is
begin
return Valeur;
end;
end Semaphore;
La norme Ada propose le protocole à priorité plafond immédiat pour éviter l’inversion
de priorité lors d’accès concurrents à un objet protégé. Par conséquent, un objet pro-
tégé peut être muni d’une priorité : sa priorité plafond, c’est-à-dire la priorité maxi-
male des tâches susceptibles de l’utiliser. Ada étant un langage sûr, une exception est
levée lorsqu’une tâche essaie d’accéder à un objet protégé ayant une priorité plafond
inférieure à sa priorité. Par conséquent, par défaut, la priorité plafond d’un objet
protégé est priority’last avec tout ce que cela implique (si l’on ne donne pas
soi-même une priorité plafond à un objet protégé, et que l’on utilise le protocole à
priorité plafond immédiat, toute tâche accédant à un objet protégé se voit munie de
la priorité maximale, hors priorité d’interruption).
318
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
d’attentes au niveau des objets protégés est elle aussi configurable. Typiquement, une
application est configurée de sorte que :
– les tâches sont ordonnancées en FIFO suivant leur priorité (ce qui correspond à
l’algorithme POSIX SCHED_FIFO), c’est-à-dire qu’il y a une file d’attente
gérée en FIFO pour chaque niveau de priorité, ceci est fait en insérant dans le
programme la directive pragma Task_Dispatching_Policy(FIFO_Within_Prio-
rities) ;
– le protocole à priorité plafond immédiat est utilisé pour éviter les inversions de
priorité lors de l’utilisation d’objets protégés, ceci nécessite l’ajout de la directive
pragma Locking_Policy(Ceiling_Locking) ;
– les files de tâches en attente d’accès à un objet protégé sont gérées dans des files
FIFO par niveaux de priorités grâce à la directive pragma Queuing_Policy
(Priority_Queuing).
Par conséquent, toute application multitâche Ada devrait utiliser ces trois pragmas.
m Le profil Ravenscar
Étant données les limitations des modèles utilisés pour la validation temporelle de
systèmes, un concepteur doit, de préférence suivre différentes règles s’il souhaite pou-
voir valider temporellement son application.
Pour le langage Ada, une norme de développement a été proposée, sous le nom de
profil Ravenscar, normalisé par l’ISO en 1999. La norme préconise les principes
suivants :
– pas de déclaration de tâches dynamiques : tout est déclaré au début de l’appli-
cation ;
– les tâches ne se terminent pas ou toutes ensemble ;
– toutes les tâches sont au même niveau (pas de hiérarchie mère/fille entre les tâches) ;
– tous les outils de communication et de synchronisation sont déclarés initialement,
et pas de façon dynamique ;
– utilisation de dates et pas de durées pour les réveils de tâches périodiques, afin
d’éviter une dérive des horloges (voir § 2.4) ;
– utilisation d’un protocole de gestion de ressources à priorité plafond ;
– pas d’utilisation du rendez-vous mais uniquement des objets protégés, avec une
© Dunod – La photocopie non autorisée est un délit.
seule primitive de type entry par objet protégé, les autres pouvant être des pro-
cédures ou bien des fonctions ;
– un et un seul mécanisme de déclenchement pour une tâche (attente de synchro-
nisation, de message ou d’interruption, ou bien attente périodique).
319
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
des tâches et objets protégés sont placées dans les spécifications des paquetages,
alors que leurs corps sont placés dans les corps des paquetages.
m Modules de données
Les modules de données DARTS peuvent être implémentés à l’aide de l’encapsula-
tion du module de données dans un objet protégé. L’exclusion mutuelle est alors
garantie par la non réentrance des primitives d’accès à un objet protégé. Ainsi, une
première implémentation de la figure 6.24 est donnée ci-après.
with Procede; use Procede;
-- Module contenant des fonctions d’accès aux capteurs/actionneurs
procedure Test_Mdd is
-- Procédure montrant un exemple de module de données
type T_Temperature_Pression is record
-- Type de données contenu dans le module de données
Pression:Float;
Temperature: Float;
end record;
protected Temperature_Pression is
-- Module de données
procedure Ecrire(Tp: T_Temperature_Pression);
-- Modifie la valeur contenue
function Lire return T_Temperature_Pression;
-- Renvoie la valeur contenue
private
-- Variables internes
Valeur: T_Temperature_Pression := (Lire_Capteur_Temperature,
Lire_Capteur_Pression);
-- Initialisation de la température et de la pression
end;
protected body Temperature_Pression is
-- Implémentation du module de données
procedure Ecrire(Tp: T_Temperature_Pression) is
begin
Valeur:=Tp;
end;
320
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
end loop;
end;
task Traitement; -- Spécification de tâche
task body Traitement is
-- Corps de tâche
TP: T_Temperature_Pression;
begin
loop
Tp:=Temperature_Pression.Lire; -- Lecture dans le module de
-- données
-- etc.
end loop;
end;
begin -- Les tâches sont lancées
null; -- Le programme principal ne fait rien
end; -- Attente de la terminaison des tâches
Cet exemple est correct d’un point de vue respect de l’exclusion mutuelle, cependant,
il ne peut pas fonctionner dans la réalité ; la cause réside dans l’initialisation du module
de données. Dans la réalité, il est nécessaire d’initialiser le matériel (alimentation,
état interne, etc.) avant de pouvoir l’utiliser. Dans cette implémentation, l’initiali-
sation du module de données par lecture des capteurs étant faite en même temps
que le démarrage du programme principal, il n’est pas possible d’effectuer une ini-
tialisation des capteurs avant de les lire. Il est donc nécessaire de permettre l’initia-
lisation du module après initialisation des capteurs, et donc d’empêcher toute lecture
avant la première écriture dans le module. Par conséquent, le code généralement
utilisé pour implémenter un module de données est potentiellement bloquant en
lecture : la lecture est une procédure gardée (entry) possible uniquement après
une première écriture.
Le code est donc modifié comme ci-après (noter le changement de la primitive
Lire en procédure gardée) :
with Procede; use Procede;
-- Module contenant des fonctions d’accès aux capteurs/actionneurs
procedure Test_Mdd is
-- Procédure montrant un exemple de module de données
type T_Temperature_Pression is record
-- Type de données contenu dans le module de données
Pression:Float;
Temperature: Float;
end record;
© Dunod – La photocopie non autorisée est un délit.
protected Temperature_Pression is
-- Module de données
procedure Ecrire(Tp: T_Temperature_Pression);
-- Modifie la valeur contenue
entry Lire(Tp: out T_Temperature_Pression);
-- TP : paramètre de sortie renvoyant la valeur contenue
-- Possible seulement après une première écriture
private
-- Variables internes
Valeur: T_Temperature_Pression;
-- L’initialisation est inutile, puisque la lecture ne peut
avoir lieu qu’après une première écriture
Initialise : Boolean := False;
end;
321
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
322
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
private
Vide : Boolean := True; -- Etat de la boîte, Faux si il y a
-- un message non lu
Contenu: Integer; -- Message contenu dans la boîte si
-- Vide=False
end;
protected body T_Bal_1_Ecr is
procedure Envoyer(V: Integer) is
begin
Contenu:=V;
-- Le contenu reçoit V, même si la boîte était non vide (dans
-- ce cas, il y a écrasement du message précédent par V)
Vide:=False; -- Il y a un message non lu
end;
-- etc.
end loop;
end;
begin -- Lancement des tâches
null; -- le programme principal ne fait rien
end;
323
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
m Synchronisation
L’implémentation de la synchronisation est identique à l’implémentation du séma-
phore à compte présentée au paragraphe 6.3.1, p. 317. Le code, placé dans un
paquetage, est donné ci-après.
package Synchronisation is
protected type T_Synchronisation is
-- Implémentation d’une synchronisation à compte
entry Wait; -- Attente de synchronisation
324
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
loop
S1.Wait;
-- etc.
end loop;
end;
begin -- Toutes les tâches sont lancées
null; -- Le programme principal ne fait rien
end; -- Attente de la terminaison des tâches
m Tâches périodiques
Ada propose deux primitives d’attente : une attente relative (delay) présente dès
Ada 83, et une attente de date (delay until) présente depuis Ada 95. Afin d’éviter
325
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
une dérive des horloges (§ 5.3.1, p. 216), il convient d’utiliser l’attente de date.
Ainsi, l’implémentation des tâches de la figure 6.29 est :
with Ada.Real_Time;use Ada.Real_Time;
with Ada.Text_IO;use Ada.Text_IO;
procedure Test_Periodique is
task type T_Periodique(Numero: Integer; Periode_Ms: Integer);
-- Type de tâche périodique affichant son numéro à chaque itération
-- Numéro : numéro de la tâche
-- Periode_Ms: Période en millisecondes
task body T_Periodique is
Periode : constant Time_Span := Milliseconds(Periode_Ms);
-- Le type Time_Span, défini dans Ada.Real_Time, permet de
-- représenter une durée
Prochaine_Activation: Time := Clock+Periode;
-- Le type Time, défini dans Ada.Real_Time, représente une date
-- Date de prochaine activation, initialisée à l’heure courante
-- (retour de la fonction clock) + la période
begin
loop
Put_Line(integer’image(Numero)); -- Affichage du numéro
delay until Prochaine_Activation; -- Attente de la prochaine
-- date d’activation
Prochaine_Activation:=Prochaine_Activation+Periode; -- Calcul
-- de la prochaine date de réveil
end loop;
end;
Periode1 : T_Periodique(1, 100); -- Instanciation d’une tâche de
-- période 100 ms
Periode2 : T_Periodique(2, 150); -- Instanciation d’une tâche de
-- période 150 ms
begin -- lancement de toutes les tâches
null; -- le programme principal ne fait rien
end; -- Attente de terminaison des tâches
Ada prévoit directement une gestion d’interruption de type ISR déclenchant une DSR
(§ 5.2.3, p. 206). Une ISR doit forcément se trouver dans un objet protégé situé
dans un paquetage. Le protocole à priorité plafond immédiat doit être utilisé, et la
priorité plafond de l’objet protégé contenant l’ISR doit être une priorité d’interrup-
tion.
Ainsi, la tâche matérielle présentée sur la figure 6.30 s’implémente comme suit.
L’interruption déclenchée par la combinaison de touches Contrôle-C, nommée
SIGINT d’après une terminologie Unix/POSIX est traitée.
-- Fichier Isr.ads
with System; use System;
package Isr is
protected Isr2 is
-- Objet protégé gérant l’interruption 2 (SIGINT)
entry Wait; -- Procédure gardée sur laquelle la tâche de
-- traitement (DSR) se met en attente de
-- l’interruption
procedure Signal; -- Procédure effectivement appelée lors de
-- l’interruption traitée
private
326
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
-- Fichier Isr.adb
package body Isr is
protected body Isr2 is
entry Wait when Pending>0 is -- La DSR se bloque ici tant qu’il
-- n’y a pas d’interruption à traiter
begin
Pending:=Pending - 1; -- Une interruption est traitée
end;
procedure Signal is
begin
Pending := Pending + 1; -- Une interruption supplémentaire est
-- à traiter
end;
end;
end Isr;
6.3.3 Exemple
L’exemple de la « gestion de la sécurité d’une mine » dont le diagramme DARTS
est donné sur la figure 3.27 est traité en langage Ada.
327
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
Commençons par le plus simple, à savoir le programme principal, qui est de la forme :
-- pragmas nécessaires dès lors que l’ordonnancement a une importance
pragma Task_Dispatching_Policy(Fifo_Within_Priorities);
-- Ordonnancement FIFO par niveaux de priorité
pragma Locking_Policy(Ceiling_Locking); -- Protocole à priorité
-- plafond immédiat
pragma Queuing_Policy(Priority_Queuing); -- Gestion des attentes
-- d’objet protégé par priorité des tâches
with Controle; -- Paquetage où les tâches sont déclarées
procedure Controle_Mine is
begin -- lancement des tâches (déclarées dans le paquetage Controle)
null; -- ne fait rien
end; -- Attente de terminaison
328
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
-- de la valeur du capteur
delay until Prochaine; -- Attend la prochaine date de réveil
Prochaine:=Prochaine+Periode; -- Calcul de la prochaine date
-- de réveil
end loop;
end Acquerir_Niveau_Methane;
329
6 • Programmation 6.3 Programmation multitâche
des systèmes multitâches en langage Ada
330
6 • Programmation 6.4 Programmation multitâche
des systèmes multitâches en langage LabVIEW
Bal_Vitesse.Envoyer(Niveau_Eau-LLS);
Etat := Alerte_Et_Pompe;
end if;
when Alerte_Et_Pompe =>
if Niveau_Methane >= MS_L2 or niveau_eau <= LLS then
-- Alerte MS_L2 ou niveau bas => extinction de la pompe
Bal_Vitesse.Envoyer(0.0);
Etat := Alerte;
elsif Niveau_Methane < MS_L1 then
-- Niveau de méthane sous le seuil d’alerte
Sync_Alarme.Signal; -- Extinction de l’alarme
Etat := Pompe;
else Bal_Vitesse.Envoyer(Niveau_Eau-LLS); -- pilotage proportionnel
end if;
end case;
end loop;
end Controler_Mine;
end Controle;
La difficulté vient du fait que si l’on désire faire communiquer les tâches, on ne peut
pas utiliser de flot de données (figure 6.32), sinon, une boucle dépend de l’autre, et
ne pourra commencer son exécution que lorsque la boucle précédente sera terminée :
la tâche 2 n’est jamais exécutée, car elle doit attendre la terminaison de la tâche 1.
331
6 • Programmation 6.4 Programmation multitâche
des systèmes multitâches en langage LabVIEW
332
6 • Programmation 6.4 Programmation multitâche
des systèmes multitâches en langage LabVIEW
333
6 • Programmation 6.4 Programmation multitâche
des systèmes multitâches en langage LabVIEW
de parallélisme du diagramme n’ayant pas pu être mis seul dans un thread) sont
ordonnancées en tourniquets classés par priorité. Notons que le thread en charge de
l’interface graphique gère les interfaces graphiques des vi en fonction de leur niveau
de priorité.
Étant donné que la priorité est définie au niveau de chaque vi (par défaut, c’est la
priorité du vi appelant), lorsqu’un vi très prioritaire appelle un sous-vi moins priori-
taire, celui-ci hérite de la priorité de l’appelant.
Notons que le nom donné aux systèmes d’exécution autres que celui de l’interface
graphique est purement indicatif. Par défaut, un vi s’exécute dans le système d’exé-
cution standard. Ceci peut être configuré différemment pour chaque vi (menu Fichier,
Propriétés du vi, exécution).
Le nombre de threads contenus dans le seul système d’exécution standard exempte
généralement les programmeurs d’utiliser des systèmes d’exécution autres que stan-
dard.
m Tâches périodiques
334
6 • Programmation 6.4 Programmation multitâche
des systèmes multitâches en langage LabVIEW
suivant est repoussée : si l’itération i se termine après la date 50(n + i), alors
l’itération i + 1 ne peut commencer qu’à la date 50(n + i – 1) au lieu de 50(n + i ).
m Modules de données
Les modules de données DARTS peuvent être implémentés en se basant sur la non
réentrance des vi : par défaut, un vi est non réentrant. Afin d’illustrer plus aisément
son fonctionnement, un parallèle est fait entre vi non réentrant et moniteur (par
exemple objet protégé Ada).
Lorsque deux tâches tentent d’accéder simultanément à une primitive d’un même
moniteur, deux accès simultanés au même vi non réentrant ne peuvent pas avoir lieu.
Dans un moniteur, il y a des variables internes (partie private en langage Ada)
représentant l’état du moniteur. En LabVIEW, le registre à décalage (figure 6.11)
sert à conserver des données d’une itération de boucle pour la suivante. Lorsque la
boucle se termine, les dernières données inscrites restent mémorisées dans le registre
à décalage. Par conséquent, lors de la prochaine lecture du registre (lors d’une exé-
cution suivante de la boucle), si le registre à décalage n’est pas initialisé, il contient
les données inscrites dans le registre à décalage. L’idée consiste donc à créer une boucle
n’ayant qu’une seule itération afin de la munir d’un registre à décalage qui mémorise
les données inscrites d’une exécution sur l’autre. Cette méthode permet de conserver
de façon rémanente des données dans un vi.
Ainsi, la figure 6.36 montre un module de données contenant un enregistrement
(cluster) pouvant être lu et modifié par des tâches concurrentes tout en respectant
l’exclusion mutuelle. Généralement, trois primitives de base sont définies : initialiser,
lire et écrire. La primitive est choisie à l’aide d’un type énuméré passé en paramètre
au vi. La figure 6.37 montre l’utilisation de ce module de données pour la lecture
par un vi périodique.
Notons qu’il est assez courant de diversifier les primitives : ainsi, si par exemple on
souhaite pouvoir modifier uniquement la température ou uniquement la pression,
il suffit d’ajouter les primitives permettant de le faire.
Malheureusement, la comparaison entre moniteur Ada et vi non réentrant s’arrête là :
il n’y a aucun moyen simple d’implémenter une procédure gardée (entry Ada)
avec cette technique.
Depuis la version 5, LabVIEW propose des boîtes aux lettres sans écrasement bornées
© Dunod – La photocopie non autorisée est un délit.
ou non ; dans ce cas, le nombre de messages est limité par la mémoire, permettant
une lecture bloquante, ou bien non bloquante (en utilisant un timeout nul ou borné
en temps). Depuis la version 7, les boîtes aux lettres sont polymorphes, c’est-à-dire
que le type des messages est donné à la création, alors qu’auparavant, les messages
étaient de type chaîne de caractère (ce qui correspond au fonctionnement des boîtes
aux lettres en langage C, obligeant à effectuer des coercions vers char * pour l’envoi
et depuis char * pour la réception de messages).
335
6 • Programmation 6.4 Programmation multitâche
des systèmes multitâches en langage LabVIEW
336
6 • Programmation 6.4 Programmation multitâche
des systèmes multitâches en langage LabVIEW
Un exemple de boîte aux lettres non bornée sans écrasement a été présenté sur les
figures 6.32 et 6.33. La figure 6.38 montre un exemple de boîte aux lettres de taille 1
sans écrasement. Par rapport à la figure 6.34, sur laquelle la boîte était non bornée,
seul un paramètre change à la création de la boîte aux lettres.
Figure 6.38 – Boîte aux lettres de taille 1 sans écrasement en langage LabVIEW.
Si cela est nécessaire, il est possible de créer des boîtes aux lettres à écrasement en
utilisant les sémaphores et une variable globale en utilisant un algorithme de type
producteur à écrasement/consommateur. Cependant, l’implémentation de ce type
de boîte aux lettres reste laborieuse en langage LabVIEW.
m Synchronisation
LabVIEW propose l’outil sémaphore à compte, ce qui devrait rendre triviale l’implé-
mentation des synchronisations. C’était le cas avant la version 7 de LabVIEW Ainsi,
la figure 6.39 propose un exemple de synchronisation fonctionnant sur LabVIEW
© Dunod – La photocopie non autorisée est un délit.
337
6 • Programmation 6.4 Programmation multitâche
des systèmes multitâches en langage LabVIEW
5 et 6. Afin de simplifier sa présentation, les tâches ne sont pas encapsulées dans des
sous-vis.
Malheureusement, LabVIEW 7 a légèrement modifié la sémantique des sémaphores
pour n’en faire que des sémaphores d’exclusion mutuelle. Par conséquent, la synchro-
nisation pourra être implémentée en LabVIEW 7 par des boîtes aux lettres non
bornées avec des messages d’un octet (par exemple booléens) n’ayant de signification
que par leur présence, et non par leur valeur.
6.4.4 Exemple
La mise en œuvre des différents outils présentés pour l’implémentation d’éléments
DARTS en langage LabVIEW est faite sur l’exemple de la « gestion de la sécurité
d’une mine » dont le diagramme DARTS est donné sur la figure 3.27. Dans cette
implémentation, la synchronisation entre la tâche de contrôle et la tâche d’alarme
est remplacée par une communication par boîte aux lettres.
Nous pouvons constater que LabVIEW dispose d’une moindre puissance d’expres-
sion que POSIX ou Ada en terme de synchronisation et de communication, mais,
en contrepartie, que l’implémentation d’une conception DARTS est extrêmement
simple à réaliser. Le module de données « Niveau Eau » est implémenté comme le
module de données de la figure 6.36, excepté qu’il contient une donnée interne de
type flottant.
338
6 • Programmation 6.4 Programmation multitâche
des systèmes multitâches en langage LabVIEW
339
6 • Programmation 6.4 Programmation multitâche
des systèmes multitâches en langage LabVIEW
340
7 • TRAITEMENT COMPLET
D’UNE APPLICATION INDUSTRIELLE
dans une cuve irriguée par l’eau distillée. L’eau distillée est elle-même refroidie par
échange thermique avec un flux d’eau industrielle en circuit ouvert. Ainsi, de l’eau
« fraîche » (aux alentours de 15 à 21 °C en fonction de la saison) permet de refroidir
l’eau distillée, qui elle-même maintient le procédé autour d’une température de
consigne.
Le but du système de contrôle est de réguler le débit d’eau industrielle en fonction
des températures mesurées, tout en assurant la sécurité du système.
Une console opérateur sert à afficher les relevés des températures (entrée et sortie
d’eau distillée, entrée et sortie d’eau froide) et des débits, et une alarme est déclenchée
si la température relevée en sortie de cuve est hors domaine (la température référence
T°ed2 en sortie de cuve doit se situer dans une fourchette donnée). De même, si
341
7 • Traitement complet 7.2 Spécification
d’une application industrielle
Eau froide
industrielle Thermocouple
T°ef1 Flux d’eau Thermocouple
chauffée par le procédé Flux d’eau froide T°ef2
industrielle Débitmètre
Dbt_ef
Électrovanne Évacuation
réglable eau froide
d’eau froide EVef
Zone d’échange thermique
Débimètre
Dbt_ed
Courbes
débits et
températu
res
Alarme
Procédé Pompe
à refroidir
Thermocouple Thermocouple
T° ed1 T°ed2
Console opérateur Bouton opérateur
l’on s’aperçoit de la présence d’une fuite dans l’un des circuits, ou de l’occurrence
d’une coupure d’eau industrielle, ou encore d’une panne de pompe, alors l’alarme
doit être déclenchée. Dans ce cas, un opérateur humain doit stopper le procédé
refroidi, puis avertir le système de cet arrêt en appuyant sur le « bouton opérateur ».
Entre le déclenchement de l’alarme et l’appui sur le bouton, le système doit ouvrir
l’eau froide au maximum afin, si possible, de préserver l’intégrité du procédé refroidi.
Notons qu’il n’est pas possible de distinguer si l’arrêt du flux d’eau industrielle est
dû à une coupure d’eau ou à une fuite, et qu’il est impossible de distinguer si l’arrêt
du flux d’eau distillée est dû à une panne de la pompe ou à une fuite.
7.2 Spécification
Nous avons présenté dans le chapitre 1 le cycle en W du développement d’un sys-
tème de contrôle-commande. La première phase consiste donc à spécifier, concevoir
et implémenter un système pilotant un simulateur purement logiciel. En effet,
typiquement, l’échangeur est piloté manuellement dans une entreprise, et un arrêt de
ses fonctionnalités pendant les phases de test du système de contrôle serait pénalisant.
De plus, des commandes erronées pourraient mettre en péril son intégrité. Enfin,
il arrive fréquemment que le matériel de commande et les dispositifs électroniques
nécessaires à son interfaçage avec le système de contrôle soient en développement
parallèlement au développement du système de contrôle.
342
7 • Traitement complet 7.2 Spécification
d’une application industrielle
Dans le second V, lors de la spécification adaptée, nous verrons qu’il est nécessaire
de prendre en compte les spécificités matérielles (relais électriques, nécessité d’ali-
menter un débitmètre avant de l’utiliser…).
Dans la spécification, nous considérons qu’il est possible de lire directement les
débits en litre/heure, qu’il est possible de piloter directement une électrovanne, etc.
La figure 7.2 représente le diagramme de contexte obtenu.
Console
Thermocouples Mise
en route
Affichage
Températures
Dbt_ef Pompe
Réguler Cmd pompe
Débit ef
échangeur
Débit ed Cmd vanne
0
Dbt_ed EVef
Bouton Alarme
Nous pouvons alors dériver le diagramme préliminaire (figure 7.3). Notons que les
alarmes ne sont pas dissociées (Acquérir T°, Réguler ef , et Vérifier débit ed envoient le
même événement Alarme en cas d’incohérence avérée). Notons que les processus
1, 2 et 3 sont des processus d’acquisition. L’événement Trigger déclenchant le pro-
cessus 5 a une sémantique particulière : en effet, Lire Bouton, lorsqu’il est déclenché,
doit scruter l’état du bouton jusqu’à ce qu’il soit en l’état appuyé, dans ce cas, il
renvoie un événement ACK (pour acknowledge). Ce type d’utilisation du Trigger
ressemble à un appel de sous-programme : il est déclenché, et lorsqu’il se termine,
envoie un événement de retour.
La sémantique de l’événement E/D envoyé sur le processus 4 est une sémantique de
type activation d’un processus fonctionnel synchronisé par flot de données : à partir
de l’événement Enable, le processus traite chaque donnée Commande débit reçue.
© Dunod – La photocopie non autorisée est un délit.
343
7 • Traitement complet 7.2 Spécification
d’une application industrielle
Consigne T°
Cmd vanne
Mise en route
<E> 1,2,3,4,6,8
Nominal
Alarme
<D> 1,2,3,8
<T> 5,7
Attente
opérateur
ACK
<D> 4,6
Arrêt
344
7 • Traitement complet 7.2 Spécification
d’une application industrielle
En cas d’alarme, les processus d’acquisition et d’affichage sont arrêtés, l’alarme est
déclenchée, et on attend la prise en compte de l’alarme par l’opérateur (déclen-
chement du processus 5, dont la commande « ouvrir la vanne d’eau industrielle en
grand » va supplanter les commandes envoyées habituellement par le processus 1).
Lorsque l’opérateur a validé l’alarme (appui sur le bouton), le système s’arrête en
coupant l’électrovanne et la pompe.
Avant d’aller plus en profondeur (décomposition des processus fonctionnels com-
plexes en DFD), nous pouvons étudier le dictionnaire de données du système et la
spécification textuelle des processus fonctionnels simples, qui deviendront de fait
primitifs (tableau 7.1).
345
7 • Traitement complet 7.2 Spécification
d’une application industrielle
Débit ed lu Débit ed
T° lues Températures
346
7 • Traitement complet 7.2 Spécification
d’une application industrielle
347
7 • Traitement complet 7.2 Spécification
d’une application industrielle
Afficher E/ événements : T
alarme S/ données : Alarme
Entraîne :
Déclencher l’alarme
348
7 • Traitement complet 7.2 Spécification
d’une application industrielle
Le tableau 7.3 représente la spécification des nouveaux processus créés (la spécifi-
cation du processus 1 – Réguler ef peut alors être retirée de la spécification des pro-
cessus primitifs). Notons que la prise en compte de l’événement Disable n’est placée
que dans le processus fonctionnel Calculer consigne, ceci afin d’éviter un conflit
d’émission de commande de débit vers Commander vanne.
Consigne T° T° lues*
Commande débit E
Calculer Alarme <E>1.2,1.3
consigne Alarme
1.3
Nominal
Débits lus E/D
Alarme D
Débit ef lu <D>1.2, 1.3
Contrôler
régulation
E/D 1.1 Panne
E/D
Débit ef Acquérir
ef Alarme Alarme
1.2
349
7 • Traitement complet 7.3 Conception
d’une application industrielle
7.3 Conception
Il est maintenant possible de passer à la conception DARTS du système. Il est tou-
jours intéressant de regrouper les processus fonctionnels en un minimum de tâches :
la validation temporelle du système est simplifiée, la cohérence fonctionnelle est plus
simple à obtenir (dans le cas étudié, il faut s’assurer que Lire Bouton envoie bien une
donnée à Commander vanne après que Calculer consigne se soit arrêté). De plus,
avec moins de tâches, le fonctionnement est plus efficace (diminution du nombre de
préemptions, des communications, de la taille de la file des processus à ordonnancer,
etc.).
Initialement, chaque processus est vu comme une tâche, les flots de données et d’évé-
nements sont implémentés par une boîte aux lettres, une synchronisation, ou rien
(cas de l’événement E/D lorsque seul l’événement E est envoyé initialement par
exemple), enfin, les zones de stockage partagées par plusieurs processus sont géné-
ralement implémentées par des modules de données. La figure 7.5 représente un
diagramme DARTS pour le système de contrôle de l’échangeur.
Notons que seuls les événements réellement utilisés par le processus de contrôle sont
reportés dans la conception. Ainsi, par exemple, l’événement E/D envoyé sur le pro-
cessus fonctionnel Commander_Vanne ne sera pas implémenté : la tâche Commander_
Vanne, implémentant les fonctionnalités du processus fonctionnel du même nom,
est prête au lancement du système, et ne sera arrêtée qu’à l’extinction du système.
La tâche Réguler débit intègre les processus fonctionnels 1, 3, 5, 9 : les processus 1
et 3 ont une cohérence fonctionnelle forte, de plus, la régulation est d’autant plus
fine qu’elle se base sur des valeurs de débit juste acquises (le débit peut en effet varier
relativement vite, contrairement aux températures). Le fonctionnement du pro-
cessus 5 correspond à un appel de sous-programme par le processus 9 : ils sont donc
regroupés en une tâche. Cependant, étant donné que la tâche obtenue serait assez
simple, et fonctionnerait au plus à la même vitesse que la régulation de débit, les
4 processus fonctionnels sont finalement intégrés dans une même tâche.
350
7 • Traitement complet 7.3 Conception
d’une application industrielle
Commande vanne
Commander
HTR (500 ms)
vanne
Cmd vanne
Débit ef Réguler
Débit ed débit Alarmer
Bouton Activer
alarme
Alarme
Écrire
Lire
HTR (1 000 ms)
Températures Acquérir
températures HTR (1 000 ms)
Afficher
Affichage
Il est intéressant d’isoler les tâches de commande des tâches de décision : en effet,
il est fréquent que la durée nécessaire à une entrée/sortie sur une carte d’acquisition
soit très grande comparée à la durée d’un calcul, même complexe. Par conséquent,
dans la mesure du possible, il est bon d’éviter de surcharger une tâche de calcul et/ou
de décision en lui faisant effectuer des entrées/sorties (sauf si celles-ci sont de toute
façon nécessaires à l’élaboration d’un calcul, comme c’est le cas ici pour les débits).
Notons cependant que nous sommes face à un système très particulier : l’alarme n’est
déclenchée, et la pompe éteinte, qu’en cas de panne, ce qui est exceptionnel. Dans
ce cas précis, étant donné que cela précède l’arrêt du système, il serait plus intéressant
de regrouper les tâches Alarme et Commander pompe avec Réguler débit. Par consé-
quent, nous donnons un schéma DARTS simplifié sur la figure 7.7.
Notons qu’il est possible de passer d’une communication synchrone (flot de données
© Dunod – La photocopie non autorisée est un délit.
351
7 • Traitement complet 7.3 Conception
d’une application industrielle
Écrire
Températures Acquérir
températures
HTR (1 000 ms)
Afficher
Affichage
Il reste alors à identifier les éléments DARTS aux éléments déjà définis dans SA-RT
et à définir les nouveaux éléments. Afin de ne pas noyer les informations impor-
tantes, les éléments DARTS portant le même nom que les éléments SA-RT corres-
pondants ne sont pas redéfinis dans le tableau 7.4.
352
© Dunod – La photocopie non autorisée est un délit.
Écrire Débits
Écrire
Écrire
Températures Acquérir
températures
HTR (1 000 ms)
Affichage
Afficher
ln
out
Écrire HTR (500 ms)
Écrire
Lire
Simulateur Lire
Simulateur Affichage
de pannes simulateur
353
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Afficher Tâche
Correspond à : Processus fonctionnel 8
In Module de données
Rôle : simule les capteurs
Type : enregistrement (Débit ef, Débit ed, Températures)
7.4.1 Le simulateur
Le simulateur peut s’exécuter en tant que tâche parmi les autres tâches du système,
ou bien tout simplement dans un autre processus, écrit dans un langage de program-
mation facilitant la simulation (LabVIEW, Matlab/Simulink, etc.). L’une des tech-
niques les plus simples permettant de faire communiquer deux processus différents
pouvant s’exécuter sur des ordinateurs différents consiste à utiliser le protocole TCP/IP.
Cela reste vrai lorsque les processus s’exécutent sur un même ordinateur : le protocole
TCP/IP est alors utilisé en boucle locale. Le simulateur utilisé dans ce chapitre sera
donc un programme lançant un serveur TCP, et exécutant en parallèle un modèle
numérique (plus ou moins réaliste) du procédé.
Le protocole applicatif défini entre le client (le système de contrôle) et le serveur
(le simulateur) est en général relativement simple : après l’établissement d’une
354
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
m Le module de communication
© Dunod – La photocopie non autorisée est un délit.
355
package Types_Communs is
356
-- types et constantes utilisés par les tâches
end;
generic
type element is private;
package Communications is Instanciation des éléments de communication génériques
-- définition de différents types with Communication;
-- de BaLs, MDD, synchros with Communication;
package CommBoolis new
end; with Communication;
package CommBoolis new
package CommBool is new Communications(element=>boolean);
Figure 7.9 – Architecture logicielle typique d’un programme de contrôle-commande simple en Ada.
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Les modules de données (MDD) sont avec ou sans valeur initiale. Notons qu’un
discriminant ne peut être qu’une valeur de type discret ou bien un pointeur. Un
inconvénient visible ici est le fait d’être obligé de passer par pointeur une valeur
initiale au module de donnée de type MDDi.
------------------------
-- Synchronisation
------------------------
protected type Synchro_C(priorité: natural := priority’last) is
-- Synchronisation à compte (les déclenchements successifs
-- non pris en compte sont accumulés)
pragma Priority(priorité);
procedure Signal;
© Dunod – La photocopie non autorisée est un délit.
entry Wait;
private
Nombre_Signalés: natural := 0;
end;
357
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Les boîtes aux lettres peuvent être de taille 1 ou n, et être à écrasement ou sans écra-
sement. Les boîtes aux lettres de taille n avec écrasement sont assez proche de ce qui
est qualifié de RT FIFO (file temps réel) permettant une communication entre tâche
émettrice à contraintes de temps et tâche réceptrice sans contrainte de temps :
la tâche à contraintes de temps ne se bloque pas si la file est pleine, mais dans ce cas
le plus ancien message est écrasé. Le paquetage générique de gestion de liste circu-
laire est donné en annexe D : il s’agit tout simplement d’un tableau, dans lequel
un élément enfilé écrase le plus ancien élément si la file est pleine.
358
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Rappelons enfin que la priorité associée à un objet protégé est la priorité plafond
de celui-ci, et qu’une tâche hérite de la priorité plafond d’un objet (protocole à
priorité plafond immédiat, pragma Locking_Policy(Ceiling_Locking)) pendant son
utilisation de celui-ci. La priorité plafond d’un objet protégé doit donc être la prio-
rité maximale d’une tâche pouvant accéder à cet objet. Dans le cas où une tâche de
priorité supérieure à l’objet protégé y accéderait, l’exception Program_Error serait
levée.
Le corps de ce paquetage est donné en annexe D.
m Le module de contrôle
Le paquetage Types_Communs est très court, puisque seuls deux types spécifiques
sont définis pour le système :
-- Paquetage Types_Communs
package Types_Communs is
type Temperatures is record
Ted1, Ted2, Tef1, Tef2 : float;
end record;
type Debits is record
ed, ef: float;
end record;
end;
359
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
--------------------------------
-- Taches
--------------------------------
-- Dans la partie simulation, le choix des priorités est
-- généralement arbitraire
-- puisqu’on emploie fréquemment une station de développement
-- tournant sous un
-- système d’exploitation non temps réel (=>indéterminisme
-- temporel)
task Reguler_Debit is
pragma Priority(Default_Priority);
end;
task Commander_Pompe is
pragma Priority(Default_Priority+1);
end;
task Acquerir_Temperatures is
pragma Priority(Default_Priority-1);
end;
task Afficher is
pragma Priority(Default_Priority-2);
end;
end;
L’implémentation suit le schéma défini dans le chapitre 6. Trois tâches sont pério-
diques, la quatrième est déclenchée par boîte aux lettres.
with Ada.Real_Time; use Ada.Real_Time;
with Affichage;use Affichage;
with Simulateur; use Simulateur;
package body Controler_Echangeur is
T0: constant Time := Clock;
-- Base des temps
360
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
return Csg;
end if;
end;
361
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Commander_Vanne(Cmd);
end loop;
end Piloter_Vanne;
Consigne_Ed:=Elaborer_Consigne(D,Mdd_Temperatures.Lire);
-- Elaboration de la consigne
Bal_Commande_Vanne.Envoyer(Consigne_Ed);
-- Envoie à la tâche de commande
end if;
when Alarme =>
if Lire_Bouton then
Commander_Alarme(False);
--Extinction de l’alarme
Bal_Commande_Vanne.Envoyer(0.0);
--Fermeture de la vanne d’eau industrielle
Commander_Pompe(False);
-- Extinction de la pompe d’eau distillée
exit; --Terminaison de la tâche
end if;
end case;
delay until Prochaine;
Prochaine := Prochaine + Periode;
end loop;
end Reguler_Debit;
end Controler_Echangeur;
362
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
-- caractères
-- Pour commander la pompe, le simulateur envoie "pompe 0" ou "pompe
-- 1"
-- en fonction de la valeur de la commande
363
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
364
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Socket: Socket_Type;
-- Identifiant de connexion au simulateur
Channel: Stream_Access;
-- Flux de caractères échangés sur la connexion
end;
end;
Val : Float;
begin
Simulateur.Lire_Debit_Ed(Val);
return Val;
exception when Socket_Error => raise Erreur_Procede;
end Lire_Debit_Ed;
365
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Address.Addr:=Addresses(Get_Host_By_Name(Adresse_Simulateur),1);
Address.Port:=Port_Type(Port_Simulateur);
-- Création de l’adresse dans le format nécessaire à
-- Gnat.Sockets
Create_Socket(Socket);
Set_Socket_Option(Socket,Socket_Level,(Reuse_Address,True));
-- Initialisation du Socket
Connect_Socket(Socket,Address);
-- Connexion au simulateur
Channel:=Stream(Socket);
-- Elaboration d’un flux de caractères (permettant des
-- lectures et écritures -- comme dans un fichier ou à
-- l’écran)
end;
entry Lire_Debit_Ef (Val : out Float ) when Initialisé is
-- Interroge le simulateur sur la valeur de Debit Ef
-- Ne peut avoir lieu qu’après initialisation
begin
String’Output(Channel,"ef");
Val:=Float’Value(String’Input(Channel));
end;
366
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
367
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
m Le programme principal
Il ne reste plus alors qu’à écrire le programme principal : celui-ci définit les trois
pragmas usuels permettant de définir un ordonnancement à priorités fixes gérant
les accès aux objets protégés avec le protocole à priorité plafond (élimine l’inversion
de priorité). Rappelons qu’au moment du lancement du programme principal,
toutes les tâches définies sont lancées en parallèle. Le programme principal ne se
termine que lorsque toutes les tâches sont terminées (dans le cas présent : jamais).
pragma Queuing_Policy(Priority_Queuing);
-- L’accès aux objets protégés est géré par niveau de priorité
pragma Locking_Policy(Ceiling_Locking );
-- Utilisation du protocole à priorité plafond
pragma Task_Dispatching_Policy(Fifo_Within_Priorities);
-- Ordonnancement des tâches à priorités
with Controler_Echangeur;
procedure Echangeur is
begin
Initialiser;
end;
m Le module de communication
Le module BaLs est l’équivalent en langage C du paquetage Ada Communications.
Il n’est pas générique, puisque tout élément échangé entre deux tâches passe par le
type chaîne de caractères (il subit une coercion en char * ) : toute notion de type
est perdue, et le programmeur doit s’assurer lui-même que les données envoyées sont
du même type que les données reçues. Ce fonctionnement est assez typique du
langage C.
Les modules procede et simulateur ont la même interface, ce qui permet, comme pour
le cas traité en Ada, de ne changer que la directive #include présente dans contrôle.c
afin de passer de la simulation à la commande réelle.
Commençons par observer le module BaLs. Les boîtes aux lettres peuvent être de
taille 1 ou n, et être à écrasement ou sans écrasement. Comme nous l’avons vu dans
le paragraphe 6.1, les éléments de communications s’appliquant aux tâches (pthread)
proposent des sémaphores et de variables conditionnelles permettant de programmer
un moniteur de Hoare. Les moniteurs permettent de programmer simplement des
éléments de communication de type boîtes aux lettres : un buffer est alloué initia-
lement, son rôle est de stocker le ou les messages sous forme de chaîne de caractères
(tableau d’octets). On peut ensuite synchroniser les envois/réceptions suivant le
368
© Dunod – La photocopie non autorisée est un délit.
#ifndef _TYPES_COMMUNS_
/* BaLs.h */ #define _TYPES_COMMUNS_
#ifndef _BALS_H_ /* types et constantes utilisés par les tâches */
#define _BALS_H_ #endif
/* définition de différents types
de BaLs */
#endif
/* simulateur.h */
#ifndef _SIMULATEUR_H_
/* BaLs.c */
#define _SIMULATEUR_H_
/* implémentation de différents types
void Initialiser( );
de BaLs */
/* mêmes types et primitives
que dans procédé */
7 • Traitement complet
d’une application industrielle
#endif /* procede.h */
#ifndef _PROCEDE_H_
#define _PROCEDE_H_
void Initialiser( );
/* controle.h */ /* primitives d’accès au procédé
#ifndef _CONTROLE_H_ et types associés
#define _CONTROLE_H_ (abstraction de l’utilisation des E/S) */
void Lancer (); #endif
/* fonction d’instanciation des éléments de
communication, création des tâches */ OU
#endif
/* procede.c */
/* Implémentation des primitives
/* controle.c */ d’accès au procédé */
/* déclaration des fonctions tâches
déclaration des éléments de communication */
/* simulateur.c */
/* types et constantes spécifiques
/* programme principal */
au simulateur, sémaphore
void main() {
protégeant le simulateur,
Initialiser( );
implémentation des primitives
Lancer( );
d’accès au simulateur */
}
7.4 Implémentation sur simulateur
369
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
m Le module de contrôle
Poursuivons en présentant le module types_communs, contenant les définitions de
type température et débit.
#ifndef _TYPES_COMMUNS_H_
#define _TYPES_COMMUNS_H_
typedef struct {
float Ted1, Ted2, Tef1, Tef2;
} Temperatures_t;
typedef struct {
float ef, ed;
} Debits_t;
#ifndef BYTE
#define BYTE unsigned char
#endif
#endif
L’interface du module de contrôle est telle qu’elle est décrite sur la figure 7.10 :
/* controle.h */
#ifndef _CONTROLER_ECHANGEUR_H_
#define _CONTROLER_ECHANGEUR_H_
void Lancer ();
/* Initialise les éléments de communication et
lance le système de tâches */
#endif
370
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Temperatures_t MDD_Temperatures={0.0,0.0,0.0,0.0};
void Acquerir_Temperatures() {
/* Acquiert périodiquement la température et la stocke dans le MDD
MDD_Temperatures */
Temperatures_t T;
struct timespec horloge ;
pthread_cond_t Reveil; /* Variable conditionnelle utilisée par la
tâche afin de se réveiller périodiquement */
/* Cette variable n’est jamais signalée */
pthread_mutex_t sReveil; /* Mutex lié à la variable conditionnelle
*/
pthread_mutex_init(&sReveil,NULL); /* Initialisation du mutex */
pthread_cond_init(&Reveil,NULL); /* Initialisation de la variable
conditionnelle */
clock_gettime(CLOCK_REALTIME, &horloge); /* heure courante de
l’horloge au démarrage de la tâche */
while (1) {
T=Lire_Temperatures();
if (!Verifier_Temperatures(T)) {
/* Une température est hors domaine */
pthread_mutex_lock(&s_Panne);
/* On prévient qu’il y a panne */
MDD_Panne=1;
pthread_mutex_unlock(&s_Panne);
}
pthread_mutex_lock(&s_Temperatures);
/* On stocke la température dans le MDD */
MDD_Temperatures=T;
pthread_mutex_unlock(&s_Temperatures);
ajouter_microsecondes(&horloge,1000000);/* Calcul de la date
du prochain reveil=date du dernier réveil+1 seconde */
pthread_mutex_lock(&sReveil);
© Dunod – La photocopie non autorisée est un délit.
void Afficher() {
/* Affiche périodiquement le débit et la température dans un graphe */
Temperatures_t T;
Debits_t D;
/* Gestion de la périodicité */
struct timespec horloge ;
pthread_cond_t Reveil;
371
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
pthread_mutex_t sReveil;
pthread_mutex_init(&sReveil,NULL);
pthread_cond_init(&Reveil,NULL);
clock_gettime(CLOCK_REALTIME, &horloge);
while (1) {
pthread_mutex_lock(&s_Temperatures);
/* Lecture des températures */
T=MDD_Temperatures;
pthread_mutex_unlock(&s_Temperatures);
pthread_mutex_lock(&s_Debits);
/* Lecture des débits */
D=MDD_Debits;
pthread_mutex_unlock(&s_Debits);
/* Affichage */
afficher_courbes(T,D);
void Piloter_Vanne() {
/* Applique une commande arrivant dans la BaL Commande_Vanne sur la
vanne */
float com;
while (1) {
/* Attente d’un message */
bal_ecr_recevoir(Commande_Vanne,(char*)&com);
/* Commande de la vanne */
Commander_Vanne(com);
}
}
void Reguler_Debit () {
/* Acquiert périodiquement et vérifie les débits, élabore une consigne
de commande de vanne,
qu’elle envoie dans la BaL Commande_Vanne, gère les pannes */
Debits_t D;
Temperatures_t T;
float Consigne_Ed;
BYTE panne;
/* Modes de fonctionnement */
#define MODE_NORMAL 0
#define MODE_ALARME 1
/* Mode courant de fonctionnement */
BYTE Etat=MODE_NORMAL;
/* Gestion de la périodicité */
struct timespec horloge ;
pthread_cond_t Reveil;
pthread_mutex_t sReveil;
pthread_mutex_init(&sReveil,NULL);
pthread_cond_init(&Reveil,NULL);
clock_gettime(CLOCK_REALTIME, &horloge);
while (1) {
switch (Etat) {
case MODE_NORMAL:
pthread_mutex_lock(&s_Panne);
372
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
break;
default: ;
}
/* Attente de la prochaine période */
ajouter_microsecondes(&horloge,1000000);
pthread_mutex_lock(&sReveil);
pthread_cond_timedwait(&Reveil, &sReveil, &horloge);
}
}
void Lancer () {
int i ;
pthread_t taches[4]; /* Tableau stockant les identificateurs de
tâches */
373
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
#include "types_communs.h"
extern const float Max_Ouverture_Vanne;
374
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
#endif
int Initialiser() {
struct sockaddr_in addr_sim;
/* Adresse du simulateur */
375
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
pthread_mutex_init(&s_Simulateur,0);
/* Initialisation du sémaphore */
if ((sock_sim = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
return 1;
/* Initialisation de la connexion TCP */
memset(&addr_sim, 0, sizeof(addr_sim));
addr_sim.sin_family = AF_INET;
addr_sim.sin_addr.s_addr = inet_addr(Adresse_Simulateur);
addr_sim.sin_port = htons(Port_Simulateur);
if (connect(sock_sim, (struct sockaddr *) &addr_sim,
sizeof(addr_sim)) < 0)
return 1;
/* Connexion TCP */
}
376
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
float Lire_Debit_Ef() {
float lu;
pthread_mutex_lock(&s_Simulateur);
Simu_Write("ef");
Simu_Read_Float(&lu);
pthread_mutex_unlock(&s_Simulateur);
return lu;
}
m Le programme principal
377
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Programme principal
Commander
Vanne
Acquérir Réguler
Afficher débits
température
MDD
Panne
378
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
m Outils de communication
La boîte aux lettres peut être simplement implémentée par un élément message queue
LabVIEW : cette boîte est créée au plus haut niveau, dans le programme principal,
et son identifiant est passé comme paramètre aux deux tâches l’utilisant. Lors de la
création d’une boîte aux lettres, à partir de la version 7 de LabVIEW, le type d’élé-
ments transportés par la boîte aux lettres est donné à la création. Ainsi, sur la
figure 7.13, la création de la boîte BaL Commande Vanne se voit fournir une cons-
tante réelle (double précision). L’identifiant de boîte aux lettres créée est alors passé
aux tâches Réguler Débit et Commander Vanne qui pourront ainsi communiquer à
l’aide de cette boîte aux lettres.
Les modules de données sont implémentés à l’aide de vi non réentrants, comme
expliqué dans le paragraphe 6.4.3, p. 335. Généralement, on donne la possibilité
d’initialiser, lire et modifier un module de données. Remarquons sur la figure 7.13
la façon dont on initialise les modules de données avant de lancer les tâches grâce
à l’utilisation d’une structure séquence. En guise d’exemple de module de don-
nées, la figure 7.14 montre le diagramme de MDD Débits. Noter l’utilisation d’un
type strict pour les débits.
Les communications avec le simulateur sont elles aussi accessibles à travers un vi
non réentrant, ce qui garantit l’exclusion mutuelle des accès à celui-ci.
© Dunod – La photocopie non autorisée est un délit.
379
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
380
© Dunod – La photocopie non autorisée est un délit. 7 • Traitement complet
d’une application industrielle
381
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Dans le cas présent, il y a deux types de tâches : des tâches périodiques, et une
tâche déclenchée par boîte aux lettres.
Chaque tâche est implémentée par une boucle sans fin. Dans le cas périodique, on
utilise le vi Attendre le prochain multiple de (voir § 6.4.3, p. 334) qui permet
d’assurer un régime périodique sans dérive des horloges (activation à chaque fois
que l’horloge atteint un multiple de x millisecondes). La figure 7.15 présente la
tâche Acquérir Températures. Noter qu’il est conseillé, pour faciliter la lecture du
programme, de choisir des icônes exprimant la fonctionnalité des vi. Ainsi, celui
de la tâche ressemble à une tâche DARTS périodique. Noter aussi que le paramè-
tre Période, passé par le programme principal, est un paramètre que nous avons
rendu obligatoire. Enfin, comme tout vi qui doit être correctement documenté, il
convient pour les tâches de mettre en évidence dans leur documentation quels élé-
ments de communication et éléments matériels sont utilisés.
382
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
La tâche Afficher est elle aussi très simple, et est présentée sur la figure 7.16. On
peut remarquer la facilité avec laquelle LabVIEW permet de réaliser des affichages
(voir la face avant d’Afficher sur la figure 7.17).
La tâche la plus complexe est Réguler Débits (figure 7.18). Elle intègre le diagramme
© Dunod – La photocopie non autorisée est un délit.
383
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
384
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Le choix effectué dans cet exemple est d’utiliser l’appel au simulateur dans chaque
tâche communiquant avec le procédé. C’est exactement comme si en Ada (voir
§ 7.4.2), nous avions directement utilisé l’objet protégé Simulateur dans les tâches.
Nous aurions pu ici aussi définir des fonctions d’accès élémentaires au procédé,
de façon à ne pas appeler le simulateur directement dans les tâches.
Rappelons qu’en LabVIEW, il aurait été tout aussi simple, voire même préférable,
d’implémenter le simulateur directement dans une tâche LabVIEW. Cependant,
dans un souci d’homogénéité avec ce qui a été fait en C et Ada, nous avons choisi
d’utiliser un simulateur sous forme de processus externe, avec lequel nous commu-
niquons via TCP/IP (voir § 7.4.1).
Le vi de communication avec le simulateur doit garantir l’exclusion mutuelle des
communications avec le simulateur : cela est fait naturellement par le comportement
non réentrant du vi. Ce vi peut être vu comme un module de donnée de haut niveau.
Notons cependant que contrairement au moniteur, l’aspect non réentrant des vi
ne permet pas de représenter une primitive gardée (de type entry Ada). Par consé-
quent, on ne peut pas imposer simplement tout appel au vi d’être précédé par un
appel d’initialisation (comparativement, observer comment on garantit en Ada
qu’initialiser est appelé avant tout autre accès).
© Dunod – La photocopie non autorisée est un délit.
385
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
386
7 • Traitement complet 7.4 Implémentation sur simulateur
d’une application industrielle
Figure 7.22 – Mise en forme d’une chaîne à envoyer suivant le protocole du simulateur.
387
7 • Traitement complet 7.5 Spécification et conception adaptées
d’une application industrielle
388
7 • Traitement complet 7.5 Spécification et conception adaptées
d’une application industrielle
Éléments matériels
Console PC industriel.
Carte Carte d’acquisition standard 8 entrées/2 sorties analogiques (0-10 V), 24 E/S
d’acquisition numériques (0-5 V).
Bouton poussoir Bouton (avec antirebond). Alimenté en 5 V cc, délivre 5 V si appuyé, 0 V sinon.
Relié à la ligne 0 du port numérique 0 de la carte d’acquisition.
en fonction de la température
Pompe Pompe de type tout ou rien fournissant une pression de 3 bars à 25 °C.
Alimentation externe : 230 V~
Reliée à la ligne 3 du port numérique 0 de la carte d’acquisition (via un relais
électrique 0-5 V cc → 0-230 V~)
389
7 • Traitement complet 7.5 Spécification et conception adaptées
d’une application industrielle
Éléments matériels
capable de scruter une température par seconde (pour les procédés thermiques,
l’évolution de température est généralement lente) et de la fournir sur le port série
sous forme d’une chaîne de caractères.
Le diagramme de contexte de la spécification adaptée est donné sur la figure 7.24.
Centrale Console
Mise
Te
en route
m
Config
pé
Affichage
ra
centrale
tu
re
s
Alim_Dbt_ed
al
gn
Alarme
Si
Bouton poussoir
390
7 • Traitement complet 7.5 Spécification et conception adaptées
d’une application industrielle
Commander
Commande débit
vanne
Gérer 4
Signal ef débits Infos E/S*
Bouton
1 Lire
bouton E/D
E/D Cmd
Signal ed 5
Commander pompe
ACK pompe
T
Infos E/S* Alarme 6
Alim E/D
Contrôler Infos E/S*
vanne T
Init échangeur
E/S OK 9 T
E/S
T° lues* 3 Config centrale Afficher
alarme
Alim Alim 7
Débit ed Débit ef E/D
Alarme
Alarme
Acquérir E/D
T°
Afficher Débits lus*
2
8
figure 7.26. Sur ce diagramme, nous pouvons noter l’apparition de deux nouveaux
processus, chargés d’effectuer une conversion volts → litres/heure en fonction des
tables d’étalonnage.
Observons maintenant le processus 4 : en pratique, il est impossible (sauf si la vanne
intègre un débitmètre et qu’elle est capable de s’ajuster de façon autonome), d’obtenir
à partir d’une commande seule un débit désiré. Il est nécessaire de mettre en place un
algorithme d’asservissement, qui, étant donné un débit, modifie la consigne d’ouver-
ture de l’électrovanne, la cible étant la consigne en litre/heures. C’est pour cette raison
que le diagramme préliminaire donné sur la figure 7.25 doit être légèrement modifié,
afin que Commander Vanne puisse asservir la vanne. De plus, lors d’un asservissement,
© Dunod – La photocopie non autorisée est un délit.
391
7 • Traitement complet 7.5 Spécification et conception adaptées
d’une application industrielle
Étalonnage
Débitmètres* Débits lus*
Débit
Acquérir Signal ef Convertir ef lu
Signal ef
signal ef ef Vérifier
lu
1.3 1.4 débits
1.6
E/D E/D Débis
E/D vérifiés
Alarme
Infos E/S
Élaborer Commande
Contrôler E/D consigne débit
débits
1.6
1.1 E/D
E/D
E/D Alarme
Consigne T° T° lues
Signal ed Acquérir Signal ed
signal ed Convertir
1.2 lu ed
1.5 Débit ed lu
Débits lus*
Étalonnage
Débitmètres*
que les unités de mesure utilisées entre les processus fonctionnels du système sont des
litres/heure pour les débits, et des degrés Celsius pour les températures : les conver-
sions en volts sont réalisées lors de l’acquisition et de la commande.
Lors du passage à la conception, il existe deux possibilités en ce qui concerne le nou-
veau processus fonctionnel Init E/S :
– le processus est découpé, chaque partie d’initialisation étant alors placée dans la
tâche qui utilise le matériel à initialiser ;
– le processus est implémenté dans le programme principal, avant le lancement des
tâches.
Il faut tenir compte d’une contrainte technique liée à l’utilisation d’un port numé-
rique : plusieurs tâches utilisent une ligne du même port, mais le port ne doit être
initialisé qu’une seule fois avant tout autre accès. Pour le port numérique, seule la
seconde solution peut être implémentée sans introduire d’éléments supplémentaires
de synchronisation entre les tâches. En ce qui concerne le port série et les entrées
ou sorties analogiques, n’importe laquelle des deux solutions peut être envisagée.
Afin de conserver une cohérence de l’initialisation, nous choisirons la seconde solution,
qui présente l’avantage supplémentaire de demander peu ou pas de modification à
l’intérieur des tâches par rapport à la programmation sur simulateur.
392
7 • Traitement complet 7.5 Spécification et conception adaptées
d’une application industrielle
Mise en route
<T> 3
Nominal Nominal
E/S OK
<E> 1,2,4,6,8
Alarme
<D> 1,2,8
<T> 5,7
Attente
© Dunod – La photocopie non autorisée est un délit.
opérateur
Erreur E/S OK
<D> 4,6
Arrêt
393
7 • Traitement complet 7.6 Implémentation de la commande réelle
d’une application industrielle
Le diagramme DARTS n’est donc presque pas modifié par rapport au diagramme
DARTS présenté sur la figure 7.7. L’unique point est l’ajout d’une lecture du module
de données Débits par la tâche Commander Vanne, qui pourrait s’appeler Asservir
Vanne.
Écrire
Températures Acquérir
températures HTR (1 000 ms)
Afficher
Affichage
394
7 • Traitement complet 7.6 Implémentation de la commande réelle
d’une application industrielle
/*
ComCentrale.h
Fichier d’en-tête de libraire de communication avec la centrale
*/
/* Différentes constantes de débit du port série */
#define BAUDS_600 0
#define BAUDS_1200 1
#define BAUDS_2400 2
#define BAUDS_4800 3
#define BAUDS_9600 4
#define BAUDS_19200 5
/* Différentes constantes de noms de port série */
#define COM1 0
#define COM2 1
#define COM3 2
#define COM4 3
395
7 • Traitement complet 7.6 Implémentation de la commande réelle
d’une application industrielle
int Initialiser_Centrale();
/* Initialise la centrale
Renvoie: 0 si tout s’est bien passé, code d’erreur sinon */
int Lire_Centrale(Temperatures_t *T);
/* Lit les températures (lecture bloquante sur port série)
Nécessite: centrale initialisée
Entraine: T contient les températures lues
Renvoie: 0 si tout s’est bien passé, code d’erreur sinon */
int Fermer_Centrale();
/* Termine la connexion avec la centrale
Nécessite: centrale initialisée
Entraine: la centrale n’est plus initialisée
396
7 • Traitement complet 7.6 Implémentation de la commande réelle
d’une application industrielle
La carte d’acquisition, de marque National Instruments® pour le cas étudié, est fournie
avec deux pilotes de périphérique unifiés pour toute carte fournie par ce construc-
teur. Ces drivers, existant sur plusieurs plates-formes, fournissent bien entendu des
librairies binaires, associées à des fichiers en-tête (.h) de spécification des fonctions
implémentées dans les librairies. La démarche est identique à celle utilisée pour la
centrale. Pour plus de simplicité, nous choisirons la librairie traditionnelle, bien que
la nouvelle librairie mx offre des performances bien meilleures.
Voici la spécification du module d’abstraction orienté procédé de la carte d’acqui-
sition. On peut remarquer la présence de nombreuses constantes qui permettront,
le cas échéant, de modifier certains paramètres matériels :
/* mondaq.h
Abstraction des accès à la carte d’acquisition pour l’échangeur */
#ifndef _MONDAQ_H_
#define _MONDAQ_H_
int Initialiser_DAQ();
/* Initialise les E/S numériques sur la carte
Renvoie: 0 si tout s’est bien passé, code d’erreur sinon */
float Lire_Debit_Ef_DAQ();
/* Renvoie le débit d’eau industrielle en l/h
© Dunod – La photocopie non autorisée est un délit.
397
7 • Traitement complet 7.6 Implémentation de la commande réelle
d’une application industrielle
#endif
int Initialiser_DAQ() {
int status;
/* Initialisation du port numérique en écriture */
if ((status=DIG_Prt_Config(CARTE_DAQ_ID, PORT_CARTE_DAQ,0,1))!=0)
return status;
/* La ligne correspondant au bouton est mise en lecture */
if ((status=DIG_Line_Config(CARTE_DAQ_ID,
PORT_CARTE_DAQ,LIGNE_BOUTON,0))!=0) return status;
/* Les débitmètres et l’électrovanne sont alimentés */
if ((status=DIG_Out_Line(CARTE_DAQ_ID, PORT_CARTE_DAQ,
LIGNE_ALIM_DBT_EF,1))!=0) return status;
if ((status=DIG_Out_Line(CARTE_DAQ_ID, PORT_CARTE_DAQ,
LIGNE_ALIM_DBT_ED,1))!=0) return status;
if ((status=DIG_Out_Line(CARTE_DAQ_ID, PORT_CARTE_DAQ,
LIGNE_ALIM_EV_EF,1))!=0) return status;
return 0;
}
BYTE Lire_Bouton_DAQ() {
i16 etat;
DIG_In_Line(CARTE_DAQ_ID, PORT_CARTE_DAQ, LIGNE_BOUTON,&etat);
return etat;
}
void Commander_Pompe_DAQ (BYTE Com ) {
DIG_Out_Line(CARTE_DAQ_ID, PORT_CARTE_DAQ, LIGNE_POMPE, Com);
}
void Commander_Alarme_DAQ(BYTE On) {
DIG_Out_Line(CARTE_DAQ_ID, PORT_CARTE_DAQ, LIGNE_ALARME, On);
}
float Lire_Debit_Ef_DAQ() {
f64 val;
AI_VRead(CARTE_DAQ_ID, PORT_DBT_EF, 1, &val);
return Conversion_volt_l_h(val);
}
float Lire_Debit_Ed_DAQ() {
f64 val;
AI_VRead(CARTE_DAQ_ID, PORT_DBT_ED, 1, &val);
return Conversion_volt_l_h(val);
}
void Commander_Vanne_DAQ (float Com ) {
AO_VWrite(CARTE_DAQ_ID, PORT_EV_EF, Conversion_l_h_volt(Com));
}
398
7 • Traitement complet 7.6 Implémentation de la commande réelle
d’une application industrielle
Il ne reste plus alors qu’à implémenter le corps du module procede, ce qui est trivial
étant donné que tous les accès au matériel sont faits de façon haut niveau.
Chaque tâche n’étant pas en attente sur une synchronisation ou bien une boîte aux
lettres (comme Commander_Vanne) se voit ajouter au début de son code un appel
© Dunod – La photocopie non autorisée est un délit.
399
7 • Traitement complet 7.6 Implémentation de la commande réelle
d’une application industrielle
Initialiser;
Starter.Go
end;
Nous pouvons constater que les clauses d’utilisation du simulateur ont été remplacées
par les clauses d’utilisation du procédé. Il n’y a plus alors qu’à implémenter les
accès au matériel dans le corps du paquetage Procédé.
La plupart des drivers de matériel (dans notre cas, par exemple, le driver de la carte
d’acquisition et le driver de la centrale d’acquisition de température) sont fournis
en C, sous forme par exemple d’une librairie au format binaire, accompagnée d’un
ou plusieurs fichiers d’en-tête C (fichiers d’extension .h).
Il en résulte l’obligation, soit de trouver un binding (interfaçage entre Ada et une
bibliothèque C) existant, comme on en trouve beaucoup sur internet, soit d’écrire
soi-même ce binding. Dans le cas présent, nous n’avons trouvé aucun binding pour
Ada. Le plus simple est alors d’adopter la même démarche que dans le paragraphe
précédent, c’est-à-dire d’écrire en C des modules d’accès de haut niveau à la centrale,
et à la carte d’acquisition. À partir de là, il reste à interfacer un paquetage Ada avec
les fonctions C définies dans mondaq.h et MaComCentrale.h (voir section précédente).
Ainsi, par exemple, la fonction C Lire_Debit_Ef_DAQ serait interfacée de la façon
suivante :
function Lire_Debit_Ef_Daq return Float;
pragma Import(C,Lire_Debit_Ef_Daq);
Le même principe appliqué à toutes les fonctions d’accès au procédé montre que,
même si l’on a choisi le langage Ada, dans la plupart des cas, l’accès au matériel
nécessite une partie de programmation en C, à moins que l’on ait le courage d’implé-
menter un driver spécifique en Ada.
400
7 • Traitement complet 7.6 Implémentation de la commande réelle
d’une application industrielle
401
7 • Traitement complet 7.6 Implémentation de la commande réelle
d’une application industrielle
Les ports analogiques sont encore plus simples à commander ou lire, de plus, la mise
à l’échelle est simplifiée grâce à l’utilitaire Measurement and Automation eXplorer
(figure 7.34). Ce logiciel permet de créer très simplement un vi de lecture en litre/
heure, comme le montre la figure 7.35 sur l’exemple de la lecture de débit d’eau
distillée.
Après l’implémentation, généralement relativement simple, des vi d’accès au matériel,
il ne reste plus alors qu’à remplacer les appels au simulateur contenus dans les tâches
et le programme principal (figure 7.36) par des appels aux vis nouvellement créés.
402
7 • Traitement complet 7.6 Implémentation de la commande réelle
d’une application industrielle
© Dunod – La photocopie non autorisée est un délit.
403
7 • Traitement complet 7.6 Implémentation de la commande réelle
d’une application industrielle
Figure 7.35 – Utilisation d’une voie virtuelle utilisant une échelle personnalisée.
404
7 • Traitement complet 7.7 Conclusion
d’une application industrielle
7.7 Conclusion
À travers cet exemple, nous pouvons constater que chaque langage a des avantages
et des faiblesses spécifiques.
Le langage C présente l’avantage d’être le langage de base dans lequel on peut trouver
tout driver. Cependant, son inconvénient majeur est son typage faible (c’est souvent
au programmeur que revient l’effort de vérifier la cohérence des types), et sa relative
difficulté de programmation, ainsi que la multitude d’implémentations partielles
des normes POSIX.
Le langage Ada présente l’élégance inhérente au multitâche natif, et l’avantage du
typage fort (le déverminage du programme écrit en C a pris aux auteurs environ
10 fois plus de temps que celui du programme écrit en Ada). Il présente cependant
l’inconvénient de ne disposer d’aucun driver, et il échoit souvent au programmeur
d’avoir à écrire un binding entre un driver fourni en langage C et Ada, le tout néces-
sitant une partie de programmation en C.
Le langage LabVIEW présente l’avantage d’offrir un multitâche implicite et naturel.
Les drivers d’éléments matériels se trouvent relativement facilement (et même en leur
absence, il est très simple d’effectuer un binding à l’aide d’une libraire partagée).
© Dunod – La photocopie non autorisée est un délit.
Son inconvénient est que, pour le moment, un seul système d’exploitation temps réel,
aux fonctionnalités relativement restreintes comparées à un exécutif temps réel pour
C ou Ada, est disponible. Cela rend le langage particulièrement bien adapté aux
programmes de contrôle de procédé sans contraintes de temps strictes (comme c’est
le cas sur l’exemple de l’échangeur).
405
8 • ÉTUDE AVANCÉE
DES SYSTÈMES TEMPS RÉEL
8.1 Introduction
8.1.1 Présentation générale de l’ordonnancement
Les méthodes d’ordonnancement, qui ont été décrites dans le chapitre 4, sont indé-
pendantes des caractéristiques temporelles intrinsèques des tâches. En effet, l’affec-
tation des priorités à un ensemble de tâches se fait de façon non formelle.
Les algorithmes basés sur les priorités peuvent être à priorités fixes ou variables.
Dans le cas où les priorités sont variables, l’ordonnanceur met à jour les priorités à
chaque réveil des tâches ou aux instants des appels de primitives, ou à chaque top
d’horloge. Il s’appuie ensuite sur le répartiteur (dispatcher) qui choisit la tâche prête
la plus prioritaire et lui octroie le processeur. On peut noter qu’à ce jour, tous les
exécutifs du commerce se basent sur des priorités fixes. Cependant, il convient de
nuancer l’aspect statique des priorités par l’utilisation de protocoles de gestion de
ressources : dans ce cas, la priorité des tâches, bien que fixe au début, peut évoluer
au cours de la vie de l’application.
Les algorithmes à priorités sont appelés algorithmes d’ordonnancement en ligne
car ils se basent sur l’état instantané du système pour prendre une décision. Les algo-
rithmes présentés dans le chapitre 4 appartiennent à cette catégorie. Les ordonnan-
cements basés sur des séquences préétablies s’appellent des algorithmes hors-ligne.
Dans ce cas, la séquence d’ordonnancement est construite à partir d’une vision
complète du système, puis exécutée par un séquenceur.
Lorsque le système contrôlé impose des contraintes strictes de temps de réponse, se
© Dunod – La photocopie non autorisée est un délit.
407
8 • Étude avancée 8.1 Introduction
des systèmes temps réel
408
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
409
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
410
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
Comme nous l’avons vu dans les exemples précédents, ces tâches correspondent par
exemple à des tâches de scrutation de capteurs pour effectuer des mesures régulières
de grandeurs physiques. La modélisation des tâches périodiques va reposer sur trois
paramètres temporels :
– r0 : date de réveil de la tâche, c’est-à-dire la première date à laquelle la tâche
demande le processeur ;
– C = Cmax : durée d’exécution maximale définie avec les restrictions exposées
précédemment ;
– T : période d’exécution, c’est-à-dire la fréquence de renouvellement de la demande
d’exécution de la tâche.
La date de réveil rk de la kième instance ou occurrence d’une tâche est donc définie
par :
r k = r 0 + kT (8.1)
Afin de limiter au maximum les indéterminismes d’exécution, nous supposons que
les tâches sont non réentrantes, c’est-à-dire que, lors d’une nouvelle demande d’exé-
cution, la tâche doit avoir terminé son exécution précédente. Comme nous nous
sommes placés dans un environnement d’exécution stricte, la tâche doit avoir terminé
son exécution avant la fin de sa période d’exécution ; la tâche est dite à échéance
sur requête.
La figure 8.1 présente l’exécution de deux occurrences d’une tâche périodique à
échéance sur requête dans un diagramme de Gantt. Nous pouvons noter que la tâche
s’exécute de façon complète dans la première occurrence, c’est-à-dire que, lorsqu’elle
obtient le processeur, elle garde pendant toute sa durée d’exécution C = Cmax. En
revanche, lors de la deuxième occurrence, la tâche est préemptée une fois lors de son
exécution et son exécution s’effectue alors en deux fois.
© Dunod – La photocopie non autorisée est un délit.
T
t
r0 r1 r2
C max
C max
411
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
L’échéance de la tâche peut être plus courte que la fin de sa période. Dans ce cas,
il est nécessaire de définir un nouveau paramètre qui est le délai critique D de la
tâche qui correspond au délai au bout duquel la tâche doit être terminée, délai mesuré
par rapport à la date de réveil de l’instance considérée.
La modélisation des tâches périodiques strictes va donc reposer sur quatre paramètres
temporels :
– r0 : date de réveil de la tâche, c’est-à-dire la première date à laquelle la tâche
demande le processeur ;
– C = Cmax : durée d’exécution maximale définie avec les restrictions exposées
précédemment ;
– D : délai critique, c’est-à-dire le délai au bout duquel la tâche doit être terminée
par rapport à la date de réveil de l’instance en cours.
– T : période d’exécution, c’est-à-dire la fréquence de renouvellement de la demande
d’exécution de la tâche.
Dans ce contexte, il est possible de définir l’échéance dk de la kième instance d’une
tâche par l’équation suivante :
d k = r k + D = r 0 + kT + D (8.2 )
La figure 8.2 représente l’exécution d’une telle tâche pour deux occurrences dans
un diagramme de Gantt. La zone d’exécution de la tâche se situe donc entre les dates
rk et dk, et la zone comprise entre dk et rk+1 ne peut pas être utilisée pour l’exécu-
tion de la tâche.
T
D
t
r0 d0 r1 d1 r2
C max
C max
Nous allons nous intéresser aux trois paramètres temporels (Ci, Di, Ti ) qui caracté-
risent une tâche ti. Afin d’avoir un comportement classique en temps réel (pas de
réentrance des tâches : Di ≤ Ti ), ceux-ci doivent impérativement respecter les condi-
tions de base suivantes :
0 ≤ Ci ≤ Di ≤ Ti (8.3)
Par conséquent, il est possible de représenter graphiquement l’espace décrivant les
différents triplets caractérisant une tâche donnée. Il est nécessaire pour cela de se
412
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
D : Délai critique
C max
C max T : période
C max
C : durée d’exécution
Il est évident qu’une telle représentation tridimensionnelle n’est pas d’une utilisation
très aisée. Aussi il est préférable de travailler dans les projections de ce volume sur
les différents plans. En particulier, nous focaliserons notre attention sur un plan D,T
(délai critique-période) représenté sur la figure 8.3 et sur le plan T,C (période-durée
© Dunod – La photocopie non autorisée est un délit.
413
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
D : Délai critique
Droite D = T
(tâche à échéance sur requête)
C max
C max T : période
Figure 8.4 – Représentation graphique dans le plan D,T des paramètres temporels
d’une tâche périodique avec une échéance plus petite que la période.
T : période
Droite D = T = C
(tâche à échéance sur requête)
C max
Figure 8.5 – Représentation graphique dans le plan C,T des paramètres temporels
d’une tâche périodique avec une échéance plus petite que la période.
En ce qui concerne les tâches dites apériodiques, le seul paramètre connu est la durée
d’exécution C de la tâche. La date de réveil ou demande processeur est aléatoire
car elle dépend du contexte d’évolution du procédé et ne peut donc pas être connue
414
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
a priori. Nous pouvons donc représenter son exécution dans le diagramme de Gantt
de la figure 8.6. Les deux dates de réveil r et r¢ ont été choisies aléatoirement. De la
même manière que pour les tâches périodiques les tâches apériodiques peuvent être
préemptées au cours de leurs exécutions et donc d’exécuter en plusieurs fois lors d’une
instance.
t
r r’
C max
C max
≤ ∆ min
D D
t
r d r’ d’
C max
C max
415
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
L ≤ D –C et L max = D – C (8.7 )
t
r
L d
r C max Lmax d
416
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
t
r d
Dl
r Dl max C max d
Di
Figure 8.10 – Illustration des paramètres « début et fin d’exécution » d’une tâche ti.
La première inégalité est une égalité dans le cas d’une exécution au plus tôt (si,k = rk),
la deuxième inégalité est une égalité dans le cas d’une exécution au plus tard (ei,k = dk)
et la troisième inégalité est une égalité dans le cas d’une exécution sans préemption
de la tâche (ei,k-si,k = C).
Deux paramètres supplémentaires permettent de caractériser le comportement de
la tâche lors de ses différentes instances d’exécution et de qualifier cette exécution.
Nous avons ainsi :
© Dunod – La photocopie non autorisée est un délit.
417
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
Di
TR i , k
Au cours de l’exécution de l’instance d’une tâche, certains paramètres, qui sont fonc-
tion de l’instant t, peuvent être utiles à l’ordonnanceur. Étant donnée une tâche
avec une durée d’exécution C, nous pouvons définir le temps d’exécution restant
C(t) fonction du temps processeur déjà alloué à la tâche Cexécuté au cours de cette
instance. Ainsi, nous pouvons identifier les trois paramètres suivants (figure 8.12) :
– le temps d’exécution restant C(t) : C(t) = C – Cexécuté
– le délai critique dynamique D(t), c’est-à-dire le temps restant avant la prochaine
échéance, soit :
D (t ) = d – t (8.12)
– la laxité dynamique L(t), c’est-à-dire le temps avant le début d’exécution de la
tâche, soit :
L (t ) = D (t ) – C (t ) = d – t – C (t ) (8.13)
D
C L(t)
t
r d
C(t)
t
D(t)
418
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
L
1
Li
exécution de la tâche
© Dunod – La photocopie non autorisée est un délit.
attente de la tâche
Fin tâche 2
Di
Dépassement échéance tâche
419
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
En ce qui concerne les quatre paramètres de base d’une tâche ti (ri, Ci, Di, Ti),
nous pouvons analyser l’origine de leur détermination :
– ri : Dans le cas général, les tâches sont à départ simultané ; mais une tâche peut
être retardée au réveil de l’application pour prendre en compte par exemple la
précédence. Ce réveil non simultané est obtenu en insérant au début du code de
la tâche une primitive de type « DELAI ».
– Ci : La durée de la tâche est directement liée au code de la tâche. On évalue
généralement la durée d’exécution pire cas Cmax et une durée minimale Cmin.
– Di : Ce paramètre permet au concepteur de limiter le temps de réponse de la
tâche. Il peut être codé au niveau d’une tâche en utilisant un temporisateur type
« chien de garde ou watchdog ».
– Ti : Cette périodicité de la tâche est fixée par les besoins de la fonction : tâche de
scrutation ou acquisition (polling)
Ces choix sont donc soit liés aux besoins de l’application (début d’exécution, pério-
dicité, durée du code) soit imposés par le concepteur (échéance). Aussi, dans la repré-
sentation graphique des paramètres possibles d’une tâche périodique, nous avons
une limitation due à ces choix. Ainsi, viennent s’ajouter des contraintes temporelles
exprimées dans le cahier des charges de l’application comme par exemple :
– une période maximale Tmax (Ti = Tmax), correspondant, par exemple, à la fré-
quence minimale d’échantillonnage ;
– une période minimale Tmin (Ti = Tmin), correspondant par exemple une période
inutile d’acquisition (trop de données à analyser ou redondance des données) ;
– une échéance maximale Dmax (Di = Dmax), afin d’obtenir un meilleur temps de
réponse.
La figure 8.4, représentant une tâche périodique quelconque, est modifiée selon ces
nouvelles contraintes.
De façon générale, cette précédence entre tâches est mise en œuvre à l’aide de pri-
mitives de synchronisations ou communications intégrées dans le code des deux
tâches. Prenons un exemple simple et considérons que deux tâches t1 et t2 ne sont pas
420
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
D : Délai critique
Droite D = T
(tâche à échéance sur requête)
Zone de choix
D max des paramètres temporels
de la tâche
C max
T : période
atomiques, c’est-à-dire que ces primitives sont situées à l’intérieur du code de chacune
des tâches (figure 8.15). La séquence d’exécution, présentée sur la figure 8.15, montre
l’attente de l’exécution de la partie t2-1 de la tâche t2 par la partie t1-2 de la tâche
t1. Nous avons l’effet de la synchronisation de la tâche t1 par la tâche t2.
La première étape à réaliser est la transformation des tâches en tâches atomiques afin
d’avoir un modèle de tâches conforme à notre étude. Ce travail est simple à effectuer,
il consiste à découper les tâches au niveau des primitives de synchronisations (dans
notre exemple : figure 8.16) ou de communications. Ce découpage conduit à quatre
tâches qui ne comportent maintenant aucune primitive bloquante à l’intérieur du
code, à l’exception d’une primitive d’attente qui peut se situer au début du code.
Ainsi, nous pouvons donner une règle générale de transformation pour obtenir
des tâches sous forme atomique :
tâche τ1 tâche τ2
τ1-1 τ2-1
ATTENDRE_EVT(evt1) SIGNAL_EVT(evt1)
© Dunod – La photocopie non autorisée est un délit.
τ1-2 τ2-2
Séquence d’exécution
τ1-1 τ1-2
tâche τ1 t
τ2-1 τ2-2
tâche τ2 t
421
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
SIGNAL_EVT(evt1)
τ1 - 1 τ1 - 2
τ2 -11 τ2 - 2
Une remarque importante peut être faite au niveau de ce découpage en tâche atomique
et des primitives de synchronisation. En effet, après avoir réalisé le découpage en
tâches atomiques, les primitives de synchronisation ne sont plus utiles si l’on associe
aux tâches obtenues le graphe de relation. Ce graphe, dit graphe de précédence,
traduit effectivement toutes les relations nécessaires au comportement correct de
l’application conforme au comportement initial souhaité par le concepteur. Comme
nous l’analyserons dans la suite de ce chapitre, l’existence ou non de ces primitives
d’« attente » au début des codes des tâches atomiques peut conduire à des dysfonc-
tionnements ou anomalies de comportement.
422
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
Une tâche ti de durée totale Ci qui utilise une ressource critique R possède dans son
code une zone protégée, appelée section critique, pendant laquelle elle accède à
cette ressource. Cette section critique Sci est protégée par des primitives permettant
de gérer l’exclusion mutuelle comme un sémaphore. Par conséquent, en termes de
temps, l’exécution de cette tâche peut être décrite par 3 valeurs (figure 8.18) :
– Ci,a : temps avant la section critique ;
– Ci,b : durée de la section critique (ressource utilisée) ;
– Ci,g : temps après la section critique.
Ces trois valeurs doivent satisfaire à l’égalité suivante :
Utilisation ressource R
(section critique Sci)
Figure 8.18 – Représentation temporelle d’une tâche contenant une section critique.
Si une tâche ti de durée totale Ci utilise plusieurs ressources, les sections critiques
doivent être correctement imbriquées. Soit Sci,1 (resp. Sci,2) la section critique de
la tâche ti utilisant la ressource R1 (resp. R2), nous devons avoir l’une des conditions
suivantes :
R1 1 2 7 10
R2 4 5 1 10
R3 5 2 3 10
423
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
La figure 8.19 représente cette répartition des différentes sections critiques Sci,, Sci,2
et Sci,3 sur la durée totale Ci de la tâche. Nous pouvons vérifier que ces sections criti-
ques vérifient deux à deux l’une des trois conditions 8.17 :
Utilisation ressources R2 et R3
1 2 3 4 5 6 7 8 9 10
Ci
Figure 8.19 – Représentation temporelle d’une tâche contenant trois sections critiques.
Pour une tâche périodique donnée ti, définie avec les quatre paramètres de base
(ri, Ci, Di, Ti), nous pouvons définir les occupations du processeur :
– Le facteur d’utilisation u comme le pourcentage du processeur nécessaire à son
exécution sur sa période Ti, soit :
ui = Ci ⁄ Ti (8.18)
– Pour une tâche périodique donnée ti, qui n’est pas à échéance sur requête, on
définit le facteur de charge ul,i comme le pourcentage du processeur nécessaire
à son exécution sur son délai critique Di :
u i,1 = C i ⁄ D i (8.19)
424
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
Tâche ri Ci Di Ti ui ul,i
τ1 0 2 6 6 0,333 0,333
τ2 0 1 8 8 0,125 0,125
τ3 0 2 10 12 0,166 0,2
Les facteurs d’utilisation ui et les facteurs de charge ul,i de chacune des tâches sont
calculés et présentés dans les deux dernières colonnes du tableau 8.2. Le facteur
d’utilisation U et le facteur de charge Ul de la configuration sont donc les sui-
vants :
n n
2 1 2 5
U = ∑ ui = ∑C i ⁄ T i = --- + --- + ------ = --- = 0,625
6 8 12 8
i =1 i =1
n n
2 1 2 79
Ul = ∑ ul,i = ∑Ci ⁄ Di = --- + --- + ------ = --------- = 0,625
6 8 10 120
i =1 i =1
Nous pouvons noter que les deux facteurs U et Ul sont identiques si toutes les tâches
sont à échéance sur requête.
De façon évidente, nous avons une condition nécessaire d’ordonnançabilité de la
configuration définie à partir du facteur d’utilisation et du facteur de charge d’une
configuration de tâches périodiques s’exécutant sur une plate-forme monoprocesseur.
Cette condition exprime que l’utilisation du processeur ne peut pas dépasser les
100 %, soit l’inégalité à vérifier :
n n
U = ∑ ui = ∑Ci ⁄ Di ≤1 (8.22)
© Dunod – La photocopie non autorisée est un délit.
i =1 i =1
m Période d’étude
Pour une configuration de n tâches périodiques T = {t1, t2, …, tn} à départ simul-
tané (∀i, ri = 0), la séquence d’exécution se retrouve régulièrement dans une situa-
tion identique au niveau des demandes processeur lorsque toutes les tâches sont à
nouveau en phase. Ainsi, l’étude de la séquence d’exécution, produite par un algo-
rithme d’ordonnancement donné, peut se limiter à un temps H appelé période
d’étude ou méta-période ou cycle majeur qui est défini par :
425
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
Ainsi, la séquence d’exécution se répète avec une période H selon un motif défini
lors de la première période d’étude (figure 8.20).
Si au moins une des tâches possède une date de réveil différente de celles des autres
tâches, alors la séquence d’exécution commence par une phase dite transitoire
dont la durée maximale Htransitoire est donnée par :
H transitoire = Max { r i } i ∈[ 1,n ] + PPCM {T i } i ∈[ 1,n ] (8.24 )
Considérons l’exemple précédent pour lequel nous avons décalé la date de réveil
de la tâche t2 de 2 (tableau 8.3). La durée maximale de la phase transitoire de la
séquence d’exécution est donc :
H transitoire = Max { r i } i ∈[ 1,3 ] + PPCM {T i } i ∈[ 1,3 ]
= Max { 0,2,0 } + 24 = 2 + 24 = 26
En réalité, l’analyse de la séquence d’exécution montre que la périodicité H = 24
commence dès le début de la séquence (figure 8.22). En revanche, la séquence d’exé-
cution n’est pas la même que celle de la configuration avec les tâches à départ simul-
426
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
Tâche ri Ci Di Ti
τ1 0 2 6 6
τ2 2 1 8 8
τ3 0 2 10 12
τ1 t
0 5 10 15 20 25 30
τ2 t
0 5 10 15 20 25 30
τ3 t
0 5 10 15 20 25 30
H
τ1 t
0 5 10 15 20 25 30
τ2 t
0 5 10 15 20 25 30
τ3 t
0 5 10 15 20 25 30
H
tanée (tableau 8.2). Il est important de noter qu’il existe d’autres séquences d’exé-
© Dunod – La photocopie non autorisée est un délit.
427
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
Tâche ri Ci Di Ti
τ1 0 1 4 4
τ2 1 3 6 6
τ3 3 1 4 4
τ1 t
0 5 10 15 20 25 30
τ2 t
0 5 10 15 20 25 30
τ3 t
0 5 10 15 20 25 30
Htransitoire H H
428
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
T libre = ( 1 – 0,625 ) × 24 = 9
Il est important de remarquer que, travaillant en général avec des algorithmes
d’ordonnancement au plus tôt (les temps creux surviennent lorsqu’aucune tâche n’est
prête), les temps libres se situeront préférentiellement en fin de séquence d’exécution.
Ce résultat peut être étendu pour une configuration de tâches à départ non simul-
tané en considérant uniquement la partie de la séquence reproduite à l’infinie (de
durée H) qui se situe après la phase transitoire. En effet, des temps libres processeur
peuvent se situer dans la phase transitoire de la séquence d’exécution correspondant
seulement au fait que les tâches ne sont pas à départ simultané.
τ1 t
0 5 10 15 20 25 30
τ2 t
0 5 10 15 20 25 30
τ3 t
0 5 10 15 20 25 30
Temps libre processeur
t
Pour H, Tlibre = 9
m Séquence saturée
Une séquence est dite saturée si l’allocation du processeur est complète (U = 1),
c’est-à-dire que la séquence ne comporte pas de degré de liberté. Ainsi, pour une
configuration de n tâches périodiques à départ simultané : T = {t1, t2, …, tn} s’exé-
cutant sur un système monoprocesseur, cela peut s’exprimer en fonction des para-
© Dunod – La photocopie non autorisée est un délit.
La figure 8.25 représente une séquence d’exécution saturée pour une configuration
à deux tâches t1 (r1, C1, D1, T1) et t2 (r2, C2, D2, T2) avec r1 < r2 et d1 < d2. Ainsi,
en fonction de la relation 8.26, nous devons vérifier :
d2 – r1 = C1 + C2
429
8 • Étude avancée 8.2 Modélisation des tâches
des systèmes temps réel
T1
D1
τ1 t
r1 d1 r1 + T1
C1
T2
D2
τ2 t
r2 d2 r2 + T2
C2
Séquence t
r1 d2
Dans la suite de ce chapitre, nous allons avoir à analyser des configurations de tâches
qui peuvent avoir des départs différés. Nous avons vu dans les sections précédentes
que le fait d’avoir un départ différé d’au moins une tâche peut avoir une incidence
sur la durée d’analyse puisque cela introduit une phase transitoire dans l’exécution
de l’application. De plus les tâches peuvent avoir des dates de réveil différées par
rapport aux autres tâches qui évoluent lors de plusieurs lancements de l’application.
Il serait donc intéressant de déterminer l’ordonnançabilité de la configuration dans
la situation de pire cas en ce qui concerne le départ des tâches.
Sans réaliser une démonstration rigoureuse, il est aisé de voir que la situation de pire
cas se produit lorsque toutes les tâches sont à départ simultané. Ainsi, l’ordonnan-
çabilité d’une configuration peut être vérifiée lorsque les tâches sont toutes à départ
simultané : activation au même instant appelé instant critique (pire cas). L’exemple,
présenté sur la figure 8.26, montre les trois séquences d’exécution obtenues pour
une configuration de deux tâches dont les paramètres sont donnés dans le tableau 8.5.
La tâche t1 a une date de réveil qui prend les valeurs 4, 2 et 0. Pour ces différentes
valeurs de dates de réveil, nous obtenons respectivement les temps de réponse de la
deuxième tâche t2 égaux à 12, 13 et 14. Ainsi, nous pouvons conclure sur cet exemple
que la situation la plus critique pour l’exécution de cette configuration se situe
lorsque les deux tâches sont en phase.
Ainsi, nous étudierons autant que faire se peut des configurations de tâche à départ
simultané (instant critique), sachant que la même configuration avec un ou plusieurs
départs différés peut conduire à une situation ordonnançable. En effet, si une confi-
guration de tâches est ordonnançable avec les tâches à départ simultané, elle le sera
obligatoirement si une ou plusieurs tâches sont à départ différé. En revanche, si une
430
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
configuration de tâches n’est pas ordonnançable avec les tâches à départ simultané,
la même configuration avec des tâches à départ différé peut être ordonnançable.
Tâche ri Ci Di Ti
τ1 4,2,0 1 4 4
τ2 0 10 14 14
τ1 t
τ2 t
temps de réponses = 12
τ1 t
τ2 t
temps de réponses = 13
τ1 t
τ2 t
temps de réponses = 14
Figure 8.26 – Exemple de séquences d’exécution pour une configuration à deux tâches périodiques
avec trois dates de réveil différentes : analyse de l’instant critique.
© Dunod – La photocopie non autorisée est un délit.
8.3 Ordonnancement
des tâches indépendantes périodiques
8.3.1 Algorithmes d’ordonnancement à priorités fixes
m Algorithme d’ordonnancement « Rate Monotonic »
431
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
n 1
--n-
U = ∑C i ⁄ T i ≤ n 2 – 1 (8.27)
i =1
U : facteur d’utilisation
1
0,82
0,78
0,69
n : nombre de tâches
1 2 3
Prenons l’exemple de la configuration à trois tâches décrite dans le tableau 8.6. Nous
vérifions que nous sommes en présence d’une configuration de tâches indépen-
dantes, périodiques, à échéance sur requête et à départ simultané. Les priorités ont
été affectées selon l’algorithme d’ordonnancement RM. Le facteur d’utilisation U
de cette configuration est donné par :
432
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
3 1
20 40 100 --3-
U = ∑C i ⁄ T i = --------- + --------- + --------- ≈ 0,75 ≤ 3 2 – 1 ≈ 0,779
100 150 350
i =1
Par conséquent, nous pouvons en déduire que cette configuration de tâches est
ordonnançable. Nous pouvons construire une partie de la séquence qui aura pour
période H :
Cette séquence, représentée sur la figure 8.28, montre en particulier les trois préemp-
tions de la tâche de plus faible priorité t3 alors que la tâche de plus forte priorité t1
s’exécute dés sa demande (dates de réveil).
Priorité
Tâche ri Ci Di Ti
selon RM
τ1 0 20 100 100 3
τ2 0 40 150 150 2
τ1 t
100 200 300
τ2 t
100 200 300
τ3 t
100 200 300
© Dunod – La photocopie non autorisée est un délit.
Préemption de la tâche 3
τlibre t
100 200 300
Nous pouvons noter aussi la présence de temps libres du processeur qui sur l’ensemble
de la période d’étude sont :
433
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
La condition, exprimée par l’équation 8.27, est très restrictive (valeur minimale de U).
Nous pouvons utiliser le théorème de la zone critique qui exprime le fait que si toutes
les tâches sont à départ simultanée et si elles respectent leur première échéance
alors la configuration est ordonnançable quel que soit l’instant d’arrivée des tâches
dans la suite. Cette condition est nécessaire et suffisante si toutes les tâches sont à
départ simultané et suffisante si les tâches sont à départ différé.
Un ensemble de n tâches T = {t1, t2, …, tn} ordonnées suivant les priorités (t1 la
tâche la plus prioritaire et tn la tâche la moins prioritaire) définies par les paramè-
tres temporels (ri, Ci, Di, Ti) est ordonnançable si et seulement si :
i
C t
∀i, 1 ≤ i ≤ n min
0 ≤ t ≤ Di ∑ ----t-j ------
Tj
≤ 1 (8.28)
j =1
La condition suffisante vue précédemment n’est pas satisfaite. Pour savoir si cette
configuration est ordonnançable, il est donc nécessaire de procéder à une simulation
complète sur la période d’étude (H = 24) ou d’utiliser la condition, dite de la zone
critique exprimée par l’équation 8.28. Le calcul est effectué jusqu’au temps Di à
chaque nouvelle activation d’une tâche pour les trois tâches de la configuration. Ainsi,
pour la première tâche avec D1 = 4, nous avons :
C t 1 4 1
-----1- ------ pour t = 4 --- --- = --- → minimum ≤ 1
t T1 4 4 4
434
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
Priorité
Tâche ri Ci Di Ti
selon RM
τ1 0 1 4 4 3
τ2 0 2 6 6 2
τ3 0 2 8 8 1
τ1 t
4 8 12 16 20 24
© Dunod – La photocopie non autorisée est un délit.
τ2 t
4 8 12 16 20 24
τ3 t
4 8 12 16 20 24
τlibre t
4 8 12 16 20 24
435
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
S’il existe au moins une tâche qui n’est pas à échéance sur requête dans la configura-
tion des tâches, alors nous allons utiliser un algorithme d’affectation des priorités
basé sur les délais critiques des tâches au lieu des périodes. Cet algorithme d’ordon-
nancement, appelé « Deadline Monotonic » ou DM (ou Inverse Deadline ou ID),
affecte la priorité la plus grande à la tâche dont le délai critique est le plus petit.
Concernant cet algorithme d’ordonnancement pour une configuration de n tâches
ayant les paramètres (ri, Ci, Di, Ti) avec ri = 0 pour tout i et au moins un délai cri-
tique Di différent de la période Ti , nous avons les résultats suivants :
– l’algorithme d’ordonnancement DM est optimal dans la classe des algorithmes
à priorités fixes, c’est-à-dire que, si une configuration de tâches est ordonnançable,
elle le sera en affectant les priorités selon DM ;
– une condition suffisante d’ordonnançabilité d’une configuration est obtenue
pour un facteur de charge Ul du processeur suivant l’inégalité suivante :
n 1
--n-
U l = ∑ C i ⁄ D i ≤ n 2 – 1 (8.29)
i =1
2 1
1 1 --n-
U l = ∑ C i ⁄ D i = --- + --- = 1,5 > n 2 – 1 = 0,82
2 1
i =1
Par conséquent, nous ne pouvons pas en déduire que cette configuration de tâches
est ordonnançable. Nous pouvons même remarquer que le facteur de charge est
supérieur à 1, mais que le facteur d’utilisation reste inférieur à 1 (condition néces-
saire pour toutes configurations de tâches s’exécutant sur un environnement mono-
processeur). Pour vérifier l’ordonnançabilité de cette configuration nous devons
construire la partie de la séquence sur la période d’étude H = 6.
Cette séquence, représentée sur la figure 8.30, montre en particulier les zones d’exé-
cution impossible qui se situent au-delà de l’échéance de la tâche t2.
436
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
Priorité
Tâche ri Ci Di Ti
selon DM
τ1 0 1 2 2 1
τ2 0 1 1 3 2
τ1 t
3 6
τ2 t
3 6
FIFO. De plus nous supposons dans nos exemples que le quantum est fixé par le
pas de temps discret d’exécution des tâches « 1 ». Remarquons aussi que, dans le cas
de l’ordonnancement « à tourniquet », la notion de période d’étude n’est plus à consi-
dérer étant donné la gestion de la file d’attente asynchrone des périodes des tâches.
Prenons l’exemple de la configuration à deux tâches décrite dans le tableau 8.9. Nous
vérifions que nous sommes en présence d’une configuration de tâches indépen-
dantes, périodiques, à échéance sur requête et à départ simultané. Les priorités ont
été affectées selon l’algorithme d’ordonnancement RM et le facteur d’utilisation est
U = 0,94. La file d’attente du tourniquet est ordonnée au départ avec la tâche t1
en premier et la tâche t2 en second. Dans ces conditions, le tracé de la séquence
437
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
Priorité
Tâche ri Ci Di Ti
selon RM
τ1 0 3 6 6 2
τ2 0 4 9 9 1
Séquence d’exécution RM
τ1
t
5 10 15
τ2 Dépassement d’écheance
t
5 10 15
Séquence d’exécution RR
τ1
t
5 10 15
τ2
t
5 10 15
De cet exemple, nous pouvons conclure que l’algorithme basé sur le tourniquet per-
met d’ordonnancer plus de configurations que l’algorithme RM. Mais, considérons
l’exemple de la configuration à trois tâches décrite dans le tableau 8.10. Nous véri-
fions que nous sommes en présence d’une configuration de tâches indépendantes,
périodiques, à échéance sur requête et à départ simultané. Les priorités ont été affectées
selon l’algorithme d’ordonnancement RM avec une liberté de choix pour les tâches
t2 et t3. Le facteur d’utilisation est U = 0,88. La file d’attente du tourniquet est
composée au départ des tâches t1 en premier, ensuite t2 et enfin t3 en dernier. Dans
ces conditions, le tracé de la séquence d’exécution montre que la séquence d’exé-
cution, basée sur l’ordonnancement « à tourniquet », ne permet pas de respecter
les échéances (tâche t1 échoue).
438
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
Priorité
Tâche ri Ci Di Ti
selon RM
τ1 0 2 3 3 3
τ2 0 1 9 9 2
τ3 0 1 9 9 1
Séquence d'exécution RM
τ1 t
5 10
τ2 t
5 10
τ3 t
5 10
Séquence d'exécution RR
Dépassement d’échéance
© Dunod – La photocopie non autorisée est un délit.
τ1 t
5 10
τ2 t
5 10
τ3 t
5 10
439
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
Dans le cas de l’algorithme « Earliest Deadline First » ou EDF, la priorité des tâches
est variable au cours de leur exécution et fonction de la prochaine échéance. Pour
une instance k d’une tâche ti, la priorité est liée à la prochaine échéance di,k de
cette tâche. À un instant t, la priorité peut être calculée à partir du délai critique
dynamique Di(t) qui s’exprime sous la forme :
D i (t ) = d i,k – t = r i,k + D i – t
Nous pouvons faire les deux remarques suivantes :
– la priorité est variable, elle change au cours de l’exécution ;
– la priorité augmente si le délai critique dynamique de la tâche diminue.
Considérons l’exemple de la configuration à trois tâches décrite dans le tableau 8.11.
Nous vérifions que nous sommes en présence d’une configuration de tâches indé-
pendantes, périodiques, à échéance sur requête et à départ simultané. Les priorités
initiales ont été affectées en fonction du délai critique qui correspond à la première
échéance. Le facteur d’utilisation de la configuration est U = 0,983 et la période
d’étude est H = 60. Par conséquent le temps libre est de 1. Cette configuration n’est
pas ordonnançable par l’algorithme à priorité fixe RM comme le montre le dia-
gramme de Gantt de la figure 8.33. Par conséquent, étant donné la propriété d’opti-
malité de l’algorithme RM, cette configuration n’est ordonnançable par aucun algo-
rithme à priorité fixe. En revanche, la figure 8.34 présente la séquence d’exécution
de cette configuration sur une partie de la période d’étude. L’évolution du délai
critique dynamique, qui conditionne la priorité des tâches, est notée sur chaque
séquence.
Comme pour les algorithmes à priorité fixe, nous disposons d’une condition d’ordon-
nançabilité pour l’algorithme EDF. Pour une configuration de tâches indépendantes,
périodiques, à échéance sur requête et à départ simultané, la condition nécessaire
et suffisante d’ordonnançabilité est exprimée par :
n
U = ∑C i ⁄ Ti ≤ 1 (8.30)
i =1
440
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
Priorité
Tâche ri Ci Di Ti
selon Di
τ1 0 1 3 3 3
τ2 0 1 4 4 2
τ3 0 2 5 5 1
τ1 t
4 8
τ2 t
4 8
Échéance dépassée
τ3 t
4 8
D 1( t ) 3 3 2 3 3 3 3 3 3 3
τ1 t
4 8 12 16 20 24
D 2 (t) 4 3 4 3 4 3 2 4 3 2 4 4 3 4
τ2 t
4 8 12 16 20 24
D 3 (t) 5 4 3 2 5 4 3 2 5 4 3 2 5 4 3 2 5 4 3 2
τ3 t
© Dunod – La photocopie non autorisée est un délit.
4 8 12 16 20 24
Pour tester l’ordonnançabilité d’une configuration de tâches dont au moins une n’est
pas à échéance sur requête, il est possible d’utiliser une analyse basée sur l’occupation
du processeur. Pour l’algorithme EDF, cette condition nécessaire et suffisante d’ordon-
nançabilité est la suivante : « Pour une longueur de séquence H’ correspondant à la
plus petite séquence d’exécution totalement occupée, dite période d’activité (U = 1),
nous devons avoir la relation suivante » :
441
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
n
h′ – D
∀ h′ ∈ H ′, h′ ≥ ∑ ----------------i + 1 ⋅ C i
Tj
(8.31)
i =1
Priorité
Tâche ri Ci Di Ti
selon Di
τ1 0 1 2 3 3
τ2 0 1 3 4 2
τ3 0 2 5 6 1
τ1 t
2 4 6 8 10 12
τ2 t
2 4 6 8 10 12
Échéance dépassée
τ3 t
2 4 6 8 10 12
442
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
Tâche ri Ci Di Ti
τ1 0 1 3 3
τ2 0 1 4 4
τ3 0 2 3 6
Tableau 8.14 – Affectation des priorités selon les algorithmes RM, DM, EDF et ML.
Tâche RM DM EDF ML
© Dunod – La photocopie non autorisée est un délit.
τ1 3 3 3 2
τ2 2 1 1 1
τ3 1 2 2 3
Étant donné que les tâches ne sont pas à échéance sur requête, il est inutile d’utiliser
l’algorithme RM ; il est ici étudié à titre d’exemple. La figure 8.36 montre les quatre
séquences d’exécution obtenues avec les quatre algorithmes. La configuration n’est
pas ordonnançable avec les algorithmes à priorité fixe RM et DM. En revanche, les
séquences tracées avec EDF et ML sont valides.
443
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
Séquence ML t
Séquence ED t
Séquence RM t
Séquence DM t
Tâche ri Ci Di Ti
τ1 0 5 10 10
τ2 0 4 9 9
ED
τ1 t
4 8 12 16 20 24
τ2 t
4 8 12 16 20 24
ML
L i( t ) 5 5 4 4 3 3 2 2 1
τ1 t
4 8 12 16 20 24
L i( t ) 5 4 4 3 3 2 2 1
τ2 t
4 8 12 16 20 24
Figure 8.37 – Comparaison des séquences d’exécution d’une configuration de deux tâches
donnée dans le tableau 8.15 et traitée avec les deux algorithmes EDF et ML.
444
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
Tâche ri Ci Di Ti
τ1 0 2 12 12
τ2 0 2 4 8
© Dunod – La photocopie non autorisée est un délit.
τ3 0 3 ≤7 ≤9
445
8 • Étude avancée 8.3 Ordonnancement
des systèmes temps réel des tâches indépendantes périodiques
Zone de choix
D : Délai critique des paramètres temporels
Droite D = T correspondant à la spécification
(tâche à échéance sur requête)
Zone de choix
Dmax des paramètres temporels
correspondant à la modélisation
Zone de choix
des paramètres temporels
correspondant à l’ordonnançabilité
Cmax
TU
D
9
Dmax = 7
Ci,0 = 3
3 4 5 6 7 8 9
Ci,0 Tu Tmax
446
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
8.4 Ordonnancement
des tâches indépendantes apériodiques
L’ordonnancement des tâches apériodiques est traité dans le cas des tâches périodiques
à contraintes strictes et des tâches apériodiques à contraintes relatives ou à contraintes
strictes (tâches sporadiques). Nous supposons que l’ordonnancement des tâches
périodiques se fait suivant les algorithmes RM, DM ou EDF. Pour répondre à la
demande d’événements déclenchant des tâches apériodiques, nous pouvons consi-
dérer trois méthodes :
– Traitement en arrière plan : les tâches apériodiques sont traitées pendant les
temps d’oisiveté du processeur.
– Traitement par utilisation d’un serveur périodique des tâches apériodiques
en environnement à priorité fixe pour les tâches périodiques (RM) : en plus de
toutes les tâches périodiques, on insère une tâche, appelée serveur, qui est destinée
à traiter les tâches apériodiques et qui possède différentes caractéristiques selon
le modèle du serveur :
• serveur à scrutation,
• serveur ajournable,
• serveur à échange de priorité,
• serveur sporadique,
• serveur à vol de temps creux,
• serveur à échange de priorité étendu.
– Traitement par utilisation d’un serveur périodique des tâches apériodiques
en environnement à priorité variable pour les tâches périodiques (EDF) : en
plus de toutes les tâches périodiques, on insère une tâche, appelée serveur, qui est
destinée à traiter les tâches apériodiques et qui possède différentes caractéristiques :
• serveur dynamique à échange de priorité,
• serveur dynamique sporadique,
• serveur à largeur de bande maximale,
• serveur Earliest Deadline Last (EDL),
• serveur à échange de priorité amélioré.
L’objet de cet ouvrage n’est pas de faire une description exhaustive de l’ensemble de
ces techniques de prise en compte des tâches apériodiques, mais de donner les prin-
© Dunod – La photocopie non autorisée est un délit.
cipes généraux de ces différents traitements en choisissant dans chacune des caté-
gories les plus représentatifs.
447
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
Processus
tâches apériodiques
FIFO ou priorité
File d’attente de priorité faible
Cette méthode ne permet pas de traiter les tâches apériodiques strictes car le temps
de réponse de ces tâches traitées en arrière plan n’est pas borné. Aussi, cette méthode
fonctionne correctement si le processeur a un taux de charge pas trop élevé.
Prenons l’exemple de deux tâches périodiques, indépendantes, à échéance sur requête
et à départ simultané (tableau 8.17). Le facteur d’utilisation de la configuration est
U = 0,75, valeur inférieure à la condition d’ordonnançabilité pour l’algorithme
d’ordonnancement RM (équation 8.27). Nous allons étudier le traitement de trois
tâches apériodiques à contraintes relatives dont les paramètres ri et Ci sont donnés
dans le tableau 8.17. L’exécution de ces tâches s’effectue dans les temps libres lais-
sés par les deux autres tâches comme le montre la figure 8.41.
Tâche ri Ci Di Ti
τ1 0 1 4 4
τ2 0 2 6 6
τ3 0 1
τ4 4 4
τ5 13 1
Nous obtenons des temps de réponse qui sont fonction de la charge processeur due
aux tâches périodiques, des dates d’arrivée des tâches apériodiques et des durées des
traitements de ces tâches apériodiques.
En connaissant la charge du système par les tâches périodiques, il est possible d’évaluer
le temps de réponse d’une tâche apériodique en supposant connues sa date d’arrivée
et sa durée d’exécution. Ainsi, par définition les ri des tâches apériodiques n’étant pas
connus, il est nécessaire de faire cette évaluation pour toute la séquence d’exécution
448
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
τ1 t
4 8 12 16 20 24
τ2 t
4 8 12 16 20 24
τ libre t
4 8 12 16 20 24
Traitement
τapériodique t
4 8 12 16 20 24
τ3 τ4 τ5
Arrivée
τapériodique t
4 8 12 16 TR5=11 20 24
TR3=6
TR4=19
de la configuration (période d’étude H). Nous pouvons donc conclure que cette
méthode de prise en compte des tâches apériodiques ne peut concerner que les tâches
à contraintes non strictes, c’est-à-dire acceptant un temps de réponse non borné.
dique.
Un ou plusieurs serveurs périodiques à scrutation sont affectés à une ou plusieurs
requêtes apériodiques. La définition des paramètres temporels de ces serveurs dépend
d’une part des besoins de l’application et d’autre part des caractéristiques du serveur
en termes de paramètres temporels et mode d’exécution (priorité, conservation de
sa capacité de traitement…).
449
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
m Serveur à scrutation
Le premier type de serveur le plus simple est le serveur à scrutation. Ce serveur à
scrutation est une tâche périodique ts possédant les paramètres temporels classiques
(rs, Cs, Ds, Ts ). Ce serveur traite les tâches apériodiques qu’il trouve en attente lors
de son activation ; s’il n’y a pas de tâches en attente, le serveur se suspend et attend
la prochaine période d’activation. Le point crucial est donc de déterminer les para-
mètres du serveur par rapport aux paramètres temporels de la requête apériodique
stricte à servir.
Nous allons nous placer dans le cas où un serveur est affecté à une requête apério-
dique stricte donnée ; par conséquent il est naturel de fixer la durée d’exécution du
serveur égale à la durée de la tâche apériodique Cs = Cap. Dans le cas où nous avons
requête apériodique stricte, caractérisée par une échéance Dap et une distance mini-
mum entre deux occurrences successives de ∆min, les caractéristiques du serveur
(Ds,Ts) peuvent être élaborées dans le cas le plus défavorable où l’activation du ser-
veur a été demandée mais annulée car l’événement n’était pas présent à l’instant de
l’activation et l’occurrence effective de l’événement se produit immédiatement après
avec un décalage très faible ∆t = e (figure 8.42). Dans ce cas extrême pour satisfaire
les contraintes temporelles attachées à cette requête, nous devons résoudre l’inéqua-
tion suivante :
T s + D s ≤ D ap ≤ ∆ min (8.33)
450
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
Tâche Serveur
apériodique à scrutation
ri ? 0
Ci Cap Cs=Cap
Di Dap Ds
Serveur Ts
périodique Ds Ds
t
e
Requête ∆ min
apériodique Dap
t
Zone de choix
Ds des paramètres temporels
correspondant à la spécification
Droite D = T
(tâche à échéance sur requête) Droite D = T = ∆min
(temps de réponse le plus grand)
Dmax Droite D = T = TR
(temps de réponse égal à TR)
Zone de choix
des paramètres temporels
© Dunod – La photocopie non autorisée est un délit.
correspondant à la modélisation
Zone de choix
Cmax des paramètres temporels
correspondant à l’ordonnançabilité
451
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
Tâche ri Ci Di Ti
τ1 0 2 8 10
τ2 0 2 8 8
τ3 0 3 – ≤ 12
D
7
4
Ci,0
3
5 6 7 8 9 10 11 12
TU Tmax-Ci,0
Figure 8.44 – Représentation graphique du choix des paramètres pour un serveur à scrutation
correspondant à une requête apériodique stricte : configuration ordonnancée avec les priorités
affectées selon l’algorithme DM.
D
7
4
Ci,0
3
5 6 7 8 9 10 11 12
TU Tmax-Ci,0
Figure 8.45 – Représentation graphique du choix des paramètres pour un serveur à scrutation
correspondant à une requête apériodique : configuration ordonnancée par l’algorithme EDF.
452
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
n p 1
----------------
∑C i ⁄ T i + ∑ C ap,j ⁄ T ap,j
(n + p)
U total = U + U s = ≤ (n + p ) 2 – 1 (8.36 )
i =1 j =1
Tâche ri Ci Di Ti
τ1 0 4 10 10
τ2 0 8 20 20
τs 0 1 5 5
© Dunod – La photocopie non autorisée est un délit.
τap,1 5 1 10
τap,2 12 2 15
Étant donné que la troisième tâche est un serveur à scrutation de tâches apériodiques,
celle-ci ne s’exécute pas à chaque réveil si aucune requête de traitement apériodique
n’est arrivée. La figure 8.46 représente la séquence d’exécution avec l’arrivée de deux
requêtes apériodiques strictes tap,1 (durée Cap,1 = 1) et tap,2 (durée Cap,2 = 2) aux
instants respectifs r1 = 5 et r2 = 12. La première tâche apériodique stricte a une
453
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
τ1 t
5 10 15 20
τ2 t
5 10 15 20
τs t
5 10 15 20
Traitement t
τapériodique
5 10 15 20
Arrivée t
τapériodique
5 10 15 20
TRs=1 TRs=9
échéance de 10 par rapport à son réveil, ce qui explique les paramètres du serveur
(2Ts = 10 ≤ Dap,1). La deuxième tâche apériodique stricte a une échéance de 15
étant donné que son temps d’exécution est le double de celui du serveur ; il sera
nécessaire d’avoir deux exécutions du serveur pour traiter complètement cette requête
(2Ts+Ts = 15 ≤ Dap,2).
Le temps de réponse du traitement de ces deux requêtes apériodiques strictes, respec-
tivement 1 et 9, montre que les bornes maximales sont respectées. En revanche,
dans le cas de la deuxième tâche apériodique stricte, le réveil inutile du serveur au
temps 10, puis l’arrivée de cette requête apériodique stricte au temps 12 conduit à
allonger le temps de réponse.
m Serveur ajournable
454
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
Capacité τs 2
1 t
5 10 15 20
Traitement t
τapériodique
5 10 15 20
Arrivée t
τapériodique
5 10 15 20
Tâche ri Ci Di Ti
τ1 0 2 7 7
τ2 0 8 8 8
τS 0 2 6 6
455
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
τ1 t
5 10 15 20
τ2 t
5 10 15 20
τs t
5 10 15 20
τlibre t
5 10 15 20
τ1 t
5 10 15 20
τ2 t
5 10 15 20
τlibre t
5 10 15 20
Capacité τs 2 t
1
5 10 15 20
Traitement
τapériodique t
5 10 15 20
Arrivée
τapériodique t
5 10 15 20
tâches apériodiques strictes définies par : tap,1 (rap,1 = 1, Cap,1 = 2), tap,2 (rap,2 = 7,
Cap,2 = 2) et tap,3 (rap,3 = 12, Cap,3 = 2). La figure 8.50 montre l’acceptation immé-
diate de ces requêtes apériodiques par le serveur ajournable. Cette acceptation à
n’importe quel instant de la séquence d’exécution conduit la tâche périodique t2 à
dépasser son échéance.
De même que dans le cas des tâches périodiques gérées selon l’algorithme Rate
Monotonic, il est possible d’avoir une autre condition suffisante d’ordonnançabi-
lité dans le cas de l’algorithme RM associé au serveur ajournable. Soit un ensemble de
n tâches périodiques {t1, t2, t3, …, ti, …, tn} définies par les paramètres temporels
(ri, Ci, Di, Ti ) et un serveur périodique ts définie par les paramètres temporels
(rs, Cs, Ds, Ts ) avec Us = Cs/Ts, la configuration est ordonnançable si :
n
Us + 2 C
U = ∑C i ⁄ T i ≤ ln ------------------
2U s + 1
avec U s = -----s
Ts
(8.37)
i =1
456
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
τ1 t
5 10 15
Depassement d’échéance
τ2 t
5 10 15 20
τlibre t
5 10 15 20
Capacité τs 2 t
1
5 10 15 20
Traitement t
τapériodique
5 10 15 20
Arrivée t
τapériodique
m Serveur sporadique
457
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
+1 +1 +1
+1 +1
+1
Capacité τs 2
1 t
5 10 15 20
Traitement t
τapériodique
5 10 15 20
Arrivée
τapériodique t
5 10 15 20
τ1 t
5 10 15 20
τ2 t
5 10 15 20
τlibre t
5 10 15 20
Capacité τs 2 Délai du serveur
1 t
5 10 15 20
Traitement t
τapériodique
5 10 15 20
Arrivée
τapériodique t
5 10 15 20
458
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
Utotal = U + Us
1 Limite RM +
serveur sporadique
0,9
Limite RM +
serveur ajournable
0,8
Limite RM
0,7
0,6 0,186 Us
0,2 0,4 0,6 0,8 1
459
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
2
Capacité τs 1
t
5 10 15 20
Traitement
τapériodique t
5 10 15 20
Arrivée
τapériodique t
5 10 15 20
Tâche ri Ci Di Ti
τ1 0 2 8 8
τ2 0 3 12 12
τs 0 3 6 6
τap,1 3 2
τap,2 6 1
τap,3 9 1
τap,4 14 2
τap,5 16 1
460
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
τ1 t
5 10 15 20
τ2 t
5 10 15 20
d=9 d=15
3 d=20
d=12
Capacité τs 2
t
1
5 10 15 20
d=9 d=12 d=15 d=20 d=22
Traitement t
τapériodique 0
5 10 15 20
Arrivée t
τapériodique
5 10 15 20
L’ordonnancement, basé sur une priorité variable Earliest Deadline First (EDF),
est réalisé au plus tôt (si une tâche est activable, elle est exécutée). De fait, les temps
creux processeur se retrouvent en fin de séquence d’exécution. Si nous voulons uti-
liser ces temps creux pour exécuter les requêtes apériodiques, il serait souhaitable
de les placer au moment de la demande, c’est-à-dire à l’instant de la requête. Ainsi,
la méthode du traitement par EDL consiste à exécuter une séquence selon EDF ;
puis, lorsque survient une requête apériodique, on l’exécute dans les temps creux
disponibles de la séquence créée par Earliest Deadline Last (EDL). Cette caractéris-
tique est visualisée sur la figure 8.56 où une même configuration de deux tâches,
t1 (0,1,3,3) et t2 (0,2,5,5), est ordonnancée par EDF et EDL.
© Dunod – La photocopie non autorisée est un délit.
461
8 • Étude avancée 8.4 Ordonnancement
des systèmes temps réel des tâches indépendantes apériodiques
EDF EDL
τ1 t τ1 t
5 10 15 Effet 5 10 15
miroir
τ2 t τ2 t
5 10 15 5 10 15
τlibre t τlibre t
5 10 15 5 10 15
Figure 8.56 – Exemple de l’exécution valide d’une même configuration de deux tâches
avec les algorithmes EDF et EDL.
τ1 et τ2 t
EDF 5 10 15 20
τ libre t
5 10 15 20
τ1 et τ2 t
EDL 5 20
10 15
(après t=8) τ libre t
5 10 15 20
Arrivée t
τapériodique
5 10 15 20
τ1 et τ2 t
Exécution
réelle
5 10 15 20
τ libre t
5 10 15 20
Figure 8.57 – Exemple de l’exécution d’une configuration de deux tâches avec la prise en
compte d’une tâche apériodique en utilisant l’association des algorithmes EDF et EDL.
462
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
Dans le cas des environnements à priorité fixe (cas industriel classique), le serveur
sporadique semble être un bon compromis au niveau de ces paramètres.
Complexité Complexité
Technique de traitement Performance Performance
calcul implémentation
Serveur à scrutation
fixe
Serveur ajournable
Serveur sporadique
variable
Priorité
Figure 8.58 – Comparaison entre les méthodes de prise en compte d’une tâche apériodique.
8.5 Ordonnancement
des tâches périodiques dépendantes
8.5.1 Ordonnancement des tâches avec contraintes de précédence
m Définition générale de la précédence
Des tâches peuvent être liées par des contraintes de précédence lorsqu’elles ont des
relations de synchronisation (sémaphores, événements) ou de communication
(boîtes aux lettres). On appelle une contrainte de précédence entre la tâche τi et la
tâche τj le cas où τi précède τj si τj doit attendre la fin d’exécution de τi pour com-
mencer sa propre exécution (figure 8.59). Nous supposons dans cette section que
les tâches ont une forme atomique telle que nous l’avons décrite dans la section 8.2.3,
c’est-à-dire : attente de synchronisation ou de communication en début de tâche
et envoi d’un événement de synchronisation ou de communication en fin de tâche.
L’expression des contraintes de précédence (ordre partiel sur l’ensemble des tâches)
© Dunod – La photocopie non autorisée est un délit.
peut se faire par exemple sous la forme d’un graphe comme celui de la figure 8.60.
Ainsi, les six tâches de la configuration (τ1, τ2, τ3, τ4, τ5, τ6) sont liées par des relations
de précédences décrites par deux graphes.
Nous pouvons remarquer que nous avons jusqu’à présent noté seulement les pré-
cédences dites simples. Pour être complet, il est nécessaire de distinguer les deux cas
de précédence :
– Contraintes de précédence simple : une contrainte de précédence entre la
tâche ti et la tâche tj ou ti précède tj si tj doit attendre la fin d’exécution de ti
pour commencer sa propre exécution. Dans ce cas, nous faisons l’hypothèse
suivante :
463
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
τi t
τj t
τ1 τ2 τ3 τ5
τ4 τ6
Figure 8.60 – Graphe de représentation des contraintes de précédence entre les tâches.
τi t
τj t
Des exemples de ces deux cas de précédence généralisée sont présentés sur la
figure 8.62. Pour le premier exemple, la tâche « Mesure de température » doit s’exé-
cuter quatre fois avant l’exécution de la tâche qui calcule une moyenne sur quatre
points mesurés. Pour le second cas, la tâche « Mesure de la pression P » s’exécute à
la même cadence que la tâche « Calcul de la fonction P/T » ; par contre la tâche
« Mesure de température T » n’a pas besoin de s’exécuter aussi rapidement étant
donné la variation lente de ce paramètre physique.
Pour pouvoir utiliser les algorithmes d’ordonnancement précédemment étudiés, il
est nécessaire d’obtenir une configuration de tâches indépendantes. Par conséquent,
les caractéristiques des tâches avec contraintes de précédence sont transformées
afin de prendre en compte implicitement cette relation entre les tâches.
464
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
Mesure 1
de température 10
T
Mesure 4 1 Calcul
de de la moyenne Calcul
température sur 4 points d’une fonction
P/T
Mesure
de pression
P
Ce problème de précédence doit être abordé selon deux points de vue : exécution
et validation :
– Dans le cadre d’un ordonnancement préemptif basé sur la priorité, quelle est la
modification des paramètres de tâches qui permettra une exécution dans le respect
des échéances ?
– Est-il possible de valider a priori l’ordonnançabilité d’une configuration de tâches
dépendantes ?
Une réponse à la première question est donnée par : si ti → tj, la transformation
des paramètres doit respecter les règles de précédence suivantes :
– la date de réveil de la deuxième tâche doit être plus grande ou égale à la date de
réveil de la première tâche : rj ≥ ri ;
– dans le respect de la politique d’ordonnancement choisie, la priorité de la pre-
mière tâche doit être plus grande que la priorité de la deuxième tâche : Prioi >
Prioj.
précédence, considérons l’exemple de trois tâches dont les paramètres sont donnés
dans le tableau 8.23. De plus deux de ces tâches intègrent des primitives de synchro-
nisation : la tâche t1 commence par une attente d’un événement et la tâche t3 finit
par l’envoi de ce même événement. À cette structure de tâches visualisée sur la
figure 8.63, s’ajoute une relation de précédence entre les deux tâches t1 et t3 repré-
sentée par un graphe de précédence (figure 8.63).
Dans une première étape, nous allons respecter les conditions sur les paramètres que
nous avons énoncées précédemment ; donc, dans notre cas, nous avons les deux
tâches t1 et t3 liées par une contrainte de précédence qui doivent satisfaire à :
465
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
Tableau 8.23 – Exemple d’une configuration de trois tâches ayant des liens de précédence.
Tâche ri Ci Di Ti
τ1 0 1 3 4
τ2 0 1 2 2
τ3 0 1 2 4
ATTENDRE_EVT(evt1)
ENVOYER_EVT(evt1)
Graphe de précédence : τ3 τ1 τ2
Figure 8.63 – Description des codes et des relations de précédence des tâches
décrites dans le tableau 8.23.
466
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
1 2 3
Prio(τ1)=1-Prio(τ2)=2-Prio(τ3)=3 Prio(τ1)=1-Prio(τ2)=3-Prio(τ3)=2 Prio(τ1)=2-Prio(τ2)=1-Prio(τ3)=3
τ1 ? t τ1 ? t τ1 t
τ2 t τ2 t τ2 ? t
τ3 t τ3 t τ3 t
non ordonnançable non ordonnançable non ordonnançable
4 5 6
Prio(τ1)=2-Prio(τ2)=3-Prio(τ3)=3 Prio(τ1)=3-Prio(τ2)=1-Prio(τ3)= 2 Prio(τ1)=3-Prio(τ2)=2-Prio(τ3)=1
τ1 ? t τ1 t τ1 t
τ2 t τ2 ? t τ2 t
τ3 t τ3 t τ3 t
non ordonnançable non ordonnançable ordonnançable
Nous allons considérer que les tâches ne contiennent pas de primitives de synchro-
nisation qui sont, en particulier, en contradiction avec la ou les relations de précé-
dence souhaitées. Dans le cas des algorithmes à priorité fixe et pour des tâches de
même période avec des contraintes de précédence, on affecte les priorités afin de
satisfaire les relations énoncées 8.41. Cette affectation des priorités ne doit pas contre-
dire les ordres de priorité entre des tâches de périodes différentes.
Considérons l’exemple du graphe de précédence de la figure 8.60 avec les six tâches.
En supposant que les tâches t1 et t2 ont une période de 4 et les quatre autres tâches
t1, t2, t3 et t4 ont une période supérieure de 5. Pour respecter à la fois l’algorithme
467
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
Cas τ1 τ2 τ3 τ4 τ5 τ6
I 6 5 4 3 2 1
II 6 5 3 4 1 2
III 6 5 3 4 2 1
IV 6 5 2 4 1 3
– Pour une instance donnée, l’échéance d’une tâche doit être inférieure à toutes
les échéances de ses successeurs immédiats diminuées de leur temps d’exécution :
Comme le montre la figure 8.65, cette transformation doit être effectuée en partant
des tâches sans prédécesseur pour le calcul des ri* et en commençant par les tâches
sans successeur pour le calcul des di*.
Pour appliquer cette transformation, considérons l’exemple d’une configuration de
cinq tâches dont les paramètres temporels sont donnés dans le tableau 8.25. Dans
une première phase considérons ces tâches sans relation de précédence et construisons
la séquence avec l’algorithme à priorité variable EDF. Le facteur d’utilisation est
U = 0,917 et la période d’étude est H = 12. Étant donné que certaines tâches ne
468
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
τ1 τ2 τ3
Calculs des ri* Calculs des di*
C1 C3
t
r1 r2 r2* d2* d2 d3
Figure 8.65 – Transformation des paramètres des tâches pour la prise en compte des relations
de précédence entre les tâches avec un algorithme à priorité variable de type EDF.
Tableau 8.25 – Exemple d’une configuration de cinq tâches ayant des liens de précédence.
Tâche ri Ci Di Ti
τ1 0 3 12 12
τ2 0 2 11 12
τ3 0 3 12 12
τ4 0 1 11 12
τ5 0 2 9 12
sont pas à échéance sur requête, il est nécessaire de tracer la séquence d’exécution
sur la période d’étude H. Cela donne une séquence valide qui est visualisée sur la
figure 8.66. Cette séquence est obtenue en considérant des tâches indépendantes,
supposons maintenant que ces tâches sont liées par des relations de précédence
données sur la figure 8.67. Il est donc nécessaire de faire le calcul des paramètres
ri* et di* des cinq tâches.
Soit le calcul des ri* :
r 1* = Max ( r 1, Max ( ∅ ) ) = 0
r 2* = Max ( r 2, Max ( r 1* + C 1 ) ) = Max ( 0, Max ( 3 ) ) = 3
© Dunod – La photocopie non autorisée est un délit.
d 5* = Min ( d 5, Min ( ∅ ) ) = 9
d 4* = Min ( d 4, Min (d 5* – C 5 ) ) = Min ( 11, Min ( 7 ) ) = 7
469
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
τ5 τ2 τ4 τ1 τ3 τ5 τ2 τ4 τ1 τ3
t
5 10 15 20
τ3 τ3
τ1
τ2 τ2
d 3* = Min ( d 3, Min ( ∅ ) ) = 12
d 2* = Min ( d 2, Min ( d 3* – C 3, d 5* – C 5 ) ) = Min ( 11, Min ( 9, 7 ) ) = 7
d 1* = Min ( d 1, Min ( d 2* – C 2, d 4* – C 4 ) ) = Min ( 12, Min ( 5, 6 ) ) = 5
Dans cette nouvelle configuration les tâches sont devenues indépendantes et à départ
différé ; alors nous obtenons la séquence donnée dans la figure 8.68.
τ1 t
5 10
τ2 t
5 10
τ3 t
5 10
τ4 t
5 10
τ5 t
5 10
τ1 τ2 τ4 τ5 τ3 τ1 τ2 τ4 τ5 τ3
t
5 10 15 20
Figure 8.68 – Séquence d’exécution des tâches du tableau 8.25, considérées comme dépendantes
(graphe de précédence de la figure 8.67), avec l’algorithme à priorité variable EDF.
470
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
Cette séquence (τ1, τ2, τ4, τ5, τ3) respecte les relations de précédence imposées par
la modification des paramètres ; remarquons que la séquence est différente de la
séquence (τ5, τ2, τ4, τ1, τ3) élaborée avec les tâches considérées sans relation de
précédence (figure 8.66). Rappelons que pour tester l’ordonnançabilité de cette nou-
velle configuration avec des tâches à départ différé (équation 8.24), il est théori-
quement nécessaire de tracer la séquence sur une durée maximale de 29 unités de
temps correspondant à ri,max+2H. En réalité cette nouvelle séquence a la même
période d’étude que précédemment sans phase transitoire.
Il est important de remarquer qu’il y a équivalence de la configuration modifiée T*
avec la configuration initiale T, c’est-à-dire :
« Si la configuration initiale était ordonnançable avec le respect des con-
traintes de précédence, cette configuration modifiée le sera et réciproque-
ment : T ⇔ T*. »
M Inversion de priorité
Le principal but, qui conditionne l’utilisation d’algorithme d’ordonnancement basé
sur les priorités, est la limitation du temps de réponse d’une ou plusieurs tâches de
la configuration. Ainsi, soit un ensemble de n tâches partageant un ensemble de
m ressources critiques en exclusion mutuelle, pour une tâche ti de la configuration
ayant une pire durée d’exécution égale à Ci, la question primordiale est :
« Quel est le pire temps de réponse de la tâche au cours de son exécution
(sur la période d’étude H) ? »
Nous allons donc analyser le comportement d’une application temps réel composée
de tâches partageant des ressources critiques pour déterminer s’il est possible de déter-
miner pour les tâches de haute priorité un temps de réponse borné : qualité première
requise pour un système critique. Pour cette étude, nous allons faire les hypothèses
suivantes :
– ordonnancement en ligne préemptif basé sur une priorité fixe ou variable ;
– gestion de la file d’attente des ressources selon la priorité ;
– temps d’exécution de la demande ou de la libération des ressources négligeables.
© Dunod – La photocopie non autorisée est un délit.
Ainsi, pour garantir un temps de réponse borné à une tâche donnée t0, il faut pou-
voir déterminer de façon précise les autres tâches qui peuvent interrompre l’exécution
de cette tâche t0. Pour une configuration de n tâches, la tache t0 peut être retardée
ou suspendue par deux types de tâches :
– les tâches plus prioritaires que la tâche t0 ;
– les tâches qui partagent au moins une ressource critique avec cette tâche t0 et
qui l’utilisent avant la demande par la tâche t0.
Dans une configuration donnée, les deux ensembles de tâches peuvent être parfai-
tement identifiés et il est alors possible d’effectuer le calcul du retard maximum dans
471
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
le pire des cas. Soit p tâches de priorités supérieures à la tâche t0, n tâches moins
prioritaires accédant à une ressource critique utilisée par cette tâche t0 ou m res-
sources utilisées par cette tâche t0 et accédées par des tâches moins prioritaires.
Dans ces conditions, nous pouvons exprimer le temps de réponse TR0, défini selon
l’équation 8.10, de la tâche t0 de la façon suivante :
p
Ti
TR 0 = C 0 + ∑ ------ C i + inf {n , m }Sc max (8.42)
T0
i =1
TR 0 = C 0 + [ T 1 ⁄ T 0 ]C 1 + Sc max (8.43)
La figure 8.69 présente une séquence d’exécution de cette configuration pour des
dates de réveil particulières qui vont amener à une situation où la tâche t0 va être
interrompue dans le pire des cas. Nous pouvons faire le commentaire suivant sur cet
exemple de privation d’exécution par occupation d’une ressource critique : le retard
pour la tâche t0, engendré par la tâche t1, est normal (t1 plus prioritaire que t0), le
retard pour la tâche t0, engendré par la tâche t3, est aussi normal (t3 partage une
ressource critique avec t0). Mais le retard engendré par la tâche t2 est un mauvais
fonctionnement de l’ordonnancement, car la tâche t2 est moins prioritaire que la
tâche t0 et ne partage aucune ressource critique avec celle-ci. Nous sommes en pré-
sence du phénomène dit d’inversion de priorité, abordé dans le chapitre 4. Dans
ce contexte, il est impossible de déterminer un temps de réponse borné pour une
tâche donnée et donc d’obtenir une application temps réel au comportement pré-
visible
M Blocage fatal
472
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
Tâche τ1 t
TR0
Tâche τ0 t
priorité
Inversion de priorité
des
tâches Tâche τ2 t
Tâche τ3 t
R2 R2
Tâche τ1 t
priorité
des R1 R1 Interblocage
tâches
Tâche τ2 t
Une autre des méthodes classiques pour éviter le blocage fatal est d’utiliser dans
l’écriture des codes des tâches la méthode des classes ordonnées. L’ensemble des
ressources du système est numéroté et les tâches doivent prendre les ressources dans
l’ordre croissant des numéros des ressources et les rendre dans l’ordre inverse. Cette
méthode permet d’obtenir des résultats très satisfaisants. Ce principe est illustré sur
la figure 8.71.
R1 R2 R2
© Dunod – La photocopie non autorisée est un délit.
Tâche τ1 t
priorité
des R1 R2 R1 et R2
tâches Tâche τ2 t
473
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
Tableau 8.26 – Exemple d’une configuration de trois tâches partageant une ressource critique.
τ1 0 0 6 0 6 16 16 1
τ2 0 0 2 0 2 6 8 3
τ3 0 5 ou 6 0 0 5 15 16 2
Il paraît logique de tester la configuration avec la durée pire cas de la tâche t3. Cette
séquence d’exécution, présentée sur la figure 8.72, est valide. Un deuxième test peut
être effectué pour une durée d’exécution plus petite de la tâche t3. Si l’ordonnance-
ment en présence de ressources critiques avait un comportement normal, le relâche-
ment d’une contrainte temporelle comme la durée d’exécution devrait conduire à
une ordonnançabilité plus aisée de la configuration. Or la séquence d’exécution,
obtenue pour une durée d’exécution de 5 pour la tâche t3, est non valide comme
le montre la figure 8.73. Lors de son réveil au temps 8, la tâche t2 trouve la ressource
prise par la tâche t1 et doit donc se mettre en attente. Quand la ressource est libérée
par la tâche t1, la tâche t2 n’a plus le temps de s’exécuter avant son échéance.
Cette anomalie de comportement permet de conclure à la fragilité des tests,
en particulier si seules les situations de pire cas sont testées avec des tâches
dont la durée d’exécution peut varier en deux instances d’exécution.
En dehors de toute considération sur la durée de la tâche t3, ce dysfonctionnement
était prévisible. En effet, la tâche t2, la plus prioritaire, partage une ressource critique
avec la tâche t1 moins prioritaire. Donc, il est normal que la tâche t2 puisse se
retrouver bloquée par cette tâche moins prioritaire qui partage une ressource critique
avec elle. Donc le temps de réponse maximum TR2 de la tâche t1 peut être calculé
sans tenir compte du phénomène d’inversion de priorité :
474
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
τ1 t
0 5 10 15
τ2 t
0 5 10 15
τ3 t
0 5 10 15
utilisation de la ressource
pas d’utilisation de ressource
τ1 t
0 5 10 15
τ2 t
0 5 10 15
τ3 t
0 5 10 15
utilisation de la ressource
pas d’utilisation de ressource
TR 2 = C 1 + C 1 = 2 + 6 = 8 > D 2 = 6
© Dunod – La photocopie non autorisée est un délit.
475
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
de telle façon que les règles de priorité, qui ont conduit à l’évaluation du temps de
réponse de l’équation 8.42, soient vraies. Ce protocole à héritage de priorité peut
se décrire ainsi (figure 8.74) :
– si la ressource est libre : une tâche accède à cette ressource ;
– si la ressource n’est pas libre : la tâche est bloquée et la tâche possédant la ressource
hérite de la priorité de la tâche bloquée.
Tâche τ1 t
priorité
des
tâches Héritage de priorité
Tâche τ2 t
Avec ce protocole à héritage de priorité, l’exemple, décrit sur la figure 8.69, a un com-
portement différent. La tâche t2, qui s’était immiscée dans l’attente de la tâche
étudiée t0, est maintenant rejetée après celle-ci, et cela à cause de la priorité de la
tâche t3 héritée de la tâche t0 jusqu’à la fin de sa section critique (figure 8.75). Les
calculs du temps de réponse donnés dans les équations 8.42 et 8.43 pour cet exemple
sont exacts.
Tâche τ1 t
TR0
Tâche τ0 t
priorité
des
tâches Tâche τ2 t
Tâche τ3 t
Héritage de priorité
476
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
Si la prise des ressources pour une même tâche est faite de manière emboîtée, ce
protocole peut s’appliquer plusieurs fois pour une même tâche : héritage multiple.
Ce processus est visualisé sur la figure 8.76.
R1 R1
Tâche τ1 t
R2 R2
priorité Tâche τ2 t
des
tâches
R1 R2 R2 R1
Tâche τ3 t
Tableau 8.27 – Exemple d’une configuration de trois tâches partageant une ressource critique.
τ1 0 1 1 0 2 6 6
τ2 0 2 0 0 2 8 8
τ3 0 0 4 0 4 12 12
© Dunod – La photocopie non autorisée est un délit.
477
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
τ1 t
4 8 12 16 20 24
Inversion de priorité
τ2 t
4 8 12 16 20 24
τ3 t
4 8 12 16 20 24
utilisation ressource
pas d’utilisation de ressource
Figure 8.77 – Séquence d’exécution de la configuration décrite dans le tableau 8.27 dans un
ordonnancement en présence de ressources critiques : phénomène d’inversion de priorité.
τ1 t
4 8 12 16 20 24
τ2 t
4 8 12 16 20 24
Héritage de priorité
τ3 t
4 8 12 16 20 24
utilisation ressource
pas d’utilisation de ressource
Figure 8.78 – Séquence d’exécution de la configuration décrite dans le tableau 8.27 dans un
ordonnancement en présence de ressources critiques : protocole à héritage de priorité.
478
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
priorité
des R1
tâches
Tâche τ2 t
Héritage de la priorité de τ1
Un exemple plus complet est présenté sur la figure 8.80. Les trois tâches partagent
deux ressources. La tâche t1, la plus prioritaire, utilise les deux ressources R1 et R2.
La tâche t2, de priorité intermédiaire, utilise la ressource R2. Enfin, la tâche t3, pos-
sédant la priorité la plus faible, utilise la ressource R1. Le protocole donne aux deux
ressources une priorité plafonnée identique à celle de la tâche t1 (PP1 = PP2 = Prio1).
Lors de l’exécution, le protocole à héritage de priorité permet de limiter le temps de
réponse de la tâche t1. En effet, la tâche t2 ne peut prendre la ressource R2 alors
qu’elle est libre, puisque la priorité de la tâche t2 possède une priorité inférieure à
la priorité plafond de la ressource utilisée (R1) : Prio2 < PP1. Avec Scmax la durée
maximale de la plus grande section critique des tâches de plus faible priorité, le
© Dunod – La photocopie non autorisée est un délit.
Ainsi, en plus de limiter le temps de réponse des tâches, ce protocole à priorité pla-
fond permet de s’affranchir des situations de blocage fatal. Même si la réalisation
du code n’a pas suivi la méthode des classes ordonnées, le blocage ne peut se mettre
en place (figure 8.81). Ainsi, nous pouvons constater que la tâche t1 ne peut pas
prendre la ressource R2 qui aurait bloqué l’application, car la priorité de la tâche t1
479
8 • Étude avancée 8.5 Ordonnancement
des systèmes temps réel des tâches périodiques dépendantes
R2 R2 R2 R2
Tâche τ1 t
R1 et R2
priorité R2 R2
des Tâche τ1 t
tâches R2
R1 R2
Tâche τ1 t
R2
R2 R1 R1 R2
Tâche τ1 t
priorité
des R1 R2 R2 R1
tâches Tâche τ2 t
a une priorité qui n’est pas strictement supérieure à la priorité plafond de la ressource
utilisée (R1).
Dans le cas de l’algorithme EDF, cet algorithme est complexe puisque la priorité
plafonnée des ressources change au fur et à mesure de l’exécution. Le coût système
est bien évidemment plus important en utilisant un algorithme d’ordonnancement
de type Earliest Deadline First avec un protocole de gestion des ressources critiques
de type héritage de priorité avec priorité plafonnée dynamique. D’autres algo-
rithmes peuvent être utilisés comme le protocole à pile. Plusieurs exécutifs (POSIX,
OSEK/VDX, Ada) mettent en œuvre une version simplifiée du protocole à héritage
de priorité et priorité plafonnée.
m Conclusion sur les protocoles utilisés pour le traitement des ressources critiques
480
8 • Étude avancée 8.6 Analyse d’ordonnançabilité
des systèmes temps réel en environnement monoprocesseur
critiques ; il faut intégrer aux durées des tâches les durées de blocage maximum dues
aux attentes des ressources critiques (cette durée de blocage dépend du protocole
de gestion des ressources). Donc chaque tâche de l’application aura une durée d’exé-
cution exprimée sous la forme :
Ci * = Ci + Bi
481
8 • Étude avancée 8.6 Analyse d’ordonnançabilité
des systèmes temps réel en environnement monoprocesseur
Tâches indépendantes
Tâches Création
apériodiques de serveurs
Algorithmes
d’ordonnancement
permettant la validation
Tâches Modification (RM, DM, ED, ML)
avec précédence paramètres
+
Tâches partageant des ressources
Protocoles
de gestion
des ressources critiques
(héritage, plafond)
482
8 • Étude avancée 8.6 Analyse d’ordonnançabilité
des systèmes temps réel en environnement monoprocesseur
k =2
car PP 1 < Prio 1
= M in { [ Max ( 5, 5 ) + Max ( 0, 0 ) + Max ( 0, 0 ) + Max ( 10, 0 ) + Max ( 15, 0 ) ]
= [ Max ( 5, 0, 0, 10, 15 ) + Max ( 5, 0, 0, 0, 0 ) ] }
= Min ( [ 5 + 0 + 0 + 10 + 15 ], [ 15 + 5 ] ) = Min ( 30, 20 ) = 20
Dans le cas du protocole à priorité plafond, nous avons l’évaluation du terme B1 :
B i = Max [ b 2,2, b 3,2, b 4,2, b 5,2, b 6,2, b 2,3, b 3,3 , b 4,3, b 5,3, b 6,3 ] car PP 1 < Prio 1
= Max [ 5, 0, 0, 10, 15, 5, 0, 0, 0 ] = 15
483
8 • Étude avancée 8.6 Analyse d’ordonnançabilité
des systèmes temps réel en environnement monoprocesseur
Tableau 8.28 – Exemple d’une configuration de six tâches partageant trois ressources critiques.
Tâche ri Ci,α Ci,β Ci,γ Ci,α Ci,β Ci,γ Ci,α Ci,β Ci,γ Ci Di Ti
τ1 0 0 0 0 0 2 2 2 2 0 4 40 40
τ2 0 0 5 10 5 5 5 10 5 0 15 50 50
τ3 0 0 10 0 0 0 0 0 0 0 10 500 500
τ4 0 0 10 0 0 0 0 0 0 0 10 500 500
τ5 0 0 0 0 20 10 20 0 0 0 50 1000 1000
τ6 0 0 0 0 15 15 20 0 0 0 50 5000 5000
Les durées de blocage sont ainsi calculées et reportées dans le tableau 8.29. À partir
de ces données, nous pouvons mettre en œuvre plusieurs techniques d’analyse de
l’ordonnançabilité de la configuration. Celles-ci permettent de valider de plus en plus
précisément l’application, c’est-à-dire que l’analyse devient de moins en moins
« pire cas ».
Tableau 8.29 – Exemple d’une configuration de six tâches partageant trois ressources critiques :
calcul des durées de blocage.
τ1 0,1 6 20 15
τ2 0,2 5 25 15
τ3 0,02 4 25 15
τ4 0,02 3 15 15
τ5 0,05 2 15 15
τ6 0,01 1 0 0
U= 0,4
Considérons une première technique d’analyse qui est une traduction directe de la
condition suffisante de l’algorithme RM (équation 8.27) en intégrant pour chacune
des tâches le temps de blocage associé, soit :
484
8 • Étude avancée 8.6 Analyse d’ordonnançabilité
des systèmes temps réel en environnement monoprocesseur
n 1
--n-
U = ∑ ( C i + B i ) ⁄ T i ≤ n 2 – 1 (8.47)
i =1
Pour l’application décrite dans les tableaux 8.28 et 8.29, nous avons un facteur
d’utilisation sans prendre en compte les ressources de U = 0,4 (tableau 8.29). La
limite supérieure permettant de décider de l’ordonnançabilité de la configuration
est donc satisfaite comme le montre le calcul suivant :
6
Ci
∑ ----
-
4 10 10 10 50 50
U = = ------ + ------ + --------- + --------- + ------------ + ------------
T i 40 50 500 500 1000 5000
i =1
1
--6-
= 0,4 ≤ 6 2 – 1 ≈ 0,735
6
Ci + Bi ( 4 + 20 ) ( 10 + 25 ) ( 10 + 25 ) ( 10 + 15 )
U = ∑ ---------------
Ti
- = -------------------- + ----------------------- + ----------------------- + -----------------------
40 50 500 500
i =1
( 50 + 15 )
+ ----------------------- + -------------
50
1 000 5 000
≈ 1,43 > 0,735
6
Ci + Bi ( 4 + 15 ) ( 10 + 15 ) ( 10 + 15 ) ( 10 + 15 )
U = ∑ ---------------
Ti
- = -------------------- + ----------------------- + ----------------------- + -----------------------
40 50 500 500
i =1
© Dunod – La photocopie non autorisée est un délit.
( 50 + 15 )
+ ----------------------- + -------------
50
1 000 5 000
≈ 1,14 > 0,735
Nous voyons que, dans les deux cas, la condition n’est pas respectée et que nous
devons conclure à une non validité de l’ordonnancement de la configuration avec
une affectation de priorité basée sur l’algorithme RM et les protocoles de gestion
de ressources critiques définis. Nous pouvons remarquer que ces calculs montrent
des facteurs d’utilisation supérieurs à 100 % ; cela met en exergue l’aspect « pire cas »
très pessimiste de ce test.
485
8 • Étude avancée 8.6 Analyse d’ordonnançabilité
des systèmes temps réel en environnement monoprocesseur
n 1
Ci Bi --n-
U = ∑ ----- + Max ----- ≤ n 2 – 1 (8.48)
T i i ∈ [ 1, n ] T i
i =1
Appliquons cette technique à l’application que nous avons déjà testée. Donc, dans
les deux cas correspondant aux deux protocoles de gestion de ressources critiques,
nous obtenons :
Cas du protocole à héritage de priorité :
4 10 10 10 50 50
U = ------ + ------ + --------- + --------- + ------------ + ------------
40 50 500 500 1000 5000
20 25 25 15 15
+ Max ------, ------, ---------, ---------, ------------
40 50 500 500 1000
= 0,4 + 0,5 = 0,9 < 0,735 NON
Cas du protocole à priorité plafond :
4 10 10 10 50 50
U = ------ + ------ + --------- + --------- + ------------ + ------------
40 50 500 500 1000 5000
15 15 15 15 15
+ Max ------, ------, ---------, ---------, ------------
40 50 500 500 1000
= 0,4 + 0,375 = 0,775 < 0,735 NON
Cette condition d’ordonnançabilité est encore trop pessimiste pour permettre de
conclure sur l’ordonnançabilité de cette configuration. Il est donc nécessaire de mettre
en œuvre une méthode plus fine.
Considérons une autre technique d’analyse qui est une traduction itérative de la
condition suffisante de l’algorithme RM (équation 8.27). Le test est effectué à partir
de la tâche la plus prioritaire en intégrant progressivement chacune des tâches avec
leur temps de blocage, soit :
j –1 1
Ci Cj + Bj --j-
∀ j ∈ [ 1, n ] : U = ∑ ----- + ---------------- ≤ j 2 – 1
Ti Tj
(8.49)
i =1
486
8 • Étude avancée 8.6 Analyse d’ordonnançabilité
des systèmes temps réel en environnement monoprocesseur
Le principe développé dans cette technique est simple. Pour une tâche tj, on consi-
dère d’une part les tâches qui peuvent l’interrompre (les tâches les plus prioritaires) ;
cela correspond au premier terme de l’équation 8.49 (sommation du facteur d’uti-
lisation de toutes les tâches plus prioritaires que de la tâche tj). Et d’autre part, il faut
prendre en compte la durée de la tâche tj et son terme de blocage Bj.
Appliquons cette technique à l’application que nous avons déjà testée. Dans les deux
cas correspondant aux deux protocoles de gestion de ressources critiques, nous
obtenons :
Cas du protocole à héritage de priorité :
( 4 + 20 )
Pour j = 1 : -------------------- = 0,6 < 1
40
4 ( 10 + 25 )
Pour j = 2 : ------ + ----------------------- = 0,8 < 0,828
40 50
4 10 ( 10 + 25 )
Pour j = 3 : ------ + ------ + ----------------------- = 0,37 < 0,779
40 50 500
4 10 10 ( 10 + 15 )
Pour j = 4 : ------ + ------ + --------- + ----------------------- = 0,37 < 0,757
40 50 500 500
4 10 10 10 ( 10 + 15 )
Pour j = 5 : ------ + ------ + --------- + --------- + ----------------------- = 0,405 < 0,743
40 50 500 500 500
4 10 10 10 50 50
Pour j = 6 : ------ + ------ + --------- + --------- + ------------ + ------------ = 0,4 < 0,735
40 50 500 500 1000 5000
Cas du protocole à priorité plafond :
( 4 + 15 )
Pour j = 1 : -------------------- = 0,475 < 1
40
4 ( 10 + 15 )
Pour j = 2 : ------ + ----------------------- = 0,6 < 0,828
40 50
4 10 ( 10 + 15 )
Pour j = 3 : ------ + ------ + ----------------------- = 0,35 < 0,779
40 50 500
4 10 10 ( 10 + 15 )
Pour j = 4 : ------ + ------ + --------- + ----------------------- = 0,37 < 0,757
© Dunod – La photocopie non autorisée est un délit.
40 50 500 500
4 10 10 10 ( 10 + 15 )
Pour j = 5 : ------ + ------ + --------- + --------- + ----------------------- = 0,405 < 0,743
40 50 500 500 500
4 10 10 10 50 50
Pour j = 6 : ------ + ------ + --------- + --------- + ------------ + ------------ = 0,4 < 0,735
40 50 500 500 1000 5000
Avec cette technique l’ordonnançabilité de la configuration est prouvée. Nous pou-
vons ainsi déclarer que la configuration des six tâches partageant des ressources cri-
tiques est ordonnançable avec l’affectation de priorité selon l’algorithme RM.
Nous pouvons remarquer toutefois que l’utilisation du protocole de gestion de res-
487
8 • Étude avancée 8.6 Analyse d’ordonnançabilité
des systèmes temps réel en environnement monoprocesseur
sources critiques de type à héritage de priorité donne une validation très proche de
la limite (cas j = 2).
Il est intéressant de remarquer que cette technique permet d’expliquer la technique
précédente. En effet, l’équation 8.48 de la technique 2 est une majoration gros-
sière de l’équation 8.49 de la technique 3.
j –1 1
C Cj + Bj --j-
∀ j ∈ [ 1, n ] : U = ∑ -----i + ---------------
Ti Tj
- ≤ j 2 – 1
(8.49)
i =1
j –1 1
C Cj + Bj --n-
⇐ ∀ j ∈ [ 1, n ] : U = ∑ -----i + ---------------
Ti Tj
- ≤ n 2 – 1
i =1
n 1
C B --n-
⇐ ∀ j ∈ [ 1, n ] : U = ∑ -----i + -----j ≤ n 2 – 1
Ti Tj
i =1
n 1
C Bi --n-
⇐ U = ∑ -----i + Max ----- ≤ n 2 – 1
T i i ∈[ 1, n ] T i
i =1
Cette technique calcule le pire temps de réponse d’un ensemble de tâches. Pour
effectuer ce calcul, il est nécessaire de définir le temps pendant lequel le processeur
est occupé à exécuter des tâches de priorité supérieure ou égale à Prioi (période
d’activité ou « busy period » de niveau i). Cette analyse s’effectue en plusieurs étapes :
– Étape 1 : évaluation de la date de fin d’exécution a0 de la tâche ti dans cette
période d’activité. Le calcul de a0 prend en compte l’exécution d’une instance
de toutes les tâches plus prioritaires que la tâche ti et le temps de blocage de la
tâche ti :
i
a0 = Bi + ∑ Ci (8.50)
j =1
– Notons que, dans cette période d’activité de niveau i, au plus un facteur de blocage
Bi est pris en compte. En effet, aucune tâche de priorité inférieure à ce niveau i
ne peut débuter et donc prendre une ressource dans cette période.
488
8 • Étude avancée 8.6 Analyse d’ordonnançabilité
des systèmes temps réel en environnement monoprocesseur
TR i = a n (8.52)
TR 2 = a 2 = 39
© Dunod – La photocopie non autorisée est un délit.
– Étape 5 : La période d’activité est terminée, car TR2 est inférieur ou égal à T2 ;
l’échéance de la tâche t2 est donc bien respectée : TR2 = 39<D2 = T2 = 50.
Poursuivons notre analyse par la tâche t3. Soit les étapes suivantes :
– Étape 1 : évaluation de la date de fin d’exécution a0 de la tâche t3 dans la période
d’activité du niveau de priorité 3 :
3
a 0 = B 3 + ∑ C j = 25 + ( 4 + 10 + 10 ) = 49
j =1
489
8 • Étude avancée 8.6 Analyse d’ordonnançabilité
des systèmes temps réel en environnement monoprocesseur
– Étape 2.1 : en utilisant le calcul de a0, nous obtenons une valeur de a1 qui fait
apparaître le fait que la tâche t1 peut s’exécuter deux fois avant la date a0, soit :
1
a0 49 49
a 1 = B 3 + C 3 + ∑ ----- C j = 25 + ( 10 ) + ------ 4 + ------ 10 = 53
Tj 40 50
j =1
2
a1 53 53
a 2 = B 3 + C 3 + ∑ ----- C j = 25 + ( 10 ) + ------ 4 + ------ 10 = 63
Tj 40 50
j =1
– Étape 3.2 : comme a2 ≠ a1 (dû à l’exécution des deux instances des tâches t1 et
t2), alors nous reprenons à l’étape 2.
– Étape 2.3 : en utilisant le calcul de a2, nous obtenons a3 :
2
a2 63 63
a 3 = B 3 + C 3 + ∑ ----- C j = 25 + ( 10 ) + ------ 4 + ------ 10 = 63
Tj 40 50
j =1
– Étape 3.3 : comme a3 = a2 (dû à la fin l’itération car, avant la date a2, les tâches
t1 et t2 peuvent s’exécuter deux fois et la tâche t3 une seule fois), alors nous
allons à l’étape 4 (figure 8.83).
– Étape 4 : nous avons obtenu le temps de terminaison de la tâche t3. Nous pou-
vons alors obtenir le temps de réponse TR3 de cette instance de la tâche t3, soit :
TR 3 = a 3 = 63
Il est important de noter que toutes ces validations ne sont valables et appli-
cables qu’en faisant l’hypothèse de durées de tâches fixes, connues et déter-
ministes.
490
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
τ1 τ2 τ3 τ3 (blocage)
t
Nouvelle
demande de τ1 a0 = 49
τ1 τ2 τ3 τ3 (blocage) τ1
t
Nouvelle
demande de τ2 a1 = 53
τ1 τ2 τ3 τ3 (blocage) τ1 τ2
t
a2= 63
8.7 Ordonnancement
en environnement multiprocesseur
8.7.1 Introduction générale
m Définitions
Le système informatique de contrôle est souvent constitué de plusieurs équipements
informatiques interconnectés par un réseau et ce pour les raisons suivantes :
– le procédé est, par nature, constitué d’équipements multiples, dotés chacun de
son unité informatique : chaîne de fabrication ;
– la sûreté de fonctionnement conduit à multiplier les équipements de contrôle
pour diminuer la défaillance de l’ensemble du procédé : systèmes embarqués (aéro-
nautique…) ;
– les contraintes de temps conduisent à utiliser plusieurs systèmes informatiques
pour une exécution en parallèle de certaines tâches : simulateur de vol…. Ce
dernier cas peut conduire à des systèmes de type multiprocesseur.
© Dunod – La photocopie non autorisée est un délit.
491
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
492
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
Le problème de placement ou d’allocation des tâches peut être résolu à partir des
paramètres statiques de l’application, soit :
– Caractéristiques statiques des tâches : durée d’exécution, place mémoire, coûts
de communications, interactions avec le procédé, importance…
– Caractéristiques statiques de l’architecture opérationnelle : nombre de nœuds,
architecture interne des nœuds (mono ou multiprocesseur), vitesses de traitement
des processeurs, caractéristiques du réseau de communication (délai de propaga-
tion, délai de transmission), etc.
La solution du placement des tâches sur les différents sites est déterminée à partir
de la prise en compte de nombreux critères comme :
– minimiser le nombre de processeurs ;
– équilibrer la charge des processeurs ;
– minimiser la communication entre les nœuds ;
– prendre en compte les contraintes de résidence ;
– minimiser le temps de réponse d’une tâche ou d’un ensemble de tâches ou d’un
site ;
– respecter le degré de redondance pour chaque tâche…
La solution ne peut être qu’un compromis par rapport à l’ensemble de ces critères.
Une fois placées sur les différents sites, les tâches peuvent migrer entre les différents
nœuds selon les besoins de l’application : surcharge d’un ou plusieurs sites, panne
d’un ou plusieurs sites. Nous avons à traiter le processus de la migration dynamique
qui peut être réalisée à chaque instance ou en cours d’exécution avec la migration du
contexte de la tâche. Il est important de noter que la migration dynamique con-
cerne un changement de site d’exécution car le code est installé sur plusieurs sites
(figure 8.84).
Tâche Tâche
E Tâche A Tâche
C B
© Dunod – La photocopie non autorisée est un délit.
Tâche Tâche
F Tâche C Tâche
G D
Réseau
493
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
Tâche ri Ci Di
τ1 0 5 10
τ2 0 [2,6] 10
τ3 4 8 15
τ4 0 10 20
τ5 5 100 200
τ6 7 2 22
Il est important de noter que, d’une part, les tâches sont à départ différé et, d’autre
part, que la tâche t2 a une durée d’exécution qui peut varier de 2 à 6. Nous allons
étudier l’exécution de cette configuration avec différentes valeurs de la durée de la
tâche t2. Dans ce type de test, il est naturel de considérer les valeurs extrêmes du
domaine de variation de la durée de la tâche t2. Ainsi, nous avons les séquences
suivantes (figure 8.85) :
– cas I : durée d’exécution de la tâche t2 de 2. La séquence est valide ; mais nous
pouvons constater que le phénomène d’inversion de priorité se produit lors de
l’exécution de la tâche t5 qui s’exécute avant la tâche t4 ;
– cas II : durée d’exécution de la tâche t2 de 6. La séquence est aussi valide ; et nous
pouvons constater que le phénomène d’inversion de priorité ne se produit pas ;
– cas III : durée d’exécution de la tâche t2 de 5. La séquence est valide ; et nous
pouvons constater que nous obtenons les meilleurs temps de réponse pour les
tâches t4 et t6.
Ces trois tests d’exécution semblent démontrer l’ordonnançabilité de la configura-
tion. Les tests correspondent aux deux valeurs extrêmes et à une autre valeur inter-
494
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
médiaire de la durée d’exécution de la tâche t2 [2,6]. Un autre test est effectué avec
une valeur de la durée d’exécution de la tâche t2 de 3, valeur faible dans l’intervalle
de variation de la durée. La figure 8.86 montre que cette exécution conduit à une
inversion de priorité et surtout aux dépassements des échéances pour les tâches t4
et t6. Nous pouvons ainsi constater que le comportement de l’exécution n’est pas
logiquement lié aux paramètres temporels des tâches, comme leur durée.
Inversion
de priorité
τ1 τ5
P r1 t
0 5 10 15 20
I τ2 τ4 τ3 τ4 τ6
P r2 t
0 5 10 15 20
τ1 τ3 τ5
Pr 1 t
II 0 5 10 15 20
τ2 τ4 τ6
Pr 2 t
0 5 10 15 20
τ1 τ3 τ5
Pr 1 t
III 0 5 10 15 20
τ2 τ4 τ6
Pr 2 t
0 5 10 15 20
Meilleurs
temps de réponse
Inversion
de priorité
τ2 τ5
Pr 1 t
© Dunod – La photocopie non autorisée est un délit.
0 5 10 15 20
τ2 τ2 τ4 τ6
Pr 2 t
0 5 10 15 20
Dépassement
d’échéance
495
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
H = PPCM {T i } i ∈ [ 1, n ]
Nous pouvons effectuer une première analyse à l’aide des algorithmes optimaux et
puissants étudiés en environnement monoprocesseur : EDF et ML. Considérons un
exemple simple, constitué de trois tâches périodiques, décrit dans le tableau 8.31.
Le facteur d’utilisation est U = 1,39 et la période d’étude H = 72. Cette configu-
ration peut donc s’exécuter sur un environnement à deux processeurs en considérant
la relation 8.54.
Nous allons supposer possible la migration en cours d’exécution. La figure 8.87
montre l’exécution de cette configuration avec l’algorithme d’ordonnancement de
type EDF. La séquence n’est pas valide à cause du dépassement d’échéance de la
tâche t1. La figure 8.88 montre la même exécution avec l’algorithme ML ; et, dans
ce cas, la séquence d’exécution est valide.
Tâche ri Ci Di Ti
τ1 0 8 9 9
τ2 0 2 8 8
τ3 0 2 8 8
496
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
τ1
τ2
τ3
τ2 τ1 Échéance
non respectée
Pr1 t
10 20
τ3
Pr2 t
10 20
1 0 1 0 1 0
τ1
6 5 4 3 2 1 0 6 5 4 3 2 1 0 6 5 4 3
τ2
6 5 4 3 2 1 0 6 5 4 3 2 1 0 6 5 4 3
τ3
τ1 τ2 τ1 τ2
Pr1 t
10 20
© Dunod – La photocopie non autorisée est un délit.
τ2 τ3 τ3 τ2 τ3 τ2 τ3
Pr2 t
10 20
497
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
À partir de cet exemple, nous pouvons faire les deux conclusions suivantes :
– Le comportement des algorithmes en environnement multiprocesseur ne peut
pas être rapproché du comportement en environnement monoprocesseur, en
particulier les propriétés ne sont plus vraies : optimalité…
– L’algorithme ML semble plus puissant que l’algorithme EDF dans un environne-
ment multiprocesseur ; ils étaient identiques en environnement monoprocesseur.
u i ≥ u i +1 pour i ∈ [ 1, n – 1 ] (8.55 )
Dans ce contexte, nous avons une condition nécessaire et suffisante d’ordonnança-
bilité qui est donnée par la relation suivante :
j n
1 1
Max Max --- ∑ u i pour j ∈ [ 1, m ] ; ---- ∑ u i ≤ 1 (8.56 )
j i =1 m i =1
Tableau 8.32 – Configuration de trois tâches à exécuter sur une plate-forme à deux processeurs
selon un algorithme basé sur la proportionnalité de l’affectation du temps processeur liée au
facteur d’utilisation de chaque tâche.
Tâche ri Ci Di Ti ui
τ1 0 2 3 3 0,66
τ2 0 2 4 4 0,5
τ3 0 3 6 6 0,5
Nous vérifions que cette configuration respecte bien le classement donné par la
relation 8.55. D’autre part, le facteur d’utilisation globale (U = 1,66) indique qu’il
est nécessaire de disposer d’une architecture au moins à deux processeurs pour
498
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
satisfaire la condition nécessaire 8.54. La période d’étude est H = 12. Enfin, nous
pouvons tester que la condition nécessaire et suffisante 8.56 est bien satisfaite, soit :
2 1 2 1 7 5 5
Max Max --- ; --- --- + --- = ------ ; --- = --- ≤ 1
3 2 3 2 12 6 6
Temps processeur
Tâche ui
dans l’intervalle [0,3]
τ1 0,66 3*0,66=2
τ2 0,5 3*0,5=1,5
τ3 0,5 3*0,5=1,5
τ1
Pr1
0 1 2 3 4 5 6 7 8 9 10 11 12
τ2 τ3
© Dunod – La photocopie non autorisée est un délit.
Pr2
0 1 2 3 4 5 6 7 8 9 10 11 12
dates
d’activation
0 1 2 3 4 5 6 7 8 9 10 11 12
499
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
500
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
Ordonnanceur demande
réparti de migration
tâche
solution surcharge à exécuter
local
Ordonnanceur
Allocateur
réparti
demande ou offre
de service
réseau
demande demande
de migration de migration
offre offre
en retour en retour
(enchère) (enchère)
migration
d’exécution
501
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
site A
site B demandeur site C
diffusion des « surplus »
offre offre
d’acceptation d’acceptation
migration
d’exécution
site foyer
site B site C
diffusion des « surplus »
offre offre
d’acceptation d’acceptation
demande migration
de migration d’exécution
offre
site foyer
en retour
(enchère)
502
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
Tâche ri Ci Di Ti Processeur
τ1 0 2 ou 7 8 8 1
τ2 0 3 8 8 2
τ3 0 5 8 8 3
τ4 0 2 8 – 1
τ5 1 4 8 – 2
τ1 τ4
Pr1 t
0 1 2 3 4 5 6 7 8
τ2
Pr2 t
0 1 2 3 4 5 6 7 8
τ3
Pr3 t
© Dunod – La photocopie non autorisée est un délit.
0 1 2 3 4 5 6 7 8
503
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
τ1
Pr1 t
demande
τ2 pour τ4 τ4
Pr2 t
demande
pour τ4 offre
τ3 pour τ4
Pr3 t
0 1 2 3 4 5 6 7 8
τ1
Pr1 t
demande
pour τ4
τ
Pr2 t
2 5
0 1 2 3 4 5 6 7 8
504
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
τ1 Site 1 τ4
m1 m2
τ3 Site 2 τ2
Figure 8.97 – Modélisation d’une application distribuée définie par deux sites,
deux tâches sur chacun des sites et deux messages échangés.
505
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
τ1 d1 τ4 d4
Site 1 t
m1 m2
Cas l Réseau t
τ2 d2 τ3 d3
Site 2 t
d1 d4
τ1 τ4
Site 1 t
m2 m1
Cas ll Réseau t
τ2 d2 τ3 d2 Dépassement d’échéance
Site 2 t
Les principales hypothèses que nécessite cette analyse temporelle des systèmes dis-
tribués sont les suivantes :
– horloge globale (utilisation d’un moyen externe ou par envoi de messages) ;
– réseau fiable (pas de perte de messages) ;
– tâches à départ simultané au démarrage du système ;
– pas de migration des tâches dans le système ;
– messages lus au début de l’exécution des tâches ;
– messages émis à la fin d’exécution des tâches.
L’ordonnancement conjoint des tâches et des messages conduit à considérer que la
date d’émission d’un message dépend du temps de réponse de la tâche émettrice et
la date de réveil de la tâche réceptrice dépend du temps de transmission ou temps
de réponse du message. Ce décalage temporel imposé par la précédence d’une tâche
ou d’un message est appelé gigue (ne pas confondre avec la gigue comme paramètres
temporels d’analyse présentée au § 8.2). Ainsi, le message émis m1-2 et la tâche
réceptrice t2 sont obligatoirement décalés des gigues respectives Jm et J2 par rap-
port au début de l’exécution de la tâche émettrice t1 (figure 8.99).
Soit un ensemble de tâches trié par ordre de priorité sur chacun des sites (la tâche
ti plus prioritaire que la tâche tj si i<j : Prioi>Prioj). Nous supposons que l’algo-
rithme choisi est basé sur une affectation de priorité fixe. La technique de calcul du
506
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
d1
τ1 t
m1-2
Jm
m1-2 t
d2
J2
τ2 t
pire temps de réponse des tâches est basée sur le même principe que celui que nous
avons utilisé dans l’analyse RMA (§ 8.6.2). Pour effectuer ce calcul, il est nécessaire
de définir le temps pendant lequel le processeur est occupé à exécuter des tâches de
priorité supérieure ou égale à Prioi (période d’activité de niveau i).
Par rapport au calcul des temps de réponse dans l’analyse RMA, les équations 8.50
et 8.51 vont être augmentées de la gigue initiale que peuvent avoir les tâches étant
donné le pire temps d’attente de message sur le réseau. Ainsi, les équations à résoudre
sont :
i –1
Jj
( q + 1 )C i + B i + ∑ ----- C j
Tj
j =1
t 0 = ----------------------------------------------------------
i –1
- (8.57)
Cj
1 – ∑ -----
Tj
j =1
et
i –1
Jj + tn
t n +1 = B i + ( q + 1 )C i + ∑ ------------- C j (8.58)
Tj
j =1
Le point fixe tn+1 = tn est recherché par une méthode itérative. Dans une période
d’activité, la charge minimale est définie par l’exécution d’une instance de chaque
tâche. Sous cette forme, q est un paramètre. Chaque incrémentation de q revient à
© Dunod – La photocopie non autorisée est un délit.
TR i = Max ( t + J i – kT i ) (8.59)
507
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
Tâches
plus prioritaires
que τi temps
Ci Ci
τi
1 2 2 3 4 temps
Ti Ti Ti
TRi = t-q Ti
Periode d’activité de niveau i de longueur t
cette figure, la tâche ti n’est pas à échéance sur requête ; en revanche, la fin de la
période d’activité se situe avant la fin de la quatrième période de la tâche ti.
Un calcul identique peut être réalisé dans le cas d’un algorithme à priorité dynamique
(EDF) conduisant à des équations de la forme de celles obtenues précédemment.
Dans le cas qui nous intéresse où nous avons une application distribuée avec un
réseau, il est nécessaire de calculer le pire temps de réponse des messages de la même
manière que pour les tâches ordonnancées en environnement à priorité fixe. La
différence essentielle réside dans le fait que la préemption n’est plus autorisée puis-
qu’un message envoyé occupe le réseau jusqu’à sa fin. Ainsi, en considérant que les
messages sont classés par ordre de priorité comme les tâches (le message mi de durée
Ci plus prioritaire que la tâche mj de durée Cj si i<j), nous avons pour m messages
la relation suivante :
i –1
Jj + tn
t n +1 = ( q + 1 )C i + ∑ ------------- + 1 C j + max (C k ) (8.60)
Tj i≤k≤m
j =1
La durée Ci d’un message mi est calculée selon le type de protocole avec les infor-
mations données dans le chapitre 4.
La mise en œuvre de l’ensemble de ces calculs nécessite dans la plupart des cas des
calculs complexes. Prenons un exemple très simple pour illustrer cette validation des
applications distribuées par évaluation des pires temps de réponse. Soit une appli-
cation distribuée constituée de trois tâches périodiques indépendantes réparties sur
deux processeurs avec un seul message échangé (figure 8.101 et tableau 8.35).
Le site 1 est ordonnancé avec une politique RM, c’est-à-dire que la tâche t1 est plus
prioritaire que la tâche t2 . Remarquons que la tâche t3 , liée à la tâche t2 par le
message m1, possède une période identique à la tâche t2 . Nous allons calculer la
gigue du message m1 et ensuite la gigue de la tâche t3 . Pour un calcul aussi simple,
il n’est pas nécessaire d’utiliser les équations précédentes. Ainsi, nous commençons
par le site 1 ; la figure 8.102 montre que la tâche t2 est préemptée par la deuxième
instance de la tâche t1 . Par conséquent, la gigue du message m1 est donnée par :
508
8 • Étude avancée 8.7 Ordonnancement
des systèmes temps réel en environnement multiprocesseur
J m1 = C 2 + 2 ⋅ C 1 = 80 + 60 = 140
Étant donné que dans cet exemple simple il n’y a qu’un seul message, celui-ci ne peut
pas être retardé donc la gigue de la tâche t3 du deuxième site est :
J 3 = J m1 + C m1 = C 2 + 2 ⋅ C 1 + C m1
= 80 + 60 + 20 = 160
Le pire temps de réponse de la tâche t3 sera donc inférieur à son échéance qui est
égale à la période :
TR 3 = J 3 + C 3 = 160 + 10 = 170 < d 3 = T 3 = 200
τ2 Site 1 τ1
m1
τ3 Site 2
Tâche ri Ci Di Ti Processeur
τ1 0 30 100 100 1
τ2 0 80 200 200 1
τ3 0 10 200 200 2
m1 0 20 – – –
© Dunod – La photocopie non autorisée est un délit.
d1 d1 et d2
τ1 τ2 τ1 τ2 τ1
Site 1 t
Jm1
m1 t
d3
J3 τ3
Site 2 t
Figure 8.102 – Séquence d’exécution de l’une application décrite dans le tableau 8.35.
509
Annexes
A • REPRÉSENTATION DE L’INFORMATION
Cette annexe présente les méthodes utilisées pour la représentation des nombres
entiers signés et des nombres fractionnaires à virgule fixe et à virgule flottante.
513
A • Représentation A.1 Représentation binaire des entiers signés
de l’information
Le tableau A.1 montre les différentes représentations possibles des entiers signés
sur 8 bits.
… … … …
–0 10000000 11111111
… … … …
– 128 10000000
514
A • Représentation A.2 Représentation des nombres fractionnaires
de l’information
tation signée ou non signée. Par exemple, si en langage C on utilise un type caractère
signé (char) il faut avoir conscience que son domaine de variation est [– 128..127]
et non [0..255].
Afin d’illustrer la technique du complément, la figure A.1 présente son application
décimale sur 8 chiffres : le complément à 10 (complément à 9+1). Notons cepen-
dant que dans ce cas, le chiffre de poids fort ne peut prendre que les valeurs 0 et 9
pour « + » et « – ».
Figure A.1 – Addition des nombres décimaux 456 et –352 en complément à 10.
515
A • Représentation A.2 Représentation des nombres fractionnaires
de l’information
signe …
1 1 0 1 0 0 1 1
516
A • Représentation A.2 Représentation des nombres fractionnaires
de l’information
2-2+exp 2-22+exp
2-1+exp 2-3+exp 2-21+exp 2-23+exp
Figure A.3 – Représentation d’un nombre à virgule flottante au format IEEE 754 sur 32 bits.
Nombre Caractérisation
0 exposant biaisé = 0
mantisse = 0
La figure A.4 montre que les flottants correspondent à des points fixes par partie,
avec autant de parties que d’exposants possibles.
© Dunod – La photocopie non autorisée est un délit.
e=0 e=0 e=0 e=0 e=1 e=1 e=1 e=1 e=2 e=2 e=254
m=0 m=1 m=2 m=3 m=223–1 m=0 m=1 m=223–1 m=0 m=1 m=223–1
0 2-149 2*2-149 3*2-149 2-126–2-149 2-126 2-126+2-149 2-125–2-149 2-125 2-125–2-148 2128–2104
e : exposant biaisé
m : valeur binaire présente dans la mantisse
Figure A.4 – Domaines et précisions des flottants IEEE 754 sur 32 bits.
517
A • Représentation A.2 Représentation des nombres fractionnaires
de l’information
Afin d’illustrer la complexité des opérations arithmétiques sur les flottants, citons
la prise en compte des sémantiques différentes suivant la représentation (infini,
« Not a Number », normalisé ou dénormalisé), citons aussi la complexité, relativement
aux opérations entières, d’une opération basique, telle l’addition de deux flottants.
Afin d’additionner deux nombres flottants, il faut :
– mettre les deux nombres au même exposant, ce qui peut faire apparaître le bit
caché dans le flottant de plus petit exposant, au détriment des bits de poids faible
qui peuvent être perdus (le flottant peut être arrondi).
– L’addition des mantisses peut alors être réalisée.
– Le flottant obtenu est alors renormalisé (si l’exposant le permet).
Afin d’optimiser ces opérations, les microprocesseurs s’adjoignent souvent un copro-
cesseur spécialisé dans les calculs à virgule flottante. Pour les microcontrôleurs, cela
est moins fréquent. Un concepteur souhaitant réaliser une régulation, par PID (Pro-
portionnelle, Intégrale, Dérivée) par exemple, devra donc être conscient du fait que
s’il manipule des nombres flottants, et qu’il ne possède pas de coprocesseur spécia-
lisé, chaque opération arithmétique flottante nécessite de nombreuses instructions
et est donc coûteuse en nombre de cycles processeur.
Enfin, il est indispensable d’avoir conscience que l’arithmétique en nombres flot-
tants perd de nombreuses propriétés arithmétiques classiques sur les nombres réels.
Par exemple, l’addition n’est pas associative (i.e. en fonction des ordres de grandeur
de trois flottants a, b, et c, on peut observer (a+b)+c ≠ a+(b+c)), cela est dû aux
arrondis lors des mises au même exposant. Il peut alors arriver, par exemple lors de
l’inversion d’une matrice de façon numérique, que les valeurs obtenues soient signi-
ficativement différentes de celles que l’on aurait obtenues à l’aide d’un calcul sym-
bolique.
518
B • STANDARDS POSIX
1003.1h Tolérance aux fautes (Fault Tolerance) Dernière version 2000 – Devenu 1003.25.
519
Nom IEEE Nom Notes
520
Nom IEEE Nom Notes
Environment Profile)
521
Nom IEEE Nom Notes
522
C • MODULE DE BOÎTES AUX LETTRES POSIX
Cette annexe présente le code source commenté d’un module de boîtes aux lettres
implémenté en C, en utilisant la norme POSIX. Le module intègre les boîtes aux
lettres de taille 1 présentées au paragraphe 6.2.1, p. 291, et y adjoint les boîtes aux
lettres de taille n (avec et sans écrasement).
523
C • Module C.1 En-tête de module
de boîtes aux lettres POSIX
524
C • Module C.1 En-tête de module
de boîtes aux lettres POSIX
525
C • Module C.2 Corps de module
de boîtes aux lettres POSIX
526
C • Module C.2 Corps de module
de boîtes aux lettres POSIX
void bal_delete(bal b) {
pthread_cond_destroy(&(*b).pas_vide);
pthread_cond_destroy(&(*b).pas_plein);
pthread_mutex_lock(&(*b).mutex);
© Dunod – La photocopie non autorisée est un délit.
free((*b).buf);
pthread_mutex_unlock(&(*b).mutex);
pthread_mutex_destroy(&(*b).mutex);
free(b);
}
bal_ecr bal_ecr_init(const unsigned taille_element) {
bal_ecr bal;
if (!(bal=(bal_ecr)malloc(sizeof(struct s_bal_ecr)))) return
0;/* Allocation de la structure */
if (!((*bal).buf=(char *)malloc(taille_element))) return 0;/*
Allocation du buffer contenant un message */
(*bal).vide=1;/* Initialement la bal est vide */
pthread_mutex_init(&((*bal).mutex),0);/* Création du sémaphore
garantissant l’exclusion mutuelle des accès à la structure */
527
C • Module C.2 Corps de module
de boîtes aux lettres POSIX
(*bal).taille_element=taille_element;
pthread_cond_init(&((*bal).pas_vide), 0);/* Variable
conditionnelle qui sera déclenchée lorsqu’un message est ajouté */
return bal;
}
528
C • Module C.2 Corps de module
de boîtes aux lettres POSIX
}
memcpy(&(*bal).buf[(*bal).fin],buf,(*bal).taille_element); /*
Copie du message à la fin de la file (indice fin d’après l’invariant)
*/
(*bal).fin=((*bal).fin+1)%(*bal).taille;
(*bal).vide=0; /* Respect de l’invariant */
pthread_mutex_unlock (&(*bal).mutex);
/* Fin de l’exclusion mutuelle */
pthread_cond_signal (&(*bal).pas_vide); /* Réveil de l’éventuel
thread en attente de message */
return (*bal).taille_element;
}
529
C • Module C.2 Corps de module
de boîtes aux lettres POSIX
pthread_cond_destroy(&(*bal).pas_plein);
pthread_cond_destroy(&(*bal).pas_vide);
pthread_mutex_lock(&(*bal).mutex);
free((*bal).buf);
pthread_mutex_unlock(&(*bal).mutex);
pthread_mutex_destroy(&(*bal).mutex);
free(bal);
}
bal_n_ecr bal_n_ecr_init(const unsigned taille, const unsigned
taille_element) {
bal_n_ecr bal;
if (!(bal=(bal_n_ecr)malloc(sizeof(struct s_bal_n_ecr))))
return 0; /* Allocation de la structure */
if (!((*bal).buf=(char **)calloc(taille,taille_element)))
return 0;/* Allocation du buffer contenant taille messages */
(*bal).vide=1; /* La bal est initialement vide */
pthread_mutex_init(&((*bal).mutex),0); /* Création du sémaphore
garantissant l’exclusion mutuelle des accès à la structure */
(*bal).debut=0;
/* Invariant: non vide => debut est l’indice du plus vieux
message non lu
vide => debut=fin*/
(*bal).fin=0;
/* Invariant: vide ou debut!=fin => fin est l’indice de la
première case disponible du tableau
non vide et debut=fin => fin est l’indice du message le
plus ancien */
(*bal).taille_element=taille_element;
(*bal).taille=taille;
pthread_cond_init (&((*bal).pas_vide), 0);
/* Variable conditionnelle qui sera déclenchée lorsqu’un message
est ajouté (i.e.
quand il y a au moins un message à lire */
return bal;
}
530
C • Module C.2 Corps de module
de boîtes aux lettres POSIX
531
D • MODULE DE COMMUNICATION ADA
Cette annexe présente le code source commenté d’un module Ada générique définis-
sant les modules de données, synchronisations et boîtes aux lettres.
m Spécification de module
533
D • Module D.1 Spécification de module
de communication Ada
private
-- Implémentation par tableau géré sous forme de liste circulaire
type Tableau_Elements is array (positive range <>) of element;
-- Tableau non contraint d’éléments
type File_Bornee(taille: positive) is record
T: Tableau_Elements(1..taille);
Debut, Fin: natural :=1;
Vide : boolean :=true;
end record;
end;
m Corps de module
534
D • Module D.1 Spécification de module
de communication Ada
end File_Bornee;
535
D • Module D.1 Spécification de module
de communication Ada
private
val: element := initial.all;
end;
------------------------
-- Synchronisation
------------------------
protected type Synchro_C(priorité: natural := priority’last) is
-- Synchronisation à compte (les déclenchements successifs
-- non pris en compte sont accumulés)
pragma Priority(priorité);
procedure Signal;
entry Wait; -- when Nombre_Signalés > 0
private
Nombre_Signalés: natural := 0;
end;
536
D • Module D.2 Corps de module
de communication Ada
Pleine := false;
end Recevoir;
end BaL_1_Ecrasement;
537
D • Module D.2 Corps de module
de communication Ada
end Communications;
538
BIBLIOGRAPHIE
539
A. TANENBAUM, Réseaux, cours et exercices, Dunod (1997).
J. TOUX, Capteurs, Techniques de l’Ingénieur, traité Mesure et Contrôle.
D. TSCHIRHART, Commande en Temps réel, Dunod, Informatique Industrielle (1990).
Sites internet
Le protocole CAN (en anglais) : http://www.can-cia.de/can/
La norme POSIX (en anglais) : http://www.opengroup.org/
La norme OSEK/VDX (en anglais) : http://www.osek-vdx.org/
Le profil Ravenscar (en anglais) : http://polaris.dit.upm.es/~str/proyectos/ork/
540
LEXIQUE ANGLAIS – FRANÇAIS
Anglais Français
Actuator Actionneur
Byte Octet
Deadlock Interblocage
541
Anglais Français
Design Conception
Dispatcher Séquenceur
Failure Défaillance
Hub Répéteur
Interoperability Interopérabilité
542
Anglais Français
Offset Décalage
Overload Surcharge
Patch Modification
Pipe Tube
Probing Test
543
Anglais Français
Reliability Fiabilité
Resources Ressources
Sensor Capteur
Scheduling Ordonnancement
Sensor Capteur
Shortest Remaining Time First Ordonnancement basé sur le temps de calcul restant
Spinlock Verrou
544
Anglais Français
Stack Pile
Swap Échange
Switch Commutateur
Task Tâche
Timer Horloge
Token Jeton
Trigger Réveil
545
SIGLES
ADARTS Ada Based Design Approach for Real-Time Systems (GOMAA, 1987)
546
Sigle Désignation complète
DM Deadline Monotonic
E/S Entrées/Sorties
547
Sigle Désignation complète
IP Internet Protocol
LL Least Laxity
ML Minimum Laxity
MUX Multiplexeur
548
Sigle Désignation complète
OS Operating System
OSEK Offene Systeme und deren Schnittstellen für die Elektronik im Kraftfahrzeug
PC Personal Computer
RM Rate Monotonic
RR Round Robin
SA Structured Analysis
SE Système d’Exploitation
549
Sigle Désignation complète
VI Virtual Instrument
550
Index
INDEX
Généralités
551
Index
552
Index
553
Index
554
Index
555
Index
556
Index
557
Index
Langage informatique
#define 271 CLOCK_CPU_TIME 217
#endif 272 clock_getres 218
#ifndef 272 CLOCK_MONOTONIC 217
#include 271 clock_nanosleep 218
A CLOCK_REALTIME 216
CLOCK_THREAD_CPU-
access 258 TIME_ID 217
ActivateTask 224 cluster 252
Ada.Real_Time 326
aliased 258 D
and then 262 default 260
Any_Priority 317 Default_Priority 317
array 257 delay 325
attendre 334 delay until 325
attendre un multiple de 334
delta 250
B DisableAllInterrupts 227
boolean 250 do 260
double 248
C
E
calloc 256
case 260 else 260
case 1 260 elsif 260
Ceiling_Locking 319 EnableAllInterrupts 227
ChainTask 224 entry 317
char 248 enum 249
character 250 errno 275
ClearEvent 225 ErrorHook 229
558
Index
F P
float 248, 250 positive 250
for 260 pragma 319
free 256 pragma Attach_Handler 327
function 267, 318 priority 317
Priority_Queuing 319
G procedure 267, 317
GetResource 225 protected type 317
pthread_attr_destroy 287
I pthread_attr_init 287
if 260 pthread_attr_setinheritsched 288
int 248 pthread_attr_setschedpolicy 288
pthread_attr_setscope 287
intConnect 238
pthread_barrier 220
integer 250
pthread_barrier_init 220
Interrupt_Priority 317 pthread_barrier_wait 220
K pthread_cond 219
pthread_cond_broadcast 220, 293
kill 213 pthread_cond_destroy 292
L pthread_cond_init 219, 292
pthread_cond_signal 219, 292
Locking_Policy 319 pthread_cond_t 291
long 248 pthread_cond_timedwait 297
long double 248 pthread_cond_wait 292
long long 248 pthread_create 218, 285, 287
loop 260 pthread_join 287
pthread_make_periodic_np 297
M pthread_mutex_lock 289
malloc 256 pthread_mutex_t 289
memcmp 255 pthread_mutex_unlock 289
memcpy 255 pthread_mutexattr_init 290
mlock 217 pthread_mutexattr_setprioceiling 290
mlockall 217 pthread_mutexattr_setprotocol 290
mmap 213, 217 pthread_mutexattr_t 290
mq_receive 212 pthread_rwlock_rdlock 219
mq_send 212 pthread_rwlock_wrlock 219
pthread_self 219
MSG_Q_ID 311
© Dunod – La photocopie non autorisée est un délit.
pthread_spin_init 220
msgQCreate 238, 308 pthread_spin_lock 220
msgQReceive 238, 307
msgQSend 238 Q
Queuing_Policy 319
N
nanosleep 218 R
natural 250 raise 213
record 250
O ReleaseResource 225
or else 262 ResumeAllInterrupts 227
others 257 rwlocks 219
559
Index
S T
SCHED_FIFO 215 task type 314
SCHED_OTHER 216 Task_Dispatching_Policy 319
sched_param 288 taskActivate 235
SCHED_RR 216 taskDelay 238, 305, 309
SCHED_SPORADIC 216 taskDelete 235
Schedule 228 taskInit 235, 305
SEM_ID 310 taskLock 237
sem_open 212 taskPrioritySet 237
sem_post 212 taskResume 305
sem_timedwait 299 taskSafe 237
sem_wait 212 taskSpawn 235, 305
semBCreate 238
taskUnlock 237
semCCreate 238, 308
taskUnsafe 237
semGive 238, 308
TerminateTask 224
semMCreate 238, 307
semTake 238, 308 tExcTask 238
SendDynamicMessage 226 Time_Span 326
SendMessage 226 timer_create 218
SendZeroMessage 226 timer_settime 218
Set_Priority 316 typedef struct 249
SetEvent 225 U
shm_open 213
short 248 union 249
sigaction 213 unsigned 248
sigemptyset 213 unsigned char 248
sigwait 213 unsigned long 248
sizeof 247 unsigned long long 248
strcmp 257 unsigned short 248
strcpy 257 use 275
strlen 257 usleep 287
struct 247
SuspendAllInterrupts 227 W
switch 260 WaitEvent 225
sysClkRateGet 309 while 260
sysClkRateSet 309 with 275
560
TECHNIQUE ET INGÉNIERIE GESTION INDUSTRIELLE
Série EEA
CONCEPTION
MÉCANIQUE ET MATÉRIAUX
CONTRÔLE-COMMANDE EEA
Conception et implémentation