WOW !! MUCH LOVE ! SO WORLD PEACE !
Fond bitcoin pour l'amélioration du site: 1memzGeKS7CB3ECNkzSn2qHwxU6NZoJ8o
  Dogecoin (tips/pourboires): DCLoo9Dd4qECqpMLurdgGnaoqbftj16Nvp


Home | Publier un mémoire | Une page au hasard

 > 

Sécurité dans les systèmes temps réel

( Télécharger le fichier original )
par Thomas Vanderlinden
Université Libre de Bruxelles - Licence en Informatique 2007
  

Disponible en mode multipage

Bitcoin is a swarm of cyber hornets serving the goddess of wisdom, feeding on the fire of truth, exponentially growing ever smarter, faster, and stronger behind a wall of encrypted energy

Faculté des sciences département d'inFormatique

 
 

MEMBRE DE L'ACADÉMIE UNIVERSITAIRE WALLONIE-BRUXELLES ET DU PÔLE UNIVERSITAIRE EUROPÉEN BRUXELLES WALLONIE

 

Sécurité dans les systèmes

temps réel

Thomas VANDERLINDEN

Mémoire présenté sous la direction des Professeurs Joël GOOSSENS et Olivier MARKOWITCH
en vue de l'obtention du grade de Licencié en Informatique
Année académique 2006-2007

1

Remerciements

Un mémoire est un travail de recherche ardu et long mais intéressant. Malgré que ce soit un travail en solitaire, il aurait été difficile de me passer de l'appui de nombreuses personnes d'un point de vue académique et moral, à qui je tiens à donner toute ma reconnaissance.

Tout d'abord, je souhaiterais remettre mes premiers remerciements au professeur qui m'a suivi toute cette année en organisant des réunions régulièrement, Monsieur Joél Goossens, promoteur de ce mémoire.

Je tiens également à remercier Monsieur Olivier Markowitch, codirecteur de ce travail, qui s'est lui aussi joint à certaines de ces réunions, et sur qui je pouvais compter pour me fournir des conseils dans le domaine de la cryptographie lorsque j'en avais besoin ainsi que Monsieur Bernard Fortz pour m'avoir suggéré certaines améliorations lors de la programmation des méthodes heuristiques présentées dans ce mémoire et le professeur Hakan Aydin pour avoir répondu à mes questions sur l'ordonnancement temps réel par courrier électronique.

Je tiens à donner toute ma gratitude à mon grand-père qui a pris la peine de relire ce travail pour apporter quelques corrections orthographiques. Sur le plan moral, je tiens à remercier mes parents, toute ma famille et mes amis, ils comptent énormément pour moi, ils ne m'ont jamais abandonné durant mes études et m'ont toujours encouragé durant l'élaboration de ce mémoire.

Soyez tous sincèrement remerciés,

Thomas.

Table des matières

1

2

Introduction

1.1 Le problème

1.2 Organisation du mémoire

Pré-requis

5

5

6

7

 

2.1

Systèmes temps réel

7

 
 

2.1.1 Introduction

7

 
 

2.1.2 Lestravaux

7

 
 

2.1.3 Les tâches

8

 
 

2.1.4 Le système

9

 
 

2.1.5 L'ordonnancement

10

 

2.2

Cryptologie

13

 
 

2.2.1 Introduction

13

 
 

2.2.2 Confidentialité

14

 
 

2.2.3 Intégrité et authentification

15

3

Etatde l'art

16

 

3.1

Introduction

16

 

3.2

Qualité de service et longueur des clés

17

 
 

3.2.1 Introduction

17

 
 

3.2.2 Modélisation du système

18

 
 

3.2.3 L'algorithme d'ajustement de la qualité de service

19

 
 

3.2.4 Conclusion

22

 

3.3

Ordonnancement dynamique basé sur des niveaux de sécurité .

23

 
 

3.3.1 Conclusion

25

 

3.4

Méthode SASES

25

 
 

3.4.1 Modélisation

26

 
 

3.4.2 Différents services

28

 
 

3.4.3 Calcul du bénéfice au système

34

 
 

3.4.4 L'algorithme SASES

35

 
 

3.4.5 Conclusion

36

TABLE DES MATIÈRES 3

3.5 Méthode en graphe de services 37

3.5.1 Modélisation 37

3.5.2 Recherche dans le graphe 38

3.5.3 Conclusion : Comparaison avec SASES 40

3.6 Conclusion 41

4 Récompense et sécurité 42

4.1 Introduction 42

4.2 Reward-Based Scheduling 43

4.2.1 Introduction 43

4.2.2 Une fonction de récompense 44

4.2.3 Modélisation du système 47

4.2.4 Calcul de la récompense moyenne du système 47

4.2.5 Approche Mandatory-First (parties obligatoires d'abord) 48

4.2.6 Optimalité 53

4.2.7 Optimalité avec des fonctions linéaires de récompense 57

4.2.8 Optimalité avec des fonctions convexes de récompense 60

4.3 Conclusion 62

5 Simulations 64

5.1 Introduction 64

5.2 Génération d'un système 65

5.2.1 Génération des périodes 65

5.2.2 Génération de l'ensemble de tâches 66

5.3 L'algorithme orienté évènement 68

5.3.1 La gestion des évènements 69

5.3.2 L'algorithme 70

5.4 Rappel du problème d'optimisation des temps optionnels . . . 72

5.5 Notations utilisées 73

5.6 Le recuit simulé 74

5.7 La recherche tabou 77

5.8 Obtention d'une solution voisine 79

5.9 Expérimentation 81

5.9.1 Limites des algorithmes 81

5.9.2 Analyse avec des fonctions linéaires de récompense . . 83

5.9.3 Simulation avec des fonctions convexes de récompense 83

5.10 Conclusion 87

6 Conclusion 89

A Complément à la démonstration de H. Aydin 93

TABLE DES MATIÈRES

B Code source de la simulation

4

95

B.1

Système

95

 

B.1.1

Systeme.h

95

 

B.1.2

Systeme.cpp

97

B.2

 

Tâche

107

 

B.2.1

Tache.h

107

 

B.2.2

Tache.cpp

108

B.3

 

Travail

109

 

B.3.1

Travail.h

109

 

B.3.2

Travail.cpp

110

B.4

Générateur

111

 

B.4.1

Generateur.h

111

 

B.4.2

Generateur.cpp

111

B.5

 

Divers

113

 

B.5.1

Divers.h

113

 

B.5.2

Divers.cpp

113

B.6

 

Event

114

 

B.6.1

Event.h

114

 

B.6.2

Event.cpp

115

B.7

Solution

115

 

B.7.1

Solution.h

116

 

B.7.2

Solution.cpp

117

B.8

Main

 

122

 

B.8.1

Main.cpp

123

Chapitre 1

Introduction

1.1 Le problème

Les systèmes temps réel prennent une place de plus en plus importante dans notre société, ils servent à contrôler, réguler en « temps réel » des dispositifs électroniques grâce à des capteurs, embarqués dans des robots, des véhicules spatiaux, etc. Ces systèmes temps réel embarqués sont souvent utilisés par le public dans la vie de tous les jours sans même qu'on ne s'en rende compte, par exemple dans les systèmes de freinage d'une voiture, le contrôle de vol d'un avion,...

Pour contrôler des systèmes critiques comme des centrales nucléaires, des réseaux électriques, etc. .., le réseau public Internet est de plus en plus utilisé car cela a deux avantages majeurs:

- la maintenance du système est facilitée, on peut se connecter au système

à partir de tout endroit où une connexion Internet est disponible;

- les coûts sont réduits étant donné qu'il ne faut pas mettre en place un

réseau privé.

Mais il en découle également comme inconvénient l'accroissement des besoins en sécurité. Le problème qui se pose est donc d'arriver à maximiser la sécurité des échanges d'informations sans que cela nuise au respect des échéances des tâches du système temps réel. Nous voulons dire par là qu'il faut dans les limites du possible, qu'un système soit toujours ordonnançable avec la sécurité mise en place afin de garantir un fonctionnement sûr du système. En effet, sécuriser au maximum le système pourrait nous amener à un système qui dépasse sa capacité de traitement. Il faut donc limiter la sécurité de chaque tâche

selon son importance par rapport aux autres afin de ne rater aucune échéance. Ce mémoire fournit des solutions pour y parvenir.

Nous nous limiterons à étudier dans ce mémoire les systèmes « hard realtime », c.-à-d. que les échéances devront toutes être respectées malgré l'augmentation du temps d'exécution des différents travaux introduite par la mise en place de la sécurité dans le système en posant comme hypothèse que les tâches sont périodiques.

1.2 Organisation du mémoire

Clairement, ce mémoire liera donc deux disciplines en informatique, l'ordonnancement de systèmes temps réel et la sécurité de systèmes informatiques.

Nous ferons d'abord un état des techniques utilisées dans la littérature pour résoudre ce problème. Nous nous pencherons sur les algorithmes itératifs permettant de chiffrer les données (ex : AES, RC6,. ..), sur l'authentification de l'information et sur le contrôle d'intégrité (ex : méthodes HMAC, CBC-MAC,...).

Nous analyserons ensuite une méthode, le « Reward Based Scheduling » fournissant une récompense en fonction du temps ajouté pour la mise en place dans notre cas de la sécurité.

Pour clôturer, nous étudierons des heuristiques qui sur base de cette méthode tentent de trouver les temps se rapprochant le plus possible des temps optimaux pour établir la sécurité tout en validant le système, c.-à-d. en vérifiant par simulation que les contraintes d'échéance soient respectées.

Chapitre 2

Pré-requis

2.1 Systèmes temps réel

2.1.1 Introduction

Nous apporterons dans cette section quelques définitions largement inspirées de [7] et notations qui pourront servir à la compréhension de la suite de ce mémoire.

Un système temps réel est constitué d'un ensemble de tâches et chacune d'elles doit effectuer une quantité de calculs. Les résultats produits par ces différentes tâches doivent être d'une certaine qualité mais aussi être fournis avant un instant donné, que nous appellerons l'échéance de cette tâche.

Ces systèmes sont utilisés principalement dans des applications critiques qui ne peuvent pas se permettre de fournir un résultat erroné ou incomplet au moment de l'échéance, comme pour le contrôle d'une centrale nucléaire, le contrôle aérien, ...

2.1.2 Les travaux

Toute tâche no i (que nous noterons Ti) prise dans un ensemble de n tâches (i ? [1, n]) composant le système, partage la quantité de calculs qu'elle doit effectuer en un certain nombre de travaux. Ces travaux obtiennent le processeur à intervalle régulier (périodique) ou non. Chaque travail (aussi appelé instance

ou job en anglais) obtient le processeur à un instant donné et peut s'exécuter sur une certaine durée.

Définition Travail: Un travail ôij, soit le je travail de la tâche Ti sera caractérisé par le tuple (a, e, d). Un instant d'arrivée aij, un temps d'exécution eij et une échéance dij. Le travail ôij devra donc recevoir eij unités d'exécution sur l'intervalle [aij, dij)

Il existe différentes classes de niveaux de contraintes temporelles, on parle d'échéances strictes (hard), fermes (firm) ou souples (soft) [1].

- Un système temps réel est dit à échéances strictes (hard deadlines) lorsqu' on ne peut rater aucune échéance, les conséquences en seraient catastrophiques;

- Pour les échéances fermes (firm deadlines), les conséquences sont moins sévères, mais la valeur de l'exécution d'un travail après son échéance est nulle et donc d'aucune utilité.

- Un travail possédant une échéance souple (soft deadline) peut terminer son exécution après son échéance, son exécution après l'échéance a toujours une certaine valeur, bien que celle-ci décroisse avec le temps suivant l'échéance.

2.1.3 Les tâches

Classiquement dans un système temps réel, les calculs (l'exécution de ces travaux) sont récurrents, les tâches peuvent être périodiques ou sporadiques. Nous nous focaliserons dans ce mémoire sur les systèmes à tâches périodiques.

Définition Tâche périodique : Une tâche périodique Ti est caractérisée par le tuple (Ai, Pi, Di, Ci)

- Ai est la date d'arrivée du premier travail de la tâche Ti, ce qui ne signifie pas que celui-ci s'exécutera à cette date.

- Pi est la période, c.-à-d. la durée qui sépare deux arrivées successives de travaux. Le deuxième travail d'une tâche Ti ne pourra donc pas s'exécuter avant l'instant Ai + Pi.

- Di est l'échéance de la tâche Ti, qui dénote la limite supérieure de temps entre l'arrivée d'un travail et la fin de son exécution.

- Ci est le temps d'exécution de chaque travail de la tâche Ti, nous parlons aussi de WCETi (Worst Case Execution Time) pour dénoter la limite supérieure du temps d'exécution de chaque travail de la tâche Ti.

Mi, Oi : Dans la partie sur le « Reward-based Scheduling », ces notations dénotent respectivement la partie obligatoire (Mandatory) et optionnelle (Optional) d'une tâche Ti. Les notations mi et oi, dénotent respectivement la limite supérieure du temps d'exécution de la partie obligatoire Mi et la limite supérieure sur le temps d'exécution de la partie optionnelle Oi. Nous y reviendrons par la suite.

Chaque tâche génère un travail à chaque instant Ai+k.Pi avec une échéance à l'instant Ai + k. Pi + Di pour tout entier k ~ 0.

Les tâches sporadiques sont similaires aux tâches périodiques à l'exception faite que Pi correspond à la durée minimum qui sépare deux arrivées de travaux delatâche Ti.

Mais il existe également des tâches non récurrentes dites apériodiques, elles sont utilisées en général pour traiter des alarmes et des états d'exception, l'instant de réveil n'est pas connu au départ et il n'y a donc pas de période.

Chaque tâche a une certaine proportion d'exécution par rapport à sa période, nous parlerons d'utilisation que l'on définit mathématiquement comme : U(Ti) = Ci/Pi. L'utilisation d'un système S est la somme des utilisations des tâches qu'il contient, autrement dit : U(S) = >ITi?S U(Ti).

Dans la partie sur le « Reward-based Scheduling », nous parlerons d'utilisation par les parties obligatoires d'un système composé de n tâches. Nous noterons ce facteur d'utilisation Um que nous définissons par Um = >In i=1mi/Pi, c.-à-d. le rapport sur le temps d'exécution des parties obligatoires par rapport au temps total disponible. U quand à lui, désignera le facteur d'utilisation total (parties obligatoires et optionnelles confondues).

2.1.4 Le système

Il existe plusieurs expressions qualifiant un système constitué de n tâches, on parle de systèmes:

- à échéance contrainte : signifie que l'échéance de toute tâche du système
ne peut pas être supérieure à la période de celle-ci (Di Pi Vi E [1, n]).

- à échéance sur requête : signifie que l'échéance de toute tâche du système coïncide avec la période de celle-ci (Di = Pi Vi E [1, n], chaque travail d'une tâche doit se terminer avant l'arrivée du travail suivant de cette même tâche. C'est donc un cas particulier de système à échéance contrainte.

- à échéance arbitraire : aucune contrainte n'est imposée entre l'échéance et la période.

- à départ simultané : l'instant d'arrivée du premier travail de chaque tâche coïncide avec les autres, on peut donc poser Ai = 0 Vi E [1, n] sans nuire à la généralité.

Nous nous intéresserons plus particulièrement dans nos analyses aux systèmes à échéance sur requête et à départ simultané.

Le processeur

Il existe deux types d'environnement possibles pour un même système de tâches:

- monoprocesseur;

- multiprocesseur.

Un environnement multiprocesseur constitué de k processeurs peut exécuter k unités de temps d'exécution simultanément contrairement à un environnement monoprocesseur qui est limité à une exécution à la fois. Nous resterons dans le cadre de ce mémoire sur des environnements monoprocesseur.

Définition oisif: Nous reprenons la traduction de la terminologie anglaise : « idle » (inoccupé) en parlant du processeur ou d'instants pour dire de ceux-ci qu'ils sont inutilisés, autrement dit qu'il n'y a pas de travail en cours d'exécution.

Nous parlerons également d'unités de temps oisives, pour désigner les moments où le processeur n'exécute aucun travail.

2.1.5 L'ordonnancement

L'ordonnancement est le mécanisme permettant de choisir la tâche qui va être exécutée par le processeur à un instant donné. L'algorithme qui va effectuer ce choix est appelé l'ordonnanceur (le scheduler).

Il existe deux manières d'appeler cet ordonnanceur:

- appels à intervalle régulier (par exemple à chaque unité de temps);

- appels basés sur des évènements comme l'arrivée, la fin d'exécution ou l'échéance d'un travail.

Nous expliquerons par la suite pourquoi nous avons préféré utiliser une méthode basée sur des évènements dans nos simulations.

A chaque fois qu'il est appelé, l'ordonnanceur va choisir le travail qui doit être exécuté au moment de l'appel si celui-ci existe bien entendu, car il est probable qu'aucun travail ne soit disponible à cet instant, le processeur restera alors oisif. Un ordonnancement dit « préemptif » signifie qu'une tâche peut être interrompue par une autre plus prioritaire. Si le processus choisi, c.-à-d. le programme en cours d'exécution, est différent de celui exécuté actuellement, l'ordonnanceur effectue alors un changement de contexte qui consiste en une sauvegarde des données de la tâche actuelle et une restauration des données de la nouvelle tâche si cela est nécessaire, on appelle ce changement de contexte une «préemption » et nous dirons du travail interrompu qu'il est «préempté».

Dans un ordonnanceur temps réel, si le système arrive à ordonnancer toutes ses tâches en respectant leurs échéances, nous dirons de celui-ci qu'il est ordonnançable.

L'ordonnanceur va donc devoir assigner des priorités aux différentes tâches ou aux travaux de ces tâches. Il existe donc deux types d'assignations des priorités :

1. priorité fixe : également appelé assignation à priorité statique, chaque tâche reçoit une priorité par rapport aux autres à l'initialisation du système et chaque travail lancé hérite de la priorité de sa tâche;

2. priorité dynamique : la priorité est donnée aux travaux des tâches, non plus aux tâches. La priorité peut passer à un travail d'une autre tâche pendant l'exécution du système, selon la règle d'assignation utilisée, qui peut se baser par exemple sur le temps restant avant la prochaine échéance ou le temps d'exécution déjà écoulé pour ce travail.

Définition faisable - ordonnançable[18] : On dit d'un ordonnancement qu'il est faisable si chaque tâche se termine avant son échéance dans cet ordonnancement. Un système est ordonnançable s'il existe un ordonnancement faisable pour ce système.

Définition optimalité : Une règle d'assignation de priorité R désigne la façon dont la priorité de sélectionner un travail par rapport à un autre est définie. On dit que R est optimale si, lorsqu'un système est faisable, il est ordonnançable étant donné la règle R.

Pour tester l'ordonnançabilité d'un système en posant l'hypothèse que celui- ci est constitué de n tâches à échéance contrainte et à départ simultané, il suffit de tester celui-ci jusqu'à l'instant P que nous appellerons l'hyper-période et qui est égale au plus petit commun multiple des périodes des différentes tâches composant le système.

P=ppcm{P1,P2,...,Pn}

En effet, à cet instant et sous ces hypothèses, nous nous retrouvons dans la même position qu'à l'instant initial : toutes les tâches envoient un nouveau travai l.

« Earliest Deadline First »

Nous allons présenter maintenant la règle d'assignation que nous utiliserons dans notre étude, EDF ( Earliest Deadline First », plus proche échéance d'abord). EDF est une règle d'assignation à priorité dynamique. Lorsqu'un évènement survient : comme la fin de l'exécution d'un travail ou l'arrivée d'un nouveau travail dans le système, l'ordonnanceur va sélectionner parmi tous les travaux prêts à être exécutés celui dont l'échéance est la plus proche. Ce travail sera alors exécuté sur le processeur. Autrement dit, plus le travail doit être exécuté rapidement, plus il aura de chance d'être exécuté.

Cette règle d'assignation est optimale, car EDF trouve toujours un ordonnancement faisable s'il en existe un. Pour un système S à échéance sur requête, EDF à une borne supérieure de 100% sur l'utilisation, cela signifie que si U(S) = 1 alors il est garanti qu' EDF trouvera un ordonnancement faisable contrairement à une règle d'assignation à priorité fixe comme RM ( Rate Monotonic ») qui donne une priorité plus élevée aux tâches ayant les périodes les plus courtes. La borne sur l'utilisation pour laquelle il est garanti de trouver un ordonnancement faisable n'est que de ln 2 ~ = 69,3%. Cette borne peut-être augmentée à 83% et 78% si nous nous limitons respectivement à 2 ou 3 tâches, d'après la démonstration développée dans [13], mais reste toutefois inférieure àEDF.

Donnons maintenant un exemple de système afin d'illustrer le principe du fonctionnement d'EDF.

Prenons un système à échéance sur requête composé de 2 tâches T1(P1 = D1 = 5, C1 = 3) et T2(P2 = D2 = 3, C2 = 1), voici comment EDF va ordonnancer les différents travaux de ces tâches dont le schéma de l'ordonnancement est représenté sur la figure 2.1.

T1,2

T1,2

T1 ,3

1 4

56

7 9

10 13

T1

o o o

T1,1

 
 

T2,3

 

T2,4

 
 

T2,2

 
 
 

T2,5

o o o o o

0 1 45 67 9 10 13 14

T2,1

T2

FIG. 2.1 - Exemple d'ordonnancement de 2 tâches avec la règle d'assignation EDF

Les deux tâches sont à départ simultané et envoient chacune leur premier travail à l'instant 0 (autrement dit O1 = O2 = 0). EDF sélectionne le travail i-2,1 pour être exécuté car l'échéance d2,1 < d1,1. A l'instant 1, i-1,1 se termine et EDF doit choisir à nouveau un travail, il ne reste que i-1,1 qui continue son exécution à l'instant 3 lors de l'arrivée d'un nouveau travail de T2 car son échéance est plus proche. Nous remarquons, qu'à l'instant 6, i-1,2 est préempté lors de l'arrivée de i-2,3 car celui-ci possède une échéance d2,3 < d1,2.

Nous stoppons notre exemple à l'hyperpériode P = 15 = ppcm(5, 3) car à cet instant nous nous retrouvons exactement dans la même situation qu'à l'instant 0 et l'ordonnancement peut recommencer de la même manière indéfiniment.

Lorsque les échéances sont égales, le choix entre les deux travaux se fait arbitrairement en donnant priorité au travail en cours d'exécution, comme à l'instant 12 dans notre exemple où i-1,3 poursuit son exécution.

2.2 Cryptologie

2.2.1 Introduction

La cryptologie est la science du secret, elle se divise en deux branches :

La cryptographie : qui étudie les différentes possibilités de cacher, protéger ou contrôler l'authenticité d'une information;

