Systèmes en temps réel – quelques liens pertinents

Cliquez ici pour des considérations propres au temps, considéré sous un angle plus général.

Quelques raccourcis :

Il est probable que vous soyez aussi intéressé(e) par ces liens sur la multiprogrammation ou par ces liens sur les systèmes d'exploitation, en particulier ceux sur les systèmes embarqués.

Vous trouverez ici quelques liens portant sur les systèmes systèmes en temps réel (STR). Si vous découvrez d'autres liens dignes d'intérêt, faites-m'en part. De même, signalez-moi des liens brisés ou périmés si vous en rencontrez, ce qui me permettra d'offrir un meilleur service à vous comme à vos collègues.

Considérations générales

Quelques saines pratiques de programmation pour des systèmes assujettis à des contraintes de basse latence, répertoriées en 2014 par Benjamin Darfler : http://codedependents.com/2014/01/27/11-best-practices-for-low-latency-systems/

Il existe des nombres particulièrement importants en programmation, en particulier ceux ayant trait à la latence de certaines opérations clés. Le lien http://www.eecs.berkeley.edu/~rcs/research/interactive_latency.html propose une exploration interactive de quelques-uns de ces nombres.

Quelques pistes à propos des STR en général :

Communication assujettie à des contraintes TR :

À propos des entrées/ sorties :

Écrire une fonction périodique (au sens usuel du terme) avec C++ 11, selon Tony Da Silva en 2015 : http://bulldozer00.com/2015/07/15/periodicfunction/

Systèmes d'exploitation de type « temps réel » (SETR)

Pour les systèmes d'exploitation TR :

Sur QNX en particulier :

Sur Linux :

STR et virtualisation – sans grandes surprises, règle générale, ça a ses limites :

Rate Monotonicity

L'approche dite Rate Monotonic à l'ordonnancement des tâches TR d'un système correspond essentiellement à ordonnancer en priorité les tâches de plus faible période; évidemment, on parle ici de tâches périodiques.

L'approche Rate Monotonic facilite le raisonnement sur un ensemble de tâches périodiques. Elle présume ce qui suit :

Quand un système ne rencontre pas la totalité de ces « exigences », il est typiquement possible d'appliquer une approche Rate Monotonic mais il faut alors l'adapter aux besoins.

