CHAPITRE II
Validation dans le modèle d'Arnold-Nivat

     

Dans le chapitre précédent , nous avons vu comment on peut, à l'aide du modèle d'Arnold-Nivat, modéliser un système concurrent. Cependant, bien que le modèle permet de décrire précisément le système en question, cette description n'est pas d'une grande utilité sans la capacité de pouvoir vérifier que le système se comporte bien de la manière désirée.
     


Validation

Maintenant que l'on dispose d'une méthode permettant de construire le produit synchronisé, on a déja effectué un certain travail de validation. En effet, seules les transitions globales qui sont définies comme vecteurs de synchronisation apparaissent dans le produit synchronisé. Par construction, le système de transitions synchronisé n'effectue donc que des actions légales et autorisées.

Cependant ceci ne garantit pas que la modélisation d'un système concurrent sous la forme d'un système de transitions synchronisé soit exempt de comportement non-désirables. Contrairement aux systèmes séquentiels, où la validation peut être effectuée à l'aide de simulations et d'analyses modulaires, les systèmes concurrents ne peuvent être validés que par des méthodes formelles. Elles seules peuvent apporter une certitude mathématique sur le respect de la spécification par une modélisation d'un système concurrent.

La syntaxe utilisée pour décrire les opérateurs, termes et expressions de ce chapitre est celle implémentée dans le logiciel MEC. Cependant, il faut noter que ce chapitre n'est pas un manuel ni d'utilisation, ni de référence pour le logiciel MEC. Le lecteur intéressé en trouvera un dans [Arnold-Bégay-Crubillé, 94].

Dans tout ce qui suit, A = <S ,T ,a ,b > est le système de transitions étiqueté de référence et on a S1,S2 inclu dans S et T1,T2 inclu dans T.

Logique de systèmes de transitions

Afin de pouvoir valider mathématiquement un système de transitions synchronisé modélisant un système concurrent, il faut disposer d'outils capables d'exprimer formellement les propriétés que l'on veut vérifier. Ces outils existent et sont appellés logiques. Elles permettent de calculer si un ensemble d'états ou de transitions est le résultat d'une expression formelle décrivant une propriété. Quelques logiques de systèmes de transitions ont été définies. Entre-autres:


C'est une extension de cette dernière que nous allons vous présenter car c'est celle qui a été implémentée dans le logiciel MEC.

Termes

Les termes de la logique des systèmes de transitions sont représentés par des ensembles d'états ou des ensembles de transitions. On appelle ces ensembles des marques. Selon le type d'ensemble représenté, on parlera de marque de transitions ou marque d'états. Les marques peuvent être définies en affectant à celles-ci le résultat de la composition d'un ou plusieurs opérateurs.

Il existe trois marques prédéfinies, dont deux sont de type indéterminé. Les marques de type indéterminé prennent le type soit d'une marque d'états, soit d'une marque de transitions dépendant du contexte dans lequel elles sont utilisées. Ce sont:

* = S lorsqu'il s'agit d'une marque d'états
* = T lorsqu'il s'agit d'une marque de transitions

{}= 0S lorsqu'il s'agit d'une marque d'états
{}= 0T lorsqu'il s'agit d'une marque de transitions


Les opérateurs ensemblistes

Comme les marques sont des ensembles, il est normal que l'on désire effectuer des opérations ensemblistes sur celles-ci. On utilise les opérateurs ensemblistes usuels. Ils agissent soit sur des ensembles de transitions, soit sur des ensembles d'états. Le langage utilisé est typé et sa syntaxe implique un type compatible sur les opérantes d'une expression et également sur les paramètres des opérateurs.

S1 \/ S2 = S1 S2 ou T1 \/ T2 = T1 T2

S1 /\ S2 = S1 S2 ou T1 /\ T2 = T1 T2


A titre d'exemple le Tableau II-1 décrit la représentation graphique de l'opérateur union opérant sur les états d'un système de transitions. Dans cet exemple, ainsi que dans les exemples suivants, les états et les transitions marqués seront représentés respectivement par des cercles et lignes en trait gras.


S1
S2
S1 \/ S2



Tableau II-1: L'opérateur ensembliste d'union sur les états

Opérateurs de liaisons

Ces opérateurs effectuent la liaison entre les marques d'états et les marques de transitions en permettant de calculer des relations entre les états et les transitions.

Cet opérateur retourne l'ensemble des états qui sont la source des transitions de l'ensemble des transitions données en paramètre. Il est défini par

