Remarque :  La simultanéité est traitée ici de manière générale, du fait qu'elle peut s'appliquer à n'importe quel système. Cependant, elle est particulièrement importante dans les systèmes qui doivent réagir à des événements extérieurs en temps réel et qui ont souvent des délais stricts à respecter. Pour répondre à ces exigences particulières de cette classe de système, le processus RUP (Rational Unified Process) dispose d'extensions système en temps réel (réactives).

Rubriques

Qu'est-ce que la simultanéité ? Haut de la page

La simultanéité est la tendance des choses à se produire au même moment dans un système. C'est bien sûr, un phénomène naturel. Dans le monde réel, à un moment donné, de nombreuses choses se produisent simultanément. Lorsque nous concevons un logiciel pour surveiller et contrôler des systèmes en temps réel, nous devons faire face à cette même simultanéité naturelle.

Lors du traitement des questions de simultanéité dans les systèmes logiciels, deux aspects sont généralement importants : être capable de détecter des événements externes se produisant de manière aléatoire et d'y répondre, tout en assurant que ces événements sont traités dans le délai minimal requis.

Si chaque activité simultanée évoluait indépendamment, de manière totalement parallèle, cela serait relativement simple : il suffirait de créer des programmes distincts pour traiter chacune des activités. Les défis de conception des systèmes simultanés augmentent en grande partie du fait des interactions qui se produisent entre les activités simultanées. Lorsque des activités simultanées interagissent, une certaine forme de coordination est requise.

Diagramme détaillé dans le contenu.

Figure 1 : Exemple de simultanéité au travail : des activités parallèles qui n'interagissent pas ne présentent que de simples problèmes de simultanéité. En revanche, lorsque des activités parallèles interagissent ou partagent les mêmes ressources, les problèmes de simultanéité deviennent importants.

Le trafic routier présente une analogie utile. Des flots de trafic parallèles sur différents axes routiers ayant peu d'interaction engendrent peu de problèmes. En revanche, des flots parallèles sur des voies adjacentes requièrent une coordination pour une interaction sécurisée, mais un type d'interaction bien plus grave se produit à un intersection, où une coordination minutieuse est indispensable.

Pourquoi sommes-nous intéressés par la simultanéité ? Haut de la page

Certaines forces directrices requérant la simultanéité sont externes. C'est-à-dire qu'elles sont imposées par les exigences de l'environnement. Dans des systèmes du monde réel, de nombreuses choses se produisent simultanément et doivent être traitées en temps réel par le logiciel. Pour ce faire, de nombreux logiciels en temps réel doivent être réactifs. Ils doivent répondre aux événements générés par l'extérieur qui peuvent se produire à des heures quelque peu aléatoires, dans un ordre aléatoire, ou les deux.

La conception d'un programme procédural conventionnel permettant de répondre à ces situations est extrêmement complexe. Il peut être considérablement plus simple de partitionner le système en éléments logiciels simultanés destinés à traiter chacun de ces événements. L'expression clé dans le cas présent est "peut être", car la complexité est également affectée par le degré d'interaction entre les événements.

La simultanéité peut également trouver ses causes inspirées en interne [LEA97]. L'exécution de tâches en parallèle peut considérablement accélérer le travail de calcul d'un système si plusieurs UC sont disponibles. Même dans un seul processeur, le multitâche peut considérablement accélérer les choses en empêchant une activité d'en bloquer une autre pendant l'attente d'une E-S, par exemple. Ce cas se produit couramment au démarrage du système. De nombreux composants nécessitent alors du temps pour être prêt au fonctionnement. L'exécution séquentielle de ces opérations serait incroyablement lente.

La capacité de contrôle du système peut être également améliorée par la simultanéité. Par exemple, une fonction peut être démarrée, arrêté ou encore influencée à mi-exécution par d'autres fonctions simultanées, chose extrêmement difficile à réaliser sans composants simultanés.

Qu'est-ce qui complique la simultanéité logicielle ? Haut de la page

Avec tous ces avantages, pourquoi n'utilisons-nous pas partout la programmation simultanée ?

La plupart des ordinateurs et des langages de programmation sont généralement séquentiels. Une procédure ou un processeur exécute une instruction à la fois. Dans un seul processeur séquentiel, l'illusion de simultanéité doit être créée par l'imbrication de l'exécution de différentes tâches. Les difficultés ne résident pas tellement dans les mécanismes qui permettent d'y parvenir, mais dans la détermination de quand et comment imbriquer les segments de programme qui peuvent interagir les uns avec les autres.

Si parvenir à la simultanéité est une opération simple avec plusieurs processeurs, les interactions deviennent plus complexes. Il y a tout d'abord la question des communications entre les tâches s'exécutant sur différents processeurs. Généralement, plusieurs couches de logiciels sont impliquées, ce qui augmente la complexité et ajoute une surcharge de temps. Le déterminisme est réduit dans des systèmes à plusieurs UC, du fait que les horloges et les délais peuvent différer, et les composants peuvent être défaillants de manière indépendante.

En fait, des systèmes simultanés peuvent être plus difficiles à comprendre car un état système global explicite leur manque. L'état d'un système simultané est l'agrégat des états de ses composants.

Exemple de système simultané en temps réel : système d'ascenseur Haut de la page

