Tâche: Décrire l'architecture d'exécution
Cette tâche définit une architecture de processus pour le système, reposant sur les classes actives, les instances de ces classes, ainsi que la relation entre ces classes et les processus et unités d'exécution de système d'exploitation.
Objet
  • Analyser les exigences liées aux accès concurrents
  • Identifier les processus et leurs cycles de vie 
  • Identifier les mécanismes de communication interprocessus et attribuer les ressources de coordination entre processus
  • Répartir les éléments de modèle entre les processus
Relations
Description principale

Les objets actifs (c'est-à-dire les instances de classes actives) permettent de représenter les unités d'exécution concurrentes dans le système : d'un point de vue théorique, chaque objet actif possède son propre fil de contrôle, et d'un point de vue conventionnel, chaque objet est la racine d'un cadre de pile d'exécution. Le mappage des objets actifs aux unités d'exécution ou processus de système d'exploitation effectifs peut varier selon les exigences de réactivité, et sera influencé par la surcharge due aux changements de contexte. Par exemple, un certain nombre d'objets actifs peuvent, grâce à un simple ordonnanceur, partager une seule unité d'exécution de système d'exploitation, donnant ainsi l'illusion d'être exécutés simultanément. Cependant, si l'un des objets actifs adopte un comportement de blocage, en exécutant par exemple des entrées-sorties synchrones, les autres objets actifs du groupe ne pourront pas répondre aux événements produits lors du blocage de l'unité d'exécution de système d'exploitation.

A l'inverse, donner à chaque objet actif sa propre unité d'exécution de système d'exploitation devrait déboucher sur une plus grande réactivité, tant que les ressources de traitement ne subissent pas d'impact négatif en raison de la charge supplémentaire due aux changements de contexte.

Dans les systèmes en temps réel, les Produits : Capsules sont recommandées pour la modélisation des accès concurrents. Comme les classes actives, chaque capsule possède son propre fil de contrôle théorique, mais les capsules ont des sémantiques composites et une encapsulation supplémentaire pour rendre plus malléable la modélisation de problèmes complexes en temps réel.

Cette tâche définit une architecture de processus pour le système, reposant sur les classes actives, les instances de ces classes, ainsi que la relation entre ces classes et les processus et unités d'exécution de système d'exploitation. De la même manière, pour les systèmes en temps réel, la définition de l'architecture de processus reposera sur les capsules et leur mappage relatif aux processus et unités d'exécution de système d'exploitation.

Au début de la phase d'élaboration, cette architecture sera peu développée mais, à la fin de de l'élaboration, les unités d'exécution et processus devront être bien définis. Les résultats de cette tâche sont enregistrés dans le modèle de conception, et plus particulièrement dans la vue de processus (voir Concept : Vue de processus).

Etapes
Analyse des exigences liées aux accès concurrents
Objet  Définir le degré d'exécution parallèle requis pour le système. Cette définition aidera à façonner l'architecture. 

Au cours de la Tâche : Identification des éléments de conception, nous avons abordé les exigences liées aux accès concurrents qui sont engendrées essentiellement par des demandes naturelles d'accès concurrents dans le domaine considéré. 

Cette analyse a débouché sur la définition d'un ensemble de classes actives qui représentaient les fils de contrôle logiques du système.  Dans les systèmes en temps réel, ces classes actives sont représentées par le Produit : Capsule.

Au cours de cette étape, nous abordons d'autres sources d'exigences concernant les accès concurrents : les exigences non fonctionnelles du système.

Les exigences liées aux accès concurrents dérivent des éléments suivants :

  • La mesure dans laquelle le système doit être distribué. Un système dont le comportement doit être distribué sur des processeurs ou des noeuds nécessite presque une architecture multiprocessus. Un système qui utilise un gestionnaire de transactions ou un système de gestion de base de données doit aussi tenir compte des processus induits par ces sous-systèmes importants.
  • L'intensité de calcul des algorithmes clé. Afin d'obtenir des temps de réponse convenables, vous pouvez être amené à placer les activités qui exigent un calcul intensif dans un processus ou une unité d'exécution à part, de sorte que le système puisse encore répondre aux entrées de l'utilisateur au cours du calcul, même avec de moindres ressources.
  • Le degré d'exécution parallèle pris en charge par l'environnement. Si le système d'exploitation ou l'environnement ne prennent pas en charge des unités d'exécution (processus légers), il est inutile de prendre en compte leur impact sur l'architecture du système.
  • La nécessité d'une tolérance aux pannes dans le système. Les processeurs de secours requièrent un processus de secours, et nécessitent le maintien de la synchronisation entre les processus primaires et de secours.
  • Le pattern d'arrivée des événements dans le système. Dans les systèmes comportant des unités ou capteurs externes, les patterns d'arrivée des événements entrants peuvent varier selon les capteurs. Certains événements peuvent être périodiques (ils se produisent à intervalles réguliers, avec une petite marge de tolérance), d'autres apériodiques (ils se produisent à intervalles irréguliers). En principe, les classes actives qui représentent des unités générant différents patterns d'événements seront attribuées à différentes unités d'exécution de système d'exploitation, avec différents algorithmes d'ordonnancement, afin de garantir le respect des échéances des événements ou des traitements (si le système l'exige). Le même raisonnement s'applique aux capsules, lorsqu'elles sont utilisées dans la conception de systèmes en temps réel.