src( T1 ) = { a(t) | t appartient à T1 }

T1
src ( T1 )


Tableau II-2: L'opérateur src

Cet opérateur retourne l'ensemble des états qui sont la cible de l'ensemble des transitions données en paramètre. Il est défini par

tgt( T1 ) = { b(t) | t appartient à T1 }


T1
tgt ( T1 )


Tableau II-3: L'opérateur tgt


Cet opérateur retourne l'ensemble des transitions dont l'état source appartient à l'ensemble des états donnés en paramètre. Il est défini par

rsrc( S1 ) = { t | a(t) appartient à S1 }


S1
rsrc ( S1 )


Tableau II-4: L'opérateur rsrc

Cet opérateur retourne l'ensemble des transitions dont l'état cible appartient à l'ensemble des états donnés en paramètre. Il est défini par

rtgt( S1 ) = { t | b(t) appartient à S1}


S1
rtgt ( S1 )


Tableau II-5: L'opérateur rtgt

Remarque

Les opérateurs src, tgt sont respectivement les inverses des opérateurs rsrc, rtgt. Nous avons en effet les identités suivantes:

S1 = src(rsrc( S1 )), S1 = tgt(rtgt( T1 )) T1 = rsrc(src( T1 )), T1 = rtgt(tgt( T1 ))

Opérateurs de parcours

Cette classe d'opérateurs est constituée d'opérateurs dont le calcul requiert un parcours non-linéaire des états et des transitions sur le graphe sous-jacent d'un système de transitions. Ce ne sont pas des opérateurs atomiques de la logique de Dicky. Ils sont introduits ici car ils sont utilisés intensivement afin de pouvoir exprimer certaines propriétés importantes.

Cet opérateur prend deux paramètres, un ensemble d'états S1 représentant les états sources et un ensemble de transitions T1 représentant les transitions que l'on est autorisé à emprunter. Le résultat est un ensemble d'états représentant tous les états qui sont accessibles depuis l'ensemble des états sources S1 et en empruntant seulement que les transitions qui appartiennent à l'ensemble des transitions autorisées T1. C'est la solution du calcul du plus petit point fixe de l'équation suivante:

Reach = tgt(rsrc(S1 \/ Reach ) /\ T1)

Cet opérateur est calculé itérativement de la manière suivante

Reachn+1 = tgt(rsrc(S1 \/ Reachn ) /\ T1)

avec Reach0 ={} comme condition initiale et Reachn+1 - Reachn = {} comme condition d'arrêt.

S1
T1
reach( S1, T1 )



Tableau II-6: L'opérateur de parcours reach

Cet opérateur prend deux paramètres, un ensemble d'états S1 représentant les états cibles et un ensemble de transitions T1 représentant les transitions que l'on est autorisé à emprunter. Le résultat est un ensemble d'états représentant tous les états d'où il est possible d'atteindre les états de l'ensemble des états cibles S1 et en empruntant seulement que les transitions qui appartiennent à l'ensemble des transitions autorisées T1. C'est la solution du calcul du plus petit point fixe de l'équation suivante:

CoReach = src(rtgt(S1 \/ CoReach ) /\ T1)

Cet opérateur est calculé itérativement de la manière suivante

CoReachn+1 = src(rtgt(S1 \/ CoReachn ) /\ T1)

avec CoReach0 = {} comme condition initiale et CoReachn+1 - CoReachn = {} comme condition d'arrêt.

S1
T1
coreach ( S1, T1 )



Tableau II-7: L'opérateur de parcours coreach

Cet opérateur retourne les transitions formant des cycles. Il prend comme paramètres deux ensembles de transitions. Le premier ensemble, T1, représente les transitions autorisées. Toutes les transitions faisant parties d'un cycle doivent appartenir à T1. De plus, au moins une des transitions de chacun des cycles doit appartenir à l'ensemble T2,


T1
T2
loop (T1, T2 )



Tableau II-8: L'opérateur de parcours loop

Cet opérateur prend comme paramètres, deux ensembles d'états S1, S2 et un ensemble de transitions autorisées T1. Il retourne l'ensemble des transitions représentant un plus court chemin de l'ensemble d'états S1 à l'ensemble d'états S2 en ne passant que par les transitions autorisées T1.

S1
S2




Tableau II-9: L'opérateur de parcours trace

Remarque: Le plus court chemin calculé est lié à l'ordre des états dans le parcours du graphe et dépend de l'implantation.


Exemple de propriétés