La cryptanalyse : qui étudie les moyens de retrouver cette information à partir du texte chiffré (de l'information cachée) sans connaitre les clés ayant servi à protéger celle-ci, c'est en quelque sorte l'analyse des méthodes cryptographiques.

La cryptographie doit garantir certains principes qualifiant la bonne sécurité d'un système:

1. Confidentialité : grâce à un chiffrement pour que les données soient illisibles par une personne tierce et ainsi garantir que l'information est restée secrète de bout en bout.

2. Authentification : afin que personne ne puisse se faire passer pour la source de l'information, que la provenance des données soit garantie.

3. Intégrité : pour que les données ne puissent pas être modifiées sans qu'on ne s'en rende compte.

2.2.2 Confidentialité

La confidentialité est historiquement le premier but des études en cryptographie : rendre secrètes des informations.

Cela se réalise par un chiffrement mathématique des données qui utilise comme paramètre une clé. Le chiffrement consiste à appliquer une suite d'opérations sur un texte clair, pour obtenir un texte chiffré, aussi appelé cryptogramme, ne pouvant être déchiffré que par l'entité qui possède la clé adéquate.

Il existe deux catégories de chiffrements:

Le chiffrement symétrique également appelé le chiffrement à clé secrète:

Le principe est de chiffrer le texte clair avec une clé et de le déchiffrer avec la même clé ou une clé dérivée de celle-ci. La clé n'est connue que par les deux entités s'échangeant des informations.

Le chiffrement asymétrique également appelé le chiffrement à clé publique: Les clés utilisées pour le chiffrement et le déchiffrement sont différentes et ne peuvent être déduites l'une de l'autre par un observateur extérieur sans la connaissance des informations nécessaires. Une des clés peut être connue de tous tandis que l'autre doit rester secrète. Le but étant que tout le monde puisse à l'aide d'une clé publique chiffrer des données que seule l'entité possédant la clé secrète puisse déchiffrer. La vision d'un ensemble de textes chiffrés ne doit apporter aucune information sur le texte clair, c'est ce qu'on appelle la notion de sécurité sémantique (propre au chiffrement asymétrique).

Dans le cadre de la sécurité dans les systèmes temps réel qui doivent respecter des contraintes de temps, nous nous intéresserons plus particulièrement au chiffrement symétrique qu'il est préférable d'utiliser car le chiffrement asymétrique a été montré comme plus complexe au niveau du temps de calcul [16].

2.2.3 Intégrité et authentification

Le réseau reliant les entités n'étant pas toujours sûr, il est important de contrôler la provenance des informations et de s'assurer qu'elles n'ont pas été modifiées en chemin, ce sont respectivement les principes d'authentification et d'intégrité des données.

Afin de garantir ces deux principes, on utilise des codes d'authentification de message (MAC : Message Authentication Code). Un MAC est un code envoyé avec le message, il est aussi appelé hachage ou empreinte du message. Le hachage correspond au message et permet de garantir la validité de celui-ci. Il est obtenu à partir d'un algorithme MAC qui prend deux paramètres en entrée: le message dont on désire garantir l'intégrité et une clé secrète connue des deux entités s'échangeant ce message.

La probabilité que des données différentes possèdent le même hachage est très faible, c'est ce qu'on appelle une « collision ». La probabilité de trouver une collision et que le message possédant le même hachage soit compréhensible par le récepteur est quasi nulle. Une modification même très légère des données provoque un changement radical au niveau du hachage obtenu. Si une modification a lieu entre la source et la destination, le hachage ne correspondra plus aux données et celles-ci seront rejetées par le récepteur.

Le fait d'utiliser une clé secrète partagée entre les deux entités en plus de la fonction de hachage garantit l'authenticité des données étant donné qu'un attaquant ne connaissant pas la clé ne peut envoyer des informations accompagnées d'un hachage correct de celles-ci.

Différentes méthodes existent pour créer une empreinte du message:

- une fonction de hachage utilisant une clé en paramètre en plus du message (ex : HMAC);

- un chiffrement par blocs (comme les méthodes CBC-MAC).

Chapitre 3

Etat de l'art

3.1 Introduction

Comme nous l'avons mentionné dans le chapitre 1, dans les systèmes embarqués qui utilisent un réseau, nous devons à la fois respecter les contraintes d'échéances, tout en assurant la confidentialité des données, l'intégrité et l'authentification. Mais malheureusement, l'utilisation des algorithmes de cryptographie amène souvent au non-respect des contraintes d'échéances, surtout pour des systèmes embarqués qui sont limités en ressources pour des raisons de coût et de consommation.

Les travaux sur la sécurité et sur les systèmes temps réel ont été étudiés la plupart du temps séparément. Il n'existe que très peu de travaux étudiant le problème de la sécurité sous des contraintes d'échéance. En étudiant les travaux et articles rédigés à propos de la sécurité dans les systèmes temps réel, nous remarquons que ceux-ci se basent généralement sur les concepts de qualité de service (QoS), qui ont déjà été plus largement analysés dans la littérature. Le rapport de recherche [14] reprend différents mécanismes de gestion de la qualité de service dans les systèmes temps réel.

Souvent, différents « niveaux de service » sont appliqués afin de respecter au mieux les échéances tout en sécurisant le plus possible le système. Une certaine valeur est associée à chaque niveau selon son importance, l'objectif étant de maximiser la valeur totale du système.

Ce chapitre présente des méthodes que nous allons analyser et éventuellement critiquer.

3.2 Qualité de service et longueur des clés

3.2.1 Introduction

En cryptographie, il est bien connu qu'en augmentant le nombre de bits dont est constitué une clé qui servira à chiffrer les données, un attaquant mettra plus de temps à casser le chiffrement [17]. La complexité pour casser le chiffrement est donc exponentielle en fonction de la longueur de la clé.

Il est donc nécessaire d'utiliser une clé de taille suffisante. Une clé formée de £ bits appartient à un espace de clés constitué de 2 clés possibles. Un attaquant qui est capable de tester m clés par seconde, mettrait donc en moyenne 2e_1 /m secondes pour trouver la bonne clé par une attaque de type brute-force (2`/m secondes au pire des cas).

Brute-force est une recherche exhaustive sur tout l'espace des clés. Il est donc indispensable que le temps mis pour casser la sécurité par ce type d'attaque soit trop important par rapport à la valeur des données et que le système de chiffrement soit robuste, c'est à dire que le seul moyen d'attaquer le système soit d'appliquer une attaque brute-force.

AES, par exemple, utilise des clés de 128, 192, ou 256 bits [21] donc la quantité de clés possibles avec ces dimensions est relativement importante. De plus, AES est un algorithme de chiffrement robuste puisqu' aucune attaque sur les versions standards d'AES n'a été découverte à ce jour1, il pourrait donc tout à fait être utilisé dans ce cadre.

Cependant, employer toujours la plus longue clé provoque une augmentation du temps de calcul utilisé par l'algorithme de chiffrement et donc il en résulte un besoin de dégrader la qualité de service. Cette diminution de la qualité de service permet de diminuer le temps d'exécution de manière à ce que les échéances soient encore toutes respectées.

Kyoung Don Kang et Sang H. Son dans leur étude [9] que nous allons présenter dans cette section, essayent de maximiser la qualité de service tout en garantissant que le système soit suffisamment sécurisé. Les auteurs, présentent leur méthode avec DES, mais ce n'est pas un algorithme de chiffrement robuste, DES a été cassé en 22 heures et 15 minutes lors du 3e« DES Challenge » en janvier 1999 [19]. Nous préfèrerons donc présenter cette méthode en nous basant sur l'algorithme de chiffrement AES.

1Il existe néanmoins des attaques sur AES en diminuant le nombre de tours à 5, 6 ou 7 [22].

3.2.2 Modélisation du système

Le modèle sur lequel nous nous basons ici est constitué d'un ou de plusieurs systèmes embarqués temps réel (SETR), par exemple des capteurs, comme représenté sur la figure 3.1. Ce SETR doit échanger ses informations avec un centre de contrôle (CC).

FIG. 3.1 - Schéma représentant le modèle SETR--CC Le SETR est constitué de n tâches T1, T2, ... Tn.

Le pire temps d'exécution Ci d'une tâche Ti tient compte:

1. du temps d'exécution normal de la tâche: Ci,c

2. du temps utilisé par le chiffrement avec des clés de £ bits: Ci,e(`) Ci = Ci,c+Ci,e(`) (3.1)

Ti

Ci,c

exécution normale: Ci,c

sécurité : Ci,e(t)? Ci = Ci,c + Ci,e(`)

FIG. 3.2 - Représentation du temps d'exécution total Ci

La longueur de la clé £ est augmentée en fonction du risque encouru. Ce risque est transmis par un détecteur d'intrusion (IDS : « Intrusion Detection System ») installé sur le SETR et sur le CC. Lorsqu'une intrusion est détectée, le niveau de risque est augmenté. Le SETR va devoir alors augmenter la taille de la clé avec laquelle il chiffre ses données. Cela entraînera une diminution de la qualité de service de certaines tâches. La notion de qualité de service utilisée ici n'est pas définie dans [9] mais nous pouvons supposer qu'il s'agit de la fréquence des prises d'informations par les capteurs. Cette diminution correspond

donc à une diminution du facteur d'utilisation de ces tâches. En effet, la qualité de service étant plus faible, moins de traitements sont nécessaires et Ci est diminué. Cela permet d'éviter de rater des échéances à cause du coût en temps trop élevé dû à la mise en place de la sécurité.

Trois niveaux de risques sont définis dans [9] : faible: R= 1, moyen: R=2, élevé: R=3.

Le niveau de défense S(L) se calcule en fonction de la longueur de la clé L. En nous basant sur les possibilités offertes par AES par rapport à la taille des clés, nous posons donc:

S(1 28) = 1, S(1 92) = 2, S(256) = 3.

Le but est de maximiser la qualité de service globale du système (QoS) qui augmente en fonction de la qualité de service Q(i) des différentes tâches.

In i=1 Q(i)

Maximiser QoS = S in i=1 max Q(i) = 1 (3.2)

Q(i) et max Q(i) sont respectivement la qualité de service courante et maximum pour la tâche Ti. Nous définissons la variable S par 3.3

{ 1 si S(L) - R S =0 sinon

(3.3)

Ce qui signifie (voir équation 3.2) que si le niveau de défense (S(L)) est inférieur au niveau de risque (R) envoyé par l'IDS, la qualité de service du système est nulle.

Nous calculons l'utilisation de la tâche Ti en tenant compte de Ci,e(`), l'utilisation du système, ne pouvant pas (pour rappel) dépasser une certaine borne d'utilisation (B), par exemple 100% pour EDF (Earliest Deadline First) sinon le système n'est plus ordonnançable.

3.2.3 L'algorithme d'ajustement de la qualité de service

Un algorithme hors-ligne (algorithme 1) calcule premièrement la qualité de service optimale pour chaque tâche Ti à utiliser en fonction de la longueur de la clé L et d'une fonction gi.

En effet, chaque tâche Ti possède une fonction gi : ui -* qi qui associe à un facteur d'utilisation ui un certain niveau de qualité de service qi, le but de

l'algorithme étant de choisir le niveau de qualité de service adéquat en tenant compte du facteur d'utilisation imposé par la mise en place de la sécurité.

Nous allons détailler dans cette section cet algorithme repris de [9].

Description de l'algorithme

L'algorithme retourne donc un tableau Q_List[e][i] qui représente la QoS optimale à choisir pour la tâche Ti lorsque la clé £ est utilisée. Les étapes qui suivent sont exécutées pour chaque longueur de clé de manière à fournir les différentes lignes Q_List[e] du tableau.

Etape 1 : Pour chaque tâche, on calcule à l'aide de gi et selon la longueur de la clé, l'enveloppe convexe des différentes paires < Utilisation, QoS> pour tous les niveaux discrets de QoS (voir figure 3.3). Le but étant de sélectionner les points sur gi garantissant une qualité de service maximum avec un facteur d'utilisation minimum.

FIG. 3.3 - Exemple de fonction gi et enveloppe convexe

Cela permet d'enlever les paires < uij, qij > qui ne sont clairement pas optimales, où j est le niveau de QoS et uij est le facteur d'utilisation requis pour la tâche Ti avec ce niveauj de QoS et avec la clé £. Il nous reste alors par exemple (figure 3.3) un ensemble de paires Hi = {< ui1, qi1 >, < ui3, qi3 >, ...}

Etape 2 : Les ensembles Hi ?Ti sont fusionnés et triés par ordre croissant d'utilisation excepté les paires < ui1, qi1 > qui correspondent à la QoS minimum qui devra être garantie à l'étape 5. Le résultat de ce tri/fusion est la liste H_List.

Etape 3 : On initialise Q_List[e] à l'ensemble vide.

Entrée : gi : ui ? qi pour chaque tâche Ti Sortie: Q_L ist[e][i]

Pour chaque clé l (128, 192,256 bits) faire

1. Pouride1 ànfaire

Pour la tâche Ti, calculer l'enveloppe convexe

Hi={<ui1,qi1 >, ... , < uij, qij >} j = L (# niveaux de QoS) Fin Pour

/*Fusion triée des enveloppes convexes de toutes les tâches par ordre non-décroissant sur l'utilisation*/

2.H_List?Fusion(H1-{<u11,q11 >},H2-{<u21,q21 >},...HN-{< un1,qn1 >})

3. Q_List[e] ? 0

4.Ur ?B

5. Pouride1ànfaire

Si (Ur <ui1) Alors

FIN, ERREUR

Fin Si

Q_List[e][i] ? qi1 Ur ? Ur - ui1

Ui ? ui1

Fin Pour

6. Pour ide 1 à |H_L ist| faire

id ? H_List[i].id

äU ? H_List[i].u - Uid

Si U> Ur) Alors FIN Fin Si Q_List[e][id] ? H_List[i].q Ur? UrU

Uid ? H_List[i].u Fin Pour

Fin Pour

Algorithme 1: Ajustement optimal de la qualité de service en fonction de la longueur de la clé

Etape 4: Ur représente tout au long de l'exécution de l'algorithme l'utilisa-

tion disponible restante, et est initialisé à la borne d'utilisation maximale B de la politique d'ordonnancement utilisée.

Etape 5 : On va maintenant initialiser Q_List[L] pour que le système supporte au moins le niveau minimum de QoS pour chaque tâche avec une clé de L bits. Si l'utilisation restante Ur est suffisante pour garantir ce niveau de service, on fixe Q_List[L][i] = qi1 et on soustrait ui1 à Ur, autrement l'algorithme se termine par une erreur puisqu'on dépasse la borne d'utilisation B même avec la QoS minimum.

Etape 6 : Et pour terminer, on procède à l'augmentation au maximum de la QoS de chaque tâche. Pour effectuer cela, on se base sur l'ensemble H_L ist obtenu à l'étape 2. On sélectionne la première tâche Tid = H_List[i].id, c.-à-d. celle qui possède la plus faible utilisation. On calcule le facteur d'utilisation supplémentaire äU nécessaire pour augmenter le niveau de QoS de Tid. Si celui-ci ne dépasse pas Ur, on augmente le niveau de QoS à la valeur H_List[i].q et on soustrait äU à l'utilisation restante Ur. On passe à l'élément suivant dans H_List jusqu'à ce que Ur soit insuffisant pour augmenter encore la QoS d'une tâche.

La longueur des clés et le niveau de qualité de service sont ajustés en ligne à l'aide du tableau Q_L ist[L][i] fourni par l'algorithme exécuté hors-ligne.

L'IDS envoie périodiquement le niveau de risque courant R, si R> S(l), on passe à une clé L' > L de façon à ce que S(L') = R, ensuite la qualité de service des tâches Ti est diminuée selon Q_List[L'][i] de manière à ne rater aucune échéance.

3.2.4 Conclusion

Nous remarquons qu'un grand intérêt de cet algorithme est que la modification en ligne de la qualité de service pour ajuster le temps d'exécution se fait avec une complexité constante (O(1)) grâce au précalcul effectué hors-ligne par l'algorithme 1.

La complexité de l'algorithme 1 se calcule par rapport au nombre des différentes tailles de clés possibles (K, ici K = 3), au nombre de niveau de QoS (L) et au nombre de tâches (n). On exécute K fois le tri-fusion de L. n paires < Utilisation, QoS >. La complexité du tri-fusion étant de O(nlogn), la complexité de l'algorithme hors-ligne est donc de O(K. Ln. log Ln) qui peut se réduire à O(nlogn) étant donné que K est constant (128, 192 ou 256 bits) tout

comme le nombre de niveaux de qualité de service (L).

Nous allons maintenant analyser la façon dont d'autres auteurs voient le problème, ne se basant plus sur la longueur des clés mais en utilisant différents niveaux de sécurité, qu'ils déterminent soit par différents algorithmes cryptographiques, soit en rajoutant différents services selon le temps disponible et l'importance des tâches.

3.3 Ordonnancement dynamique basé sur des ni-

veaux de sécurité

Tao Xie, dans son étude [27] expose une technique qui établit certains « niveaux de sécurité ».

Les niveaux de sécurité sont définis comme étant une combinaison du transport de l'information sur le réseau, des garanties sur le chiffrement de cette dernière et de l'authentification du client.

Les niveaux choisis dans [27] qui se basent sur différents modes fournis par le protocole de sécurité SSL (« Secure Sockets Layers ») sont:

1. Routage uniquement

2. Routage + chiffrement

3. Routage+SSL

4. Routage + SSL (avec chiffrement de l'information)

5. Routage + SSL (avec authentification du client)

6. Routage + SSL (avec chiffrement de l'information + authentification du client)

Plus le niveau de sécurité augmente, plus le temps supplémentaire nécessaire pour établir la sécurité est important.

Chaque tâche possède comme paramètre, en plus de son échéance, un domaine de sécurité, c.-à-d. une borne inférieure et supérieure, qui sont les valeurs minimales et maximales admises comme niveau de sécurité de cette tâche.

A leur arrivée, les tâches sont prises en charge par un contrôleur d'admission, qui fixe le niveau de sécurité et décide si oui ou non la tâche peut être exécutée.

Le contrôleur de sécurité entre alors en jeu. Il augmente le plus possible le niveau de sécurité de chaque tâche dans la file des tâches acceptées en respectant deux contraintes, augmenter le niveau de sécurité ne doit pas:

1. empêcher la tâche de respecter son échéance;

2. ni provoquer l'éjection d'une tâche ayant déjà été acceptée.

Il choisira de façon optimale le niveau de sécurité parmi les valeurs dans le domaine de sécurité de la tâche.

EDF ordonnancera ensuite les tâches, en prenant la tâche au sommet de la queue, c.-à-d. la tâche ayant l'échéance la plus proche. Le schéma de fonctionnement de cette méthode et les structures utilisées sont représentés sur la figure 3.4.

FIG. 3.4 - Schéma représentant l'architecture et les structures utilisées

La notion de « Success Ratio » (SR) est établie. SR est défini comme étant le rapport entre le nombre de tâches acceptées (Na) par le contrôleur d'admission et le nombre de tâches reçues (Nr).

Na

SR =(3.4) Nr

Le but étant de maximiser SR tout en maximisant la valeur en sécurité du système, il compare 4 algorithmes utilisant tous EDF comme politique d'ordonnancement:


· EDF_MINS : le plus petit niveau de sécurité spécifié par la tâche est auto-

met donc d'obtenir la plus importante proportion de tâches respectant leur échéance (SR) mais il néglige la sécurité.

· EDF_MAXS : Le contrôleur d'admission choisit le plus haut niveau de sécurité spécifié dans les paramètres de la tâche (Si max). Ce qui garantit au contraire, un niveau de sécurité maximum au détriment du SR.

· EDF_RNDS : qui est une autre façon de voir le problème, en prenant le niveau de sécurité au hasard entre les deux bornes. Ce qui implique un SR et un niveau de sécurité global se situant entre celui de EDF_MINS et EDF_MAXS.

· EDF_OPTS : a été mis au point pour pallier le manque d'optimalité de EDF_RNDS. Il trouve un équilibre entre le niveau de sécurité et le SR. Ce dernier est de peu inférieur au SR utilisé avec EDF_MINS mais la qualité de la sécurité en est fortement améliorée.

3.3.1 Conclusion

Malheureusement, cette technique autorise certaines tâches à ne pas être acceptées à cause d'une augmentation de la sécurité sur d'autres tâches, c'est ce qu'on appelle des systèmes temps réel souples (soft real-time). Nous souhaitons plutôt nous intéresser ici à des systèmes qui tentent d'atteindre un niveau maximum de sécurité sans rejeter de tâches, c'est pourquoi nous ne rentrons pas dans le détail de l'optimisation apportée par l'algorithme EDF_OPTS, le lecteur intéressé par cette méthode peut consulter [27]. Cette méthode nous permet d'approcher la notion de niveau de sécurité que l'on retrouve dans les différents travaux analysés dans ce chapitre. La section suivante établit les niveaux de sécurité en fonction de l'algorithme utilisé, nous analyserons cette méthode et nous donnerons notre opinion sur la pertinence d'un tel choix.

3.4 Méthode SASES

Nous allons donc maintenant présenter une technique étudiée dans [26], appelée SASES (Security-Aware Scheduling for Embedded Systems).

Cet algorithme tente de fournir une bonne sécurité au système tout en tenant compte cette fois de l'importance du respect de toutes les échéances (système temps réel strict).

3.4.1 Modélisation

Nous nous concentrerons sur l'étude de tâches temps réel périodiques qui sont indépendantes l'une de l'autre et nous supposerons que les tâches sont à échéance sur requête et à départ simultané. Donc le système sera ordonnançable par une politique d'ordonnancement EDF si la contrainte U 1 est satisfaite (voir chapitre 2).

Nous utilisons ici trois services de sécurité :

1. L'authentification ;

2. La confidentialité;

3. L'intégrité.

nous utiliserons pour désigner ces services respectivement les notations a,c,g. Le modèle permet de rajouter d'autres services.

Les tâches

Chaque tâche est composée d'un ensemble de paramètres, Ti = (Ci, Pi, Li, Si, Wi):

Ci : son pire temps d'exécution (worst-case execution time), sans tenir compte de la sécurité;

Pi : sa période;

Li : la taille des données à protéger pour chaque travail, exprimée en Ko;

Si : un ensemble de bornes définissant le niveau de sécurité minimum et maximum à fournir à Ti pour les différents services de sécurité.

Wi: un vecteur ~wa i , wc i , wg ] contenant les poids des différents services k les

i

uns par rapport aux autres. Ces poids sont attribués par l'utilisateur selon l'importance de la sécurité de ces services pour la tâche.

X wki = 1 et wki ? [0,1]

k?{a,c,i}

La taille des données

Il n'est pas précisé dans [26] comment Li est fixé mais nous supposons que l'utilisateur fournit le débit de traitement des données qu'il souhaite imposer pour chaque tâche Ti. Nous noterons par Debi ce débit exprimé en Ko/s. Il est

alors possible par la formule 3.5 de calculer la taille des données à traiter pour chaque travail de la tâche Ti. La quantité de données que nous noterons Li exprimée en Ko se calcule en fonction de la période Pi exprimée en nombre de tics d'horloge du processeur.

Pi

Li = Debi· (3.5)

#tics/ s

Niveaux de sécurité

Chaque service possède une borne inférieure et supérieure pour le niveau de sécurité.

] , [sg

Si = (Sa i , Sc i , Sg i ) = ([sa ] , [sc

i min, sa i min, sc i min, sg ]) (3.6)

i max i max i max

Sa i , Sci et Sgi dénotent respectivement pour Ti les bornes pour le service d'authentification, de confidentialité et d'intégrité. Le niveau de sécurité peut s'étendre de 0 à 1. Le but de l'ordonnanceur sera donc de choisir le niveau de sécurité si le plus approprié dans le domaine Si, c.-à-d. si = (sa i , sc i , sg i ) où sk i E Sk i V k E {a, c, g}.

Temps de calcul de la sécurité

Posons seci, le temps d'exécution utilisé pour la sécurisation d'un travail de la tâche Ti.

Nous calculons seci comme exprimé dans 3.7.

seci = X seck i (sk i ) (3.7)

k?{a,c,g}

C'est donc la somme des seck i(sk i ), c.-à-d. la somme des temps d'exécution nécessaires pour l'application de chaque service de sécurité k en fonction du niveau de sécurité choisi par l'ordonnanceur pour chacun d'eux.

La méthode pour calculer les temps de calcul seck i (sk i ) pour chaque service de sécurité est peu définie dans [26]. Nous avons donc analysé et critiqué la méthode d'un autre article [25] des mêmes auteurs.

Dans [25], chaque niveau de sécurité correspond à un algorithme cryptographique (algorithme de chiffrement, fonction de hachage ou méthode d'authentification). Selon ce même travail, plus le niveau de sécurité est important, plus le débit de traitement de l'algorithme associé à ce niveau est faible et donc le temps dépensé pour appliquer la sécurité sera plus important. Par la suite, nous expliquerons pourquoi nous ne sommes pas entièrement du même avis que l'auteur de [25].

A l'aide des tests de performance fournis dans la librairie crypto++ [4], nous avons calculé le débit de différents algorithmes sur notre processeur, Intel Pentium M Processor 735 - 1.7 GHz. Pour chaque service nous avons choisi une liste d'algorithmes de manière à répartir correctement la valeur des niveaux de sécurité en nous basant sur le choix fait par Tao Xie et Xiao Qin dans [25].

Nous allons maintenant montrer comment est calculé le temps d'exécution nécessaire pour les différents services.

3.4.2 Différents services

La confidentialité

Imaginons qu'un système embarqué temps réel, que nous noterons SETR, veuille transmettre une information (P) à un serveur, que nous noterons CC (centre de commande).

Le service de confidentialité est indispensable pour rendre les données circulant entre ce SETR et le CC inintelligibles.

Le SETR va chiffrer (voir 3.8) cette information grâce à un algorithme de chiffrement (E), en utilisant la clé de chiffrement (Ke) lui ayant été fournie par le CC pour l'algorithme E. Afin de réduire le coût en temps, nous pouvons supposer qu'il n'y a pas de négociation des clés en ligne et que l'enregistrement des clés se fait lors de la maintenance du système.

Le chiffrement est symétrique, ce qui signifie que chaque entité (source et destination) possède la même clé secrète que ce soit pour chiffrer ou déchiffrer les données. Un chiffrement symétrique est utilisé car il est plus rapide qu'un chiffrement asymétrique [16], ce qui est utile dans les systèmes temps réel pour diminuer le temps d'exécution requis par la mise en place de la sécurité.

Le cryptogramme C est alors obtenu par 3.8:

C = E(P){Ke,Cpt} (3.8)

Un compteur Cpt est inclus dans le message à chiffrer, il permet d'éviter une attaque par rejeu, c'est à dire une attaque où l'attaquant renvoie une copie d'un ancien message. Il est incrémenté à chaque message P envoyé par le SETR, s'il ne l'est pas, le CC va rejeter P.

Il existe plusieurs manières de chiffrer un message. Chaque algorithme de chiffrement correspond à un niveau de sécurité sc l et possède un certain débit de chiffrement i(scl), c.-à-d. la quantité de données que l'algorithme à ce niveau peut traiter par unité de temps. Ce débit dépend de la rapidité du processeur et de l'algorithme utilisé, autrement dit du niveau de sécurité choisi.

Selon la quantité de données Li à traiter dans chaque travail de la tâche Ti, on peut calculer la durée du chiffrement secc ipour un travail de Ti grâce à l'équation 3.9.

Li

secc i = (3.9)
u(sc i)

où u(sci) est une fonction qui donne le débit i(sc l ) de l'algorithme dont le niveau de sécurité est le plus proche et sc i .

Nous avons testé onze algorithmes de chiffrement différents. En fonction des performances de ces différents algorithmes nous établissons un niveau de sécurité sc l pour chacun d'eux qui se situe entre 0, 16 et 1, l étant le numéro de l'algorithme dans la liste 3.1.

La valeur 1 est attribuée à IDEA, l'algorithme le plus « fort » selon [25], mais possédant aussi le débit le plus faible (= 4,44 Ko/ms) et donc le temps d'exécution le plus important pour une même quantité de données. Le niveau de sécurité des autres algorithmes est déduit de celui-ci, par la formule 3.10.

4, 44

sc l = , 1 l 11 (3.10)

ic

l

Nous fournissons dans le tableau 3.1, la liste des algorithmes de chiffrement que nous avons obtenus grâce aux tests de performance, triés en ordre croissant par rapport au niveau de sécurité avec leurs performances respectives (le débit) et le niveau de sécurité qui en découle. Ils seront utilisés pour garantir la confidentialité des données.

Algorithmes de chiffrement

Niveau de sécurité sc l

Débit ucl (en Ko/ms)

Rijndael (128 bits)

0,16

27,04

SEAL-3.0-BE

0,22

19,72

Salsa20

0,38

11,52

Blowfish

0,49

9,09

RC6

0,53

8,44

Twofish

0,65

6,87

DES

0,75

5,93

Camellia (256 bits)

0,87

5,11

Shacal-2 (128 bits)

0,90

4,93

IDEA

1,00

4,44

TAB. 3.1 - Confidentialité - Niveaux de sécurité et débits des algorithmes

Nous remarquons que la façon dont les algorithmes sont classés est étonnante. En effet, Xie et Qin évaluent les performances d'un algorithme cryptographique en fonction du débit de celui-ci. Mais d'après nos tests, nous observons par exemple, que le débit de l'algorithme Rijndael-128 bits (AES) est plus important que celui de DES ce qui rejoint les résultats obtenus dans [25]. AES utilisera donc moins de temps pour le chiffrement et recevra un niveau de sécurité inférieur.

Or, la valeur en sécurité d'un algorithme ne dépend pas uniquement du débit de celui-ci mais de sa complexité et de la longueur des clés utilisées (comme nous l'avons mentionné à la section 3.2.1). Le chiffrement DES, à cause de la faible longueur de la clé (56 bits), a été cassé en 1999 en moins d'un jour. Rijndael quant à lui, utilise des clés de minimum 128 bits, ce qui est usuellement admis comme étant la limite inférieure acceptable pour la taille des clés d'un algorithme cryptographique permettant d'atteindre une sécurité satisfaisante (du moins jusqu'à ce jour) [2].

Il n'est donc pas logique de poser pour l'algorithme DES, un niveau de sécurité supérieur à AES. Lorsqu'on dispose de plus de temps lors de l'ordonnancement, on passerait à un algorithme moins performant aussi bien en terme de débit que de résistance aux attaques.

Nous allons néanmoins continuer à expliquer l'idée exposée dans cette étude. Elle pourrait être intéressante à appliquer en définissant autrement les niveaux de sécurité. Il est possible de définir le niveau de sécurité par rapport à la longueur des clés en utilisant le même algorithme pour les différents niveaux de sécurité, ce qui réduirait la quantité de ressources nécessaires pour implémenter ces différents algorithmes dans le système embarqué. Citons par exemple,

l'idée exposée dans [10], qui est d'utiliser les variantes 128, 192, 256 bits d'AES.

L'intégrité

Le service d'intégrité sert à garantir que les données n'ont pas été modifiées entre le SETR et le CC.

Le temps d'exécutionsecg i nécessaire pour ajouter le contrôle d'intégrité au temps normal d'exécution est calculé dans [25] à partir d'un tableau fournissant les débits de différentes fonctions de hachage de la même manière que pour le service de confidentialité (par l'équation 3.11).

Li

secg i = (3.11)

ó(sgi)

Nous avons calculé le débit de neuf fonctions de hachage différentes (voir tableau 3.2) et avons ensuite établi un niveau de sécurité pour chacune de celles-ci par rapport à leurs performances dans la mesure où plus le hachage est long, plus la sécurité est renforcée mais le débit de traitement est plus faible.

Le calcul du niveau de sécurité des différentes fonctions s'effectue de manière analogue au service de confidentialité, nous comparons grâce à la formule 3.12 les différentes fonctions de hachage par rapport aux performances de SHA-256 (possédant le plus faible débit : 8,98 Ko/ms) dont le niveau de sécurité est fixé à 1. Le niveau de sécurité sgl varie entre 0, 05 et 1.

si l = 8, 98 , 1 = l = 9 (3.12)

ui

l

A nouveau, l'auteur suppose qu'une fonction de hachage dont le temps de calcul est plus important (plus faible débit), sera de meilleure qualité, ce qui est probable mais n'est pas prouvé. Nous n'avons pas trouvé de contre-exemple comme nous l'avons fait avec AES par rapport à DES dans le cadre de la confidentialité.

Remarque: On ne peut garantir l'intégrité des données en appliquant uniquement sur celles-ci une simple fonction de hachage à sens unique sans clé comme une des fonctions du tableau 3.2. Il est important de chiffrer le message dont on désire garantir l'intégrité avec une clé secrète connue uniquement par le

Fonctions de hachage

Niveau de sécurité sg l

Débit ugl (en Ko/ms)

Adler-32

0,05

170,00

CRC-32

0,08

117,98

MD5

0,15

58,99

RIPE-MD128

0,35

25,36

Tiger

0,39

22,69

SHA-1

0,43

20,71

RIPE-MD160

0,56

16,05

HAVAL-5

0,63

14,36

SHA-256

1,00

8,98

TAB. 3.2 - Intégrité - Niveaux de sécurité et débits des fonctions de hachage

SETR et le CC, car autrement un attaquant pourrait modifier le message et hacher à nouveau celui-ci. Etant donné que le message correspondra au résultat de la fonction de hachage, il sera accepté alors que celui-ci a été modifié.

On peut supposer ici que la fonction de hachage est appliquée et ensuite chiffrée par le service de confidentialité. Mais si seule l'intégrité doit être garantie, cette méthode ne convient pas, il est préférable d'utiliser des MAC (Message Authentication Code) qui garantissent à la fois l'authentification et l'intégrité des données.

L'authentification

Il est indispensable de vérifier l'authenticité des données circulant dans le système, c.-à-d. qu'il faut garantir que c'est bien la source concernée qui envoie les données. Dans ce but, nous utilisons des codes d'authentification de message (MAC).

Le service d'authentification est divisé dans [25] en 3 classes qui utilisent chacune une méthode d'authentification différente :

1. faible, nous utiliserons pour ce niveau HMAC-MD5;

2. acceptable, avec HMAC-SHA-1;

3. forte, en utilisant CBC-MAC-AES.

Chaque classe a besoin d'un certain temps de calcul. Ces temps sont listés dans le tableau 3.3 issu de [25], la différence de niveau est calculée à partir de ces temps de calcul.

Méthodes d'authentification

Niveau de sécurité sal

Temps de calcul ual (en ms)

HMAC-MD5

0,55

90

HMAC-SHA-1

0,91

148

CBC-MAC/AES

1 ,00

163

TAB. 3.3 - Authentification - Niveaux de sécurité et temps de calcul des différentes méthodes

On obtient secai = ual(sa i ) comme étant le temps nécessaire pour garantir l'authenticité des données au niveau sai (faible, acceptable, fort).

Les services d'intégrité et d'authentification sont séparés, or un MAC garantit à la fois l'intégrité et l'authentification des données. Il est donc, selon nous, inutile d'utiliser le service d'intégrité comme il est décrit dans [25]. Il serait préférable de lier ces deux services en un seul.

Voici comment appliquer un service qui garantit l'authentification et l'intégrité par une méthode de type HMAC (keyed-hash message authentication code) utilisée avec une fonction de hachage à sens unique comme SHA-1 ou MD5:

Après avoir chiffré l'information afin d'en garantir la confidentialité, il nous faut garantir que cette information n'a pas été modifiée et provient effectivement du SETR. Dans ce but, un code d'authentification du message (MAC que nous noterons ici M) sera calculé et rajouté à la suite du message:

M= H(S|D|C|Cpt)Km (3.13)

Une méthode d'authentification (H) du tableau 3.3 est appliquée avec la clé Km sur la concaténation de l'adresse de la source (S: le SETR), de la destination (D: le CC), du message chiffré et du compteur Cpt. Nous tenons a préciser que la clé Km doit être choisie comme différente de Ke (la clé utilisée pour le chiffrement). En effet, pour des raisons de sécurité [17], il est préférable d'utiliser des clés différentes pour l'algorithme de chiffrement et la fonction de hachage. Ensuite le SETR envoie le message crypté Msg et le résultat du hachage du message M vers le CC, ce transfert est donc constitué de:

Message S -* D: Msg, M (3.14)

Msg = S, D, C, Cpt (3.15)

Le code M garantit en effet l'intégrité des informations transmises.

Si un opposant, que nous appellerons Oscar, intercepte et modifie le message, le CC s'en rendra compte, à moins qu'Oscar n'arrive à modifier le résultat

M produit par la fonction de hachage pour que celui-ci corresponde à la modification effectuée sur Msg, ce qui voudrait dire qu'Oscar possède la clé Km.

L'authentification est également garantie par la connaissance de Km étant donné que l'adresse de la source et de la destination sont intégrées au code, Oscar ne peut modifier ces adresses sans que le code M ne soit erroné si celui-ci ne dispose pas de Km.

Le récepteur du message, ici le CC calcule le code M' = H(Msg)Km, si M' =6 M le CC va rejeter le message. Si M' = M, cela veut dire que le message n'a pas été altéré et il va donc extraire C et le déchiffrer. Notons, que le même algorithme peut être utilisé pour le chiffrement et la fonction de hachage sans affecter la sûreté du système [17], ce qui peut à nouveau être intéressant pour limiter la quantité de ressources à installer sur le système embarqué.

3.4.3 Calcul du bénéfice au système

SASES utilise une fonction pour calculer le bénéfice en sécurité accru par une tâche Ti:

>i:SLi = bi

k?{a,c,i}

wk i sk (3.16)

i

bi = Pi P , c.-à-d. le nombre de travaux de cette tâche que l'on peut ordonnancer jusqu'à l'hyperpériode P, wki est le poids spécifié par l'utilisateur pour le service k de la tâche Ti et sk i est le niveau de sécurité du ke service qui est identique pour tous les travaux de la tâche.

L'ordonnancement est faisable si:

- les niveaux de sécurité respectent les bornes 3.18;

- les échéances dij= j. Pi sont toutes respectées. Cela est le cas si les
tâches sont planifiées avec une politique d'ordonnancement EDF et que
le facteur d'utilisation U < 1 (cf. équation 3.19), suivant l'hypothèse selon
laquelle les tâches sont périodiques et à échéance sur requête. Cela re-
vient à dire que la somme des rapports entre le temps d'exécution total
(temps nécessaire pour la sécurisation seci ajouté au temps d'exécution
normal Ci) et la période Pi de chaque tâche n'est pas supérieur à 100%.
Le problème peut donc s'écrire comme une maximisation d'un niveau de sécu-
rité global (SV), étant la somme des niveaux de sécurité des n tâches formant

le système:

maximisation de SV = >.n SLi = >.n >.bi wkisk i (3.17)

i=1 i=1 k?{a,c,i}

à condition que min(Sk i ) sk i max(Sk i ) (3.18)

etque U=

>.n

i=1

[ ]

Ci + > k?{a,c,i} seck i (sk i )

1. (3.19)

Pi

3.4.4 L'algorithme SASES

Nous allons montrer maintenant comment, à partir des caractéristiques des tâches (Ci, Pi, Li, Si), le niveau de sécurité est augmenté le plus possible en tenant compte du poids wki de la sécurité de chaque service de celles-ci. Nous avons repris l'idée de l'algorithme présenté dans [26] en le modifiant quelque peu pour le rendre plus compréhensible (voir algorithme 2).

Description de l'algorithme

1. Premièrement les niveaux de sécurité de tous les services pour chaque tâche sont initialisés à leurs valeurs minimales.

2. Une structure EnsembleServices contient tous les services qui doivent encore être traités, chaque service est représenté par le numéro de la tâche et le service de sécurité concerné (a = authentification, c = confidentialité, g =intégrité).

3. Si l'utilisation (définie en 3.19) est supérieure à 1 le système n'est pas ordonnançable même avec le minimum de sécurité.

4. On sélectionne le service de sécurité k' de la tâche i' qui possède le poids

wk'

i'le plus important et qui consomme le moins de temps en l'augmentant d'un niveau de sécurité (Äsk'

i' ).

5. Si le fait d'augmenter le niveau de sécurité de ce service de Äsk'

i' ne lui fait pas dépasser son niveau maximal de sécurité et que le système reste ordonnançable avec cette augmentation (U 1), on augmente le niveau de celui-ci, autrement on retire sk'

i' de EnsembleServices de façon à ce qu'il ne soit plus choisi à l'étape 4.

6. L'algorithme prend fin lorsque l'ensemble des services est vide, c.-à-d. lorsque tous les services de chaque tâche auront été traités.

EnsembleServices ? o Pouride1 ànfaire

Pour chaque k E {a, c, g} faire

}

sk i ? min {Sk i

EnsembleServices ? EnsembleServices u {i, k} Fin Pour

Fin Pour

Si ( 2n [Ci+>1k?{a,c,g} seck i (sk

Pi < 1) Alors

i )]

i=1

Tant que (EnsembleServices =6 o) faire


· Choisir {i', k'} dans EnsembleServices tel que:

wk'

i' Äsk'

i' / [seck'

i' (sk'

i' + Äsk'

i' ) - seck'

i' (sk'

i' )] i )] }

= maxiE[1,n], kEa,c,g {wk i Äsk i / [seck i (sk i + Äsk i ) - seck i (sk

}

Si (sk'

i' + Äsk'

i' < max {Sk'

i'

ET 2n [Ci+>1 k?{a,c,i} seck

Pi < 1 avec sk'

i (sk i )] i' = sk'

i' + Äsk'

i' ) Alors

i=1

//On augmente le niveau de sécurité

sk'

i'? sk'

i' + Äsk'

i'

Sinon

EnsembleServices ? EnsembleServices \ {i', k'}

//de façon à ne plus en tenir compte dans le choix
· Fin Si

Fait

Sinon

//pas ordonnançable avec le niveau de sécurité le plus bas

Fin Si

Algorithme 2: Algorithme SASES

3.4.5 Conclusion

Nous avons présenté dans cette section l'algorithme SASES permettant de sélectionner un ensemble de moyens cryptographiques pour sécuriser les données de chaque tâche tout en respectant leurs échéances. L'algorithme que nous exposerons dans la section suivante est une autre façon de choisir les services les plus appropriés de manière plus efficace que le parcours effectué par l'algorithme SASES.

3.5 Méthode en graphe de services

3.5.1 Modélisation

Man Lin et Laurence T.Yang dans leur étude [12], représentent chaque principe de sécurité par un groupe de services. Par exemple, il y aura un groupe pour les différentes méthodes d'authentification, un autre pour les algorithmes de chiffrement,. . . Ces différentes méthodes seront ici appelées « services ».

De la même manière que dans l'algorithme SASES, chaque groupe i possède un certain poids Wi par rapport aux autres, et dans chacun des Ng groupes, les services ont une certaine qualité, autrement dit une valeur qui leur est propre. Nous noterons Qij, la qualité du service j dans le groupe i.

Les services sont représentés comme étant les noeuds d'un graphe, le noeud < i,j > représentant le service j du groupe i. Bien entendu, un seul service maximum dans chaque groupe peut-être sélectionné à la fois de manière à former un ensemble de services qui seront utilisés pour sécuriser le système, nous appellerons cet ensemble un chemin. Dans la figure 3.5, un exemple de chemin P = {< 1,1 >; <2,2>; <3,2 >} est représenté en gris.

FIG. 3.5 - Représentation du chemin P

Les services sont classés par ordre décroissant par rapport à leur qualité (par exemple: Q1,1 > Q1,2). Nous noterons secik le temps nécessaire à la sécurisation de la tâche Ti par le service sélectionné dans le groupe k. Lorsque la qualité d'un groupe est diminuée, secik sera moins important également, ce qui permet donc d'ajuster la qualité de service pour respecter les échéances.

La vérification du respect des échéances se fait sur base de la condition d'ordonnançabilité nécessaire et suffisante imposée par EDF, selon laquelle la somme des facteurs d'utilisation des n tâches dont est composé le système doit

être inférieur à 100% (voir équation 3.20).

Xn
i
=1

Ci + PNg

k=1secik = 1 (3.20)

Pi

Nous tenons compte dans le calcul du facteur d'utilisation, du temps d'exécution normal Ci et de la mise en place de la sécurité pour chaque groupe.

Le but est de sélectionner dans chaque groupe, le service qui permettra de maximiser la qualité globale Q(P) du chemin P tout en respectant les échéances.

Q(P)= XNg Qi (3.21)

i=1

De plus, la qualité Qi (la valeur inscrite dans le noeud) d'un groupe doit être supérieure à une borne inférieure Min Qi, la qualité minimale acceptée pour chaque groupe de services.

Qi> Min Qi (3.22)

3.5.2 Recherche dans le graphe

Il est proposé dans [12], une méthode de recherche par un parcours de graphe en profondeur ( depth-first ») qui permet de choisir les meilleurs services permettant de garantir que le système soit toujours ordonnançable en utilisant ceux-ci. La recherche est améliorée afin de ne pas devoir tester toutes les possibilités de manière exhaustive. Nous allons présenter maintenant ces améliorations du parcours du graphe.

En effet, il existe trois moyens d'élaguer le graphe afin de restreindre l'espace de recherche:

1. Premièrement, sont supprimés dans chaque groupe i, les services qui ne respectent pas la contrainte de qualité minimale acceptable, soit les noeuds < i,j> tels que Qij < MinQi (voir figure 3.6).

2. En parcourant ensuite le graphe par un parcours en profondeur, si un chemin n'est pas faisable (voir figure 3.7[b]), c.-à-d. s'il ne respecte pas la contrainte d'ordonnançabilité 3.20, on sait que l'ordonnançabilité ne sera

FIG. 3.6 - Elagage par rapport à la valeur minimale de qualité de service

pas non plus respectée avec un chemin plus long formé par le même préfixe, on peut donc élaguer les chemins ([d], [e] et [f]). On continue alors le parcours en profondeur comme s'il n'y avait pas de noeuds fils (voir [c]), la qualité de service du groupe courant est donc baissée, autrement dit on passe directement au noeud suivant dans le groupe, sans être descendu dans le graphe.

FIG. 3.7 - Elagage en fonction du test de faisabilité

3. Il est encore possible de supprimer des branches en évitant d'analyser des chemins qui ne donneront pas une meilleure qualité de service. Soit un chemin P de longueur Ng faisable, avec une qualité de service Q(P) accrue par celui-ci.

- On remarque que diminuer la qualité du dernier groupe Ng sur P ne mènera pas à une augmentation de Q(P) (dans l'exemple présenté à la

figure 3.8 Ng = 4). On poursuit alors le parcours en profondeur en remontant au niveau (groupe) Ng - 1 et en diminuant la qualité de service à ce niveau (voir figure 3.8[a']) pour tester à nouveau le niveau Ng par après.

- Lorsque la qualité Qi de tous les niveaux i entre L et Ng est au maximum et que le chemin est faisable, on peut directement remonter au niveau L - 2 et continuer le parcours pour essayer de trouver un meilleur chemin.

Dans la figure 3.8, sont représentés 3 exemples de chemins faisables ([a], [b] et [c]) et les chemins analysés respectivement juste après ceux-ci ([a'], [b'], [c']) dans l'ordre du parcours en profondeur du graphe.

Pour [c], la recherche se termine car il n'y a plus moyen d'augmenter la qualité de service.

FIG. 3.8 - Elagage en fonction de la qualité de service

3.5.3 Conclusion : Comparaison avec SASES

Remarquons que contrairement à l'algorithme SASES, cette méthode attribue le même algorithme cryptographique (service) dans un groupe pour toutes les tâches. Cela rend le calcul plus rapide mais est selon nous moins performant au niveau de la valeur en sécurité du système étant donné que lorsque l'algorithme augmente la sécurité de toutes les tâches en même temps, il est probable que le système ne soit plus ordonnançable et donc qu'on doive baisser le niveau de sécurité de toute les tâches de manière à ne rater aucune

échéance. Cela n'aurait sans doute pas été le cas en modifiant la qualité de service tâche par tâche. Malgré sa complexité supérieure, SASES permet une granularité plus importante au niveau de la sécurité et est donc selon nous plus approprié à ce problème d'optimisation de la sécurité.

3.6 Conclusion

Nous avons dans ce chapitre développé plusieurs méthodes existantes dans la littérature permettant de choisir le meilleur moyen de sécuriser le système, nous avons décrit les algorithmes utilisés, les points forts et les points faibles de ces différentes techniques.

Comme nous l'avons fait remarquer dans ce chapitre, le fait d'établir le niveau de sécurité par rapport au débit des algorithmes n'est pas adapté, il serait préférable de garder le même algorithme et de faire varier la taille de la clé ou le nombre de tours de l'algorithme cryptographique selon le temps disponible de manière à représenter la valeur en sécurité comme une évolution de la sécurité en fonction du temps et d'attribuer une récompense à cette évolution.

Nous nous sommes donc intéressés plus particulièrement aux travaux de H. Aydin [3] qui a développé une méthode qui attribue une récompense selon un supplément de temps d'exécution de manière à profiter au maximum du temps disponible pour exécuter une partie optionnelle qui sera dans notre cas destinée à la sécurité.

Le reste de ce mémoire sera consacré à cette méthode (chapitre 4) et à la façon dont nous avons implémenté celle-ci (chapitre 5) de manière à en évaluer les performances.

Chapitre 4

Récompense et sécurité

4.1 Introduction

Comme nous l'avons défini au chapitre 2, chaque tâche dans un système temps réel doit se terminer avant une certaine échéance en produisant le résultat attendu. Si le système n'est pas ordonnançable, il faudra alors choisir des parties qui resteront non exécutées de façon à ce que toutes les tâches respectent leur échéance. Le système peut ne plus être ordonnançable suite à une demande de précision trop fine du résultat, par exemple, lors du raffinement de la qualité d'une image ou pour citer notre cas, si le niveau de sécurité attribué aux différentes tâches est trop important.

La communauté de recherche dans ce domaine, a mis au point plusieurs techniques. Nous pouvons par exemple « sauter » [11] entièrement certains travaux d'une tâche donnée suivant un facteur de saut qui détermine combien de travaux sur un certain nombre de travaux de cette tâche peuvent être abandonnés par l'algorithme d'ordonnancement.

Mais nous supposons dans ce cas, que les données produites par une tâche qui n'a pas été exécutée entièrement ne sont d'aucune utilité. Or, dans de nombreux secteurs comme le traitement d'images, de la voix, le contrôle de robot, et bien d'autres encore, une solution partielle est souvent suffisante. Pour résoudre notre problème de sécurité, nous pourrions également utiliser de telles techniques qui peuvent nous apporter en un temps acceptable, une sécurité partielle mais suffisante.

4.2 Reward-Based Scheduling

4.2.1 Introduction

Ce chapitre est basé sur les travaux de H. Aydin [3]. Nous n'envisagerons dans cette section que le cas des tâches périodiques avec un système temps réel à échéance sur requête.

Oi

?

mi + tij

Mi

mi + oi

0 mi

Ti

FIG. 4.1 - Partie obligatoire et optionnelle d'un travail 'i-ij de la tâche Ti

Comme son nom l'indique, le principe de cet ordonnancement est d'associer une récompense à l'exécution d'une tâche.

Chaque tâche Ti peut être décomposée en une sous-tâche (partie) obligatoire (Mi) et une sous-tâche optionnelle (Oi) (voir figure 4.1).

La longueur de la partie obligatoire de chaque travail de Ti sera notée mi et oi désignera la longueur maximum que peut prendre la partie optionnelle d'un travail de Ti. Il est primordial que la partie obligatoire se termine avant l'échéance, et nous attribuerons à l'exécution de la partie optionnelle une fonction de récompense non-décroissante.

Ce type de technique est basé sur les modèles IRIS [5] (« Increasing Reward with Increasing Service »), ce qui signifie en reprenant notre cas, que plus un travail s'exécutera longtemps, plus la qualité de la sécurité s'en verra améliorée et donc plus la récompense sera importante.

Dans le modèle IRIS, la distinction entre partie obligatoire et optionnelle n'est pas faite, on permet uniquement aux tâches d'obtenir une augmentation de la récompense en fonction de l'allongement du temps d'exécution sans imposer de borne supérieure pour celui-ci exceptée bien entendu l'échéance de la tâche.

De manière à ne pas continuer à sécuriser une tâche au-delà d'un certain niveau de sécurité maximum, ce qui risquerait de nous empêcher de sécuriser les autres tâches efficacement, nous remarquons qu'il serait préférable dans le cadre de la sécurité, d'utiliser également une borne supérieure pour le temps d'exécution de la partie optionnelle, ce qui est possible avec cette version « Reward Based Scheduling ».

Nous allons donner priorité à la partie obligatoire, la partie optionnelle sera ensuite exécutée en utilisant le temps restant avant l'échéance si la partie obligatoire courante d'aucune tâche n'est encore présente dans le système.

Conditions d'ordonnançabilité et d'optimalité

Un niveau minimum de sécurité sera imposé et sera pris en compte dans la partie obligatoire.

Une condition nécessaire pour l'ordonnançabilité du système est que les parties obligatoires et optionnelles se terminent toutes avant l'échéance de manière à fournir une sortie minimale acceptable.

Mais il faudra également essayer de maximiser le plus possible la moyenne pondérée des récompenses obtenues par les différentes tâches afin de rendre cet ordonnancement optimal.

Malheureusement, ces deux objectifs sont la plupart du temps antagonistes étant donné que si l'on désire respecter les échéances, il faut généralement sacrifier en partie la sous-tâche optionnelle et donc la récompense s'en verra diminuée.

4.2.2 Une fonction de récompense

Chaque tâche se voit attribuer une fonction de récompense qui sera utilisée pour tous ses travaux. L'abscisse représente le temps d'exécution utilisé par la partie optionnelle du travail, l'ordonnée retourne la récompense obtenue avec ce temps. Plusieurs types de fonctions de récompense sont modélisables selon le type d'application qui nous intéresse.

Types de fonctions: Convexe:

Définition fonction convexe [23]: Une fonction f d'un intervalle I vers < est dite convexe si, V x ety E I et tout t dans [0, 1] on a:

f(tx + (1 - t) y) = t f(x) + (1 - t) f(y).

De manière géométrique, cela correspond à un graphe dont la partie
« bombée » est tournée vers le bas (ex : f(x) = x2). Il en découle que,

plus le traitement est important, plus la façon dont la récompense augmente sera importante.

Concave:

Définition fonction concave [23]: On dit que la fonction f est concave si la fonction opposée est convexe. Cela équivaut à : V x et y E I et tout t dans [0,1] on a:

f(tx + (1 - t) y) = t f(x) + (1 - t) f(y).

De manière géométrique, la partie bombée est tournée vers le haut, donc la partie la plus importante est située au début de l'exécution, c'est à ce moment que la récompense augmentera le plus rapidement, ce qui est par exemple surtout très utile dans le traitement d'images, où l'amélioration de la qualité est importante au début et faible par la suite.

Linéaire : (qui sont des cas particuliers de fonctions concaves et convexes) la récompense augmente uniformément pendant l'exécution de la partie optionnelle.

Choix du type de fonction

FEISTEL propose en 1973 [15], les paramètres et principes de base pour construire un bon algorithme de chiffrement. Parmi ceux-ci, on retrouve la taille des blocs, la taille de la clé, le nombre de tours dans l'algorithme, la complexité d'un tour et la complexité de génération des sous-clés pour chaque tour. Augmenter ces paramètres élève la sécurité mais diminue la vitesse de chiffrement. Il pourrait donc être intéressant d'augmenter ceux-ci lorsque l'ordonnancement le permet.

Nous choisirons ici d'utiliser des fonctions convexes qui se révèlent être intéressantes pour modéliser la valeur en sécurité d'un système en fonction du temps dépensé pour mettre celle-ci en place.

En effet, comme nous l'avons vu au chapitre 3, la valeur en sécurité d'un système dépend de la dif~culté à « casser » cette sécurité. Plus la longueur de la clé utilisée pour le chiffrement est importante, plus une recherche exhaustive sur l'ensemble des clés consommera du temps avant de trouver la clé correcte. En rajoutant 1 bit à la clé l, l'espace de toutes les clés possibles et donc le temps mis par une attaque exhaustive sont multipliés par deux.

Soit une récompense de 2 pour une clé de 1 bit, la récompense vaudra donc 2l pour une clé de l bits, où la récompense représente le nombre de clés

à tester au pire cas. Il reste donc à calculer le temps de chiffrement en fonction de la longueur de la clé et de la taille des données à chiffrer. A l'aide de la librairie crypto++ [4], nous avons calculé la durée de chiffrement d'une quantité de données de 1Mo avec les trois variantes officielles d'AES (voir tableau 4.1). Il est malheureusement impossible de modifier la longueur de la clé bit à bit car l'algorithme n'a pas été prévu pour cela à la base : le nombre de tour, la taille des clés et la taille des blocs sont dépendants les uns des autres.

Longueur des clés

Temps de chiffrement (en ms) pour 1 Mo de données

Ri]ndael / 128 bits

38,16

Ri]ndael / 192 bits

41,93

Ri] ndael / 256 bits

46,31

TAB. 4.1 - Calcul de la durée de chiffrement selon la longueur de la clé

Sur la figure 4.2 est représentée graphiquement l'évolution de la valeur en sécurité du système en fonction du temps obtenu à partir du tableau 4.1. L'amélioration de la sécurité est si importante que sur le graphique, la différence de récompense n'était plus faite entre la version 128 et 192 bits par rapport à la version 256 bits, nous avons donc opté pour un graphique à l'échelle semilogarithmique. Nous obtenons bien une fonction convexe exponentielle pour représenter l'évolution de la récompense en fonction du temps.

FIG. 4.2 - Valeur en sécurité du système en fonction du temps

4.2.3 Modélisation du système

Nous considérons un ensemble S de n tâches périodiques: T1, T2,..., T n de période Pi V i E [1, n] à échéance sur requête et à départ simultané.

L'ordonnancement sera testé jusqu'à l'hyperpériode P (définie dans la chapitre 2), ce qui signifie que sur l'intervalle [O, P), chaque tâche Ti enverra bi = Pi P travaux. Dans ce modèle, chaque tâche est donc constituée de bi travaux, nous nous référons au je travail de Ti par ôij. Ici, nous supposerons que les tâches sont toutes indépendantes l'une de l'autre et prêtes à l'instant t = O.

Lorsque l'exécution de la partie obligatoire Mi est achevée, la partie optionnelle devient disponible pour l'exécution et s'exécute aussi longtemps que l'ordonnanceur le permet avant l'échéance.

4.2.4 Calcul de la récompense moyenne du système

Comme nous l'avons dit dans la section 4.2.2, nous allons associer une fonction de récompense à la partie optionnelle, que nous noterons Ri(tij) et qui retournera la récompense accrue par le travail ôij lorsqu'il reçoit tij unités d'exécution au delà de sa partie obligatoire.

{fi(tij) si O = tij = oi

Ri(tij) = (4.1)

fi(oi) sinon,

fi est une fonction réelle continue positive non-décroissante et oi représente la longueur maximale de la partie optionnelle.

mi+oi représente dans notre cas le temps maximum nécessaire pour sécuriser complètement les informations. Au delà de ce seuil (tij> oi), la récompense ne sera plus augmentée (voir équation 4.1) et la priorité sera alors donnée à un travail qui n'a pas encore suffisamment sécurisé ses informations.

La récompense moyenne de Ti sera définie comme:

P

bi j=1 Ri(tij)
REWi = , (4.2)
bi

C'est-à-dire, la moyenne des récompenses accrues par chaque travail de la tâche jusqu'à l'hyperpériode (= P = ppcm{P1, P2, ... Pn}), bi étant le nombre de fois que l'on peut placer la période de la tâche Ti dans cet intervalle (= bi = Pi P ).

Pour calculer la moyenne pondérée de la récompense (REWW), nous additionnons les récompenses REWi de chaque tâche Ti à laquelle nous associerons un certain poids selon l'importance de la partie optionnelle de la tâche (voir équation 4.3). Nous intégrerons par la suite ce poids directement dans la fonction fi(tij) sous forme d'un coefficient ki.

REWW = Xn wiREWi, (4.3)

i=1

4.2.5 Approche Mandatory-First (parties obligatoires d'abord)

Dans cette approche, le principe est comme nous l'avons mentionné plus haut, de donner priorité aux parties obligatoires. Nous n'exécuterons les parties optionnelles que si la partie obligatoire de chaque tâche a été complètement exécutée.

Diverses techniques utilisant l'approche Mandatory-First existent. Nous pouvons les classer en approches statiques ou dynamiques.

Utilisation avec une méthode statique

Il existe plusieurs méthodes statiques, nous pouvons citer parmi celles-ci « Rate Monotonic » et « Least Utilization ».

Soit l'ordonnanceur donne une plus haute priorité :

- aux parties optionnelles de la tâche ayant la plus petite période, cette méthode est appelée « Rate Monotonic » (RM),

- soit en utilisant une méthode « Least Utilization » (LU) qui assigne les priorités statiquement aux parties optionnelles selon leur facteur d'utilisation (calculé par: (oi - mi)/Pi). Les tâches possédant un facteur d'utilisation plus faible reçoivent une priorité plus importante [6].

Utilisation avec une méthode dynamique

« Best Incremental Return » (BIR) est une des meilleures méthodes dynamiques car à chaque unité de temps, l'algorithme sélectionne la partie Oi qui augmente le plus la récompense en faisant le calcul fi(tij+ Ä) - fi(tij), où Ä est la

durée minimum que l'ordonnanceur attribuera à une tâche optionnelle. La fonction qui fournit la plus grande différence est élue et la tâche associée à cette fonction peut exécuter sa partie optionnelle.

BIR est la méthode la plus ef~cace en terme de récompense mais est très gourmande en calcul et n'est donc pas utilisée en pratique excepté comme étalon, pour comparer les performances d'autres algorithmes dans les simulations. De plus, BIR n'est pas optimal étant donné qu'il ne tient pas compte du temps restant avant les échéances.

Nous pouvons citer d'autres méthodes d'ordonnancement dynamiques telles « Earliest Deadline First » (EDF) qui donne une priorité plus importante à l'exécution des parties optionnelles de la tâche ayant l'échéance la plus proche ou « Least Laxity First » (LLF) qui sélectionne un des travaux rij prêt à l'instant courant t dont la laxité £ij(t) est la plus faible. La laxité se calcule par la formule 4.4.

£ij(t) = dij(t) - remij(t) (4.4)

dij(t) est le temps restant avant l'échéance du travail rij et remij(t) est le temps d'exécution restant de celui-ci calculé à l'instant t. Un travail qui a une échéance proche et qui a encore un temps important d'exécution devant lui est donc plus facilement sélectionné par LLF.

« Least Attained Time » (LAT) est une méthode intéressante dans le cas de fonctions concaves car elle donne une plus grande priorité aux parties optionnelles des tâches ayant utilisé le moins de temps processeur (priorité = 1/tij), ce qui permet d'équilibrer le temps d'exécution des parties optionnelles.

Résultats de simulations

Dans son étude [3] sur le « Reward-Based Scheduling », H. Aydin a effectué des tests sur ces différentes méthodes et a constaté que:

- Plus l'utilisation du système par les parties obligatoires augmente, plus l'écart s'accentue entre la récompense accrue par l'algorithme optimal et celle accrue par les méthodes évoquées plus haut (statiques ou dynamiques) utilisées avec une approche Mandatory-First.

Cela s'explique par le fait que ces méthodes perdent des occasions d'exécuter des parties optionnelles avantageuses au niveau de la récompense en favorisant constamment les parties obligatoires. Nous reviendrons de manière plus détaillée sur l'algorithme optimal par après.

- La récompense accumulée par une méthode LAT se rapproche plus de la

méthode BIR lorsque les fonctions de récompense sont concaves. Comme LAT, des fonctions concaves favorisent des temps d'exécution équilibrés pour les parties optionnelles.

- L'écart entre la méthode optimale et les approches Mandatory-First est moins important lorsque les tâches possèdent des fonctions linéaires de récompense.

En effet, les approches Mandatory First perdent des occasions d'exécuter des premiers slots valables de parties optionnelles. Or, l'exécution d'un premier slot de partie optionnelle est plus important si la fonction est concave vu que c'est à ce moment que la récompense augmente le plus.

Exemple d'ordonnancement

Schématisons un exemple basé sur un cas d'école afin de montrer les limites d'un algorithme Mandatory-First.

Considérons deux tâches où P1=4, m1=1, o1=1, P2=8, m2=3 et o2=5 avec des fonctions de récompenses f1(t1) = k1t1 pour T1 et f2(t2) = k2t2 pour T2, k1 et k2 étant donc les coefficients dans les fonctions linéaires de récompense de T1 et T2 respectivement. Nous supposerons que k2 est négligeable par rapport à k1 (c.-à-d. k1 » k2).

Avec une approche Mandatory-First nous obtenons donc l'ordonnancement représenté dans la figure 4.3. Nous observons que quelle que soit l'assignation de priorité (statique ou dynamique) utilisée dans cet exemple avec l'approche Mandatory-First, le processeur sera occupé dans cet exemple par les parties obligatoires jusqu'à l'instant t = 5. Sur l'intervalle inoccupé [5, 8] l'algorithme choisira de planifier O1 complètement (soit une unité) étant donné qu'il fournit une plus grande récompense que O2. Le temps restant (2 unités) sera consacré àO2.

Un algorithme optimal produirait cependant l'ordonnancement reproduit sur la figure 4.4. L'exécution de M2 est retardée d'une unité afin de permettre l'ordonnancement de O1 sur la première période de T1, ce qui nous fournirait une plus grande récompense que celle fournie par l'exécution d'une unité supplémentaire de O2 et les échéances des parties obligatoires sont toujours respectées. Comme k1 » k2, on peut conclure que la récompense accrue par l'algorithme Mandatory-First ne peut valoir qu'environ la moitié de celle accrue par l'algorithme optimal.

M1

o o

M1

O1

1 4 5 6

T1

O2

o

6 8

M2

1 4

T2

FIG. 4.3 - Ordonnancement généré par un algorithme Mandatory-First

T1

T2

1 2

4 5 6

M2

2 4

FIG. 4.4 - Ordonnancement généré par un algorithme optimal

M2

6 7

M1

o o

o

O1

O2

M1

O1

Pour poursuivre notre comparaison entre le « meilleur » algorithme MandatoryFirst et un algorithme optimal, nous pouvons prouver qu'au pire cas, la proportion de récompense accumulée par l'approche Mandatory-First par rapport à la récompense obtenue par un algorithme optimal peut être arbitrairement proche de zéro si on prend un r arbitrairement grand dans le théorème 1 issu de l'article d'Aydin [3]. Il n'y a donc pas de garantie de performance des méthodes Mandatory-First.

Théorème 1 Il existe une instance du problème d'ordonnancement périodique basée sur la récompense pour lequel la proportion:

Récompense du meilleur algoritthme MandatorryFirss 2

= .

Récompense d un algoritthme optimal r

pour n'importe quel entier r = 2.

des fonctions de récompense respectivement: f1(t1) = k1 . t1, f2(t2) = k2. t2 et posons r = P2/P1, k1/k2 = r. (r-1), m2 = 1 2 .(r.o2) et

P1 = m1 + o1 + r

= m1 +

m2

m2 r-1

ce qui implique que o1 = m2

r(r-1) et que P2 est l'hyperpériode du système.

Suivant ces hypothèses, nous pouvons observer qu'à chaque période de T1, l'algorithme peut exécuter en plus de la partie obligatoire M1, une partie de O1 et/ou M2.

Avec une approche Mandatory-First, le processeur exécute uniquement les parties obligatoires jusqu'à l'instant t = P2 - P1 + m1 (voir figure 4.5). Le temps restant, est utilisé pour ordonnancer tout d'abord O1 entièrement car k1 > k2 (k1 =r.(r-1).k2),etensuitet2= m 2 r = o2 2 unités de O2.

Nous calculons donc la récompense moyenne obtenue grâce à l'algorithme Mandatory-First:

f1 (o1)

RMFA=REW1+REW2=

+ f2(t2),

r

d'après les formules 4.2 et 4.3 en sachant que P1 /P = P1 /P2 = 1 /r et P2/P = 1

FIG. 4.5 - Un ordonnancement généré par un algorithme « Mandatory-First ».

Un algorithme optimal, aurait choisi quant à lui de retarder l'exécution de M2 de o1 unités à chaque période de T1. Cela permet d'accroître la récompense fournie par T1 à chaque travail. L'ordonnancement résultant est représenté sur la figure 4.6 et la récompense moyenne en utilisant un tel algorithme serait:

= f1(o1),

r . f1(o1)

ROPT = REW1 + REW2 = REW1 = r

FIG. 4.6 - Un ordonnancement généré par un algorithme optimal.

Nous obtenons donc la proportion sur la récompense entre les deux algorithmes (Vr ~ 2) en remplaçant les:

RMFA

+

f2 (t2)

1

= r

ROPT

f1(o1)

k2 t2

+

1

= r

k1 · o1

1

= r

2

= .

r

1

+r(r - 1)

m2

r

r(r - 1)

m2

Il suffit ensuite de choisir un système avec une constante r tendant vers oc pour que cette proportion tende vers 0.

ut

4.2.6 Optimalité

Dans cette section nous allons formaliser le problème de maximisation de la récompense sur un ordonnancement de tâches périodiques. Notre objectif, est de trouver les valeurs optimales tij dans l'équation 4.5 qui maximiseront cette récompense. Nous verrons par la suite qu'il peut être intéressant de poser tij = tik Vi, k mais nous analyserons également le cas plus général (où i,j, k tel que tij=6 tik). L'équation 4.5 est construite à partir des équations 4.2 et 4.3, en supposant que le poids wi de la tâche Ti est intégré dans la fonction de

récompense Ri(tij).

Pi

P

>2n
i
=1

>2bi

j=1

Ri(tij), (4.5)

Il faudra ajouter à cette équation quelques contraintes.

1. Il faut imposer que le temps utilisé par toutes les parties obligatoires et optionnelles de chaque tâche jusqu'à l'hyperpériode (P) ne dépasse pas le temps processeur disponible sur celle-ci. Autrement dit, que

>2n >2bi (mi + tij) P.

i=1 j=1

(réécrit d'une autre manière dans l'équation 4.7)

Malheureusement, cette contrainte est nécessaire mais en aucun cas suf-
fisante pour établir la faisabilité du système avec les valeurs des mi et

tij.

2. Il faut donc exprimer la faisabilité totale avec les valeurs mi et tij. Mais à notre connaissance, il n'existe pas dans la littérature, de formule mathématique permettant de tester la faisabilité avec des temps d'exécution différents pour chaque travail d'une même tâche, ce qui peut être ici le cas puisque tij pourrait varier d'un travail ôij de Ti à l'autre. Nous effectuerons ce test en simulant l'ordonnancement jusqu'à l'hyperpériode.

3. Un temps négatif n'a pas d'interprétation physique et tij dépassant oi n'est d'aucune utilité étant donné que la récompense n'est plus augmentée. Ce qui nous amène à poser 0 tij oi, nous pouvons donc remplacer Ri() par fi() (voir équation 4.1).

Reprenant toutes ces contraintes, le problème que nous appellerons REW-PER est donc de trouver les valeurs optimales pour les tij :

maximisant

>2n
i
=1

1
bi

>2bi

j=1

fi(tij) (4.6)

à condition que >2n bimi + >2n >2bi tij P (4.7)

i=1 i=1 j=1

0 tij oi i=1, ...,n j=1, ... , bi (4.8)

Il existe un ordonnancement faisable avec les valeurs {mi} et {tij}.

(4.9)

Cette dernière condition 4.9 revient à dire que le système avec les valeurs {mi} et {tij} doit être ordonnançable avec EDF sur l'intervalle [0, P). Nous obtiendrons donc un ordonnancement faisable avec les valeurs mi et des valeurs optimales pour tij.

Notons que si la contrainte 4.7 n'est pas satisfaite, la contrainte 4.9 ne l'est pas non plus. Nous aurions donc pu nous limiter à poser la contrainte 4.9, mais 4.7 reste intéressante pour tester la faisabilité.

En effet, lorsque 4.7 n'est pas satisfait, nous savons que le système n'est alors pas ordonnançable et il n'est donc pas nécessaire d'effectuer la simulation jusqu'à l'hyperpériode pour tester son ordonnançabilité.

H. Aydin nous démontre par le théorème 2 dans son article [3] comment il est possible de supprimer la contrainte 4.9 en utilisant des temps d'exécution des parties optionnelles tij égaux à chaque appel (tij = tik ?i, k). Cette démonstration est applicable uniquement dans le cadre d'une utilisation de fonctions de récompense concaves ou linéaires (qui sont des cas particuliers de fonctions concaves). Nous remarquons intuitivement que si les fonctions sont convexes, il est plus intéressant de prolonger l'exécution de tij pour certains travaux puisque la récompense augmente de manière exponentielle.

Théorème 2 Soit un exemple donné du problème REW-PER, il existe une solution optimale où les parties optionnelles d'une tâche Ti reçoivent le même temps d'exécution à chaque travail, c'est-à-dire, tij = tik 1 <j < k < Pi P . De plus, n'importe quelle méthode d'ordonnancement périodique temps-réel qui peut utiliser entièrement le processeur (EDF , LLF , RMS-h) peut être utilisée pour obtenir un ordonnancement faisable avec ces assignations.

Démonstration: Abandonnons la contrainte 4.9 sur la faisabilité et appelons MAX-REW le problème REW-PER sans la contrainte de faisabilité.

Assertion 1 Posons tijcomme une solution optimale de MAX-REW, 1 < i < n 1 <j <Pi P = bi. Alors, t' ij, en posant

ti1 + ti2 + ... + ti bi

t' i1 = t' i2 = ... = t' i bi = t' i = bi

1<i<n 1<j<bi, est aussi une solution optimale de MAX-REW.

1. t ij satisfait les contraintes du problème MAX-REW (4.7 et 4.8) si tijles satisfait déjà.

En effet,

- La contrainte 4.7 est toujours respectée avec les valeurs t ij car >Ibi

j=1tij=

>I

bi j=1 t ij = bit i . - La contrainte 4.8 est également respectée puisque par hypothèse tij =

oi ?j, donc maxj {tij} = oi. D'autre part, t i= maxj {tij} autrement bit i >

>I

bi j=1tij, ce qui contredirait l'hypothèse de l'assertion. Cela implique que t i = oi,iE[1,n].

2. H. Aydin démontre mathématiquement dans [3] que la récompense ne peut pas décroître en utilisant des parties optionnelles identiques, en prouvant que >Ibi

j=1fi(tij) = bifi(t i) si fi(t) est une fonction concave (incluant le cas des fonctions linéaires). Le lecteur intéressé peut consulter cette démonstration que nous avons reprise dans l'annexe A.

ut

Nous pouvons dès lors simplifier le problème MAX-REW et le réécrire comme une:

maximisation de Xn fi(ti) (4.10)

i=1

à condition que

Xn
i
=1

Pti + Pi

Xn
i
=1

P mi = Pi

Xn
i
=1

P (mi + ti) = P (4.11)

Pi

0=tij =oi i=1, ... , n. (4.12)

(mi+ti)

La contrainte 4.11 peut encore être simplifiée comme >In Pi = 1. Ce qui

i=1

Ci

est équivalent à exprimer la condition U = >In Pi = 1 nécessaire et suffisante

i=1

sur l'utilisation pour qu'une politique d'ordonnancement périodique classique (comme par exemple EDF, LLF,...) permette d'obtenir un ordonnancement faisable avec n tâches pour lesquelles le temps d'exécution Ci = mi + ti? i E [1, n]. Cela nous permet de ne pas devoir tenir compte de la contrainte de faisabilité 4.9 du problème REW-PER.

Limites de l'optimalité

Malheureusement l'optimalité avec tij = tik?j,k ne tient pas dans tous les cas. Il faudra donc poser quelques hypothèses sur le système de tâches pour

que le système fournisse une récompense optimale avec des parties optionnelles ayant des temps d'exécution identiques à chaque période, tout en satisfaisant la contrainte de faisabilité. Ces hypothèses sont démontrées à chaque fois par un contre-exemple dans l'article [3].

Récapitulons ces hypothèses :

1. Toutes les tâches doivent être à échéance sur requête, c.-à-d. que l'échéance doit être égale à la période pour chacune d'elles.

2. L'optimalité ne tient généralement pas si l'algorithme d'ordonnancement utilise une règle d'assignation statique. C'est pourquoi nous utiliserons EDF comme politique d'ordonnancement (dynamique).

3. La fonction de récompense doit être de type concave.

Nous allons maintenant voir comment résoudre le problème MAX-REW sur un système constitué de tâches périodiques à échéance sur requête avec une fonction linéaire de récompense associée à chaque tâche. Les parties optionnelles seront de même taille de manière à garantir l'optimalité (voir théorème 2) et l'ordonnancement sera basé sur une règle d'assignation dynamique des priorités, « Earliest Deadline First » (EDF). Nous respecterons donc toutes les hypothèses citées ci-dessus.

Nous nous baserons sur un algorithme présenté dans [3]. Celui-ci est optimal lorsque les fonctions de récompense sont linéaires, il nous assure donc d'obtenir la meilleure récompense possible. Cependant, cet algorithme n'est plus optimal lorsqu'une des fonctions de récompense est convexe. Il sera présenté à la section 4.2.7 et utilisé dans le chapitre suivant comme étalon afin de le comparer à des méthodes heuristiques.

4.2.7 Optimalité avec des fonctions linéaires de récompense

Soit une fonction linéaire de récompense de la forme : fi(ti) = kiti, en augmentant la valeur ti de i unités de temps, la récompense accrue augmentera de kii mais nous utiliserons bii unités de la laxité disponible d. Nous noterons par bi (4.14) le nombre de travaux de la tâche Ti qu'il est possible de placer dans l'hyperpériode. Donc, la valeur apportée au système par la tâche Ti par unité consommée sur la laxité est wi = kiÄ

biÄ = ki bi avec bi = Pi P

Définition laxité : Nous définissons la laxité d au début de l'exécution de l'al-
gorithme, comme le temps restant disponible pour exécuter des parties option-

nelles jusqu'à l'hyperpériode, soit:

d = P - Xn mi·bi (4.13)

i=1

P

avecbi= (4.14)

Pi

Nous allons donc calculer la valeur apportée wi pour chaque tâche Ti. Ensuite les tâches seront triées par rapport à cette valeur, les tâches ayant un wi plus important seront favorisées.

Posons T1 comme la tâche ayant la plus grande valeur wi, T2 la suivante, et ainsi de suite. Si le fait de choisir le temps maximal o1 pour les parties optionnelles des travaux de la tâche T1 ne nous fait pas dépasser d (b1 ·o1 = d), nous fixons le temps optionnel t1 à o1 et la laxité restante est diminuée de b1 · o1.

Ensuite, on recommence la même procédure avec la tâche T2, T3,. . . jusqu'à ce qu'à la ie étape, la laxité restante soit insuffisante pour accepter de prendre oi comme temps optionnel. Dans ce cas, la laxité restante est divisée par le nombre de travaux (bi) de Ti jusqu'à l'hyperpériode. Cette valeur est attribuée comme temps optionnel ti et toute la laxité est alors consommée, l'utilisation du processeur, parties obligatoires et optionnelles confondues (U) vaut donc dans ce cas 1, ce qui permet d'ordonnancer le système avec EDF.

Il est aussi possible que la laxité soit suffisante pour appliquer ti ? oi à toutes les tâches, U est alors inférieur à 1 et donc le système est également ordonnançable avec EDF.

Les dernières tâches de la liste triée, n'ayant pas un poids wi suffisamment important, reçoivent 0 comme temps optionnel.

Voici l'algorithme 3 exprimé de façon plus formelle, utilisé pour définir la taille optimale des ti? i E [1, n]:

Entrées : Ensemble des n tâches avec leurs caractéristiques (k, b, m, o) et l'hyperpériode P

Sorties : Vecteur des temps optionnels ti calculés pour chaque tâche Ti

Tri des tâches en fonction des valeurs wi = ki bi ; i -- 1; //Indice des tâches dans la liste triée.

d-- P-->n i= 1mi.bi;

Tant que (i n ET d> 0) faire Si (bioi < d) Alors

ti -- oi;

d-- d--bioi;

Sinon

ti -- bi d;

d--0;

Fin Si

i--i+1;

Fait

//les tâches restantes n'exécutent pas leur partie optionnelle Tant que (i n) faire

ti--0; i--i+1;

Fait

Algorithme 3: Recherche des ti optimaux - Cas linéaire

Après au plus n itérations de la première boucle, la latitude (ou laxité) d est complètement consommée.

En plus de ce parcours qui possède une complexité au pire cas en O(n), nous devons effectuer un tri dont la complexité du meilleur algorithme est en O(nlogn). La complexité de l'algorithme est donc identique à celle du tri, soit O(nlog n).

Deux types de simulation

Nous constatons, que la taille des ti peut être un nombre rationnel positif. En effet, le résultat de bi dn'est pas forcément une valeur entière.

Nous nous sommes confrontés à ce problème en réalisant l'implémenta-

tion1 de cet algorithme. En effet, le fait que les temps ne soient pas des valeurs entières nous a forcés à utiliser une technique de simulation « orientée évènement » (« Event driven ») plutôt qu'une simulation basée sur une horloge où le temps progresse de façon linéaire d'une unité à chaque tour de boucle du simulateur.

Explications:

Dans la simulation orientée évènement, chaque évènement est rajouté par une insertion triée dans une file à priorité par rapport à l'instant auquel l'évènement doit se déclencher.

Un évènement peut être:

1. l'arrivée d'un travail et l'échéance du travail précédent étant donné que nous travaillons sur des systèmes à échéance sur requête;

2. la fin d'exécution d'un travail.

Dans ce type de simulation, on fait donc uniquement appel à l'ordonnanceur lorsqu'un évènement se produit, ce qui permet de ne pas réveiller l'ordonnanceur continuellement sur des périodes oisives et sur les longues périodes durant lesquelles un travail pourrait s'exécuter sans être préempté. Nous évitons donc ainsi les tours de boucle inutiles dans lesquels aucun évènement important ne se produit à l'exception de l'augmentation de Lït unités d'exécution du travail en cours d'exécution.

Soit Lït, l'incrémentation du temps à chaque tour de boucle de l'ordonnanceur dans une simulation basée sur une horloge, le fait d'utiliser la notion d'évènements a comme avantage majeur qu'il ne faut pas choisir un Lït très petit, par exemple Lït = 0.001 afin de gérer les décimales ce qui provoquerait également des erreurs de précision.

Nous expliquerons plus en détail la simulation basée sur les évènements dans le chapitre suivant lorsque nous décrirons notre simulation complète.

4.2.8 Optimalité avec des fonctions convexes de récompense

Dans le cas où les fonctions de récompense sont de type convexe, des temps d'exécution optimaux pour les parties optionnelles sont beaucoup plus complexes à obtenir au niveau du temps de calcul. H. Aydin [3] nous démontre

1Le code C++ de la simulation et de l'algorithme 3 sont fournis dans l'annexe B (méthode linOPT())

que c'est un problème NP-difficile même lorsque les parties optionnelles sont identiques d'une instance à l'autre.

Problème NP-difficile, NP-complet

Les problèmes sont séparés en plusieurs classes de complexité, citons parmi celles-ci les 2 classes les plus connues :

- Classe P : Le problème peut être résolu de manière déterministe en un temps polynomial.

- Classe NP : Le problème peut être résolu de manière non-déterministe en un temps polynomial.

Le caractère non-déterministe de la classe NP signifie que le problème peut être représenté par un arbre, sur lequel à chaque étape (chaque noeud) il y aurait un choix à faire entre plusieurs actions et la machine devant résoudre ce problème ne pourrait en effectuer qu'une, sans être certain que la branche choisie soit la bonne. Le problème est donc exponentiel selon la taille des données. Si une machine pouvait tester tous les noeuds fils en même temps à chaque étape (ce qui est encore théorique mais non applicable en pratique), un problème de la classe NP pourrait être résolu en un temps polynomial.

Définition Problème NP-difficile (241: :Un problème est NP-difficile s'il est au moins aussi dur que tous les problèmes dans NP. Soit un problème ii- et un algorithme permettant de réduire en un temps polynomial (dans P) ce problème à un problème ii-', il est donc possible de résoudre ii-' si l'on sait résoudre ii-, ce qui rend ii- aussi difficile à résoudre que ii-'. ii- est alors NP-difficile si pour tout problème ii-' de NP , ii-' se réduit en un temps polynomial à ii-.

Définition Problème NP-complet (241: :Un problème est NP-complet si il est dans NP et est NP-difficile.

Pour prouver que notre problème est NP-difficile, tentons de faire l'analogie de celui-ci au problème subset-sum, également appelé le problème du sac à dos, bien connu pour être NP-complet. Si cette analogie est correcte, l'hypothèse sera prouvée.

Premièrement, définissons le problème subset-sum.

Définition Problème Subset-sum : Pour un ensemble S donné {s1, s2, ... , sn} de nombres entiers positifs et le nombre entier M, existe-t-il un ensemble SA ? S tel que >siESAsi = M?

Démonstration: Construisons un ensemble de n tâches de même période M sans partie obligatoire. Ce qui simplifie la contrainte 4.11 du problème MAXREWpar l'équation 4.16 car mi = 0 V i.

Posons W = >n i=1si avec si = oi et choisissons comme fonction de récompense la fonction fi(ti) = t2 i+ (W - si)ti qui est strictement convexe si ti = 0. Elle peut être réécrite comme ti(ti - si) + Wti.

Etant donné que la période M est identique pour chaque tâche, l'hyperpériode P dans MAX-REW peut être remplacée par M et nous pouvons réécrire le problème MAX-REW ainsi:

maximisons Xn ti(ti-si)+ W Xn ti (4.15)

i=1 i=1

à condition que Xn ti = M (4.16)

i=1

0 = ti = si. (4.17)

Nous observons que si 0 < ti < si, alors ti(ti - si) < 0 ce qui ne maximise clairement pas la récompense car si ti = 0 ou ti = si nous obtenons la quantité ti(ti - si) = 0. Et donc, la première partie de l'équation 4.15 peut être supprimée, il reste à maximiser W>n i=1 ti. La récompense totale que nous noterons Rew = W. M à cause de la contrainte 4.16.

Résoudre la question « Rew est-elle égale à W. M ? » revient à résoudre la question « Existe-il un ensemble de ti tel que>ni=1 ti = M, chaque ti étant égal à 0 ou àsi ? », autrement dit Rew = W. M si et seulement s 'il existe un ensemble SA ç S tel que >si?SAsi = M. L'analogie au problème Subset-Sum est faite, ce qui implique que MAX-REW et donc REW-PER avec des fonctions convexes de récompense est NP-difficile. ut

4.3 Conclusion

Nous avons développé dans ce chapitre, la méthode de H. Aydin : le « RewardBased Scheduling ». Dans son travail [3], cette méthode est appliquée avec des fonctions concaves de récompense, nous avons montré pourquoi dans le cadre de la sécurité, les fonctions convexes semblent plus appropriées. Il a été démontré que le problème est NP-complet avec de telles fonctions.

Cependant, même s'il n'existe pas encore à ce jour de méthodes déterministes et polynomiales en temps pour résoudre le problème 2 avec des fonctions convexes de récompense, nous analyserons dans le chapitre 5 des méthodes heuristiques afin de trouver une solution au problème se rapprochant le plus possible de la solution optimale.

2étant donné qu'à l'heure actuelle, l'égalité NP = P n'a pas encore été démontrée est récompensée par 1 000 000$ à celui qui la prouvera

Chapitre 5

Simulations

5.1 Introduction

Le but principal de ce chapitre sera d'essayer en se basant sur l'idée développée au chapitre 4 de développer un algorithme permettant d'attribuer des durées plus ou moins longues aux parties optionnelles des différents travaux, lorsque la récompense évolue de manière convexe en fonction de ce temps.

Pour parvenir à ce but, nous avons du implémenter un simulateur nous permettant de tester si le système est toujours ordonnançable avec les durées choisies. Nous allons donc dans ce chapitre montrer comment nous générons un ensemble de tâches formant ce système. Ensuite nous présenterons deux méthodes heuristiques que nous avons implémentées : le recuit simulé et la recherche tabou. Nous ferons une comparaison des résultats obtenus avec ces méthodes par rapport à l'algorithme présenté dans le chapitre précédent (section 4.2.7) qui est optimal dans le cas où les fonctions de récompense sont linéaires et nous effectuerons ensuite une comparaison avec des fonctions convexes de récompense.

5.2 Génération d'un système

5.2.1 Génération des périodes

Un système est composé de tâches, chaque tâche Ti lance une instance (un travail) à chaque instant kPi V k = 0. La simulation devant être effectuée sur l'intervalle [0, P], il est indispensable lors de la génération de cet ensemble de tâches, de choisir judicieusement les valeurs des périodes Pi des différentes tâches.

En effet, si Pi est choisi de manière arbitraire entre deux bornes, et si le nombre de tâches est important, cela pourrait nous amener à ce que le plus petit commun multiple de ces valeurs soit énorme. La simulation serait donc beaucoup plus longue à effectuer étant donné qu'il faut simuler l'évolution du système jusqu'à l'hyperpériode P = ppcm {Pi} V Ti.

Nous avons donc décidé, afin de faciliter nos simulations d'utiliser la technique exposée dans [8] qui consiste à choisir la période comme un multiple des premières puissances de nombres premiers:

YPi = pup

{p|p est premier}

p est un nombre premier et up est la puissance de p sélectionnée au hasard.

Une matrice contient un ensemble de nombres premiers et leurs premières puissances (ex : 21, 22, ...).

De manière à ne pas obtenir des périodes trop importantes, nous avons construit cette matrice avec de petits nombres premiers (p | p est premier E [2, 11]) et surtout de petites puissances (u p E [1,2]), nous avons choisi d'utiliser dans notre simulation la matrice ÷ :

?

÷=

? ? ? ? ?

5
11

224
339
777
5
11

?

25 ?????

11

L'algorithme (repris de [8]) qui retourne une période à partir de la matrice ÷ est décrit ci-dessous :

periode ? 1

Pour chaque ligne i de x faire

p ? Arrondi(random(1 , |xi|)) periode ? periode x xi,p Fin Pour

Retourner periode

Algorithme 4: Détermine la période

Sur chaque ligne, l'algorithme sélectionne une valeur et il multiplie les valeurs sélectionnées entre elles. Avec la matrice x, la période maximum est donc : 4 x 9 x 25 x 7 x 11 = 69300, c.-à-d. le produit des valeurs les plus importantes sur chaque ligne, c'est aussi la taille de la plus grande hyperpériode car n'importe quelle autre période calculée sera automatiquement un diviseur de 69300.

5.2.2 Génération de l'ensemble de tâches

Nous générons ensuite l'ensemble des tâches Ti avec leurs caractéristiques: - Pi : La période, générée par l'algorithme 4 ci-dessus;

- mi : le temps d'exécution de la partie obligatoire;

- oi : le temps d'exécution maximal de la partie optionnelle.

- coeffi : qui est le coefficient de la fonction de récompense de la tâche Ti, nous le choisirons au hasard entre 1 et 100.

L'instant d'arrivée Ai = 0 V Ti et l'échéance Di = Pi V Ti, car nous travaillons sur des systèmes à départ simultané et à échéance sur requête.

Il s'avère compliqué de générer au hasard un système composé d'exactement n tâches avec un facteur global d'utilisation Um, n et Um étant fournis en entrée à l'algorithme. Soit le nombre de tâches est correct mais l'utilisation n'est pas proche de Um, soit l'utilisation est proche de Um mais le nombre de tâches est différent de n.

Nous avons donc écrit deux algorithmes : l'algorithme 5 génère un système d'exactement n tâches indépendamment de l'utilisation, l'algorithme 6 génère un certain nombre de tâches avec un facteur d'utilisation des parties obligatoires se rapprochant le plus possible de Um passé en paramètre.

Entrée : n

Sortie : Ensemble de tâches

i<--0

utilisation_courante <-- 0

Tant que (i n) faire

P <-- période générée par l'algorithme 4

m <-- random(0, P))

o <-- P -- m

coeff <-- random(1,100)

taskU <-- mP

Si (utilisation_courante + taskU < 1) Alors

utilisation_courante <-- utilisation_courante + taskU

AjoutTache(P, m, o, coeff)

i <-- i + 1 Fin Si

Fait

Algorithme 5: Génération d'un ensemble de n tâches

Entrée : Um

Sortie : Ensemble de tâches

utilisation_courante <-- 0

Tant que (utilisation_courante < Um -- 0.05) faire
P <-- période générée par l'algorithme 4

m <-- random(0, P))

o <-- P -- m

coeff <-- random(1,100)

taskU <-- mP

Si (utilisation_courante + taskU < Um) Alors

utilisation_courante <-- utilisation_courante + taskU AjoutTache(P, m, o, coeff)

Fin Si

Fait

Algorithme 6: Génération d'un ensemble de tâches avec un facteur d'utilisation proche de Um

L'utilisateur donne en paramètre l'utilisation du système par les parties obli-

mi

gatoires (pour rappel : Um = Pn Pi ) dont il voudrait se rapprocher ou le nombre

i=1

de tâches : n.

Tant que l'on ne dépasse pas le facteur d'utilisation ou le nombre de tâches désiré selon l'algorithme, on génère une nouvelle tâche. La période est calculée à partir de l'algorithme 4, le temps d'exécution de la partie obligatoire est choisi au hasard entre 0 et la période jusqu'à ce que le système ait atteint l'utilisation ou le nombre de tâches désiré.

Nous avons d'autre part décidé de choisir des durées maximales pour les parties optionnelles de mêmes longueurs que le temps restant avant l'échéance laissé par les parties obligatoires, soit pour chaque tâche Ti, oi = Pi - mi.

Dans l'algorithme 6, si l'utilisation courante en tenant compte de la tâche venant d'être générée ne dépasse pas Um, nous rajoutons la tâche et augmentons l'utilisation courante. Dans l'algorithme 5, nous vérifions uniquement que les parties obligatoires soient ordonnançables après avoir ajouté la tâche (utilisation_courante + taskU < 1).

5.3 L'algorithme orienté évènement

Comme nous l'avons expliqué au chapitre précédent, il nous a fallu mettre au point un algorithme d'ordonnancement basé sur la notion d'évènements et non plus un algorithme qui effectue une incrémentation du temps à chaque tour de boucle.

Dans cette section, nous fournirons au lecteur une description de l'algorithme « Event-driven » 7 et de la façon dont nous avons implémenté celui-ci.

Nous utiliserons les termes :

- « Arrivée » : pour désigner l'évènement qui, pour une tâche donnée, annonce l'arrivée dans le système d'un nouveau travail et l'échéance du travail précédent de cette tâche par la même occasion;

- « FinJob » : pour l'évènement qui annonce la terminaison d'un travail.

Le système comporte en plus des caractéristiques des différentes tâches, une file d'évènements.

Chaque évènement que l'on peut insérer dans cette file comporte 4 informations :

1. le temps : c'est-à-dire l'instant auquel cet évènement doit se déclencher;

2. un pointeur vers le travail ou la tâche à laquelle il se rapporte;

3. son type : qui identifie l'évènement (Arrivée, FinJob), le type est utilisé pour l'insertion dans la file, nous y reviendrons en présentant la gestion des évènements;

4. et un pointeur vers le système : ce qui permet à la gestion d'un évènement d'insérer elle-même un nouvel évènement dans la file du système. Par la suite, pour des raisons d'optimisation nous avons supprimé ce pointeur, et passons par la tâche qui possède un pointeur vers le système.

5.3.1 La gestion des évènements

Comme nous l'avons mentionné plus haut, il y a deux types d'évènements possibles. Lorsqu'un de ceux-ci se déclenche, l'algorithme fait appel à sa routine de gestion.

Voici comment celle-ci procède en fonction du type de l'évènement.

- Arrivée : Lorsqu'une arrivée survient pour la tâche Ti, on vérifie premièrement le respect de l'échéance, c.-à-d. qu'on vérifie que le travail précédent soit bien terminé, si tel n'est pas le cas, le système n'est pas ordonnançable et la simulation peut donc s'arrêter. Ensuite, on signale qu'un travail est prêt à être exécuté pour Ti et on insère l'arrivée suivante à l'instant t + Pi dans la file des évènements, où t est l'instant courant.

- FinJob : Le travail qui se termine est marqué comme terminé.

L'insertion triée dans la file des évènements se base à la fois sur l'instant de déclenchement, mais aussi sur le type de l'évènement à rajouter. Il est indispensable de gérer les évènements qui se produisent au même instant dans l'ordre suivant:

1. FinJob;

2. Arrivée.

La gestion de l'événement « Arrivée » comprend également la gestion des échéances qui contrôle premièrement l'échéance du travail précédent avant d'en lancer un nouveau. Etant donné que nous travaillons sur des systèmes à échéance sur requête, il est inutile de gérer un évènement « Echéance ».

En effet, si la fin d'un travail survient au même instant que l'échéance de sa tâche, le système est ordonnançable. Il faut donc que le travail soit marqué comme terminé avant de tester s'il reste un travail pour cette tâche. Dans le cas contraire, la gestion de l'échéance nous renverrait qu'il y a encore un travail en cours d'exécution et que le système n'est donc pas ordonnançable, ce qui est

faux. Il ne faut donc pas non plus lancer un travail 'rij avant de tester l'échéance de 'ri ,j-1, pour les mêmes raisons.

5.3.2 L'algorithme

Basant notre simulation sur des systèmes à départ simultané, au début de l'exécution de l'algorithme nous rajoutons dans la file d'évènements une Arrivée à l'instant 0 pour chaque tâche présente dans le système.

1. L'évènement au sommet de la file est sélectionné, c.-à-d. celui qui possède le plus petit instant de déclenchement. Cet évènement est géré de manière différente selon qu'il s'agisse d'une « Arrivée » ou d'une « Fin- Job » et est ensuite supprimé de la file. On gère tous les évènements qui se déclenchent à cet instant.

2. Après, l'algorithme sélectionne le travail le plus prioritaire, prêt à être exécuté. C'est ici qu'intervient la politique d'ordonnancement. Nous avons utilisé EDF comme règle d'assignation des priorités (voir section 2.1.5).

3. Le temps restant avant le prochain évènement (rem) qui se trouve au sommet de la file est calculé par rapport à l'instant courant. Si le temps d'exécution restant du travail (remjob) que l'on vient de sélectionner est inférieur ou égal au temps restant avant l'évènement suivant, nous insérons une FinJob à l'instant t = currenttime + remjob currenttime est l'instant actuel. Autrement nous savons que l'exécution de ce travail prioritaire ne sera pas terminée avant que le prochain évènement ne se déclenche. Dans ce cas, nous diminuons simplement le temps d'exécution restant du travail que l'on se prépare à lancer, remjob = remjob - rem.

4. Nous procédons au changement de contexte, c.-à-d.que le travail qui était exécuté est mis dans l'état « wait », les données de celui-ci sont sauvegardées, les données du travail prioritaire sont restaurées et le travail prioritaire est exécuté.

5. Nous reprenons à l'étape 1 jusqu'à atteindre l'hyperpériode à partir de laquelle nous savons que le système est ordonnançable si toutes les échéances jusqu'à cet instant (compris) ont été respectées.

Voici l'algorithme de la simulation basée sur les évènements en pseudocode (algorithme 7).

currenttime ? O;

Pour Chaque tâche T faire

insère Arrivée(T, instant O) Fin Pour

Tant que (currenttime = hyperpériode) faire

Tant que (currenttime = temps de l'évènement au sommet) faire

EV ? évènement au sommet de la file

currenttime ? temps de l'évènement EV

gérer EV (voir section 5.3.1)

supprimer EV

Fait

PRIOR ? travail le plus prioritaire

rem ? temps de l'évènement suivant -currenttime

Si (remjob = rem) Alors

insère FinJob(PRIOR, currenttime + remjob)

Sinon

remjob ? remjob - rem

/*remjob est une variable appartenant au travail le plus prioritaire actuel et initialisée à son temps d'exécution.*/

Fin Si

travail courant ? état wait

PRIOR ? état run

Fait

Algorithme 7: Ordonnanceur basé sur la gestion d'évènements

Il serait possible de rendre ce simulateur plus général, de façon à ce qu'il puisse gérer les échéances arbitraires et les départs différés (ce qui ne nuit pas à l'optimalité d'EDF). Il faudrait alors en ajoutant un évènement « Echéance », séparer la gestion des arrivées dans laquelle on vérifiait que l'échéance soit respectée en même temps que la gestion de l'arrivée du travail suivant. L'instant de départ Ai pouvant être différent d'une tâche Ti à l'autre il faudra effectuer le test d'ordonnançabilité sur un intervalle : [min {Ai} , P + max {Ai}]. Cependant, ici nous nous focaliserons sur des systèmes à départ simultané et à échéance sur requête, inutile donc de rendre la simulation plus longue en temps de calcul.

5.4 Rappel du problème d'optimisation des temps

optionnels

Nous allons maintenant reprendre le problème d'optimisation analysé dans le chapitre précédent.

Problème:

maximiser

Xn
i
=1

1
bi

Xbi

j=1

fi(tij) (5.1)

à condition que 0 tij oi i = 1, ... , n j = 1, ... , bi (5.2)

Il existe un ordonnancement faisable avec les valeurs {mi} et {tij}.

(5.3)

bi pour rappel, est le nombre de fois que l'on peut placer la période de la tâche Ti dans l'hyperpériode, soit bi = P/Pi.

La fonction à maximiser représente la somme des moyennes des récompenses pour chaque tâche du système, la récompense d'un travail ôij étant obtenue par la fonction fi(tij).

Ci

Si le facteur d'utilisation U = Pn Pi 1 alors un algorithme utilisant EDF

i=1

comme politique d'ordonnancement trouverait un ordonnancement faisable. Mais étant donné que les tij peuvent être de durées différentes, nous ne pouvons pas utiliser cette condition nécessaire et suffisante sur la faisabilité de l'ordonnancement.

En effet, dans notre cas, le temps d'exécution Ci = mi + tij peut varier d'un travail à l'autre comme les tij ne sont pas forcément identiques pour une tâche Ti donnée, le calcul du facteur d'utilisation du système n'a donc plus de sens. Nous continuerons néanmoins à parler d'utilisation en ne prenant en compte que les parties obligatoires des tâches.

D'après nos contacts avec H. Aydin, il n'existe pas d'autres moyens que celui de simuler l'ordonnancement jusqu'à l'hyperpériode P. Il est néanmoins possible de poser une condition nécessaire supplémentaire de manière à ne pas avoir à simuler l'ordonnancement jusqu'à l'hyperpériode lorsque celle-ci n'est pas respectée.

Xn bimi + Xn Xbi tij P (5.4)

i=1 i=1 j=1

Si la condition 5.4 n'était pas respectée, cela reviendrait à dire qu'il y aurait plus d'unités d'exécution, parties obligatoires et optionnelles confondues, que le nombre d'unités disponibles entre l'instant 0 et l'hyperpériode. Cette condition n'est malheureusement pas suffisante pour garantir que le système est faisable.

Il est possible de réexprimer l'équation 5.4 de ce problème comme: >2n >2bi tij = d = P -- >2n bimi

i=1 j=1 i=1

d est la latitude maximum disponible après avoir déjà ordonnancé les parties obligatoires jusqu'à l'hyperpériode P. Nous pourrons donc ordonnancer dans ces d unités de temps oisives les parties optionnelles.

Il nous faut trouver les valeurs des tij qui maximisent l'équation 5.1 tout en respectant les contraintes formulées par les équations 5.2 et 5.3.

Comme nous l'avons montré au chapitre 4 (page 45), l'augmentation de la sécurité en fonction du temps est mieux représentée par une fonction convexe, mais le problème étant NP-COMPLET lorsque les fonctions de récompenses sont convexes, nous utiliserons des méthodes heuristiques pour trouver une solution se rapprochant le plus possible de la solution optimale. Nous citerons et décrirons dans ce chapitre les méthodes heuristiques de base permettant de répondre à ce problème d'optimisation.

5.5 Notations utilisées

Nous utiliserons quelques notations pour présenter ces algorithmes :

- xn : est la solution courante à l'étape n, dans notre problème elle sera constituée de :

- un tableau stockant les valeurs des tij;

- la récompense obtenue avec ces valeurs.

Remarque: Notons que nous utilisons ici le terme « solution » pour désigner ce qui n'est en réalité qu'une tentative de solution et non la solution finale au problème. Cette solution courante peut ne pas être ordonnançable et ne sera alors très certainement pas retenue comme la meilleure solution.

- X : est l'ensemble de toutes les solutions valables (qui respectent les contraintes 5.1, 5.2 et 5.3) dans lequel la solution finale sera choisie par l'algorithme utilisé.

- V(xn) : est le voisinage de la solution xn, c'est donc un ensemble de solu-

tions qui se rapprochent par leurs caractéristiques de la solution xn. Toute la difficulté étant de trouver une manière de définir ce rapprochement dans le contexte de l'ordonnancement des parties optionnelles. Nous expliquerons dans la section 5.8 comment nous obtenons une solution voisine.

- F(xn) : représente le retour de la fonction objectif, autrement dit la récompense obtenue avec cette solution xn. Dans notre contexte, cela représente donc la récompense accrue par les tij de la solution x n (équation 5.1). Si le système n'est pas ordonnançable après avoir effectué une simulation avec xn, F(xn) sera nul.

5.6 Le recuit simulé

Le principe de cet algorithme est basé sur les techniques de recuit utilisé en sidérurgie. Le métal est chauffé de manière à fournir une certaine énergie au système qui permet aux atomes de se déplacer. Les atomes se positionnent de façon à libérer le métal de toutes tensions internes dues aux déformations ayant été appliquées au métal. Ensuite la baisse de température fige lentement les atomes dans une position d'énergie minimale.

L'algorithme du recuit simulé gardera donc ce même principe. Au début de son exécution, l'algorithme autorise des changements importants de la solution courante ensuite la solution se fige peu à peu et le système finit par se geler dans un optimum global.

On part de la solution initiale x0, pour laquelle tous les temps optionnels (tij) valent O, la récompense est donc minimale étant donné qu'on travaille sur des fonctions non-décroissantes. En toute logique, on pourrait même travailler avec des fonctions de récompense de la forme fi(0) = O ?i car une récompense n'est pas utile si la partie optionnelle est nulle.

A chaque étape n, une solution voisine x de la solution courante xn (c'est-àdire ? V(xn)) est choisie au hasard, nous expliquerons à la section 5.8 comment nous sélectionnons la solution voisine.

Si la récompense accrue grâce à cette solution est meilleure qu'avec la précédente, c.-à-d. si F(x ) est meilleur que F(xn), x devient la nouvelle solution courante. Autrement, on tire au sort selon une certaine probabilité p, le fait de garder la solution x n ou de choisir x comme nouvelle solution pour l'étape n+ 1.

Remarque: Même si ce nouvel ensemble de durées pour les parties optionnelles est moins intéressant au niveau de la récompense, cela pourrait devenir par après plus avantageux car nous pourrions rebondir sur une meilleure solution que xn par la suite, c'est ce qu'on appelle la phase de diversification.

Nous notons p, la probabilité de prendre x comme nouvelle solution malgré le fait que la récompense obtenue avec x soit inférieure à celle obtenue avec la solution précédente xn. La probabilité p diminue lorsque la température T baisse, et ce de manière à figer petit à petit la solution.

p est donc calculé en fonction de la température T mais aussi en fonction de la diminution de récompense IF subie en acceptant x comme nouvelle solution.

IF = F(xn) - F(x )

La fonction p(T, IF) a été reprise par analogie au recuit en sidérurgie, il s'avère expérimentalement que la distribution de Boltzmann ci-dessous est un bon choix pour calculer p [20].

T

p(T,IF) = e- ÄF

La température T quant à elle est fonction du temps et diminue par palier de manière géométrique, tous les L tours de boucle de l'algorithme. On choisit une température initiale T0 qui ne diminue pas durant L tours, ensuite la température diminue à T1 = aT0 après L tours, T2 = a2T0, et ainsi de suite. Après kL tours de boucle la température vaudra Tk = akT0.

Les valeurs T0, L et a doivent être choisies expérimentalement:

- La température initiale T0 est calculée par rapport à la probabilité p souhaitée au début de l'exécution de l'algorithme.

Exemple : Si l'on souhaite obtenir une probabilité de 50% d'accepter une

solution qui est moins bonne que la solution courante, il faut que e 50%, autrement dit, que T0 = Fi

ln 2 .

(IF) est la moyenne des détériorations déduite par simulation. Avant l'exécution à proprement parlé du recuit simulé, nous effectuons 100 tours de boucle afin de calculer cette moyenne et de fixer la valeur T0.

- L, dépend de l'importance du voisinage de la solution. En effet, la phase d'exploration devra durer plus longtemps avant de diminuer la température si le nombre de solutions voisines est important. Le nombre de possibilités de solutions voisines dépend ici du nombre de travaux présents jusqu'à

F)

T0 =

l'hyperpériode, qui est proportionnel au nombre de tâches dont est composé le système. Nous avons donc décidé de donner comme valeur à L ce nombre de tâches.

- Plus á est important, plus le refroidissement sera lent, nous avons choisi á = 0,95.

Ci-dessous, nous avons repris un algorithme de base (issu de [20]) en le modifiant légèrement, sans entrer dans les détails du calcul des variables, pour présenter de manière intuitive le principe de fonctionnement de celui-ci au lecteur.

Fà - F(x0); (x0 E X, solution initiale)

n-0;

Tant que (vrai) faire

x* - une solution tirée au sort de V(xn); Si (F(x*) ~ F(xn)) Alors

xn+1 - x* ;

Si (F(x*)> àF) Alors Fà - F(x*); Fin Si

Sinon

q - random(0, 1); q reçoit un nombre au hasard entre 0 et 1

Si (q p) Alors

xn+1 - x*;

Sinon

xn+1 - xn;

Fin Si

Fin Si

Si (condition d'arrêt) Alors STOP; Fin Si

n - n + 1;

Fait

Algorithme 8: Recuit simulé - Squelette de base

Fà est la sauvegarde du meilleur retour obtenu par la fonction objectif.

La condition d'arrêt peut être de différents types:

- Pour reprendre l'analogie avec le recuit en sidérurgie, nous pourrions nous arrêter lorsque la température est descendue en dessous d'une certaine borne;

- ou lorsque la fonction F n'a pas augmenté de 0, 1% pendant 100 paliers

consécutifs;

- mais afin de comparer le plus justement possible le recuit simulé avec la recherche tabou (que nous analyserons à la section 5.7), nous stopperons la recherche après un nombre fini d'itérations (1000) afin d'attribuer le même temps processeur aux deux algorithmes.

5.7 La recherche tabou

Le principe de la recherche tabou est d'associer à l'algorithme de base, une mémorisation des solutions visitées. L'algorithme de la recherche tabou ne prend pas en compte le facteur température utilisé dans le recuit simulé. La mémoire est constituée d'une liste « tabou », qui empêche de multiples cycles dans l'algorithme, elle est appelée tabou car une solution dont les caractéristiques correspondent à celles rencontrées dans la mémoire ne sera pas reprise comme solution courante. Nous parlons ici de caractéristiques car il est possible de limiter la taille de la mémoire tabou en ne prenant en compte que quelques caractéristiques au lieu de devoir comparer tout le tableau des valeurs tij. Nous enregistrerons dans cette liste les permutations effectuées sur les parties optionnelles lors de l'obtention d'une solution voisine (voir section 5.8).

La liste tabou est gérée de manière FIFO (First in / First out), ce qui veut dire que les changements récents remplacent les plus anciens, cela empêche donc de refaire un changement que l'on vient d'effectuer et de cycler sur les permutations. Cependant, il est à noter qu'un cycle plus long que la taille de la mémoire reste possible, mais nous avons remarqué qu'avec une taille suffisante de permutations enregistrées (à partir de 10, valeur que nous avons choisie pour notre implémentation de l'algorithme) les situations de cycles restent rares, le nombre de choix parmi les temps optionnels des travaux à permuter pouvant être important.

A chaque étape n, on extrait du voisinage de la solution courante V(xn), un sous-voisinage que nous notons V*. Une fois V* extrait, toutes les solutions de cet échantillon sont comparées et la meilleure solution (x*) dont les caractéristiques ne sont pas tabou, est enregistrée dans la mémoire et comme nouvelle solution courante xn+1. Le fait que x n offre une meilleure récompense n'a pas d'importance. Cela permet de garder le principe d'exploration des solutions et de ne pas rester calé dans un maximum local. Cependant il est possible de rajouter des critères qui pourraient autoriser une solution tabou à être réutilisée. Une augmentation forte de la récompense pourrait être un critère. Nous

réutilisons une solution tabou si toutes les solutions de V* sont tabou, nous sélectionnons alors la meilleure parmi celles-ci.

Si c'était possible, on prendrait V* = V(xn) mais dans notre cas, cela serait fort coûteux en temps de calcul de trouver la meilleure solution de tout le voisinage. Nous préférons donc tirer au sort un échantillon de V(xn) afin d'obtenir V*.

Nous nous sommes à nouveau basé sur l'algorithme issu de l'ouvrage [20] que nous avons légèrement modifié pour présenter la structure de base de la recherche tabou que voici :

Fà - F(x0); (x0 E X, solution initiale)

Tant que (vrai) faire

V* - sous-voisinage de V(xn);

x* - meilleure solution de V*;

Tant que (x* = solution tabou) faire

x* - meilleure solution suivante de V*;

Fait

Si (x* est tabou V x* E V*) Alors

x* - meilleure solution de V*;

Fin Si

xn+1 - x*;

mise à jour de la liste tabou;

Si (condition d'arrêt) Alors STOP; Fin Si n - n + 1;

Fait

Algorithme 9: Recherche Tabou - Squelette de base

Nous avons effectué des simulations afin de déterminer la taille idéale L du sous-voisinage V*. Cette taille dépend du nombre de tâches étant donné que chaque tâche possède sa propre fonction de récompense. Nous avons donc choisi de calculer cette taille par la formule 5.5.

L = x. nbtaches (5.5)

nbtaches est le nombres de tâches présentes dans le système et x est un coefficient que nous avons déduit par simulation.

Nous avons effectué 1000 simulations de l'algorithme de la recherche tabou pour plusieurs valeurs de x différentes, sur un même système composé de 6

tâches. Nous avons comparé la récompense moyenne par rapport à l'algorithme optimal, pour chaque valeur de x, voir graphique 5.1. Nous en sommes arrivés à la conclusion que la meilleure valeur pour la taille de V est L = 41 nbtaches

FIG. 5.1 - Recherche de la meilleure taille du sous-voisinage

5.8 Obtention d'une solution voisine

Sur base d'une solution xn, l'algorithme du recuit simulé doit construire une solution voisine x qui se rapproche par ses caractéristiques de la solution xn, l'algorithme de la recherche tabou doit fournir un voisinage V(xn), c.-à-d. un ensemble de solutions voisines de xn.

Après avoir simulé le système avec la solution xn, nous procédons comme suit pour obtenir une solution voisine :

1. Si le système est ordonnançable avec la solution xn, on sélectionne un tij au hasard dans le tableau des temps optionnels et nous l'augmentons d'un certain palier sans dépasser la valeur maximale des temps optionnels (oi) pour Ti. En effet, si tij + palier> oi alors tij - oi

2. Si la contrainte nécessaire 5.4:

In i=1 bimi + In Ibi

j=1 tij P a été violée, le système n'est pas ordonnan-

i=1

çable, on sélectionne alors un tij au hasard parmi un ensemble S constitué
des travaux optionnels pour lesquels tij - palier > 0 et tij - tij - palier.

En effet, diminuer les temps optionnels est dans ce cas le seul moyen de récupérer un système ordonnançable.

S'il n'existe pas de tels travaux (S est vide), rien n'est diminué mais lorsque le palier aura atteint une valeur suffisamment petite, S ne sera plus vide.

3. De plus, nous permutons (avec une probabilité de 50%) deux tij du tableau afin de diversifier la recherche pour que l'algorithme ne reste pas calé dans un maximum local.

Le palier dépend de la tâche et est sélectionné au hasard entre 0 et paliermaxi. paliermaxi est la valeur maximale que peut atteindre le palier de Ti. Le palier est sélectionné entre ces deux bornes de manière à ce que le palier ne soit pas contraint à être uniquement diminué. Nous initialisons paliermaxi à min(oi, Pi - mi), c.-à-d. le temps maximum restant pour la partie optionnelle de Ti.

Nous avons donc décidé d'après nos tests, de diminuer paliermaxi V Ti de manière géométrique avec un facteur diminpalier = 0.97 (paliermaxi ? paliermaxi. diminpalier) à chaque fois que trois simulations consécutives renvoient que le système n'est pas ordonnançable. Pour déterminer ce facteur, nous avons fixé 3 comme le nombre de fois qu'une tentative de solution obtenue peut ne pas être ordonnançable avant de diminuer le palier. Ensuite, à partir de cette constante fixée, nous avons effectué des tests avec différents facteurs et comparé la récompense obtenue par rapport à l'algorithme optimal (voir figure 5.2).

FIG. 5.2 - Ajustement du facteur de diminution du palier maximum

5.9 Expérimentation

Nous avons effectué des tests afin de comparer la récompense obtenue par les deux méthodes heuristiques. Pour cela, nous avons d'abord généré différents fichiers contenant chacun les spécifications de 1000 systèmes tous composés de n tâches.

Les résultats produits par nos tests sur de petits ensembles de 100 systèmes divergeaient d'une simulation à l'autre mais restaient identiques entre eux avec un ensemble de 1000 systèmes et similaires aux résultats produits par un ensemble de 10000 systèmes, nous avons donc estimé qu'il s'agissait d'un bon compromis entre le temps de calcul et la qualité du résultat.

Pour chaque tâche, nous enregistrons la période, le temps d'exécution obligatoire, le temps optionnel maximum et le coefficient utilisé dans la fonction de récompense.

Les tests ont été faits en donnant le même temps processeur aux deux algorithmes1, le temps processeur dépendant essentiellement du nombre de simulations effectuées avec les différentes solutions. Nous nous limiterons à 1000 simulations par algorithme, soit à 900 itérations de la boucle principale pour le recuit simulé car 100 itérations sont utilisées pour déterminer la température initiale et à1000

L pour la recherche tabou étant donné qu'il faut comparer et donc simuler L (la taille du sous-voisinage V*) systèmes à chaque tour de boucle.

5.9.1 Limites des algorithmes

Nous remarquons que l'efficacité de l'algorithme dépend principalement du nombre de travaux à simuler jusqu'à l'hyperpériode et ce pour deux raisons, la présence d'un nombre important de travaux:

1. nous oblige à limiter le temps d'exécution des algorithmes, ce qui les rendent moins efficaces;

2. provoque de mauvais choix effectués au hasard sur ce grand nombre de travaux.

1implémentationsdu recuitsimulé : recuit() etdelarecherchetabou : tabousearch() dans l'annexe B, section B. 1.

Problème de lenteur d'exécution

Nous avons fortement été ralenti par l'optimisation de la simulation et le choix de structures de données adéquates:

- utilisation d'une file à priorité pour gérer les évènements;

- stockage des évènements et travaux statiquement pour éviter les allocations et désallocations continuelles de la mémoire;

- enregistrement des modifications effectuées sur le tableau des temps optionnels afin de ne pas devoir recopier entièrement le tableau à chaque fois que l'on revient à une solution précédente,...

A chaque travail correspond un évènement « Arrivée » et « FinJob ». S'il y a 100 travaux à simuler jusqu'à l'hyperpériode, il y aura donc pour chaque simulation avec une solution xn, 200 évènements à gérer. Comme nous autorisons l'algorithme à faire 1000 simulations, qu'il y a 1000 systèmes différents par fichier et que nous testons nos 2 méthodes sur 12 fichiers différents, chaque fichier se distinguant par le nombre de tâches des systèmes qui composent celui-ci. Un rapide calcul permet de conclure qu'une instruction de 1us dans la gestion des évènements équivaut à 10-6s x 200 x 1000 x 1000x2 x 12 = 4800s = 80 minutes supplémentaires sur la simulation totale.

Problème du choix des temps optionnels à modifier

Il est bien entendu évident que si le nombre de travaux optionnels est important, le choix de la partie optionnelle dont on va augmenter le temps d'exécution est primordial et cela reste le cas malgré le fait qu'on joue sur la granularité en augmentant fortement les temps optionnels au début de l'exécution de l'algorithme et en diminuant cette augmentation par le système de palier.

Or, le recuit simulé choisi au hasard tij qu'il va modifier. Si le choix se porte sur un travail ôij qui n'augmente que de très peu la récompense, à l'étape suivante nous risquons de continuer à travailler sur une solution qui n'est pas très intéressante. Dans la recherche tabou, c'est la solution de V* qui rapporte le plus au système qui sera sélectionnée avant de passer à l'étape suivante. C'est pourquoi la recherche tabou fournit un bien meilleur résultat lorsque le nombre de tâches est important. En effet, si le nombre de tâches est important, la probabilité d'avoir plus de travaux est beaucoup plus importante également et l'algorithme aura plus de chance de choisir un travail dont la tâche possède un coefficient plus élevé dans la fonction de récompense.

Voilà pourquoi nous avons essayé de réduire le nombre de travaux par la

méthode donnée à la section 5.2 en choisissant la période de manière plus sélective que totalement aléatoirement entre deux bornes.

5.9.2 Analyse avec des fonctions linéaires de récompense

La comparaison est faite par rapport à l'écart de récompense entre le recuit simulé, la recherche tabou et la méthode optimale (présentée au chapitre 4) selon le nombre de tâches. Pour rappel, la méthode optimale n'est effectivement optimale que lorsque les fonctions de récompense sont linéaires. Dans ce cas, les méthodes heuristiques n'ont donc aucun intérêt, mais c'est notre seul moyen de comparaison étant donné que la méthode optimale dans le cas linéaire ne l'est plus avec des fonctions de récompense convexes.

Nous avons donc premièrement effectué un test où chaque tâche possédait une fonction linéaire de récompense de la forme:

f (tij) = ki . tij (5.6)

Les résultats de nos simulations sont représentés sur la figure 5.3, l'abscisse représente le nombre de tâches dont sont composés les 1000 systèmes et l'ordonnée représente le rapport moyen de la récompense sur ces 1000 systèmes par rapport à l'algorithme optimal (algorithme étalon).

Nous remarquons que le rapport diminue en fonction du nombre de tâches comme nous l'avons expliqué dans la section précédente et que la recherche tabou fourni effectivement de meilleurs résultats que le recuit simulé, malgré que la récompense moyenne totale vaille 80% de celle obtenue avec l'algorithme optimal sur des systèmes de 12 tâches générés comme présenté à la section 5.2.2. Ce rapport est néanmoins une bonne performance pour une méthode heuristique.

5.9.3 Simulation avec des fonctions convexes de récompense

Ensuite, nous avons trouvé intéressant de comparer l'efficacité des algorithmes lorsque les fonctions de récompense sont de type convexe étant donné que le niveau de la sécurité d'un système et donc la récompense augmente de manière convexe en fonction du temps utilisé pour appliquer celle-ci. Ce serait donc selon nous le type de fonction qui pourrait caractériser au mieux l'augmentation de la valeur en sécurité en fonction du temps. Nous avons choisi pour cela

FIG. 5.3 - Cas linéaire -- Comparaison entre le recuit simulé, la recherche tabou et l'algorithme optimal

une fonction de la forme:

f (tij) = ki . t2 ij , (5.7)

Nous avons réutilisé les mêmes ensembles de systèmes pour effectuer cette simulation, le coefficient ki reste donc également identique (sélectionné pour rappel entre 1 et 100).

Nous nous rendons compte que nos méthodes heuristiques ne fournissent un meilleur rapport de récompense par rapport à l'algorithme étalon (l'algorithme optimal avec des fonctions linéaires) que lorsque le nombre de tâches est très faible (2 ou 3 tâches). Ce rapport décroît très vite lorsque le nombre de taches augmente étant donné que lorsque le nombre de tâches augmente, le nombre de travaux jusqu'à l'hyperpériode deviendra plus important. Une erreur dans le choix des parties optionnelles à modifier est alors beaucoup plus pénalisante que dans le cas des fonctions linéaires de récompense.

Nous avons également comparé nos deux méthodes par rapport à l'algorithme de la descente en cascade2. Cet algorithme ressemble à l'algorithme du recuit simulé, excepté qu'on ne tient pas compte de l'évolution de la température. Lorsqu'une solution n'est pas meilleure que la précédente on ne la reprend pas à l'étape suivante, autrement dit la probabilité p (utilisée dans le recuit simulé) de continuer avec une solution fournissant une moindre récompense est nulle.

2voir fonction desc_cascade() dans l'annexe B, section B.1.

Nous constatons que l'algorithme de la descente en cascade offre pour ce problème une meilleure récompense moyenne que le recuit simulé. Ceci est dû au fait qu'en acceptant dans le recuit simulé de repartir à l'itération suivante sur une solution fournissant une moins bonne récompense, nous risquons aussi de partir sur une mauvaise voie. Nous remarquons qu'il est donc préférable de n'accepter la solution obtenue que si celle-ci est meilleure que la solution précédente, ce qui nous amène à la conclusion que le recuit simulé n'est pas adapté à notre problème.

Cela dit, il est évident que la récompense moyenne fournie par les méthodes heuristiques est supérieure en comparaison à celle fournie par un algorithme qui choisirait au hasard les valeurs tij sans aucune forme d'intelligence et qu'on laisserait s'exécuter sur le même temps processeur que les méthodes heuristiques citées plus haut. En effet, le système ne serait pas ordonnançable la plupart du temps et la récompense serait alors nulle.

Les résultats de cette simulation sont représentés sur la figure 5.4.

La recherche tabou surpasse à nouveau largement le recuit simulé pour la même raison que dans le cas linéaire, mais nous ne sommes pas parvenus à dépasser la récompense moyenne obtenue avec l'algorithme étalon.

FIG. 5.4 - Cas convexe -- Comparaison entre le recuit simulé, la recherche tabou, l'algorithme de la descente en cascade et l'algorithme étalon en fonction du nombre de tâches

Comparaison par rapport à l'utilisation

Nous avons ensuite généré 10 fichiers contenant chacun les spécifications de 1000 systèmes. Les systèmes enregistrés dans un fichier possèdent tous le même facteur d'utilisation Um (par les parties obligatoires), ces systèmes sont générés grâce à l'algorithme 6 présenté à la section 5.2. Um varie d'un fichier à l'autre entre 0, 1 et 1 par pas de 0, 1, le nombre de tâches pouvant ici être différent d'un système à l'autre pour un même fichier.

Si l'utilisation augmente, cela signifie que le rapport entre la durée des parties obligatoires et de la période s'intensifie, il y aura donc moins de temps disponible pour l'exécution des parties optionnelles. Cela implique que la récompense attribuée à l'ordonnancement s'en verra diminuée étant donné que celle-ci n'est attribuée qu'à la partie optionnelle.

Pour montrer cela, nous avons effectué une simulation sur laquelle les tâches possèdent toutes la même fonction de récompense : f(tij) = 2tij/300. Nous avons choisi une fonction exponentielle de manière à nous rapprocher le plus possible de l'évolution de la valeur en sécurité du système en fonction du temps.

Remarque: L'exposant tij est divisé par 300 de manière à réduire considérablement la récompense, étant donné que les parties optionnelles peuvent être relativement importantes lorsque l'utilisation par les parties obligatoires est faible. Si cette division n'était pas effectuée, le résultat du calcul de la récompense serait trop important.

Nous présentons les résultats de cette simulation sur la figure 5.5. Sur ce graphe dont l'abscisse est graduée à l'échelle logarithmique, nous constatons que la récompense moyenne régresse effectivement de manière exponentielle en augmentant le facteur d'utilisation Um.

Lorsque Um = 1, la partie optionnelle est nulle et il en découle donc également une récompense nulle, quelque soit l'algorithme utilisé. Dans ce cas, le rapport est de 100% par rapport à l'algorithme étalon aussi bien pour le recuit simulé que pour la recherche tabou.

Cependant, lorsqu'il y a des parties optionnelles à ordonnancer, les performances des méthodes heuristiques dépendent encore du nombre de tâches comme nous l'avons vu plus haut. Ceci explique l'irrégularité des résultats obtenus sur la figure 5.6 étant donné que pour réussir à obtenir l'utilisation désirée, le nombre de tâches n'est pas fixé et varie d'un système à l'autre, il suffit par exemple qu'un fichier comporte beaucoup de systèmes composés de deux tâches pour que le rapport de récompense entre les heuristiques et l'algorithme

FIG. 5.5 - Récompense en fonction du facteur d'utilisation étalon soit plus élevé.

FIG. 5.6 - Cas convexe - Rapport de la récompense en fonction de l'utilisation

5.10 Conclusion

Nous avons expliqué dans ce chapitre comment nous avons effectué nos simulations. Nous avons montré comment les tâches sont générées et avons présenté la méthode basée sur des évènements qui fait appel à l'ordonnan-

ceur EDF. Une fois la simulation de l'ordonnancement présentée, nous avons développé deux méthodes heuristiques : le recuit simulé et la recherche tabou qui utilisent notre simulation de manière à résoudre le problème d'optimisation évoqué au chapitre 4 et décrit à nouveau dans ce chapitre en utilisant ici des fonctions de récompense convexes.

D'après les résultats de ces simulations que nous avons exposés à la section 5.9, nous avons fait remarquer que le recuit simulé n'est pas adapté à ce problème. Etant donné la taille du problème, il est préférable d'effectuer à chaque étape des comparaisons entre différentes solutions possibles avant d'en choisir une pour servir de base à l'étape suivante. L'algorithme de la recherche tabou effectue ces comparaisons et offre donc de meilleurs résultats au niveau de la récompense apportée par la sécurité. Nous avons prouvé par nos résultats que la qualité de la récompense dépend du nombre de tâches présentes dans le système et que cette récompense diminue en fonction du facteur d'utilisation des parties obligatoires.

Chapitre 6

Conclusion

Nous avons présenté dans ce mémoire différentes méthodes permettant de sécuriser par des moyens cryptographiques un système temps réel tout en contrôlant le respect des échéances des différentes tâches composant ce système.

Remarquons que la littérature est encore pauvre à ce sujet, nous entendons par là que beaucoup d'articles existent à propos de la cryptographie, et de très nombreux articles se concentrent sur l'ordonnancement temps réel mais le lien entre ces deux disciplines n'est que très rarement établi.

Après avoir analysé et expliqué les points faibles de différentes méthodes existantes dans lesquelles on retrouve la notion de niveaux de sécurité, nous nous sommes basés sur l'idée d'offrir une récompense de manière proportionnelle à la durée allouée pour mettre en place la sécurisation du système. Dans ce but, nous nous sommes appuyés sur une technique nommée « Reward Based Scheduling » [3] dont le principe est de diviser le travail d'une tâche en une partie obligatoire et une autre facultative en offrant une récompense dépendant de la longueur de la partie facultative. La sécurité serait affectée à la partie optionnelle tout en intégrant une sécurité minimum dans la partie obligatoire.

Nous avons montré que l'évolution de la qualité de la sécurité peut se caractériser par une fonction convexe exponentielle lorsque la sécurité évolue selon la taille des clés de chiffrement employées.

Nous nous sommes alors retrouvé devant un problème NP-complet qui est d'allouer une certaine durée pour l'exécution de la partie optionnelle de chaque travail, cette durée pouvant être différente d'un travail à l'autre pour une même tâche et l'ordonnancement devant rester faisable avec ces parties optionnelles.

Il nous a fallu nous pencher sur des méthodes heuristiques qui essayent en augmentant ou en diminuant étape par étape la longueur des parties optionnelles de se rapprocher de la meilleure récompense qui soit. Nous avons eu l'occasion de nous rendre compte que la programmation des méthodes heuristiques est ardue car celles-ci dépendent de nombreuses variables qu'il faut ajuster pour améliorer les performances de l'algorithme. Nous avons montré que la recherche tabou en comparaison au recuit simulé donne de meilleurs résultats dans ce cadre.

Pour conclure ce mémoire, nous avons comparé nos résultats avec ceux de l'algorithme présenté par H. Aydin dans [3], ceux-ci restent supérieurs aux nôtres dans de nombreux cas, mais nous avons néanmoins montré par nos simulations que dans certaines circonstances (par exemple, lorsque le nombre de tâches est faible ou lorsque l'utilisation par les parties obligatoires est importante) il est possible d'obtenir une récompense plus élevée en utilisant des méthodes heuristiques, d'où l'intérêt porté à ces méthodes. Celles-ci restent actuellement le seul moyen de résoudre des problèmes d'optimisation comme celui développé dans ce mémoire.

Bibliographie

[1] AHMED, Q. N., AND VRBSKY, S. V. Maintaining security in firm real-time database systems. acsac 00 (1998), 83.

[2] AVOINE, G. RFID et sécurité font-elles bon ménage? In Conférence SSTIC (2006).

[3] AYDIN, H., MELHEM, R., MOSSÉ, D., AND MEJIA-ALVAREZ, P. Optimal reward-based scheduling for periodic real-time tasks. IEEE TRANSACTIONS ON COMPUTERS 50,2 (feb 2001).

[4] DAI, W. Librairie crypto++ 5.4. http://www.cryptoppcom/ ,.

[5] DEY, J. K., KUROSE, J., AND TOWSLEY, D. On-line scheduling policies for a class of IRIS (Increasing Reward with Increasing Service) real-time tasks. IEEE TRANSACTIONS ON COMPUTERS 45,7 (jul 1996).

[6] ENGEN, M. Scheduling periodic tasks in real-time system that allow imprecise results. Norwegian University of Science and Technology, 2006.

[7] GOOSSENS, J. Techniques avancées de systèmes d'exploitation. Faculté des Sciences, Département Informatique, Université Libre de Bruxelles, 2004-2005.

[8] GOOSSENS, J., AND MACQ, C. Limitation of the hyper-period in real-time periodic task set generation. In Proccedings of RTS'2001, Paris, France (2001), teknea, Ed., pp. 133-148.

[9] KANG, K. D., AND SON, S. H. Towards security and qos optimization in realtime. ACM SIGBED Review.

[10] KANG, K.-D., AND SON, S. H. Systematicsecurityandtimelinesstradeoffs in real-time embedded systems. rtcsa 0 (2006), 183-1 89.

[11] KOREN, G., AND SHASHA, D. Skip-over : algorithms and complexity for overloaded systems that allow skips. rtss 00 (1995), 110.

[12] LIN, M., AND YANG, L. T. Schedulability driven security optimization in real-time systems. In ARES '06 : Proceedings of the First International

Con ference on Availability, Reliability and Security (ARES'06) (Washington, DC, USA, 2006), IEEE Computer Society, pp. 314-320.

[13] LIU, C. L., AND LAYLAND, J. W. Scheduling algorithms for multiprogramming in a hard-real-time environment. J. ACM 20, 1 (1973), 46-61.

[14] MARCHAND, A., AND SILLY-CHETTO, M. Simulation et évaluation d'algorithmes d'ordonnancement temps-réel sous des contraintes de QoS. Laboratoire d'Informatique de Nantes-Atlantique, Septembre 2004.

[15] PENTTONEN, M. Data security 2003. http://www.csukufi/~junolain/ secu2003/secu2003html ,.

[16] PERRIG, A., STANKOVIC, J., AND WAGNER, D. Security in wireless sensor networks. Commun. ACM 47, 6 (2004), 53-57.

[17] S C H N E IER, B. Applied cryptography : protocols, algorithms, and source code in C, 2nd ed. Wiley, New York, 1996.

[18] SH IH, W. K., AND LIU, J. W.-S. Algorithms for scheduling imprecise computations with timing constraints to minimize maximum error. IEEE Transactions on Computers 44, 3 (1995), 466-471.

[19] STURM, G. Encryption standards: Aes vs. des. http://www.logicat/lvas/ krypto/Sturm/html/index html .

[20] T EG HEM, J., AND PI R LOT, M. Optimisation approchée en recherche opérationnelle. Hermes Science, May 2002.

[21] U.S. DEPARTMENT OF COMMERCE/N.I.S.T. Processing Standards Publication 197 - Advanced Encryption Standard (AES).

[22] WAGNER, D. The boomerang attack. Lecture Notes in Computer Science 1636(1999), 156-1 70.

[23] WIKIPEDIA. Fonction convexe. http://fr.wikipedia.org/wiki/Fonction convexe.

[24] WI KI PEDIA. Théorie de la complexité. http://fr.wikipedia.org/wiki/Th% C3%A9orie_de_la_complexit%C3%A9.

[25] XIE, T., AND QIN, X. Scheduling security-critical real-time applications on clusters. IEEE Trans. Comput. 55, 7 (2006), 864-879.

[26] XIE, T., QIN, X., AND SUNG, A. An approach to satisfying security needs of periodic tasks in high performance embedded systems. The 12th Annual IEEE International Con ference on High Performance Computing (HiPC 2005, poster session).

[27] XIE, T., SUNG, A., AND QIN, X. Dynamic task scheduling with security awareness in real-time systems. ipdps 16 (2005), 274a.

Complément à la démonstration de

H. Aydin

Nous allons présenter ici la preuve que la récompense ne décroît pas lorsqu'une fonction concave de récompense fi(t) est utilisée avec des temps optionnels de tailles identiques pour tous les travaux de la tâche Ti. Cette preuve est utilisée dans la démonstration du théorème 2 (page 55) et est extraite de l'article [3].

Il faut donc prouver que:

>2bi fi(tij) = bifi(t0 i ), (A.1)

j=1

t0 i = ti1+ti2+...+tibi

bi et bi est le nombre de travaux de la tâche Ti jusqu'a l'hyperpé-

riode.

>Ibi

Démonstration: Si fi(t) est une fonction linéaire de la forme fi(t) = ki · t, alors j=1fi(tij) = ki i , ce qui vérifie l'équation A. 1.

(ti1 + ti2 + ... + tibi ) = kibit0

Observons à présent le cas plus général, où fi est concave et pourrait être non-linéaire avec la définition d'une fonction concave:

afi(x) + (1 - a)fi(y) = fi (ax + [1 - a] y) , ?x,y 0 = a = 1 (A.2)

La preuve de la validité de A. 1 est faite par induction. Si bi = 1, l'inégalité A. 1 est trivialement vérifiée. Supposons qu'elle tienne toujours avec bi = 2,3... m - 1.

Cette hypothèse implique d'après A. 1 que:

m-1X

j=1

(ti1 + ... + ti(m-1) )

fi(tij) = (m - 1)fi . (A.3)

m - 1

Choisissons = m-1

m , x =

pouvons écrire:

ti1+...+ti(m_1)

m-1 , y = tim, en remplaçant dans A.2, nous

m-1
m

fi

(ti1 + ... + ti(m -1) ) ( ti1 + ... + ti(m) )

+ 1 mfi(tim) = fi

m - 1 m

 

.(A.4)

En combinant les équations A.3 et A.4, nous obtenons:

1 m

Xm

j=1

(ti1 + ... + ti(m) )

fi(tij) = fi ?m. (A.5)

m

Ce qui établit la validité de A. 1. ut

Code source de la simulation

Nous présentons dans cette annexe le code source C++ de notre simulation ayant servi à obtenir les résultats exposés à la fin du chapitre 5.

B.1 Système

La classe système est la classe principale de notre simulation. Elle contient l'ensemble des tâches stockées dans un vecteur et la file des évènements triée sur base de l'instant de déclenchement de ceux-ci. L'algorithme qui permet de simuler l'ordonnancement du système jusqu'à l'hyperpériode est défini dans cette classe (simulation(Solution* solution)) tout comme les différentes méthodes heuristiques : la descente en cascade (desc_cascade()), le recuit simulé (recuit()) et la recherche tabou (tabousearch()) qui ont été présentées au chapitre 5.

Nous avons également implémenté dans cette classe la méthode optimale avec des fonctions linéaires dont nous avons détaillé l'algorithme au chapitre 4 (page 59).

Le calcul de la récompense obtenue par ces différentes méthodes est effectué par la fonction calcRew().

B.1.1 Systeme.h

Listing B.1 - Systeme.h

1 #ifndef SYSTEM E_H
#define SYSTEM_H

#include "Tache. h" #include "Event.h"

6 #include "Solution.h"
#include "Divers.h"

#include<queue> #include <vector>

11 using namespace std;

typedef priority_queue<Event *, deque<Event *>, EventComparison> QUEUE;

class Tache;

16

class Systeme{

QUEUE eventQueue;//~le qui contient les évènements triés par rapport à l 'instant de déclenchement

double temps;//temps écoulé depuis le lancement de la simulation int hyperP;//l 'hyperpériode (ppcm des périodes)

21 int nbtaches;

double reward;

double d;//slack (durée disponible pour les parties optionnelles)

bool ordo;

26

void tri (int deb, int fin);

int partition (int deb, int fin);

public:

vector<Tache*> task;

31 vector<Tache*> sortedTasks;

Solution * sol ; // solution à simuler

//constructeur/destructeur

Systeme();

36 ~Systeme();

//accesseurs

inline int getHyperP(){return hyperP ;}//hyperpériode

inline int getNbTaches(){return nbtaches;}

inline double getRew(){return reward;}

41 // méthodes

void addEvent(Event* newEvent){eventQueue.push(newEvent) ;}

inline void setOrdo(bool o){ordo=o;}

int calcHyperPer();

double calcRew();

46 void calcSlack();

Tache* addTask(Tache* T);

Travail * getPriorJob();

//algorithmes

51 double linOPT();//Algorithme étalon optiomal dans le cas linéaire

double recuit(); //Recuit simulé

double desc_cascade() ;//Descente en cascade

double tabousearch() ;//Recherche Tabou

56 int simulation(Solution* solution);

void clean ();

void displayTasks(); //affichage

B.1 .2 Systeme.cpp

Listing B.2 - Systeme.cpp

1 #include "Systeme.h" #include "Solution.h" 3 #include "Tache. h"

#include <stdio.h> #include <time.h> #include <math.h>

8 #include <limits> #include <algorithm> #include <vector> #include <list>

13 using namespace std;

Systeme: :Systeme()

{

hyperP=0;nbtaches=0;temps=0.0;ordo=true;reward=0.0;

18 }

Systeme: :~Systeme()

{

sortedTasks.clear();

23 for(unsigned i=0; i< task.size (); i++)

{

deletetask[i];

}

task. clear ();

28 int taille =( int ) eventQueue.size();

for(int i=0; i < taille ; i++) eventQueue.pop();

}

Tache* Systeme::addTask(Tache* T)

33 {

task. push_back(T);

T-->sys=this;

nbtaches++;

return T;

38 }

int Systeme::calcHyperPer()

{

int d=0,i=0,ppcm,max;

43 max=task[0]-->getP();

for(i=0;i<nbtaches;i++)

{

if (task[ i ]-->getP() > max) max=task[i]-->getP(); } // max des période est la tâche i

48 for(ppcm=0;d!=nbtaches;)

{

ppcm+=max;

d=0;

for( i =0;i<nbtaches;i++)

53 {

mf (ppcm%task[i]-->getP()==0)

d++;

}

}

58 mf (d==nbtaches)

{

hyperP=ppcm;

return ppcm;

}

63 else return --1;

}

vomd Systeme : :calcSlack()

{

68 mnt sum=0;

for(mnt i=0; i<nbtaches;i++)

{

task[ i ]-->setb(getHyperP()/task[i]-->getP());

task[ i ]-->setw(task[i]-->getk()/(double)(task[i]-->getb())) ; // retour marginal de la fonction

73 sum+=task[i]-->getb()*task[i]-->getM();

} sortedTasks=task;

tri (0, nbtaches--1);

/* for(int i=0;i<nbtaches;i++)

78 {

sortedTasks[i]-->Affiche();

}*/

// printf (" \n");

d=getHyperP() --sum;

83 }

vomd Systeme::tri(mnt deb, mnt fin)

{ // Fonction QuickSort utilisée pour trier les tâches par rapport à leurs poids mnt middle;

88 mf (deb<fin)

{

middle = partition (deb, fin);

tri (deb, middle);

tri (middle+1, fin);

93 }

}

mnt Systeme::partition(mnt deb, mnt fin) {

98 double x = sortedTasks[deb]-->getw();

mnt i = deb--1;

mnt j =fin+1;

Tache* temp;

do

103 {

j----;

}while (x >sortedTasks[j]-->getw()); 108 do

{

i++;

}while (x<sortedTasks[i]-->getw());

if (i<j)

113 {

temp=sortedTasks[i]; // swap des éléments aux positions i et j

sortedTasks[i]=sortedTasks[j ];

sortedTasks[j]=temp;

}

118 }while(i<j);

return j ; // retourne l 'index du milieu

}

double Systeme::linOPT() 123 {

int i,bi , oi ,bo;

double div,slack = d;

sol = new Solution(this,false); for ( i =0; i<nbtaches;++i)

128 {

bi =sortedTasks[i]-->getb();

oi =sortedTasks[i]-->getO(); bo=bi*oi;

if (bo<slack)//b_i*o_i<slack 133 {

for(int j=0; j<bi;j++)

{

sol -->otime[sortedTasks[i]-->getid()--1][j]=(double)oi;

// printf ("Temps optionnel[Tache no %d,Job no %d]:

%lfln",sortedTasks[i]-->getid(),j+1,otime[sortedTasks[i]-->getid()--1][j]); 138 }

slack--=bo;

}

else

{
143 div = slack/bi ;

for(int j=0; j<bi;j++)

{

sol -->otime[sortedTasks[i]-->getid()--1][j]=div;

// printf ("Temps optionnel[Tache no %d,Job no %d]:

%f\n",sortedTasks[i]-->getid(),j+1,otime[sortedTasks[i]-->getid()--1][j]); 148 }

break;

}

}

i++;

153 while(i<nbtaches)

{

bi =sortedTasks[i]-->getb(); for(int j=0; j<bi;j++)

{

158 sol -->otime[sortedTasks[i]-->getid()-- 1 ][j]=0;

// printf ("Temps optionnel[Tache no %d,Job no %d]:

%f\n ",sortedTasks[i]-- >getid(),j+ 1, otime[sortedTasks[i]-->getid()-- 1][j]);

}

++i;

}

163 simulation(sol);

sol-->printSol(); reward=sol-->rew; delete sol;

return reward;

168 }

double Systeme::recuit()

{

int sim=1, L=nbtaches, cou nter=0;

173 double diminPalier=0.97, moydeltaF=0;

double* paliermax = new double[nbtaches];

for(int i=0;i<nbtaches;i++)

paliermax[i]=min(task[ i ]-->getO(),task[i ]-->getP()--task[i]-->getM());

int cptnonordo=0;

178 double q,p,temper=nbtaches,alpha=0.95,deltaF=0,savrew;

list <Modif*> modiflist;

// solution initiale tous les travaux recoivent 0 comme temps optionnels Solution tempsol(this,true);

183 Solution bestsol(this, true);

int cpt=0;

while(cpt<1 00)//test pour trouver la moyenne deltaF

{

savrew = tempsol.rew;

188 tempsol .getSol Vois(sim,&modiflist, pal iermax); //mets la solution

voisine dans tempsol

si m=si mu lation(&tempsol) ;//simulation avec la solution temporaire

if(sim<1)

{
193 cptnonordo++;

//si ce n'est pas ordonnancable pendant 3 tours

if (cptnonordo%3==0)for(int i=0;i<nbtaches;i++) paliermax[i]*=diminPalier;

}

else cptnonordo=0;

198 // printf ("paliermax = %f\n",paliermax);

// printf ("Recompense: %f\n",reward);

if (tempsol.rew > savrew)

{

//on continue à utiliser la nouvelle solution

203 if (tempsol > bestsol) bestsol = tempsol;

else

{

q=hasard(0.0,1 .0);

208 deltaF=savrew -- tempsol.rew;

moydeltaF+=deltaF;

counter++;

p=exp(--deltaF/temper);

if (q>p)tempsol .back(&modjfl jst) ; //on revient

213 //sinon on continue à utiliser la solution (tempsol)

} cpt++;

if (cpt%L==0)temper*=alpha;

clean ();

218 }

cpt=0;

moydeltaF/=cou nter;

temper=moydeltaF/log(2.0);

for ( int j =0; j <nbtaches ;j++)paljermax[j]=task[ j ]-->getP()--task[j]-->getM(); 223 tempsol.jnjt();

wh i le(cpt<900)

{

savrew = tempsol.rew;

tempsol .getSol Vojs(sjm,&modjfljst, pal jermax); //mets la solution voisine dans tempsol

228 sjm=sjmulatjon(&tempsol) ;//simulation avec la solution temporaire

if(sjm<1)

{

cptnonordo++;

//si ce n'est pas ordonnancable pdt 3 tours

233 if (cptnonordo%3==0)for(int

j=0;j<nbtaches;j++)paljermax[j]*=djmjnPaljer;

}

else cptnonordo=0;

// printf ("paliermax = %f\n",paliermax);

// printf ("Recompense: %f\n",reward);

238 if (tempsol.rew > savrew)

{

//on continue à utiliser la nouvelle solution

if (tempsol > bestsol)

bestsol = tempsol;

243 }

else

{

q=hasard(0.0,1 .0);

deltaF=savrew -- tempsol.rew;

248 p=exp(--deltaF/temper);

if (q>p)//on revient à la solution précédente tempsol.back(&modjfljst);

//sinon on continue à utiliser la solution (tempsol)

}

253 cpt++;

if (cpt%L==0)temper*=alpha;

clean ();

reward = bestsol.rew;

258 delete[] paliermax; bestsol. printSol (); return reward;

}

263 double Systeme::desc_cascade()

{

mnt sim=1;

double diminPalier=0.97, savrew;

double* paliermax = new double[nbtaches];

268 for ( mnt i =0; i <nbtaches ;i++)

paliermax[i]=min(task[ i ]-->getO(),task[i ]-->getP()--task[i]-->getM()); mnt cptnonordo=0;

list <Modif*> modiflist;

// solution initiale tous les travaux recoivent 0 comme temps optionnels 273 Solution tempsol(thms,true);

Solution bestsol(thms, true);

mnt cpt=0;

for ( mnt i =0; i <nbtaches ;i++)paliermax[i]=task[ i ]-->getP()--task[i]-->getM(); tempsol. init ();

278 whmle(cpt<1 000)

{

savrew=tempsol. rew;

tempsol .getSol Vois(sim,&modiflist, pal iermax); Ilmets la solution voisine dans tempsol

si m=si mu lation(&tempsol) ;//simulation avec la solution temporaire 283 mf(sim<1)

{

cptnonordo++;

Ilsi c'est pas ordonnancable pdt 3 tours

mf (cptnonordo%3==0) for(mnt i=0;i<nbtaches;i++)paliermax[i]*=diminPalier;

288 }

else cptnonordo=0;

II printf ("paliermax = %f\n",paliermax);

II printf ("Recompense: %f\n",reward);

mf (tempsol.rew > savrew)

293 {

lion continue à utiliser la nouvelle solution

mf (tempsol > bestsol) bestsol = tempsol;

} else

298 {

tempsol .back(&modiflist); lion revient

ilsinon on continue à utiliser la solution (tempsol)

} cpt++;

303 clean ();

}

reward = bestsol.rew;

delete[] paliermax;

bestsol. printSol ();

308 return reward;

}

double Systeme: :tabousearch() {

313 mnt sim,cpt=0,cptnonordo=0;

vector<Solution*> V; mnt tailleV =41 *nbtaches;//sous voisinage list <Tabou> taboulist; mnt tabousize=1 0;

const mnt cptmax=1 000/tailleV;

318 double diminPalier=0.97;

double* paliermax=new double[nbtaches];

for(mnt i=0; i<nbtaches; i++) paliermax[i] =

min(task[ i ]-->getO(),task[i ]-->getP()--task[i]-->getM());

vector<Solution*>:: iterator it;

323 Solution x(thms,true);

// solution initiale tous les travaux recoivent 0 comme temps optionnels Solution bestsol(thms,true);

Solution* voisine, *tmpbestsol;

328 sim = simulation(&x);

clean () ; II on nettoie la simulation: remise du temps à 0, récompense,...

wh m le(cpt<cptmax)

{

for ( mnt i =0; i < tai lleV ; i ++)// V=sous-- voisinage de x

333 {

voisine = x. getSolVoisine(sim,paliermax); sim=simulation(voisine);

mf (sim<1)

{

338 cptnonordo++;

// si c'est pas ordonnancable pdt 3 tours

mf (cptnonordo%3==0)

for(mnt i=0;i<nbtaches;i++) paliermax[i]*=diminPalier;

// printf ("paliermax: %f\n ",paliermax);

343 }

else cptnonordo=0;

V. push_back(voisine);

clean ();

}

348 sort(V.begin(), V.end(), trisolutions ());

II tri décroissant par rapport à la récompense

it =V.begin();

do

{
353 tmpbestsol=*it;

it++;

}

whmle(tmpbestsol-->tabou .isTabou (&tabou list) && it!=V.end());

mf ( it ==V.end())

358 x=**(V.begin()); Ilmême s'il est tabou

else x = *tmpbestsol;

if (x > bestsol)bestsol=x;

taboulist . push_front(x.tabou); //mise à jour de la liste tabou if (( int) taboulist .size ()>tabousize)taboulist.pop_back();

363 for( it =V.begin(); it !=V.end(); it ++)

delete *it ;

V.clear() ; cpt++;

}

368 delete[] paliermax;

taboulist . clear () ; reward=bestsol.rew; bestsol. printSol () ; return reward;

373 }

void Systeme::clean()

{

Event* ev;

378 int siz = (int)eventQueue.size();

for(int i=0; i < siz ; i++)

{

ev= eventQueue.top();

eventQueue.pop();

383 }

for(int t=0;t<nbtaches;t++)

{

task[t]-->clean();

388 }

reward=temps=0.0;

}

int Systeme::simulation(Solution *solution)

393 {

double sumopt=0, sumtaski=0,remTimeBeforeNextEvent; int Mi,bi ;

sol = solution ;

for ( int i =0; i <nbtaches;i++)

398 { //on regarde d'abord si ça vaut la peine de simuler

Mi=task[i]-->getM();

bi=task[i]-->getb(); for(int j =0; j<bi ; j ++)

{

403 sumopt += sol-->otime[i][j];

}

sumtaski += bi*Mi + sumopt;

sumopt=0;

}

408 if (sumtaski > getHyperP() + 0.00001)

{

// printf ("In******* PAS ORDONNANCABLE **********In");

// printf (" Il y a plus d'unites (%f) que l 'hyperperiode (%d)In",sumtaski,getHyperP());

sol -->rew=0;

413 reward=0;

ordo=false; return --1;

}

418 Travail * priorjob ;

Event* nextEvent;

//displayTasks();

for( int t =0 ;t<nbtaches;t++)//au début toutes les tâches envoient un job (départ simultané)

{

423 addEvent(task[t]-->arrivee);

}

// printf ("*** Hyper--periode= %d ***In",getHyperP());

do//on exécute la simulation sur l ' intervalle d'étude P

{

428 // printf ("I nInstant %f: In",eventQueue.top()-->temps);

do

{

nextEvent = eventQueue.top();

temps = nextEvent-->temps;

433 ordo = nextEvent-->processEvent();

eventQueue.pop();

}while(ordo && fabs(temps -- eventQueue.top()-->temps) < 0.000001);//tant que l'instant est identique

if (!ordo)break;

438 priorjob = getPriorJob() ; // politique EDF

if ( priorjob !=NULL)//s'il y a au moins un job en attente

{

remTimeBeforeNextEvent = eventQueue.top()-->temps -- temps;

if (( priorjob -->getRem() < remTimeBeforeNextEvent) || fabs(priorjob-->getRem() -- remTimeBeforeNextEvent) < 0.000001)

443 {

priorjob -->finJob-->temps = temps + priorjob-->getRem();

addEvent(priorjob-->finJob);

} else

448 {

priorjob -->setRem(priorjob-->getRem()--remTimeBeforeNextEvent);

}

}

}while(temps<getHyperP());

453 if (ordo)

{

// printf ("I n******* Systeme ordonnancable ********** In"); calcRew();

return 1;

458 }

sol -->rew=0;

reward=0;

// printf ("I n******* PAS ORDONNANCABLE **********In"); return 0;

463 }

Travail * Systeme::getPriorJob()

{

double resttime, min=(double)INT_MAX;

468 Travail *job, *priorjob=NULL;

for(int t=0;t<nbtaches;t++)

{ //EDF on compare chaque tâche et on garde le job actif dont l'échéance est la plus proche

job = task[t]-->getJob();

if (job!=NULL)

473 {

resttime=(double)job-->getP()--fmod(temps,(double)job-->getP());//temps

restant avant la prochaine échéance

if (resttime<min)

{

min=resttime;

478 priorjob =job;

}

}

}

return priorjob ; //retourne celui qui a le plus petit temps restant avant

l 'échéance

483 }

void Systeme::displayTasks()

{

for( int i =0; i <nbtaches;i++) 488 {

task[ i]-->Affiche();

}

}

493 double Systeme::calcRew()

{

double sum1 =0, sum2=0; int bi ;

for( int i =0; i <nbtaches;i++) 498 {

bi =task[ i ]-->getb(); sum1=0;

for(int j =0; j<bi ; j ++)

{

503 sum1+=task[i]-->fonction(sol-->otime[i][j]) ;

} sum2+=sum1/(double)bi;

}

sol -->rew=reward=sum2;

508 return reward;

B.2 Tâche

Le système comporte un ensemble de tâches et chaque tâche est définie par certaines caractéristiques : sa période (P), la longueur de la partie obligatoire (M) et la longueur maximum de la partie optionnelle (O). Une instance de la tâche est séparée en deux parties : obligatoire (jobOblig) et optionnelle (jobOpt), nous enregistrons dans chaque tâche un pointeur vers ces deux parties. Chaque tâche possède une fonction de récompense, les fonctions des différentes tâches se différencient par un certain coefficient k. Un pointeur vers l'évènement Arrivée (d'un nouveau travail) est stocké et l'instant de déclenchement de l'arrivée est augmenté de P à chaque instance de manière à gérer l'arrivée suivante.

B.2.1 Tache.h

Listing B.3 - Tache.h

1 #ifndef TACHE_H #define TACHE_H

#include "Travail .h" #include <math.h> 6 #include <list>

using namespace std;

class Travail; class Systeme; 11 classEvent;

class Tache{

int id;

int P;//période

16 int M;//longueur de la partie obligatoire

int O;//longueur maximum de la partie facultative

int b; // nb de fois que l'on peut placer la tâche dans l' intervalle d'étude (hyperpériode)

Travail * jobOblig;

Travail* jobOpt;

21 int numopt;//numéro du job actuel

double k;//coeff de la fonction de récompense

double w;//k/b: importance de la tâche (proportionnel au coef~cient)

public:

Tache(int ident, int per, int man, int opt, double coeff);

26 -'Tache();

Systeme* sys;

Event* arrivee;

//accesseurs

inline int getid(){return id ;};

31 inline int getP(){return P;}; inline int getM(){return M;}; inline int getO(){return O;};

mnlmne mnt getnumopt(){return numopt;}

mnlmne mnt getb(){returnb;};

36 mnlmne double getk(){return k;}

mnlmne double getw(){return w;}

mnlmne bool isJobRunning(){return jobOblig-->isRunning() || jobOpt-->isRunning() ;}

// méthodes

vomd sendJob();

41 Travail* getJob();

vomd clean ();

mnlmne vomd setb(mnt x){b=x;};

mnlmne vomd setw(double x){w=x;};

vomd setArriveeSuivante();

46 vomd Affiche ();

mnlmne double fonction(double t){return k* pow((double)2,(double)t/300);} vomd efface( Travail * job);

};

#end mf

B.2.2 Tache.cpp

Listing B.4 - Tache.cpp

1 #mnclude "Tache. h" #mnclude "Travail .h" #mnclude "Systeme.h"

5 Tache::Tache(mnt ident, mnt per, mnt man, mnt opt, double coeff)

{

id=ident; P=per;M=man;O=opt;k=coeff;

jobOblig = newTravail(thms,true);

jobOpt = new Travail(thms,false);

10 arrivee = new Arrivee(thms,0);

numopt=0;

}

Tache: :~Tache()

{

15 delete jobOblig;

delete jobOpt;

delete arrivee;

}

vomd Tache::Affiche()

20 {

printf ("\nTache %d:\n\nPeriode: %d\nPartie obligatoire: %d\nPartie

facultative: %d\nCoeff de recompense: %.2f\nRetour marginal:

%f\n",id, P,M,O,k,w);

}

Travail * Tache::getJob()

25 {

mf (jobOblig-->isRunning()) return jobOblig; mf (jobOpt-->isRunni ng())return jobOpt; return NULL;

void Tache::sendJob()

{

if (M>0.0)

{

35 jobOblig-->setJob();

}

if (sys-->sol-->otime[this-->getid()--1 ][numopt] > 0.000001)

{ //s' il y a un temps optionnel> 0

jobOpt-->setJob();

40 }

numopt++;

}

void Tache::clean()

45 {

jobOblig-->stop();

jobOpt-->stop();

arrivee -->temps=0;

numopt=0;

50 }

void Tache: :setArriveeSu ivante()

{

sys-->addEvent(this-->arrivee);

}

B.3 Travail

Dans notre simulation, une tâche lance à chaque période deux travaux, nous identifions par oblig le fait que le travail correspond à la partie obligatoire ou optionnelle de la tâche. Ce qui permet de choisir M comme temps d'exécution si la partie est obligatoire ou le temps optionnel (otime) déterminé par les méthodes heuristiques si la partie est optionnelle.

B.3.1 Travail.h

Listing B.5 - Travail.h

1 #ifndef TRAVAIL_H #define TRAVAIL_H

class Tache;
class Event;

6

#include<list>

using namespace std;

class Travail

11 {

private:

Tache* tache;//tâche à laquelle il appartient int P;

16 bool oblig; // partie obligatoire ou optionnelle

bool running;

public:

Event* finJob;

21 //contructeur, destructeur

Travail (Tache* tache, bool mandatory=true); ~Travail ();

//accesseurs

inline double getRem(){return remtime;}
26 inline int getP(){return P;}

inline bool isMand(){return oblig ;}; inline bool isRunning(){return running;}; //méthodes

inline void stop(){running=false;}

31 inline void run(){running=true;}

void setJob();

inline void setRem(double rem){remtime=rem ;};

};

36 #endif

B.3.2 Travail.cpp

Listing B.6 - Travail.cpp

1 #include "Travail .h" #include "Tache. h" #include "Systeme.h"

4

Travail :: Travail (Tache* task, bool mandatory)

{

9 this-->tache=task;

running=false;

P=task-->getP();

oblig =mandatory;

finJob = new FinJob(this,0);

14 }

void Travail ::setJob()

{

if(oblig)

19 {

remtime=(double)tache-->getM();

// printf ('+ La tache %d envoie un travail obligatoire de %f unites\n ', tache-->getid(),remtime);

} else

24 {

// printf ("+ La tache %d envoie un travail optionnel avec

t=%f\n", tache-->getid(),remtime);

}

running=true;

29 }

Travail::-' Travail ()

{

delete finJob;

}

B.4 Générateur

La classe « Generateur » est utilisée pour créer un ensemble de systèmes. Les systèmes seront encodés dans un fichier de manière à récupérer aisément ceux-ci lors de l'exécution de la simulation.

B.4.1 Generateur.h

Listing B.7 - Generateur.h

1 #mfndef GENERATEUR_H

2 #define GENERATEUR_H

#mnclude "Systeme.h"

class Generateur

7{

publmc:

vomd genStaticT(mnt nbsys, mnt n, char fichier []) ; // génère nbsys systèmes de n tâches dans fichier vomd genStaticU(mnt nbsys, double U, char fichier []) ;

12 // génère nbsys systèmes avec une utilisation U dans fichier
Systeme* getNextSystem(long &pos, char fichier[]);

// retourne le système à la position pos dans le fichier

};

#end mf

B.4.2 Generateur.cpp

Listing B.8 - Generateur.cpp

1 #mnclude "Generateu r. h"

#mnclude "Divers.h"

#mnclude "Tache. h"

4

vomd Generateur::genStaticT(mnt nbsys, mnt n, char fichier [])

{ //génère nbsys systèmes de n tâches indépendamment de l'utilisation FILE * simul;

mnt i=O, P, M, O;

9 double currentload = O,coeff;

simul = fopen (fichier , "w");

for( int k=0;k<nbsys;k++)

{

i =0; currentload=0;

14 while(i!=n)

{

P = genPeriode();//Période

M = hasardint(0,P);

O = P--M;//partie facultative max

19 coeff = hasard(1 , 100);// coefficient de la fonction

if (currentload + (double)M/P <= 1) {

currentload += (double)M/P;

24 fprintf (simul,"%d %d %d %lf\n",P,M,O,coeff);

i++;

}

}

printf ("U=%lf\n",currentload);

29 fprintf (simul,"*\n") ;
}

fclose(simul);

}

void Generateur::genStaticU(int nbsys, double U, char fichier[])

34 { //génère nbsys systèmes dans le fichier avec une utilisation proche de U indépendamment du nombre de tâches

FILE * simul;

int i=0, P, M, O;

double currentload = 0,coeff;

simul = fopen (fichier , "w");

39 for(int k=0;k<nbsys;k++)

{

i =0; currentload=0;

while(currentload<=U--0.0005) {

44 P = genPeriode();//Période

M = hasardint(0,P);

O = P--M;//partie facultative max

coeff = hasard(1 , 100) ;// coefficient de la fonction

49 if (currentload + (double)M/P <= U)

{

currentload += (double)M/P;

fprintf (simul,"%d %d %d %lf\n",P,M,O,coeff);

}

54 }

printf ("U=%lf\n",currentload);

fprintf (simul,"*\n") ;

}

fclose(simul);

59 }

Systeme* Generateur::getNextSystem(long &pos,char fichier[]) {

FILE* simul=fopen(fichier, "r");

64 fseek(simul,pos,SEEK_SET);

mnt P,M,O,cpt=0;

Systeme *S = new Systeme();

double coeff;

wh m le(true)

69 { //on crée le système suivant du fichier.

cpt++;

fscanf (simul, "%d %d %d %lf\n", &P, &M, &O, &coeff); S-->addTask(new Tache(cpt,P,M,O,1));

// printf ("%d %d %d %lf\n", P, M, O, coeff);

74 mf ((char)fgetc(simu l)=='*') break;

fseek(simul, --1, SEEK_CUR);

}

S-->calcHyperPer(); S-->calcSlack();

79 pos=ftell (simul);

fclose(simul);

return S;

}

B.5 Dmvers B.5.1 Dmvers.h

Cette classe contient différentes fonctions de base utilisées par les autres classes, permettant de générer un nombre aléatoire entre deux bornes, de générer une période pour une tâche,...

Listing B.9 - Divers.h

1 #mfndef DIVERS_H

#define DIVERS_H

3

const mnt Matrix [5][3]={{2,2,4},{3,3,9},{5,5,25},{7,7,7},{1 1,1 1 ,1 1 }};

double hasard(double a, double b);

mnt hasardint(mnt min, mnt max);

double min(double x,double y);

8 // inline int round(double x){return(x)>=0?(int) ((x)+0.5) :( int) ((x)-- 0.5);}

mnt genPeriode(); #end mf

B.5.2 Dmvers.cpp

Listing B.10- Divers.cpp

1 #mnclude "Divers.h"

#mnclude<stdlib. h>

4

double hasard(double a, double b)

return (a + (b--a)*((double) rand() / (RAND_MAX)));

}

9 mnt hasardint(mnt min, mnt max)

{

return rand() % (max--min+1) + min;

}

double min(double x,double y)

14 {

return x<y?x:y;

}

mnt genPeriode()

{

19 mnt period = 1,p;

for(mnt i=1;i<5;i++)

{

p = hasardint(0,2);

period*=Matrix[i ][ p];

24 }

return period;

}

B.6 Event

La classe Event permet de gérer les évènements lorsque ceux-ci se déclenchent. (voir description section 5.3, page 68)

B.6.1 Event.h

Listing B.1 1 - Event.h

1 #mfndef EVE NT_H #define EVENT_H

4 #mnclude<math.h> #mnclude<stdio. h>

class Tache;

class Travail;

9

class Event {

publmc:

Event(double t, mnt typ) : temps(t),type(typ){ } double temps;

14 mnt type;

vmrtual bool processEvent() = 0;

};

struct EventComparison {//comparateur utilisé pour l'insertion triée 19 bool operator () (Event* left, Event* right) const

{

if (fabs( left -->temps -- right-->temps)<0.000001) return left-->type > right-->type;

return left -->temps > right-->temps;

}

24 };

class FinJob:public Event{

public:

FinJob(Travail* trav, double temps) : Event(temps,1), job(trav){} 29 Travail* job;

virtual bool processEvent();

};

class Arrivee:public Event{

34 public:

Arrivee (Tache* tache, double temps): Event(temps,3), task(tache){} Tache* task;

virtual bool processEvent();

};

39 #endif

B.6.2 Event.cpp

Listing B.12 - Event.cpp

1 #include "Event.h" #include "Tache. h" #include "Travail .h"

bool Arrivee :: processEvent()

6{

if (task-->isJobRunning()) return false;//test du respect de l 'échéance

task-->sendJob();//on lance un nouveau travail temps+=task-->getP();//prochaine arrivee fixée à l'instant courant + période

11 task-->setArriveeSuivante();//repositionnement dans la queue d'évènements
return true;

}

bool FinJob::processEvent()

16 {

job-- >stop() ; //le travail se termine

return true;

}

B.7 Solution

Une solution, qui est en fait une tentative de solution et non la solution définitive, comprend un tableau otime[i][j] qui contient la longueur des parties optionnelles pour tous les travauxj de la tâche i. On passe d'une solution à l'autre en choisissant une solution voisine grâce à la fonction getSolVoisine() ou getSol Vois(). La fonction getSolVoisine() se différencie par le fait qu'elle enregistre

les permutations dans la liste tabou. Une description de la façon dont est sélectionnée une solution voisine est présentée à la section 5.8, page 79. D'autre part, pour l'algorithme du recuit simulé, les modifications effectuées sont enregistrées grâce à la classe Modif, de manière à revenir facilement à la solution précédente sans devoir à chaque fois recopier tout le tableau otime. Cette solution est ensuite transmise en paramètre à la fonction simulation() qui va tester si le système est ordonnançable et si tel est le cas, calculer la récompense accrue avec ces temps optionnels. Cette récompense est enregistrée dans la variable rew qui sera comparée avec la récompense des solutions suivantes.

B.7.1 Solution.h

Listing B.13 - Solution.h

1 #ifndef SOLUTION_H #define SOLUTION_H #include<list>

#include<vector>

using namespace std; 6 class Systeme;

class Modif {

public:

11 int i;

int j; double val;

Modif(int k, int l, double value):i(k), j(l) ,val (value){}

};

16

21

class Tabou {

int a;
int b;

int c;

int d;

public:

Tabou():a(--1 ),b(--1 ),c(--1 ),d(--1 ){}

26 Tabou(int a1,int b1,int a2,int b2):a(a1),b(b1),c(a2),d(b2){}

Tabou (const Tabou & tab) :a(tab.a) , b(tab. b),c(tab.c) , d(tab. d) {} Tabou& operator=(const Tabou& tab);

inline bool operator==(Tabou& x){return (x.a==this-->a &&

x.b==this-->b && x.c==this-->c && x.d==this-->d)II(x.a==this-->c &&

x.b==this-->d && x.c==this-->a && x.d==this-->b);}
inline void set(int i, int j, int k, int l){a=i;b=j;c=k;d=l;}

31 bool isTabou ( list <Tabou>* taboulist);

};

class Solution

{

36 int nbtaches;

int* b;

Systeme* sys;

public:

Solution(Systeme* sys, bool init) ; 41 Solution(const Solution& s);
~Solution();

Solution* getSolVoisine(int sim, double* paliermax);

void getSolVois(int sim, list <Modif*>* backup,double* paliermax); 46 void back( list <Modif*>* modiflist) ;

bool addModif(int i, int j , double val, list <Modif*>* modiflist) ; bool cherchejobO(int &i, int &j, double palier);

void printSol () ;

void init () ;

51 double ** otime; // Tableau des temps optionnels

double rew;//récompense obtenue (calculée après la simulation) Tabou tabou; //Swap effectué sur la solution

bool operator>(const Solution& s);//comparaison de la récompense 56 bool operator<(const Solution& s);//idem

Solution& operator=(const Solution& s);//opérateur d'assignation

};

struct trisolutions

{

61 bool operator()(Solution const* s1, Solution const* s2)

{

if (!s1)

return false;

if (!s2)

66 return true;

return s1-->rew > s2-->rew;

}

};

#endif

B.7.2 Solution.cpp

Listing B.14 - Solution.cpp

1 #include "Solution.h" #include "Systeme.h"

#include "Tache. h"

5

Solution :: Solution(Systeme* systeme, bool init)

{

rew = 0;

sys = systeme;

10 nbtaches = sys-->getNbTaches();

b = new int[nbtaches];

otime = new double*[nbtaches]; for ( int i =0; i<nbtaches;i++)

{

15 b[ i ] = sys-->task[i]-->getb();

otime[i] = new double[b[i]];

if( init )for(int j=0;j < b[i ]; j++) otime[i ][j] = 0;

}

}

20 Solution : :~ Solution ()

{

for(int i=0;i<nbtaches;i++)

{

delete[] otime[i];

25 }

delete[] b;

delete[] otime;

}

30 Solution :: Solution(const Solution& s)

{

rew = s.rew;

sys = s.sys;

tabou = s.tabou;

35 nbtaches = s.nbtaches;

b = new int[nbtaches];

otime = new double*[nbtaches];

for(int i=0;i<nbtaches;i++)

{

40 b[i] =s.b[i];

otime[i] = new double[b[i]];

for(int j=0;j < b[i ];j++) otime[i ][j] = s.otime[i ][j ];

}

}

45 void Solution:: init ()

{

for(int i=0;i<nbtaches;i++)

{

for(int j=0;j < b[i ];j++) otime[i ][j] = 0;

50 }

rew=0;

}

Solution& Solution ::operator=(const Solution& s)

{

55 rew=s.rew;

sys = s.sys;

tabou = s.tabou;

nbtaches = s.nbtaches;

for(int i=0;i<nbtaches;i++)

60 {

b[i] =s.b[i];

for(int j=0;j < b[i ]; j++) otime[i ][j] = s.otime[i ][j ];

}

return *this;

65 }

bool Solution::operator>(const Solution& s) {

return (this-->rew> s.rew);

70 }

bool Solution ::operator<(const Solution& s)

{

return (this-->rew < s.rew);

75 }

Solution* Solution :: getSolVoisine(int sim, double* paliermax)

{

int i = hasardint(0,nbtaches--1);

80 double palier=hasard(0.0,paliermax[i]);

// printf (" palier = %lf, paliermax = %lfIn", palier,paliermax[i ]) ; int j = hasardint(0,b[i]--1);

Solution* x = new Solution(*this);//copie

if (sim==1)//si c'est ordonnançable on augmente un tij de palier ou =maxoptional

85 {

double maxOptional = x-->sys-->task[i]-->getO();

// printf ("Tache %d, OMAX= %lfln",i+1,maxOptional); if (x-->otime[i][j]+ palier <= maxOptional)

{

90 x-->otime[i][j]+=palier ;

// printf ("On augmente otime[%d][%d] de %f --> = %fln",i,j,palier,x-->otime[i][j]);

} else

{

95 x-->otime[i][j]=maxOptional;

// printf ("On augmente otime[%d][%d] a %f\n",i,j,x-->otime[i][j]);

}

}

if (hasard(0,1)<0.5)

100 { //permutation de 2 temps optionnels au hasard sans dépasser oi

int k = hasardint(0,nbtaches--1);

int l = hasardint(0,b[k]--1);

double backup = min(x-->otime[i][j],x-->sys-->task[k]-->getO());
x-->otime[i][j ] = min(x-->otime[k][l],x-->sys-->task[i]-->getO());

105 x-->otime[k][l] = backup;

// printf ("On swap t[%d][%d] et t[%d][%d]ln",i, j ,k, l);

x-->tabou.set(i, j ,k, l ) ;

}

if (sim==--1)//pas ordonnançable car plus d'unités que l'hyperpériode

110 {

if (x-->otime[i][j ] -- palier < 0.0001)

x-->cherchejobO(i,j,palier) ;

//on cherche un job qui possède un temps optionnel > palier

115

if (x-->otime[i][j ]--palier > 0.0001)

{

x-->otime[i][j]--=palier;

// printf ("On diminue otime[%d][%d] de %f --> = %f\n",i,j,palier,x-->otime[i][j]) ;

120 }

else

{

x-->otime[i][j ]=0;

// printf ("On diminue otime[%d][%d] a 0ln",i,j);

125 }

}

return x;

}

130 void Solution :: back( list <Modif*>* modiflist)

{

for( list <Modif*>:: iterator it = modiflist -->begin() ; it != modiflist -->end();)

{

otime[(* it )-->i][(* it )-->j]=(* it )-->val;

135 delete (* it ) ;

it = modiflist -->erase(it);

}

}

bool Solution ::addModif(int i, int j, double val, list <Modif*>* modiflist)

140 {

for( list <Modif*>:: iterator it = modiflist -->begin()

; it != modiflist -->end();it++)

{

if ((* it )-->i==i && (*it)-->j==j)return false;// il y a déjà eu une

modification sur ce job

}

145 modiflist -->push_back(new Modif(i,j,val));

return true; //on a rajouté une modification

}

void Solution ::getSolVois(int sim, list <Modif*>* backup,double* paliermax)

{

150 int i = hasardint(0,nbtaches--1);

double palier=hasard(0.0,paliermax[i]); int j = hasardint(0,b[ i ]--1);

if (sim==1)//si c'est ordonnançable

{

155 double maxOptional = sys-->task[i]-->getO();

// printf ("Tache %d, OMAX= %lfln",i,maxOptional); if (otime[i ][ j ]+ palier <= maxOptional)

{

addModif(i,j ,otime[i ][ j ], backup);

160 otime[i ][ j]+= palier ;

// printf ("On augmente otime[%d][%d] de %f --> = %fln",i,j,palier,otime[i][j]);

}

else

{

165 addModif(i,j ,otime[i ][ j ], backup);

otime[i ][ j]=maxOptional;

// printf ("On augmente otime[%d][%d] a %f\n",i,j,otime[i][j ]) ;

}

170 //permutation de 2 temps optionnels au hasard sans dépasser oi

if (hasard(0,1 )<0.5)

{

int k = hasardint (0, nbtaches-- 1);

int l = hasardint(0,b[k]--1);

175 double sav = min(otime[i][j ], sys-->task[k]-->getO());

addModif(i,j ,otime[i ][j ], backup);

otime[i ][ j] = min(otime[k][ l ], sys-->task[i]-->getO()); addModif(k,l,otime[k][ l ], backup);

otime[k][l] =sav;

180 // printf ("On swap t[%d][%d] et t[%d][%d]\n",i,j,k, l);

}

if (si m==-- 1 )//pas ordonnançable car plus d'unités que l'hyperpériode

{

if (otime[i ][j] -- palier <0.00001)

185 cherchejobO(i,j, palier);

//on cherche un job qui possède un temps optionnel> palier

if (otime[i ][ j]--palier > 0.00001)

{

addModif(i,j ,otime[i ][j ], backup);

190 otime[i ][j]--=palier;

// printf ("On diminue otime[%d][%d] de %f --> =

%f\n ", i,j,palier,x-->otime[i][j ]);

} else

{

195 addModif(i,j ,otime[i ][j ], backup);

otime[i ][j]=0;

// printf ("On diminue otime[%d][%d] a 0\n",i,j);

}

}

200 }

bool Solution ::cherchejobO(int &i, int &j, double palier) // cherche au hasard un job > palier que l 'on peut diminuer

{ //de manière à ne pas boucler en retombant sur des doublons

205 bool trouve=false;

list <int*> combi;

for( int m=0;m<nbtaches;m++)

{

for(int n=0;n<b[m];n++)

210 {

if (otime[m][n]>=palier)

{

int* coord = new int[2];

coord[0]=m;

215 coord[1]=n;

combi.push_back(coord);

}

}

}

220 int size=(int)combi.size();

if (size>0)//s' il y a au moins unjob>=palier

int x = hasardint(0,size-1);//entre 0 et size-1

list <int*>:: iterator it = combi.begin();

225 for( int h=0;h<x;h++)it++;//se positionner sur le xme élément

i = (* it) [0];

j = (*it)[1]; trouve=true;

}

230 for( list <int*>:: iterator

it =combi.begin(); it !=combi.end(); it++)delete[](* it);

combi.clear();

return trouve;

}

235 void Solution:: printSol ()

{

/* printf ("\ nRecompense: %lf",rew);

for (int i=0;i<nbtaches;i.i-.i-)

{

240 printf ("\ n\n Tache %d: ",i.i-1);

for ( int j=0;j<b[i]; j.i-.i-) printf ("% lf , ", otime[i ][j]);

J

printf ("\ n"); */

}

245 //** *****************************

Tabou & Tabou: :operator=(const Tabou & tab)

{

a = tab.a;

250 b=tab.b;

c = tab.c;

d = tab.d; return *this;

}

255 bool Tabou: :isTabou (l ist <Tabou> *tabou l ist)

{

for( list <Tabou>::iterator it =taboulist->begin();it!= taboulist ->end();it++)

{

if ((* it )==*this) return true;

260 }

return false;

}

B.8 Main

Chaque fichier simulation_i.txt représente un ensemble de 1000 systèmes ayant soit le même nombre de tâches (comme dans cet exemple), soit le même facteur d'utilisation. Nous appliquons les algorithmes sur tous les systèmes de chaque fichier. Ensuite, pour chaque fichier on compare la récompense entre les différents algorithmes. Le résultat des simulations est enregistré dans le fichier resultat.txt.

B.8.1 Main.cpp

Listing B.15 - Main

1 #include "Generateur.h" #include<stdio.h>

3 #include<time.h>

#include <stdl ib. h>

using namespace std; 8 int main()

{

srand((unsigned)time(NULL)); //initialise le random

Generateur G;

13

const int TAILLE=1000;//nombre de systèmes à générer par fichier clock_t time1 = clock() ;

FILE* resultat = fopen(" resultat . txt " ,"w");

fprintf ( resultat , "*************************\n");

18 fclose ( resultat ) ;

for(int i=1;i<12;i++)

{

char buffer [20];

sprintf ( buffer , "simulation%d.txt", i ) ;

23 //G.genStaticT(TAILLE,i, buffer) ;//génère un fichier statique de

1000 systèmes de i taches long pos=0;

int nbsys,cpt=0;

double

rewopt=0, rewrecu it=0, rewtabo u =0, rewcascad e=0, m oy recu it=0, m oytabou =0, moycascade=0;

for (nbsys=1;nbsys<=TAILLE;nbsys++)

28 {

printf ("*** %d me systeme ***\n",nbsys);

Systeme* S = G.getNextSystem(pos,buffer);

rewopt=S-->linOPT(); S-->clean();

33 rewrecuit=S-->recuit();

S-->clean();

rewtabou=S-->tabousearch();

S-->clean();

rewcascade=S-->desc_cascade();

38 delete S;

//rapport moyen entre les différentes méthodes et

l'algorithme étalon moyrecuit += rewrecuit/rewopt;

moytabou += rewtabou/rewopt;

moycascade += rewcascade/rewopt;

43

printf ("****recuit ---- moyenne par rapport optimal: %lf\n",(moyrecuit)/nbsys);

printf ("****tabou --- moyenne par rapport optimal: %lf\n", (moytabou)/nbsys);

printf ("****cascade --- moyenne par rapport optimal: %lf\n",(moycascade)/nbsys);

}

48 resu ltat =fopen(" resu ltat. txt" ,"a") ;

fprintf (resultat , "%d tâches: recuit=%f, tabou=%f, cascade=%f\n", i, (moyrecuit)/TAILLE, (moytabou)/TAILLE, (moycascade)/TAILLE);

fclose( resultat);

}

clock_t time2=clock();

53 printf ("Temps de la simulation : %f secondes\n",

(double)(time2-time1)/CLOCKS_PER_SEC);

return 0;

}






Bitcoin is a swarm of cyber hornets serving the goddess of wisdom, feeding on the fire of truth, exponentially growing ever smarter, faster, and stronger behind a wall of encrypted energy








"Les esprits médiocres condamnent d'ordinaire tout ce qui passe leur portée"   François de la Rochefoucauld