Comme dans de nombreux problèmes architecturaux, ces exigences peuvent parfois s'exclure mutuellement. Il est courant, du moins au départ, d'avoir des exigences conflictuelles. Classer les exigences par ordre d'importance vous aidera à résoudre ces conflits.

Identification des processus et des unités d'exécution
Objet  Définir les processus et unités d'exécution qui existeront dans le système. 

L'approche la plus simple consiste à attribuer tous les objets actifs à une unité d'exécution ou un processus commun et à utiliser un simple ordonnanceur d'objets actifs, ce qui permet de minimiser la surcharge due au changement de contexte. Cependant, dans certains cas, il peut s'avérer nécessaire de répartir les objets actifs sur un ou plusieurs processus ou unités d'exécution. Ce sera très probablement le cas pour les systèmes en temps réel, où les capsules utilisées pour représenter les unités d'exécution logiques doivent parfois répondre à des exigences strictes en matière d'ordonnancement.

Si un objet actif qui partage une unité d'exécution de système d'exploitation avec d'autres objets actifs effectue un appel synchrone à un autre processus ou unité d'exécution, et que cet appel bloque l'unité d'exécution partagée, tous les autres objets actifs situés dans le processus appelant seront automatiquement suspendus. Cependant, un appel synchrone du point de vue de l'objet actif peut être traité de manière asynchrone par l'ordonnanceur qui contrôle le groupe d'objets actifs : celui-ci suspend l'objet actif appelant (qui attend l'achèvement de son appel synchrone), puis ordonnance l'exécution d'autres objets actifs. 

Au terme de l'opération "synchrone" initiale, l'objet actif appelant peut être repris. Toutefois, cette approche peut parfois se révéler impossible. En effet, il arrive que l'ordonnanceur ne puisse être conçu pour intercepter tous les appels synchrones avant leur blocage. Notez que l'ordonnanceur peut généralement traiter de la même manière un appel synchrone entre des objets actifs qui utilisent le même processus ou la même unité d'exécution de système d'exploitation (l'effet équivaut à celui d'un appel de procédure pour l'objet actif appelant).

En conclusion, les objets actifs devraient être regroupés dans les processus ou unités d'exécution selon la nécessité qu'ils ont d'être exécutés en concurrence avec des appels synchrones qui bloquent l'unité d'exécution. En d'autres termes, un objet actif partage le même processus ou la même unité d'exécution qu'un autre objet qui utilise des appels synchrones bloquants, seulement s'il n'a pas besoin d'être exécuté en concurrence avec cet objet et s'il peut tolérer la suspension de son exécution pendant le blocage de l'autre objet. Ainsi, dans une situation extrême, lorsque la réactivité est critique, il peut être nécessaire d'attribuer chaque objet actif à une seule unité d'exécution ou un seul processus.

Dans les systèmes en temps réel, lorsque les interfaces des capsules utilisent des messages, il est plus simple de concevoir un ordonnanceur qui garantisse, au moins pour les communications entre capsules, l'absence de blocage des unités d'exécution de système d'exploitation, même lorsqu'une capsule communique de manière synchrone avec une autre capsule. Cependant, une capsule peut toujours émettre une requête qui bloquerait l'unité d'exécution directement au système d'exploitation (par exemple, une requête de temps d'attente synchronisée). Pour les services de niveau inférieur appelés par les capsules, il est nécessaire d'établir des conventions qui permettent d'éviter ce comportement, si les capsules doivent partager une unité d'exécution commune (et utiliser un simple ordonnanceur pour simuler les accès concurrents).

En règle générale, dans les situations ci-dessus, il est préférable d'utiliser des unités d'exécution, plus légères, plutôt que des processus complets, puisque ces premières amoindrissent la surcharge. Malgré cela, il se peut que vous souhaitiez tirer parti de quelques caractéristiques spéciales des processus dans certains cas spécifiques. Puisque les unités d'exécution partagent le même espace adresse, elles sont par nature plus risquées que les processus. Si l'éventualité d'un écrasement accidentel pose problème, il faut préférer les processus. De plus, étant donné que les processus représentent des unités de récupération indépendantes dans la plupart des systèmes d'exploitation, il peut être utile d'attribuer des objets actifs aux processus en fonction de la nécessité qu'ils ont d'être récupérés indépendamment l'un de l'autre. En d'autres termes, tous les objets actifs qui doivent être récupérés ensemble, en tant qu'unité, peuvent être réunis dans le même processus.

Pour chaque flux de contrôle différent requis par le système, créez un processus ou une unité d'exécution (processus léger). Une unité d'exécution doit être utilisée lorsque des flux de contrôle imbriqués sont requis (c'est-à-dire s'il est nécessaire, dans un processus, d'avoir un flux de contrôle indépendant au niveau de la sous-tâche).

Par exemple, des fils de contrôle séparés peuvent être utilisés pour effectuer les actions suivantes :

  • Séparer les domaines entre différentes zones du logiciel
  • Tirer parti des processeurs multiples dans un noeud ou des noeuds multiples dans un système distribué
  • Augmenter l'utilisation de l'unité centrale en attribuant des cycles à d'autres activités lors de la suspension d'un fil de contrôle
  • Classer les activités par priorité
  • Autoriser le partage des charges entre plusieurs processus et processeurs
  • Accroître la disponibilité du système grâce aux processus de secours
  • Prendre en charge le SGBD, le gestionnaire de transactions ou d'autres sous-systèmes importants.

Exemple

Dans un guichet automatique, il est nécessaire de gérer des événements asynchrones provenant de trois sources différentes : l'utilisateur du système, les périphériques du guichet automatique (lors d'un bourrage dans le distributeur de billets, par exemple) ou le réseau (dans le cas d'une instruction de fermeture venant du réseau). Pour ce faire, nous pouvons définir trois unités d'exécution séparées au sein du guichet, comme dans l'illustration ci-dessous, à l'aide des classes actives du langage UML.

Illustration des processus et unités d'exécution dans un guichet automatique

Processus et unités d'exécution dans le guichet automatique

Identification des cycles de vie des processus
Objet  Identifier les instants de création et de destruction des processus et fils de contrôle. 

Chaque processus ou fil de contrôle doit être créé et détruit. Dans une architecture monoprocessus, la création du processus s'opère au démarrage de l'application, et la destruction du processus à l'arrêt de l'application. Dans les architectures multiprocessus, de nouveaux processus (ou unités d'exécution) sont générés par le processus initial, ou naissent à partir du dédoublement de ce processus créé par le système d'exploitation au démarrage de l'application. Ces processus doivent également être explicitement détruits.

Il est nécessaire de déterminer et de documenter la séquence d'événements qui conduit à la création ou à la destruction d'un processus, ainsi que le mécanisme de création et de suppression.

Exemple

Dans le guichet automatique, un processus principal, chargé de coordonner le comportement de tout le système, est démarré. Il génère à son tour un certain nombre de fils de contrôle subordonnés afin de surveiller divers éléments du système : les périphériques du système et les événements émanant du client et du réseau. La création de ces processus et fils de contrôle peut être illustrée à l'aide des classes actives du langage UML, et la création des instances de ces classes actives peut être représentée par un diagramme de séquence, comme dans l'exemple ci-dessous :

Illustration du processus de démarrage du système et de la création de fils de contrôle

Création de processus et de fils de contrôle lors de l'initialisation du système

Identification des mécanismes de communication interprocessus
Objet  Identifier les moyens de communication utilisés par les processus et unités d'exécution. 

Les mécanismes de communication interprocessus (IPC) permettent l'envoi de messages entre des objets exécutés dans différents processus.

Les mécanismes courants de communication interprocessus sont les suivants :

  • La mémoire partagée, avec ou sans sémaphore pour assurer la synchronisation
  • Les rendez-vous, en particulier lorsque la prise en charge est assurée directement par un langage tel qu'Ada
  • Les sémaphores, utilisés pour bloquer les accès simultanés à des ressources partagées
  • L'envoi de messages, point à point et point-multipoint
  • Les boîtes aux lettres
  • Les appels RPC (appels de procédure éloignée)
  • La diffusion d'événements, à l'aide d'un "bus logiciel" ("architecture de bus de messages")

Le choix d'un mécanisme IPC influera sur la modélisation du système. Dans une "architecture de bus de messages", par exemple, il n'est pas nécessaire de définir des associations explicites entre les objets pour envoyer des messages.

Attribution des ressources de coordination entre processus
Objet Attribuer les ressources rares.
Anticiper et gérer les potentiels goulots d'étranglement concernant les performances. 

Les mécanismes de communication interprocessus sont généralement rares. La taille et le nombre des sémaphores, de la mémoire partagée et des boîtes aux lettres sont habituellement fixes et ne peuvent s'accroître sans coût important. De leur côté, les appels RPC, les messages et les diffusions d'événement absorbent la bande passante du réseau, qui est de moins en moins disponible. Lorsque le système excède un certain seuil de ressources, ses performances se dégradent généralement de manière non linéaire : lorsqu'une ressource rare est épuisée, les requêtes suivantes la concernant sont susceptibles de produire des effets désagréables.

Si des ressources rares sont indisponibles, vous devez envisager plusieurs stratégies :

  • diminution du besoin de la ressource rare par la réduction du nombre de processus
  • modification de l'utilisation des ressources rares (pour un ou plusieurs processus, choisissez une ressource différente, moins rare, à utiliser pour le mécanisme IPC)
  • augmentation de la quantité de la ressource rare (par exemple, augmentation du nombre de sémaphores). Cette stratégie peut être adoptée pour des changements relativement petits, mais possède souvent des effets secondaires ou des limites fixes.
  • partage de la ressource rare (par exemple, attribution de la ressource en cas de nécessité uniquement, puis abandon de cette ressource après utilisation). Cette stratégie coûte cher et risque de se limiter à freiner la crise des ressources.

Quelle que soit la stratégie choisie, le système devrait se dégrader en douceur (plutôt que de tomber en panne) et fournir un retour d'informations approprié à un administrateur système pour permettre (si possible) la résolution de l'incident sur le terrain, une fois le système déployé.

Si le système nécessite une configuration spéciale de l'environnement d'exécution (souvent contrôlée par la reconfiguration du noyau du système d'exploitation) afin d'augmenter la disponibilité d'une ressource critique, l'installation du système doit l'effectuer automatiquement ou demander à un administrateur système de l'effectuer pour que le système puisse être opérationnel. Par exemple, il peut être nécessaire de redémarrer le système pour que le changement prenne effet.

Mappage des processus dans l'environnement d'implémentation
Objet  Mapper les "flux de contrôle" aux concepts pris en charge par l'environnement d'implémentation. 

Les processus conceptuels doivent être mappés à des constructions spécifiques dans l'environnement d'exploitation. De nombreux environnements offrent plusieurs choix de types de processus, et généralement, permettent tout au moins l'utilisation de processus et d'unités d'exécution. Les choix seront effectués en fonction du degré de couplage (les processus sont autonomes, tandis que les unités d'exécution sont exécutées au sein d'un processus) et des exigences liées aux performances du système (la communication interprocessus entre les unités d'exécution est généralement plus rapide et plus efficace que la communication entre les processus).

De nombreux systèmes peuvent contenir un nombre maximal d'unités d'exécution par processus ou de processus par noeud. Il se peut que ces limites ne soient pas absolues, mais imposées par la disponibilité des ressources rares. Les unités d'exécution et processus déjà exécutés sur un noeud cible doivent être pris en compte au même titre que les unités d'exécution et processus suggérés dans l'architecture de processus. Au terme du mappage, il faut également tenir compte des résultats de l'étape précédente, Attribution des ressources de coordination entre processus, afin de s'assurer qu'un nouveau problème de performances n'est pas en train d'apparaître.

Mappage des éléments de conception aux fils de contrôle
Objet  Déterminer les fils de contrôle dans lesquels les classes et sous-systèmes de contrôle doivent être exécutés. 

Les instances d'une classe ou d'un sous-système donné doivent être exécutées dans au moins un fil de contrôle qui fournit l'environnement d'exécution pour la classe ou le sous-système ; elles peuvent être exécutées dans plusieurs processus différents.

En adoptant simultanément deux stratégies différentes, nous déterminons le "bon" nombre d'accès concurrents et définissons le "bon" ensemble de processus :

De l'intérieur vers l'extérieur

  1. A partir du modèle de conception, regroupez les classes et sous-systèmes dans des ensembles d'éléments coopérants qui (a) coopèrent de près l'un avec l'autre et (b) doivent être exécutés dans le même fil de contrôle. Prenez en compte l'effet produit par l'introduction d'une communication interprocessus au milieu d'une séquence de messages avant la séparation des éléments dans différents fils de contrôle.
  2. A l'inverse, séparez les classes et sous-systèmes qui n'interagissent pas du tout entre eux, en les plaçant dans des fils de contrôle différents.
  3. Ce groupement doit se poursuivre jusqu'à ce que le nombre de processus soit réduit au minimum, tout en permettant encore la distribution et l'utilisation des ressources physiques.

De l'extérieur vers l'intérieur

  1. Identifiez les stimuli externes auxquels doit répondre le système. Définissez un fil de contrôle à part pour traiter chaque stimulus et un fil de contrôle de serveur à part pour fournir chaque service.
  2. Prenez en compte l'intégrité des données et les contraintes liées à la sérialisation afin de réduire l'ensemble initial de fils de contrôle et d'atteindre le nombre de fils qui peut être pris en charge par l'environnement d'exécution.

Ce processus déterministe, non linéaire, conduit à une vue de processus optimale ; il nécessite peu d'itérations pour atteindre un compromis acceptable.

Exemple

Le diagramme suivant illustre la répartition des classes, dans le guichet automatique, entre les processus et fils de contrôle du système.

Illustration de la répartition des classes entre les processus et fils de contrôle dans un guichet automatique

Mappage des classes aux processus dans le guichet automatique



Propriétés
Plusieurs occurrences
Commandé par les événements
En cours
Facultatif
Planifié
Réitérable
Plus d'informations