Comme exemple permettant d'illustrer les concepts à présenter, nous allons utiliser un système d'ascenseur. Plus précisément, un système informatique conçu pour contrôler un groupe d'ascenseurs dans un édifice. Il est évident que de nombreux événements peuvent se produire simultanément dans un groupe d'ascenseurs ou qu'il ne se passe rien du tout ! A tout moment, quelqu'un à n'importe quel étage peut appeler un ascenseur et d'autres demandes peuvent être en attente. Certains des ascenseurs peuvent être à l'arrêt, tandis que d'autres transportent des passagers ou vont répondre à un appel, ou les deux. Les portes doivent s'ouvrir et se fermer aux moments opportuns. Les passagers peuvent obstruer les portes, ou maintenir les boutons d'ouverture et de fermeture de porte enfoncés, ou sélectionner les étages, puis changer d'avis. Des affichages doivent être mis à jour, des moteurs doivent être contrôlés, etc., le tout sous la supervision du système de contrôle des ascenseurs. De plus, c'est un excellent modèle pour l'exploration de concepts simultanés et pour lequel nous partageons un degré raisonnablement courant de compréhension et un vocabulaire de travail.


Diagramme détaillé dans le contenu.
Figure 2 :  Scénario impliquant deux ascenseurs et cinq passagers potentiels répartis sur onze étages

Comme les passagers potentiels effectuent sollicitent le système à des moments différents, celui-ci tente de fournir les meilleurs services globaux en sélectionnant les ascenseurs devant répondre aux appels en fonction de leur état courant et des temps de réponse estimés. Par exemple, lorsque le premier passager potentiel, André, appelle un ascenseur pour descendre, les deux sont à l'arrêt, aussi le plus proche, l'ascenseur 2, répond, bien qu'il doive d'abord monter pour aller chercher André. D'autre part, quelques instants plus tard, lorsque le deuxième passager potentiel, Bernard, appelle un ascenseur pour monter, l'ascenseur 1 le plus éloigné répond car il sait que l'ascenseur 2 doit descendre vers une destination encore inconnue avant de pouvoir répondre à un appel de montée émanant du bas.

La simultanéité en tant que stratégie Haut de la page

Si le système d'ascenseur n'était composé que d'un seul ascenseur et qu'il ne doive répondre qu'à un seul passager à la fois, nous pourrions être tentés de penser que l'opération pourrait être réalisée par un programme séquentiel ordinaire. Même dans ce "simple" cas, le programme nécessiterait plusieurs branches pour répondre aux différentes situations. Par exemple, si le passager n'est jamais monté et n'a jamais sélectionné d'étage, l'ascenseur doit être réinitialisé pour lui permettre de répondre à un autre appel.

L'exigence normale permettant de traiter les appels émanant de plusieurs passagers potentiels et des demandes de plusieurs passagers illustre les forces directrices externes de simultanéité, abordées précédemment. Comme les passagers potentiels mènent leurs propres vies simultanées, ils appellent l'ascenseur à des moments visiblement aléatoires, quel que soit l'état de l'ascenseur. Il est excessivement difficile de concevoir un programme séquentiel capable de répondre à ces événements externes à n'importe quel moment, tout en continuant à guider l'ascenseur en fonction des décisions passées.

Abstraction de la simultanéité Haut de la page

Afin de concevoir des systèmes simultanés efficaces, nous devons être capables de raisonner sur le rôle de la simultanéité dans le système, et pour ce faire, nous avons besoin d'abstractions de la simultanéité elle-même.

Les blocs de construction fondamentaux des systèmes simultanés sont des "activités" qui s'exécutent plus ou moins indépendamment les uns des autres. Une abstraction graphique utile pour penser à de telles activités est le "timethread" de Buhr. [BUH96] Notre scénario d'ascenseur de la figure 3 en utilisait en fait une certaine forme. Chaque activité est représentée sous forme de ligne sur laquelle l'activité voyage. Les gros points représentent les endroits où une activité commence ou attend qu'un événement se produise avant de s'exécuter. Une activité peut en déclencher une autre pour poursuivre, représentée dans la notation du "timethread" par l'atteinte de l'emplacement d'attente sur l'autre timethread.

Diagramme détaillé dans le contenu.

Figure 3 :  Visualisation des unités d'exécution

Les blocs de construction de base du logiciel sont des procédures et des structures de données, mais seuls, ils sont inappropriés pour raisonner sur la simultanéité. Lorsqu'un processeur exécute une procédure, il suit un chemin particulier en fonction des conditions courantes. Ce chemin peut être appelé l'"unité d'exécution" ou l'"unité de contrôle". Cette unité de contrôle peut suivre différentes branches ou boucles en fonction des conditions du moment, et dans des systèmes en temps réel peut s'interrompre pendant un certain temps ou attendre une heure planifiée pour reprendre son activité.

Du point de vue du concepteur de programme, l'unité d'exécution est contrôlée par la logique du programme et planifiée par le système d'exploitation. Si le concepteur logiciel décide qu'une procédure en invoque d'autres, l'unité d'exécution passe d'une procédure à l'autre, puis revient en arrière pour poursuivre là où elle en est restée si une instruction de retour est rencontrée.