Les opérateurs peuvent être combinés ensemble pour produire des expressions décrivant les propriétés que l'on désire valider. On peut se poser la question suivante: Comment fait on pour travailler de manière concrète avec ces opérateurs? Evidement, lors de l'analyse des résultats des calculs, on ne travaille pas sur une représentation graphique comme dans ce chapitre, ce qui serait tout à fait inutile dès que la taille des systèmes de transitions impliqués dépasse quelques dizaines d'états. Disposer de la liste des éléments appartenant à une marque peut être utile dans certains cas, mais ce n'est pas très utile en général lorsque le nombre d'éléments est très grand.

Or les propriétés que l'on désire tester sont habituellement des propriétés booléennes où ce que l'on veut savoir sont des réponses à des questions comme: La marque contient elle ou non des éléments? Il est suffisant de se baser uniquement sur le cardinal de celles-ci pour une interprétation rapide du résultat d'un calcul.

Voyons maintenant quelques exemples de propriétés génériques. Ce sont des propriétés importantes qui ne dépendent pas de la nature des systèmes concurrents ni de la modélisation de ceux-ci.

Successeurs et prédécesseurs

En combinant les opérateurs src, rsrc, tgt et rtgt, il est possible de créer de nouvelles fonctions permettant d'effectuer un parcours par étape d'un système de transitions. Ceci n'est pas une propriété, mais est utilisé à maintes reprises dans la définition des propriétés génériques qui vont suivre.

Les états successeursEtatsSucc(S1) = tgt(rsrc(S1))
Les états prédécesseursEtatsPred(S1) = src(rtgt(S1))
Les transitions suivantes TransSuiv(T1) = rsrc(tgt(T1))
Les transitions précédentesTransPrec(T1) = rtgt(src(T1))

Table II.1: Successeurs et prédécesseurs

Accessibilité

Cette propriété est fortement liée à l'utilisation de l'opérateur reach. On veut savoir si, à partir des états synchronisés initiaux, et en ne passant que par des transitions autorisées, il est possible d'atteindre des états donnés.

Par exemple, dans un système de transitions modélisant une chaîne de montage, on peut définir une marque de transitions ActionsLegales représentant les opérations que la chaîne de montage peut effectuer, et définir également une marque d'état Defectueux représentant des états dans lequel la chaîne de montage produit un élément défectueux.

On peut alors poser la question suivante: A partir de la mise en route de la chaîne de montage, est-il possible qu'en n'effectuant que des opérations de montage correctes, on puisse produire un élément défectueux?

L'expression ci-dessous nous donne l'ensemble des états où l'on a produit un élément défectueux malgré le fait que l'on ait effectué que des opérations correctes.

Defect:= Defectueux /\ reach(Defectueux, ActionsLegales)

Le système est exempt d'état défectueux non contrôlés si on a

|Defect| = 0.

Inter-blocage

Une propriété souvent non désirable d'un système concurrent est l'inter-blocage (deadlock). C'est le blocage non désiré d'au moins un des processus du système concurrent par un autre processus. Dans le produit synchronisé, ceci se traduit par un état synchronisé d'où ne sort aucune transition synchronisée. Il s'agit donc d'un état d'où il est impossible d'effectuer une action.

Cette condition particulière se retrouve habituellement lorsque deux processus tentent de s'approprier une ressource partagée et que le processus ayant réussi à obtenir cette ressource se trouve dans l'impossibilité de la libérer.

Pour vérifier que le système est exempt d'inter-blocage, il suffit de calculer l'expression suivante:

Deadlock:= * - src()

Le système est exempt d'état bloquant lorsque

|Deadlock| = 0.

Co-accessiblité

Pour beaucoup de systèmes concurrents, il existe des états biens spécifiques signifiant que le processus modélisé est en état d'attente. Par exemple, un daemon sous Unix, qui attend une action particulière afin de s'activer et d'effectuer la tâche qu'il a à faire.

Or, une recherche négative des inter-blocages ne signifie pas pour autant que le processus fonctionne bien. Une propriété intéressante à vérifier est que, de tout état il est possible de revenir, par une séquence d'actions, à un des états d'attente. Si ce n'était pas le cas, il serait alors possible de bloquer le processus dans un cycle d'actions infini et on ne pourrait plus alors avoir accès au processus de nouveau.

On est donc en mesure de vérifier s'il existe des états d'où on ne peut jamais revenir à au moins un des états marqués Attente, signifiant que le processus est prêt à accepter une tâche. Ceci ce traduit par l'expression:

PasDeRetour:= * - coreach(Attente, *)

Le système est exempt d'état défectueux non contrôlés lorsque

|PasDeRetour| = 0.

Etats critiques

Nous venons de définir l'inter-blocage et la co-accessiblité. Une des propriétés intéressantes qui en découle est celle des états critiques. Ce sont des états, qui une fois atteints par le système, conduisent à un état bloquant, et ce quelque soit la suite de transitions empruntée par la suite.

Il faut tout d'abord calculer les états bloquants, tels que définis précédemments:

Deadlock:= * - src()

Calculons maintenant la marque EtatsCritiques. Les états inévitablement bloquants sont certainement inclus dans ceux d'où on ne peut pas revenir à un des états marqués Initial. Encore une fois tel que défini précédemment:

PasDeRetour:= * - coreach(Initial, *)

Il faut enlever de PasDeRetour les états d'où il est impossible d'atteindre un des états bloquants. C'est le cas des états formant un cycle ne pouvant pas conduire à un état bloquant et ne pouvant pas revenir à l'état initial. Les états restants sont inévitablement bloquant.

EtatsInevBloquant:= PasDeRetour /\ coreach(Deadlock,*)

Les états critiques forment la frontière de cet ensemble d'états, et sont calculés par

EtatsCritiques:= tgt(rtgt(*-PasDeRetour) /\ rsrc(PasDeRetour)))

Transitions critiques

Les états critiques nous permettent de définir une autre propriété, les transitions critiques. Ce sont les transitions qui forment la frontière entre l'ensemble des états critiques et le reste des états du système de transitions. Une fois que le système a emprunté une de ces transitions critiques, il va se retrouver inévitablement dans un des états bloquants.

TransCritiques:= rtgt(*-PasDeRetour) /\ rsrc(PasDeRetour)

Famine

Un autre comportement important est celui de la famine (livelock). Il s'agit d'une autre forme de blocage d'un processus par un autre. A la différence de l'inter-blocage toutefois, la famine ne bloque qu'un seul des deux processus.

Cette situation se retrouve lorsque deux processus ont l'occasion d'obtenir l'accès à une ressource partagée. Il suffit qu'un des processus obtienne toujours la ressource en la reprenant dès qu'il la libère et ce avant que le deuxième processus n'ait le temps de s'en emparer. Le processus bloqué est toujours en condition d'attente tandis que l'autre processus continue de s'exécuter normalement.

Il y a donc un cycle dans la séquence des actions éxécutées, et c'est donc l'opérateur loop qui sera utilisé dans la détection des transitions formant une famine possible.

On définit les marques Execution1 et Execution2 comme représentant respectivement les états durant lesquels les processus 1 et 2 sont actifs et possèdent l'utilisation exclusive de la ressource. Ceci peut être fait car le processus qui ne possède pas la ressource reste dans l'état d'attente. Execution1 contient donc les états synchronisés dans lesquels le ième sous-état, correspondant à l'indice du système de transitions modélisant le processus 2 dans la liste des systèmes de transitions du produit synchronisé, est l'état d'attente du processus 2.

De plus on définit également les marques d'états Obtention1 et Obtention2 représentant les états où respectivement le processus 1 ou le processus 2 peut obtenir la ressource.

Il faut maintenant déterminer quels sont les états où les deux processus sont sur le point d'obtenir la ressource.

ObtentionDouble:= Obtention1 /\ Obtention2

Nous allons maintenant trouver un cycle de famine pour le processus 1. C'est-à-dire une séquence d'actions durant laquelle le processus 1 prend la ressource, l'utilise, la relâche et ensuite peut la reprendre immédiatement.

Les transitions menant ou sortant des états ou le processus 1 possède l'utilisation exclusive de la ressource sont:

TransExecution1:= rsrc(Execution1) \/ rtgt(Execution1)

Un cycle de famine pour le processus 1 doit nécessairement passer par l'une des transitions sortant des états de ObtentionDouble et doit être entièrement inclus dans TransExecution1. Le calcul suivant nous donne les transitions faisant partie de ce cycle

TransFamine:= loop(TransExecution, rsrc(ObtentionDouble))

Il y a un cycle de famine pour le processus 1 lorsque

|TransFamine| = 0

Si c'est le cas, les états impliqués dans ce cycle sont donnés par l'expression suivante:

EtatsFamine:= src(TransFamine) /\ tgt(TransFamine)