En procédant ainsi, pour un ensemble de tâches est le temps d'exécution de la tâche et et sa période, un test d'« ordonnançabilité » suffisant (que l'on doit à Liu et Layland, 1973, qui ont introduit ce concept) est .

Plateformes TR prises en charge

Un document de NIST décrivant les règles pour un Java TR est disponible sur http://www.itl.nist.gov/div897/ctg/real-time/rtj-final-draft.pdf

Pour C++

Texte de 2014 par Scot Salmon mettant en valeur que C++ est plus approprié qu'auparavant pour le développement TR : http://www.embedded.com/design/programming-languages-and-tools/4429790/How-to-make-C--more-real-time-friendly

Pour Java

Le Java Path Finder, pour accroître la résilience des systèmes programmés en Java (merci à Richard Lavoie pour ce lien) : http://babelfish.arc.nasa.gov/trac/jpf

À propos de Java TR :

Quelques extraits choisis de la spécification RTSJ 2.0 suivent.

Ordonnancement

Il est sous-entendu de par l'introduction que l'approche privilégiée pour l'ordonnancement des tâches TR au sens strict du terme est basée sur le respect des échéances, ce qui laisse entendre un biais pour l'ordonnancement sur la base de priorités dynamiques, probablement  de type EDF. Il semble raisonnable de présumer qu'il en va toutefois de la responsabilité des programmeuses et des programmeurs d'accorder aux tâches les plus pressantes la priorité la plus haute.

« In a realtime system, the system tries to schedule the most critical task that is ready to run first. This task runs either until it is finished, or it needs to wait for some event or data, or a more critical task is released or a more critical task becomes schedulable after waiting for its event or data.

Realtime scheduling is commonly done with a priority preemptive scheduler, where tasks that have short deadlines are given higher priority than tasks that have longer deadlines. The programmer is responsible for encoding some notion of task importance to priorities. The goal is to see that all tasks nish within their deadlines. Scheduling analysis, such as Rate Monotonic Analysis, can be used to help determine this. »

Transfert de contrôle asynchrone

Le transfert de contrôle asynchrone (ATC) englobe tous les mécanismes de transfert de contrôle situés hors du comportement « normal » du programme, incluant entre autres les exceptions et les interruptions. Les exceptions de RTSJ peuvent d'ailleurs être créées et véhiculées par des mécanismes différents de ceux de la JVM standard (en particulier la classe AsynchronouslyInterruptedException)

Les méthodes susceptibles de subir un ATC doivent être identifiées en tant que telles. L'exception InterruptedException de la JVM standard sert à titre de signal ATC

Certains blocs, en particulier les blocs synchronized et les constructions static, sont tels qu'ils doivent être menés à terme pour éviter un interblocage. Conséquemment, tout ATC rencontré en chemin ne sera traité qu'à la fin du bloc.

L'interruption d'un thread TR demande un traitement soigneux de la part des programmeuses et des programmeurs, évidemment.

« Many event-driven computer systems that tightly interact with external physical systems (e.g., humans, machines, control processes, etc.) may require mode changes in their computational behavior as a result of significant changes in the non-computer real-world system. It simplifies the architecture of a system when a task can be programmatically terminated when an external physical system change causes its computation to be superfluous. Without this facility, a thread or set of threads have to be coded so that their computational behavior anticipates all of the possible transitions among possible states of the external system. When the external system makes a state transition, the changes in computation behavior can be managed by an oracle that terminates a set of threads required for the old state of the external system, and invokes a new set of threads appropriate for the new state of the external system. Since the possible state transitions of the external system are encoded in only the oracle and not in each thread, the overall system design is simpler.

There is a second requirement for a mechanism to terminate some computation, where a potentially unbounded computation needs to be done in a bounded period of time. In this case, if that computation can be executed with an algorithm that is iterative, and produces successively re ned results, the system could abandon the computation early and still have usable results. The RTSJ supports aborting a computation by signalling from another thread, or the passage of time, with a feature termed Asynchronous Transfer of Control (ATC). »

Gestion de la mémoire

Les auteurs de la spécification RTSJ 2.0 sont d'avis qu'il existe des moteurs de collecte d'ordures appropriés pour des systèmes TR stricts, mais préconisent tout de même des mécanismes alternatifs.

En particulier, pour éviter des fuites de mémoire, RTSJ propose des zones de mémoire réclamées en bloc, de manière pleinement déterministe, et choisies par les programmeuses et les programmeurs. Ces zones échappent au moteur de collecte d'ordures en étant prises ailleurs, un peu à l'image des variables locales placées sur la pile. Le moteur de collecte d'ordures peut lire dans ces zones (au cas où elles contiendraient des références sur des objets placés sur le tas) mais ne les réclamera pas lui-même.

Pour éviter les accidents, il est possible de marquer certains threads comme étant de type No Heap et d'éviter que ces threads ne fasse un new inattendu.

Pour ce qui est de l'accès direct à de la mémoire physique, RTSJ offre des mécanismes sous la forme de :

  • Mémoire physique immortelle, ce qui permet de gérer des blocs de mémoire spécifiques (un peu à la manière des spécialisations de certaines spécialisations assistées de new et de delete en C++), qui ne sera pas à risque d'être considérée comme faisant partie de la mémoire virtuelle
  • Mémoire physique délimitées (Scoped Memory), blocs qui peuvent être placés en mémoire virtuelle mais qui ont une portée déterministe
  • Un cas particulier de mémoire délimitée est le Stacked Memory : la spécification formelle de la JVM n'impose pas que les variables locales à une fonction soient placées sur la pile, ce qui peut rendre certaines programmeuses nerveuses et certains programmeurs nerveux. La Stacked Memory garantir que les variables y seront placées sur la pile et gérées efficacement

Cependant, RTSJ exige que les accès à ces zones de mémoire soient faits avec des instructions « normales » de la JVM. L'accès à des zones mémoires qui exigeraient des instructions que la JVM n'offre pas n'est pas supporté par cette spécification.

La sémantique d'accès, les temps d'accès et les temps d'allocation dans chaque cas sont définis à même la spécification RTSJ.

« The Java language is designed around automatic memory management, in particular garbage collection. Unfortunately, though garbage collection is a functional safety and security feature, conventional garbage collectors interrupt the normal flow of control in a program. Therefore, garbage-collected memory heaps had been considered an obstacle to realtime programming due to the potential for unpredictable latencies introduced by the garbage collector.

Though convention collectors still have these drawbacks, there are now realtime collectors that can be used for hard realtime application. Still, the RTSJ provides an alternative to garbage collection for systems which require it, either because they do not have a garbage collector or deterministic garbage collector, or require heap partitioning for some other reason. Extensions to the memory model, which support memory management in a manner that does not interfere with the ability of realtime code to provide deterministic behavior, are provided to support these alternatives.

This goal is accomplished by providing memory areas for the allocation of objects outside of the garbage-collected heap for both short-lived and long-lived objects. In order to provide additional separation between the garbage collector and schedulables which do not require its services, a schedulable can be marked no-heap to indicate that it never accesses the heap. »

Modularité

À l'image de ce vers quoi semble tendre Java 9, la RTSJ 2.0 se veut modulaire. Il semble en effet que l'un des obstacles à l'adoption de la version précédente de cette spécification est qu'elle était présentée comme un « tout ou rien », ce qui a refroidi les ardeurs de bien des implémenteurs.

« The original RTSJ specification was conceived, with the exception of some optional features, as a monolith specification. This has inhibited the adoption of the RTSJ beyond the hard realtime community, because some of the features were considered to have an overly negative impact on overall JVM performance. Version 2.0 addresses this by breaking the specification into modules[...]

Modules provide a means of grouping like functionality together in a way that promotes maximal adoption for various implementation classes. A conventional JVM could simply implement the Base Module, without providing any realtime guarantees at all, to provide programmers with the benefits of features such as asynchronous event programming as an alternative to conventional threading. A hard realtime implementation could implement all modules to provide the maximal flexibility and functionality to the realtime programmer. Both would benefit from easier migration of code to realtime systems. Every RTSJ implementation shall provide the Base Module functionality, but all other modules are optional. The optional modules are the Device Module and the Alternate Memory Management module »

Ordonnancement

L'ordonnancement minimal pour supporter RTSJ 2.0 est un ordonnancement à priorité fixe (FPS) de 28 niveaux de priorité, les dix plus basses correspondant aux priorités standards (non-TR). Les classes dérivées de Schedulable (les tâches TR, quels que soient les types effectifs) offrent toutefois de l'information plus précise pour permettre à un ordonnanceur plus sophistiqué de prendre le relais. RTSJ permet de remplacer l'ordonnanceur de base par un autre implémentant un algorithme de notre choix.

Synchronisation

L'héritage de priorités doit être implémenté par toute implémentation de RTSJ 2.0. Le protocole de plafonnage de priorité est optionnel.

Périphériques et interruptions

La spécification RTSJ vise entre autres à permettre de déployer des programmes Java TR en tant que modules du noyau d'un système d'exploitation, ou pour réagir à l'occurrence d'un événement tel qu'une interruption. Une partie de la spécification porte sur les outils pour y arriver, et les considérations propres à la latence lors d'une réaction à une interruption y sont documentées.

Système d'exploitation

Enfin, RTSJ offre une interface pour accéder de manière relativement directe aux services du système d'exploitation, mais ces services sont essentiellement construits en fonction des systèmes POSIX.

Dépréciations

Quelques parties de la spécification RTSJ précédente sont dépréciées avec RTSJ 2.0. Parmi elles, notons la classe NoHeapRealtimeThread (NHRT), en particulier, qui est remplacée par d'autres combinaisons de services. de même que quelques éléments des interfaces au système d'exploitation (surtout du fait que POSIX ne les supporte plus).

Un exemple de petit programme TR avec RTSJ serait :

import javax.realtime.RealtimeThread;
public class CoucouTR extends RealtimeThread {
   public void run() {
      System.out.println("J'aime mon prof!");
   }
   public static void main(String [] args) {
      CoucouTR coucou = new CoucouTR();
      coucou.start();
      try {
         coucou.join();
      } catch (InterruptedException ie) {
      } 
   } 
}

Notez que ce thread est considéré apériodique car il n'est pas assujetti à une contrainte de constance, donc il n'a pas de période de démarrage fixée a priori.

Ce programme fonctionne du fait que le join() suspend le code appelant jusqu'à complétion du thread coucou, et du fait que le thread coucou s'arrêtera de lui-même. En effet, une fois coucou lancé, ce dernier étant un thread TR strict et RTSJ étant une véritable plateforme TR, coucou prendra la main et main(), qui se loge dans un thread « normal », ne pourra plus s'exécuter.

À l'inverse, le programme suivant ne fonctionne pas :

import javax.realtime.RealtimeThread;
public class CoucouTR extends RealtimeThread {
   volatile boolean meurs;
   public CoucouTR() {
      meurs = false;
   }
   public void finExécution() {
      meurs = true;
   }
   public void run() {
      while(!meurs) { // oups!
         System.out.println("J'aime mon prof!");
      }
   }
   public static void main(String [] args) {
      CoucouTR coucou = new CoucouTR();
      coucou.start();
      try {
         coucou.finExécution(); // oups!
         coucou.join();
      } catch (InterruptedException ie) {
      } 
   } 
}

En effet, dans ce cas, coucou ne sera arrêté que lorsque main() aura appelé sa méthode finExécution(), or puisque coucou est un thread TR et main() est dans un thread normal, il se trouve que main() n'aura jamais l'occasion de s'exécuter.

Enfin, un exemple de thread TR périodique serait le TiPoints ci-dessous. Notez que cette version s'interrompt après un nombre d'affichages déterminé à la construction, plutôt que d'attendre qu'une touche ait été pressée :

import javax.realtime.PeriodicParameters;
import javax.realtime.RealtimeThread;
import javax.realtime.RelativeTime;
public class TiPoints extends RealtimeThread {
   int nbIt;
   public TiPoints(PeriodicParameters pp, int n) { // on suppose n >= 0 pour simplifier le tout
      super(null, pp);
      nbIt = n;
   }
   public void run() {
      for (int i = 0; i < nbIt; ++i) {
         System.out.print(".");
         waitForNextPeriod();
      }
   }
   public static void main(String [] args) {
      PeriodicParameters pp = new PeriodicParameters(new RelativeTime(1000, 0)); // chaque 1000 ms
      TiPoints tiPts = new TiPoints(pp, 10); 
      tiPts.start();
      try {
         tiPts.join();
      } catch (InterruptedException ie) {
      }
   }
}

Pour Erlang

Erlang et le temps réel strict :

Pour .NET

À propos de .NET en version TR :

Systèmes embarqués

À propos des systèmes embarqués :

Autres sujets

D'autres cours sur le sujet :

Combien de temps faut-il pour réaliser un changement de contexte? http://blog.tsunanet.net/2010/11/how-long-does-it-take-to-make-context.html

Quelques articles disponibles à travers la bibliothèque numérique du Association for Computing Machinery (ACM) :


Valid XHTML 1.0 Transitional

CSS Valide !