Du point de vue de l'UC, il n'y a qu'une seule unité d'exécution principale qui "tisse" dans le logiciel, assistée de petites unités distinctes qui sont exécutées en réponse aux interruptions matérielles. Comme tout le reste s'appuie sur ce modèle, il est important pour les concepteurs de le savoir. Les concepteurs de systèmes en temps réel, à un plus haut degré que des concepteurs d'autres types de logiciels, doivent comprendre comme un système fonctionne à un niveau très détaillé. Ce modèle, cependant, est à un tel faible niveau d'abstraction qu'il ne peut représenter qu'une granularité très générale de simultanéité de l'UC. Pour concevoir des systèmes complexes, il est utile de pouvoir travailler à divers niveaux d'abstraction. L'abstraction, bien sûr, est la création d'une vue ou d'un modèle qui supprime des détails inutiles, de façon à pouvoir mettre l'accent sur ce qui est important pour le problème concerné.

Pour monter d'un niveau, nous envisageons généralement un logiciel en termes de couches. Au niveau le plus fondamental, le système d'exploitation est réparti en couches entre le matériel et le logiciel d'application. Il fournit l'application avec des services basés sur le matériel, comme la mémoire, le calendrier et les E-S, mais il résume l'UC pour créer une machine virtuelle, indépendante de la configuration matérielle réelle.

Réalisation de la simultanéité : mécanismes Haut de la page

Gestion des unités de contrôleHaut de la page

Pour prendre en charge la simultanéité, un système doit fournir plusieurs unités de contrôle. L'abstraction d'une unité de contrôle peut être réalisée de nombreuses façons par le matériel et les logiciels. Les mécanismes les plus courants sont des variations de [DEI84] ou [TAN86]:

  • Multitraitement : plusieurs UC s'exécutent simultanément.
  • Multitâche : le système d'exploitation simule la simultanéité sur une seule UC en
    imbriquant l'exécution de différentes tâches.
  • Solutions basées sur l'application : le logiciel d'application prend la responsabilité de
    basculer entre les différentes branches de code aux moments appropriés.

Multitâche Haut de la page

Lorsque le système d'exploitation fournit un fonctionnement multitâche, l'unité commune de simultanéité est le processus. Un processus est une entité fournie, prise en charge et gérée par le système d'exploitation dont le seul but est de fournir un environnement dans lequel un programme peut s'exécuter. Le processus fournit un espace mémoire pour l'utilisation exclusive de son programme d'application, une unité d'exécution pour l'exécuter et, parfois des moyens permettant d'envoyer des messages et d'en recevoir d'autres processus. En effet, le processus est une UC virtuelle permettant l'exécution d'un fragment simultané d'une application.

Pour chaque processus, trois états sont possibles :

  • bloqué : attend la réception d'entrées ou l'obtention du contrôle d'une ressource ;
  • prêt : attend que le système d'exploitation lui permette de s'exécuter ;
  • exécution en cours : utilise réellement l'UC.

Des priorités relatives sont souvent affectées aux processus. Le noyau du système d'exploitation détermine quel processus doit s'exécuter à un moment donné en fonction de son état, de ses priorités et du principe d'organisation. Les systèmes d'exploitation multitâches partagent en fait une seule unité de contrôle entre tous leurs processus.

Remarque : les termes "tâche" et "processus" sont souvent utilisés l'un pour l'autre. Malheureusement, le terme "multitâche" est généralement utilisé pour signifier la capacité à gérer plusieurs processus à la fois, alors que "multitraitement" fait référence à un système doté de plusieurs processeurs (UC). Nous adhérons à cette convention du fait qu'elle est la plus communément acceptée. Cependant, nous utilisons le terme "tâche" avec parcimonie et dans ce cas, c'est pour faire une nette distinction entre l'unité de travail réalisée (la tâche) et l'entité qui fournit les ressources et l'environnement pour se faire (le processus).

Comme nous l'avons dit préalablement, du point de vue de l'UC, il n'y qu'une seule unité d'exécution. Tout comme un programme d'application peut passer d'une procédure à une autre en invoquant des sous-routines, le système d'exploitation peut transférer le contrôle d'un processus à un autre dans le cas d'une interruption, de l'exécution d'une procédure, ou d'un autre événement. Du fait de la protection de mémoire offerte par un processus, ce "basculement de tâche" peut s'accompagner d'une surcharge considérable. En outre, du fait que le principe d'organisation et les états des processus ont peu de choses en commun avec le point de vue de l'application, l'imbrication des processus est généralement à un niveau d'abstraction trop bas pour penser au type de simultanéité qui est important pour l'application.

Afin de raisonner clairement sur la simultanéité, il est important de maintenir une séparation nette entre le concept d'une unité d'exécution et celui de basculement des tâches. Chaque processus peut être envisagé, gérant sa propre unité d'exécution. Lorsque le système d'exploitation bascule d'un processus à l'autre, une unité d'exécution est temporairement interrompue et une autre démarre ou reprend son exécution là où elle s'est précédemment arrêtée.

Multi-unité d'exécution Haut de la page

Plusieurs systèmes d'exploitation, en particulier ceux utilisés dans des applications en temps réel, offrent une alternative plus légère aux processus, appelée "unités" ou "unités légères".

Les unités constituent un moyen pour parvenir à une granularité de simultanéité légèrement plus fine dans un processus. Chaque unité appartient à un seul processus et toutes les unités d'un processus partagent l'espace mémoire et d'autres ressources contrôlées par ce processus.

Généralement, chaque unité se voit attribuée une procédure à exécuter.

Remarque : Il est malheureux que le terme "unité (thread)" soit surchargé. Lorsque nous utilisons ce terme, comme nous le faisons ici, nous faisons référence à une "unité physique" fournie et gérée par le système d'exploitation. Lorsque nous faisons référence à une "unité d'exécution", à une "unité de contrôle" or à une "unité temporelle (timethread)" comme dans la discussion à venir, nous voulons dire une abstraction qui n'est pas nécessairement associée à une unité physique.

Multitraitement Haut de la page

Bien sûr, plusieurs processeurs offrent une opportunité pour une exécution réellement simultanée. Le plus souvent, chaque tâche est affectée de manière permanente à un processus dans un processeur particulier, mais dans certaines circonstances, des tâches peuvent être affectées de manière dynamique au processeur suivant disponible. Le moyen peut-être le plus accessible pour y parvenir est d'utiliser un "multiprocesseur symétrique". Dans une telle configuration matérielle, plusieurs UC peuvent accéder à la mémoire via un bus commun.

Les systèmes d'exploitation prenant en charge des multiprocesseurs symétriques peuvent affecter des unités de manière dynamique à n'importe quelle UC disponible. Solaris de SUN et Windows NT de Microsoft sont des exemples de systèmes d'exploitation prenant en charge des multiprocesseurs symétriques.

Questions fondamentales relatives aux logiciels simultanés Haut de la page

Nous avons précédemment déclaré de manière visiblement paradoxale que la simultanéité augmente et réduit la complexité d'un logiciel. Des logiciels simultanés fournissent des solutions plus simples aux problèmes complexes, principalement du fait qu'ils autorisent une "séparation des problèmes" sur les activités simultanées. De ce fait, la simultanéité est simplement un outil supplémentaire permettant d'augmenter la modularité des logiciels. Lorsqu'un système doit effectuer des activités pour la plupart indépendantes ou répondre à des événements principalement indépendants, leur affectation à des composants simultanés individuels simplifie naturellement la conception.

La complexité supplémentaire associée aux logiciels simultanés émane presque entièrement de situations dans lesquelles ces activités simultanées sont presque, mais pas tout à fait indépendantes. En d'autres termes, la complexité émane de leurs interactions. D'un point de vue pratique, les interactions entre activités asynchrones impliquent invariablement l'échange d'une certaine forme de signaux ou d'informations. Les interactions entre unités de contrôle simultanées donnent naissance à un ensemble de problèmes, particulier aux systèmes simultanés et qui doivent être corrigés pour garantir que le système se comporte correctement.

Interaction asynchrone par rapport à l'interaction synchrone Haut de la page

Bien qu'il existe plusieurs réalisations spécifiques différentes de la communication interprocessus (IPC) ou des mécanismes de communication inter-unités, ils peuvent tous être classés en deux catégories :

Dans la communication asynchrone, l'activité émettrice envoie ses informations que le récepteur soit prêt ou non à les recevoir. Une fois les informations lancées de cette façon, l'émetteur poursuit avec ce qu'il doit faire ensuite. Si le récepteur n'est pas prêt à recevoir les informations, celles-ci sont placées dans une file d'attente, de laquelle il peut les récupérer ultérieurement. L'émetteur et le récepteur opèrent de manière asynchrone, et ne peuvent donc émettre aucune supposition sur l'état de l'autre. La communication asynchrone est souvent appelée transmission de messages.

La communication synchrone inclut la synchronisation entre l'émetteur et le récepteur en plus de l'échange d'informations. Pendant l'échange des informations, les deux activités simultanées fusionnent entre elles, exécutant en fait un segment partagé du code, puis se séparent à nouveau une fois la communication terminée. Ainsi, pendant cet intervalle, elles sont synchronisées l'une avec l'autre et immunisées contre les conflits de simultanéité pouvant se produire entre elles. Si une activité (émetteur ou récepteur) est prête à communiquer avant l'autre, elle est suspendue jusqu'à ce que l'autre soit prête également. De ce fait, ce mode de communication est parfois appelé rendez-vous.

Un problème potentiel avec la communication synchrone réside dans le fait qu'en attendant que son homologue soit prêt, une activité n'est pas capable de réagir aux autres événements. Pour de nombreux systèmes en temps réel, ceci n'est pas toujours acceptable du fait qu'il se peut qu'il ne soit pas possible de garantir une réponse dans les délais dans une situation importante. Un autre inconvénient est qu'elle est sujette aux interblocages. Un interblocage se produit lorsque une ou plusieurs activités sont impliquées dans un cercle vicieux d'attente l'une de l'autre.

Lorsque des interactions sont nécessaires entre activités simultanées, le concepteur doit choisir entre le mode synchrone ou asynchrone. Par synchrone, nous entendons qu'une ou plusieurs unités de contrôle simultanées doivent avoir rendez-vous à un certain moment. Ceci signifie généralement qu'une unité de contrôle doit attendre qu'une autre réponde à une demande. La forme d'interaction synchrone la plus simple et la plus courante se produit lorsqu'une activité simultanée A requiert des informations d'une activité simultanée B pour poursuivre son propre travail.

Des interactions synchrones constituent, bien sûr, la norme des composants logiciels non simultanés. Des appels de procédure ordinaire sont des exemples d'interaction synchrone : lorsqu'une procédure en appelle une autre, l'appelant transfère instantanément le contrôle sur la procédure appelée et "attend" effectivement que le contrôle lui soit à nouveau transféré. Dans le monde simultané, cependant, un appareil supplémentaire est nécessaire pour synchroniser des unités de contrôle, sinon indépendantes.

Des interactions asynchrones ne requièrent pas de rendez-vous temporel, mais nécessitent un appareil supplémentaire pour prendre en charge les communications entre deux unités de contrôle. Souvent cet appareil prend la forme de canaux de communication avec des files de message de sorte que ces messages puissent être envoyés et reçus de manière asynchrone.

Notez qu'une seule application peut mélanger des communications synchrones et asynchrones, selon qu'elle doit attendre une réponse ou a un autre travail qu'elle peut faire pendant le traitement du message par le récepteur.

N'oubliez pas que la véritable simultanéité des processus ou des unités d'exécution n'est possible que sur des multiprocesseurs, avec une exécution simultanée des processus ou des unités d'exécution. Sur un processeur unique, l'illusion d'exécution simultanée des unités ou des processus est créée par le planificateur du système d'exploitation qui fractionne les ressources de traitement disponibles en petits morceaux de sorte qu'il semble que plusieurs unités d'exécution ou processus s'exécutent simultanément. Une mauvaise conception fera échouer ce découpage de temps en créant plusieurs processus ou unités d'exécution qui communiquent fréquemment de manière synchrone, entraînant les processus ou unités d'exécution à passer une grande partie de leur temps bloqués et en attente d'une réponse émanant d'un autre processus ou unité d'exécution.

Conflit de ressources partagées Haut de la page

Des activités simultanées peuvent dépendre de ressources limitées qui doivent être partagées entre celles-ci. Les périphériques d'E-S en sont des exemples typiques. Si une activité nécessite une ressource qui est utilisée par une autre, elle doit attendre son tour.

Conditions d'indétermination : une question d'état cohérent Haut de la page

Il se peut que le problème fondamental de la conception de systèmes simultanés soit d'éviter les conditions d'indétermination. Si une partie du système doit réaliser des fonctions dépendantes d'un état (autrement dit, des fonctions dont les résultats dépendent de l'état courant du système), il faut s'assurer que l'état est stable pendant l'opération. En d'autres termes, certaines opérations doivent être "atomisées". Chaque fois que deux ou plusieurs unités de contrôle ont accès aux mêmes informations d'état, une certaine forme de "contrôle de simultanéité" est nécessaire pour assurer qu'une unité ne modifie pas l'état tandis que l'autre effectue une opération dépendante de l'état atomisé. Des tentatives simultanées d'accès aux mêmes informations d'état qui pourraient rendre l'état incohérent en interne sont appelées "conditions d'indétermination".

Un exemple typique de condition d'indétermination peut facilement se produire dans le système d'ascenseur lorsqu'un étage est sélectionné par un passager. Notre ascenseur fonctionne avec une liste d'étages à visiter lorsqu'il monte ou descend. Chaque fois qu'il arrive à un étage, une unité de contrôle supprime cet étage de la liste appropriée et passe à la destination suivante de la liste. Si la liste est vide, l'ascenseur change de direction si l'autre liste comporte des étages ou attend si les deux listes sont vides. Une autre unité de contrôle est responsable du placement des demandes d'étage dans la liste appropriée lorsque les passages sélectionnent leurs étages. Chaque unité effectue des combinaisons d'opérations sur la liste qui ne sont pas atomisées : par exemple, vérifier l'emplacement disponible suivant et le remplir. Si les unités imbriquent leurs opérations, elles peuvent aisément utiliser le même emplacement de la liste.

Interblocage Haut de la page

Un interblocage est une condition dans laquelle les deux unités de contrôle sont toutes deux bloquées, chacune attendant l'autre pour effectuer une action. Ironiquement, un interblocage se produit souvent lorsqu'un mécanisme de synchronisation est appliqué pour éviter des conditions d'indétermination.

L'exemple de condition d'indétermination avec l'ascenseur peut entraîner un cas relativement bénin d'interblocage. L'unité de contrôle de l'ascenseur pense que la liste est vite et de ce fait, ne visite jamais d'autre étage. L'unité de demande d'étage pense que l'ascenseur s'applique à vider la liste et de ce fait, il ne l'avertit pas qu'il doit se mettre en action.

Autres questions pratiques Haut de la page

Outre les questions fondamentales, certaines questions pratiques doivent être explicitement prises en considération dans la conception d'un logiciel simultané.

Compromis de performances Haut de la page

Dans une seule UC, les mécanismes requis pour simuler la simultanéité lors du basculement entre tâches utilisent des cycles d'UC qui sinon seraient dépensés sur l'application elle-même. D'un autre côté, si le logiciel doit attendre des périphériques d'E-S par exemple, les améliorations de performances accordées par la simultanéité peuvent largement compenser la surcharge supplémentaire.

Compromis de complexité Haut de la page

Un logiciel simultané nécessite des mécanismes de coordination et de contrôle non requis dans des applications de programmation séquentielle. Ceci rend les logiciels simultanés plus complexes et augmente les risques d'erreur. Les problèmes des systèmes simultanés sont également plus difficiles à diagnostiquer du fait de la multiplicité d'unités de contrôle. D'un autre côté, comme nous l'avons précédemment indiqué, si les forces directrices externes sont elles-mêmes simultanées, un logiciel simultané qui traite différents événements de manière indépendante peut être largement plus simple qu'un programme séquentiel qui doit appréhender les événements dans un ordre arbitraire.

Non déterminisme Haut de la page

Comme de nombreux facteurs déterminent l'imbrication d'exécution de composants simultanés, le même logiciel peut répondre à la même séquence d'événements dans un ordre différent. Selon la conception, de telles modifications de l'ordre peuvent aboutir à des résultats différents.

Rôle du logiciel d'application dans le contrôle de la simultanéité Haut de la page

Un logiciel d'application peut être ou ne pas être impliqué dans l'implémentation du contrôle de simultanéité. Il existe un spectre entier de possibilités pour augmenter l'implication :

  1. Des tâches d'application peuvent être interrompues à n'importe quel moment par le système d'exploitation (multitâche pré-emptif).
  2. Des tâches d'application peuvent définir des unités atomisées de traitement (sections sensibles) qui ne doivent pas être interrompues et informent le système d'exploitation lorsqu'elles sont entrées et quittées.
  3. Des tâches d'application peuvent décider quand passer le contrôle de l'UC à d'autres tâches (multitâche coopératif).
  4. Un logiciel d'application peut prendre la responsabilité complète de la planification et du contrôle de l'exécution de diverses tâches.

Ces possibilités ne constituent pas un ensemble exhaustif et ne sont pas mutuellement exclusives. Dans un système donné, leur combinaison peut être utilisée.

Abstraction de la simultanéité Haut de la page

L'erreur courante dans la conception d'un système simultané consiste à sélectionner les mécanismes spécifiques devant être utilisés pour la simultanéité, trop tôt dans le processus de conception. Chaque mécanisme apporte avec lui certains avantages et inconvénients et la sélection du meilleur mécanisme pour un cas particulier est souvent déterminée par de subtils compromis. Plus un mécanisme est choisi tôt, moins les informations sur lesquelles basées la sélection seront nombreuses. Figer le mécanisme tend également à réduire la flexibilité et l'adaptabilité de la conception aux différentes situations.

Comme avec la plupart des tâches de conception complexes, la simultanéité est mieux comprise en employant plusieurs niveaux d'abstraction. D'abord, les exigences fonctionnelles du système doivent être bien comprises en termes du comportement souhaité. Ensuite, les rôles possibles pour la simultanéité doivent être explorés. Ceci est mieux réalisé à l'aide de l'abstraction des unités sans engagement envers une implémentation particulière. Dans la mesure du possible, la sélection finale des mécanismes permettant la réalisation de la simultanéité doit restée ouverte pour permettre le réglage des performances et la flexibilité d'une distribution différente des composants pour diverses configurations produit.

La "distance conceptuelle" entre le domaine du problème (par exemple, un système d'ascenseur) et le domaine de la solution (constructions logicielles) demeure l'une des plus grandes difficultés dans la conception du système. Les "formalismes visuels" sont extrêmement utiles pour comprendre et véhiculer des idées complexes, telles qu'un comportement simultané, et en fait, combler cet écart conceptuel. Parmi les outils jugés utiles pour de tels problèmes, citons :

  • des diagrammes de module permettant d'envisager des composants agissant simultanément ;
  • des unités temporelles (timethreads) permettant d'envisager des activités simultanées et interactives (pouvant être orthogonales aux composants) ;
  • des diagrammes de séquence permettant de visualiser les interactions entre composants ;
  • des diagrammes de transition d'état définissant les états et les comportements dépendants de l'état des composants.

Objets en tant que composants simultanés Haut de la page

Pour concevoir un système logiciel simultané, nous devons associer les blocs de construction du logiciel (procédures et structures de données) aux blocs de construction de simultanéité (unités de contrôle). Nous avons discuté du concept d'activité simultanée, mais l'on ne construit pas des systèmes à partir d'activités. Les systèmes sont construits à partir de composants et il paraît évident de construire des systèmes simultanés à partir de composants simultanés. Prises en elles-mêmes, ni les procédures, ni les structures de données, ni les unités de contrôle constituent des modèles très naturels de composants simultanés, mais les objets semblent être un moyen très naturel pour associer tous ces éléments nécessaires dans un package clair.

Un objet rassemble des procédures et des structures de données en un composant cohésif avec son propre état et son propre comportement. Il encapsule l'implémentation spécifique de cet état et de ce comportement et définit une interface par laquelle d'autres objets ou logiciels peuvent interagir avec. Des objets modélisent généralement des entités ou des concepts du monde réel et interagissent avec d'autres objets en échangeant des messages. Ils sont désormais bien acceptés comme le meilleur moyen de construire des systèmes complexes.

Diagramme détaillé dans le contenu.

Figure 4 :  Simple ensemble d'objets pour le système d'ascenseur


Envisageons un modèle d'objet pour le système d'ascenseur. Un objet de station d'appel à chaque étage contrôle les boutons d'appel de montée et de descente à l'étage. Lorsqu'un passager relâche un bouton, l'objet de station d'appel répond en envoyant un message à un répartiteur d'ascenseur qui sélectionne l'ascenseur le plus à même de fournir le service le plus rapide, envoie l'ascenseur et accuse réception de l'appel. Chaque objet d'ascenseur contrôle simultanément et indépendamment son comparse, répondant aux sélections d'étage des passagers et aux appels du répartiteur.

La simultanéité peut prendre deux formes dans un tel modèle d'objet. La simultanéité inter-objet a lieu lorsque deux ou plusieurs objets effectuent des activités indépendamment via des unités de contrôle distinctes. La simultanéité intra-objet a lieu lorsque plusieurs unités de contrôle sont actives dans un seul objet. Dans la plupart des langages orientés objet d'aujourd'hui, les objets sont "passifs", ne disposant pas d'unité de contrôle. Les unités de contrôle doivent être fournies par un environnement externe. Le plus couramment, l'environnement est un processus de système d'exploitation standard créé pour exécuter un "programme" orienté objet, écrit dans un langage, tel que C++ ou Smalltalk. Si le système d'exploitation prend en charge le traitement de plusieurs unités, celles-ci peuvent être actives dans les mêmes objets ou des objets différents.

Dans la figure ci-dessous, les objets passifs sont représentés par les éléments circulaires. La zone interne ombrée de chaque objet représente ses informations d'état et l'anneau externe segmenté représente l'ensemble de procédures (méthodes) qui définit le comportement de l'objet.

Diagramme détaillé dans le contenu.
Figure 5 :  Illustration de l'interaction des objets

La simultanéité intra-objet entraîne avec elle tous les défis des logiciels simultanés, tels que le potentiel de conditions d'indétermination lorsque plusieurs unités de contrôle accèdent au même espace mémoire, dans le cas présent, les données encapsulées dans l'objet. On pourrait penser que l'encapsulation des données fournirait une solution au problème. Le problème, bien sûr, est que l'objet n'encapsule pas l'unité de contrôle. Bien que la simultanéité inter-objet évite en grande partie ces problèmes, un problème curieux demeure. Pour que deux objets simultanés interagissent en échangeant des messages, au moins deux unités de contrôle doivent traiter le message et accéder au même espace mémoire pour le transmettre. Un problème connexe (mais encore plus difficile) est celui de la distribution des objets entre les différents processus ou même processeurs. Les messages entre objets de différents processus requièrent la prise en charge de communications interprocessus et requièrent généralement que le message soit codé et décodé en données pouvant être transmises à travers les limites du processus.

Aucun de ces problèmes n'est insurmontable, bien sûr. En fait, comme nous l'avons montré à la section précédente, chaque système simultané doit faire avec, ce sont donc des solutions éprouvées. Le fait est simplement que le "contrôle simultané" entraîne une surcharge de travail et introduit des risques supplémentaires d'erreur. De plus, il obscurcit l'essence du problème d'application. Pour toutes ces raisons, nous voulons réduire le besoin que des programmeurs d'application le traite explicitement. Un moyen d'y parvenir consiste à créer un environnement orienté objet prenant en charge le transfert de messages entre objets simultanés (y compris le contrôle de simultanéité) et à réduire ou éliminer l'utilisation de plusieurs unités de contrôle dans un seul objet. En effet, ceci encapsule l'unité de contrôle avec les données.

Modèle Objet actif Haut de la page

Des objets dotés de leurs propres unités de contrôle sont appelés "objets actifs". Afin de prendre en charge des communications asynchrones avec d'autres objets actifs, chaque objet actif est doté d'une file d'attente de messages ou "boîte aux lettres". Lorsqu'un objet est créé, l'environnement donne sa propre unité de contrôle que l'objet encapsule jusqu'à ce qu'il meurt. Tout comme un objet passif, l'objet actif est mis au repos jusqu'à l'arrivée d'un message émanant de l'extérieur. L'objet exécute le code approprié pour traiter le message. Tout message arrivant pendant que l'objet est occupé est placé en file d'attente dans la boîte aux lettres. Lorsque l'objet termine le traitement d'un message, il retourne chercher le message suivant en attente dans la boîte aux lettres ou attend l'arrivée d'un autre. De bons candidats comme objets actifs dans le système d'ascenseur incluent les ascenseurs eux-mêmes, les stations d'appel à chaque étage et le répartiteur.

Selon leur implémentation, des objets actifs peut être rendus relativement efficaces. Ils entraînent cependant une certaine surcharge par rapport à un objet passif. Ainsi, comme toutes les opérations ne doivent pas être simultanées, il est courant de mélanger des objets actifs et des objets passifs dans le même système. Du fait de leurs styles de communication différents, il est difficile d'en faire des homologues, mais un objet actif constitue un environnement idéal aux objets passifs, remplaçant le processus du système d'exploitation utilisé antérieurement. En fait, si l'objet actif délègue tout le travail aux objets passifs, il devient l'équivalent d'un processus de système d'exploitation ou d'unité d'exécution avec des capacités de communication interprocessus. Des objets actifs plus intéressants, cependant, ont un comportement qui leur est propre pour exécuter une partie du travail, déléguant les autres parties aux objets passifs.

Diagramme détaillé dans le contenu.

Figure 6 :  Un objet "actif" fournit un environnement aux classes passives

De bons candidats comme objets passifs dans un objet ascenseur actif incluent la liste des étages auxquels l'ascenseur doit s'arrêter pendant qu'il monte et une autre liste pour la descente. L'ascenseur doit pouvoir demander la liste pour l'arrêt suivant, ajouter de nouveaux arrêts à la liste et supprimer les arrêts qui ont été faits.

Du fait que des systèmes complexes sont presque toujours constitués de sous-systèmes, plusieurs niveaux plus bas, avant d'aboutir aux composants du niveau feuille, une extension naturelle au modèle Objet actif est de permettre aux objets actifs de contenir d'autres objets actifs.

Bien qu'un objet actif doté d'une seule unité d'exécution ne prenne pas en charge une véritable simultanéité intra-objet, la délégation du travail à des objets actifs contenus constitue une alternative raisonnable pour de nombreuses applications. Il présente l'avantage important d'une encapsulation complète de l'état, du comportement et de l'unité de contrôle sur une base par objet qui simplifie les questions de contrôle de simultanéité.

Diagramme détaillé dans le contenu.

Figure 7 :  Système d'ascenseur montrant des objets actifs imbriqués

Considérons, par exemple, le système d'ascenseur partiel décrit ci-dessus. Chaque ascenseur est doté de portes, d'un treuil et d'un panneau de commande. Chacun de ces composants est parfaitement modélisé par un objet actif simultané dans lequel l'objet Porte contrôle l'ouverture et la fermeture des portes de l'ascenseur, l'objet Treuil contrôle le positionnement de l'ascenseur grâce au système de levage mécanique et l'objet Panneau de commande contrôle les boutons de sélection d'étage et les boutons d'ouverture et de fermeture des portes. L'encapsulation des unités de contrôle simultanées en tant qu'objets actifs aboutit à un logiciel beaucoup plus simple que si tous ces comportements étaient gérés par une seule unité de contrôle.

Problème d'état cohérent des objets Haut de la page

Comme nous l'avons dit lors de la discussion sur les conditions d'indétermination, pour qu'un système se comporte de manière correcte et prévisible, certaines opérations dépendantes de l'état doivent être atomisées.

Pour qu'un objet se comporte correctement, il est absolument nécessaire que son état soit cohérent en interne avant et après le traitement d'un message. Au cours du traitement d'un message, l'état de l'objet peut être dans une condition transitoire et peut être indéterminé du fait que des opérations peuvent être partiellement achevées.

Si un objet achève toujours sa réponse à un message avant de répondre à un autre, la condition transitoire n'est pas un problème. L'interruption d'un objet pour en exécuter un autre ne pose pas non plus de problème du fait que chaque objet pratique une stricte encapsulation de son état. A vrai dire, ceci n'est pas complètement vrai, comme nous allons bientôt le voir.

Toute circonstance dans laquelle un objet interrompt le traitement d'un message pour en traiter un autre ouvre la possibilité de conditions d'indétermination et de ce fait, nécessite l'utilisation de contrôles de simultanéité. En retour, ceci ouvre la possibilité d'un interblocage.

La conception simultanée est généralement plus simple de ce fait, si des objets traitent chaque message jusqu'au bout avant d'en accepter un autre. Ce comportement est implicite dans la forme particulière de modèle d'objet actif présenté.

Le problème d'état cohérent peut se manifester de deux façons différentes dans des systèmes simultanés et ceux-ci sont peut-être plus facile à comprendre en termes de systèmes simultanés orientés objet. La première forme est celle que nous avons déjà abordée. Si l'état d'un seul objet (passif ou actif) est accessible à plusieurs unités de contrôle, des opérations atomisées doivent être protégées par l'atomicité naturelle des opérations d'UC élémentaires ou par un mécanisme de contrôle simultané.

La seconde forme du problème d'état cohérent est peut-être plus subtile. Si plusieurs objets (actifs ou passifs) contiennent les mêmes informations d'état, les objets sont inévitablement en désaccord sur l'état pendant de brefs intervalles.  Dans une faible conception, ils peuvent être en désaccord plus longtemps, même pour toujours. Cette manifestation d'état incohérent peut être considérée comme un "double" mathématique de l'autre forme.

Par exemple, le système de contrôle de mouvement de l'ascenseur (le treuil) doit s'assurer que les portes sont fermées et ne peuvent pas s'ouvrir avant que l'ascenseur ne bouge. Une conception sans protections appropriées pourrait permettre aux portes de s'ouvrir en réponse à un passager appuyant sur le bouton d'ouverture des portes juste au moment où l'ascenseur commence à bouger.

Il peut apparaître qu'une solution simple à ce problème soit de permettre aux informations d'état de résider dans un seul objet. Bien que ceci puisse aider, cela peut avoir un impact négatif sur les performances, en particulier dans un système réparti En outre, il ne s'agit pas d'une solution totalement éprouvée. Même si un seul objet contient certaines informations d'état, tant que les autres objets simultanés prennent des décisions en fonction d'un certain moment, les changements d'état peuvent invalider les décisions des autres objets.

Il n'y a pas de solution magique au problème d'état cohérent. Toutes les solutions pratiques nous obligent à identifier des opérations atomisées et à les protéger d'un certain type de mécanisme de synchronisation qui bloque l'accès simultané pendant de courtes durées acceptables". Celles-ci dépendent beaucoup du contexte. Cela peut durer le temps que l'UC met pour stocker tous les octets dans un nombre en virgule flottante ou le temps nécessaire à l'ascenseur pour atteindre l'étage supérieur.



RUP (Rational Unified Process)   2003.06.15