INSTITUT NATIONAL POLYTECHNIQUE DE GRENOBLE N◦ attribué par la bibliothèque THÈSE pour obtenir le grade de DOCTEUR DE l’INPG Spécialité : « Informatique : Systèmes et Logiciels » préparée au laboratoire LSR-IMAG, projet SARDES, dans le cadre de l’Ecole Doctorale « Mathématiques Sciences et Technologies de l’Information » présentée et soutenue publiquement par Juraj P OLAKOVIC le 25 Juin 2008 Architecture logicielle et outils pour systèmes d’exploitation reconfigurables Directeur de thèse : Jean-Bernard S TEFANI JURY M. Mme M. M. M. M. M. Gilles Valérie Gilles Jacques Jean-Philippe Jacques Jean-Bernard M ULLER I SSARNY G RIMAUD M OSSI ÈRE FASSINO P ULOU S TEFANI Président Rapporteur Rapporteur Examinateur Examinateur Examinateur Directeur de thèse ii Abstract Dynamic reconfiguration allows modifying a system during its execution, and can be used to apply patches and updates, to implement adaptive systems, dynamic instrumentation, or to support third-party modules. Dynamic reconfiguration is important in embedded systems, where one does not necessarily have the luxury to stop a running system. While some operating systems do offer mechanisms for dynamic reconfiguration, the proposed mechanisms are essentially hardwired in the system. This results in a fixed trade-off between flexibility of reconfigurations and the system’s efficiency which may be far from optimal in certain operational contexts, thus limiting the system reuse. We present an architecture-based programming model allowing both construction of customized reconfigurable system kernels and programming of their reconfigurations. This model is based on the F RACTAL component model and its C implementation for constructing component-based operating systems, called T HINK. The framework supporting our approach encompasses an architecture compiler for building customized system kernels and a reconfiguration compiler. We developed several prototypes of reconfigurable systems that show the flexibility of our approach and the impact of different implementations of reconfiguration mechanisms on the system’s performance. Résumé La reconfiguration dynamique est la capacité d’un système logiciel à permettre sa modification pendant son exécution et peut être utilisée pour mettre-à-jour une partie fautive du système, introduire des algorithmes spécialisés, autoriser des extensions faites par des tiers, adapter le système à un nouvel environment et ajouter des sondes de monitoring ou debugging, etc. Les systèmes d’exploitation existants offrent des mécanismes de reconfiguration dynamique, néanmoins ceux-ci sont figés par l’implémentation du système. Par conséquent le compromis entre la flexibilité et l’efficacité du système reconfigurable est fixe et il n’est pas possible de réutiliser le système dans d’autres contextes opérationnels (avec des mécanismes de reconfiguration différents). Nous présentons une approche architecturale pour la construction de systèmes reconfigurables à la carte et la programmation de leurs reconfigurations. Notre modèle de programmation est basé sur le modèle à composants F RACTAL et son implémentation en C, appelée T HINK. Le canevas associé au modèle comprend un compilateur d’architecture qui permet de construire des systèmes reconfigurables et un compilateur de reconfigurations. Pour illustrer notre approche, nous avons réalisé plusieurs prototypes de systèmes reconfigurables qui ont permis de montrer la flexibilité de notre approche ainsi qu’une évaluation quantitative et l’impact des différentes implémentations de reconfiguration dynamique sur l’efficacité d’un système concret. iii iv Table des matières 1 Introduction 1.1 Une définition . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Intérêts de la reconfiguration dynamique . . . . . . . . . . . 1.3 Systèmes reconfigurables . . . . . . . . . . . . . . . . . . . 1.4 Objectifs de la thèse . . . . . . . . . . . . . . . . . . . . . . 1.4.1 Mécanismes de reconfiguration dynamique . . . . . 1.4.2 Construction des systèmes reconfigurables à la carte 1.4.3 Programmation des reconfigurations . . . . . . . . . 1.5 Organisation du document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I Problématique et Etat de l’art 2 Etat de l’art 2.1 Systèmes reconfigurables . . . . . . . . . . . . . . . . . . . . . . 2.2 Systèmes d’exploitation reconfigurables . . . . . . . . . . . . . . 2.2.1 Classification des systèmes d’exploitation reconfigurables 2.2.2 Modèles d’architecture . . . . . . . . . . . . . . . . . . . 2.3 Synthèse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1 Flexibilité . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.2 Efficacité . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.3 Garanties . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.4 Outils . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . II 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 2 3 4 5 5 5 6 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Contributions 9 9 10 10 13 15 15 17 17 18 20 21 Vers un modèle de construction de systèmes reconfigurables 3.1 Concepts fondamentaux de la reconfiguration dynamique . 3.1.1 Architecture et la reconfiguration dynamique . . . 3.1.2 Etat et la reconfiguration dynamique . . . . . . . . 3.2 Aperçu de la contribution . . . . . . . . . . . . . . . . . . 3.2.1 Limitations . . . . . . . . . . . . . . . . . . . . . 3.2.2 Mise-en-oeuvre . . . . . . . . . . . . . . . . . . . 3.2.3 Organisation de la contribution . . . . . . . . . . . 3.3 Fondations de l’approche . . . . . . . . . . . . . . . . . . 3.3.1 Le modèle à composant Fractal . . . . . . . . . . v . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 24 24 25 26 28 29 29 29 30 TABLE DES MATIÈRES 3.3.2 4 5 6 III 7 TABLE DES MATIÈRES THINK : une implémentation de Fractal en C . . . . . . . . . . . . . . . . . . . 31 Interface de programmation pour la reconfiguration dynamique 4.1 Interface de programmation des reconfigurations . . . . . . . 4.1.1 Motivation des choix de conception . . . . . . . . . . 4.1.2 Identification et introspection . . . . . . . . . . . . . 4.1.3 Contrôle d’état . . . . . . . . . . . . . . . . . . . . . 4.1.4 Modifications de l’architecture . . . . . . . . . . . . . 4.2 Utilisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3 Exemple d’un programme de reconfiguration . . . . . . . . . 4.4 Exemples d’implémentation . . . . . . . . . . . . . . . . . . 4.4.1 Comptage de références . . . . . . . . . . . . . . . . 4.4.2 Intercepteurs dynamiques . . . . . . . . . . . . . . . 4.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 37 37 38 40 42 42 44 46 46 51 55 Compilation des systèmes reconfigurables 5.1 Le principe de la compilation des systèmes reconfigurables 5.2 Le compilateur Think ADL . . . . . . . . . . . . . . . . . 5.3 Mise-en-oeuvre avec Think ADL . . . . . . . . . . . . . . 5.3.1 Spécification des composants reconfigurables . . . 5.3.2 Transformations architecturales . . . . . . . . . . 5.3.3 Intercepteurs et usines d’intercepteurs . . . . . . . 5.4 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 57 58 60 61 63 64 66 Reconfiguration des architectures Think avec FScript 6.1 Le langage FScript . . . . . . . . . . . . . . . . . 6.1.1 Eléments du langage . . . . . . . . . . . . 6.1.2 Bibiolothèque d’actions et de fonctions . . 6.1.3 Exemples . . . . . . . . . . . . . . . . . . 6.2 Supports d’exécution pour FScript . . . . . . . . . 6.2.1 Répartition du support FScript . . . . . . . 6.3 Un compilateur FScript . . . . . . . . . . . . . . . 6.3.1 Défis de l’implémentation . . . . . . . . . 6.3.2 Caractéristiques du prototype . . . . . . . 6.3.3 Mise-en-oeuvre avec Think ADL . . . . . 6.3.4 Le composant de reconfiguration généré . . 6.3.5 Support du système à l’exécution . . . . . 6.4 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 67 68 69 69 70 71 73 74 74 75 76 78 78 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Evaluation Evaluations 7.1 Méthode d’évaluation . . . . . . 7.1.1 Sujet d’évaluation . . . 7.1.2 Critères d’évaluation . . 7.1.3 Prototypes d’évaluation 7.2 Evaluation qualitative . . . . . . 7.2.1 Description du système . 79 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 81 81 82 83 83 83 TABLE DES MATIÈRES 7.3 7.4 7.5 TABLE DES MATIÈRES 7.2.2 Compilation du système . . . . . . . . . . . . . . . 7.2.3 Programmation et compilation d’une reconfiguration Micro-benchmarks . . . . . . . . . . . . . . . . . . . . . . 7.3.1 Intercepteurs dans un modèle à threads . . . . . . . 7.3.2 Un modèle d’exécution événementielle . . . . . . . Evaluation de performances de systèmes reconfigurables . . 7.4.1 Décodeur vidéo H.264 reconfigurable . . . . . . . . 7.4.2 Reconfiguration dynamique des noeuds de capteurs . Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . 7.5.1 Evaluation qualitative . . . . . . . . . . . . . . . . . 7.5.2 Evaluation quantitative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Conclusion 8 86 87 88 88 90 92 92 96 98 98 99 101 Conclusion et perspectives 101 8.1 Bilan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 8.2 Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 8.3 Perspectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 Annexes 107 A Caractéristiques variées des systèmes embarqués 107 B Modèles d’exécution 111 B.1 Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 B.2 Modèle d’exécution événementiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 C Historique du compilateur Think ADL 113 D Optimisations architecturales sélectives D.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . D.1.1 Historique . . . . . . . . . . . . . . . . . . . . . . . . D.1.2 Objectif . . . . . . . . . . . . . . . . . . . . . . . . . D.2 Les problèmes . . . . . . . . . . . . . . . . . . . . . . . . . . D.2.1 Problèmes identifiés de l’implémentation actuelle . . . D.2.2 Problèmes liés à l’implémentation des optimisations . D.3 Vers une implémentation . . . . . . . . . . . . . . . . . . . . D.3.1 Un nouveau langage d’implémentation de composants D.3.2 Spécification des optimisations . . . . . . . . . . . . . D.3.3 Support de compilation . . . . . . . . . . . . . . . . . D.4 Optimisations architecturales et reconfiguration dynamique . . D.5 En conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . 115 115 115 116 117 117 118 118 118 119 119 119 119 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Réferences 121 References 121 vii TABLE DES MATIÈRES TABLE DES MATIÈRES viii Chapitre 1 Introduction Avec la chute des prix des circuits intégrés et les avancées technologiques permettant entre autre leurs miniaturisation, les systèmes embarqués prolifèrent dans notre entourage. Ces systèmes varient du simple capteur ou divers objets communiquants jusqu’aux systèmes embarqués dans les voitures, avions ou dans l’électronique grand public, en passant par des systèmes de télécommunication. Souvent, ces systèmes sont inaccessibles physiquement ou sont sous le contrôle d’un utilisateur final (ou dans sa possession, comme par exemple un téléphone portable). Avec cet avénement de l’informatique ubiquitaire (Weiser, 1991), émerge le problème de la mise-àjour, voire de modification complète, du logiciel qui est embarqué sur ces systèmes. La manière simple consiste à écrire une nouvelle image du système et à le redémarrer. Cependant, ces systèmes fournissent souvent un service qui ne peut pas être interrompu pour mettre à jour le logiciel du système. De plus, un tel arrêt et redémarrage du système implique la perte de l’état du système. Par conséquent il faut se tourner vers des solutions qui permettent une mise-à-jour du système, sans interruption de service et sans perte d’état. Par exemple, le système d’exploitation d’un téléphone portable peut à un moment nécessiter une mise-à-jour de sécurité. Dans ce cas, l’approche de la modification du système avec un redémarrage occasionne une gène à l’utilisateur – le redémarrage lui est visible avec toutes les conséquences (surtout commerciales, dûes à l’inconfort d’utilisation) que cela implique1 . 1.1 Une définition Nous definissons la reconfiguraiton comme : La reconfiguration dynamique est la capacité d’un système logiciel à permettre la modification d’une sous-partie du système pendant son exécution, sans interruption de service. En d’autres termes, une reconfiguration dynamique est une modification de la configuration architecturale (ou structurelle) d’un système pendant son exécution. La configuration i du système évolue vers la configuration i+1 (Kramer & Magee, 1990), par exemple par l’ajout, la suppression ou le remplacement d’une partie de l’architecture. Contrairement à la reconfiguration statique – qui consiste à arrêter le système pour effectuer les modifications et le redémarrer – la reconfiguration dynamique n’interrompt pas l’exécution du système et de ce fait est trasparente à l’utilisateur. 1 ”Do you want to restart your computer now ?” 1 1.2. Intérêts de la reconfiguration dynamique Chapitre 1. Introduction Dans la littérature, d’autres définitions sont proches de la définition de reconfiguration dynamique, notamment l’adaptabilité et l’extensibilité. Une adaptation est une modification d’un système en réponse à un changement du contexte dans lequel il se trouve, créant des conditions particulières ou nouvelles (David, 2005). La notion d’adaptation considère non seulement le système, mais aussi son contexte environnant et des critères permettant d’évaluer la qualité d’une adaptation. Une adaptation peut donner lieu à une modification du système, i.e. reconfiguration dynamique, que nous considérons alors comme un mécanisme de bas niveau à l’adaptation. Quant à l’extensibilité, elle réfère à des mécanismes d’un système d’exploitation permettant l’ajout de nouvelles fonctionalités (ou d’interface plus appropriées) et non à la mise-à-jour des parties existantes. Notre définition de la reconfiguration dynamique englobe l’extensibilité du système. La problématique de la reconfiguration dynamique est souvent considérée comme un cas particulier d’une problématique plus vaste qu’est la configuration d’un système. Dans ce cas la reconfiguration est la capacité de configuration à l’exécution (Denys et al., 2002). La reconfiguration dynamique est aussi appelée online reconfiguration (dans K42 (Soules et al., 2003), mutation (dans MMLite (Helander & Forin, 1998) ou dynamic software update (DSU, dans les travaux de Hicks (Hicks, 2001; Hicks & Nettles, 2005)). 1.2 Intérêts de la reconfiguration dynamique dans les systèmes embarqués Nous référons au système embarqué comme à un sous-système éléctroniquement programmable faisant partie d’un système hétérogène plus large (définition empruntée du projet européen Artist, (Artist2, n.d.)). Cette large définition englobe toute une gamme de systèmes plus ou moins complexes avec des caractéristiques différentes, en particulier, cette définition comprend aussi bien la partie matérielle que la partie logicielle d’un tel système. Le logiciel comprend éventuellement un système d’exploitation et la partie applicative. Dans ce travail nous nous intéréssons plus spécifiquement à la partie logicielle des systèmes embarqués. Nous divisons les systèmes embarqués, en deux catégories – i) les systèmes pour lesquels un arrêt de fonctionnement et réinitialisation pour la mise-à-jour du logiciel est acceptable, voire prévue, et ii) les systèmes où un tel arrêt est inacceptable (par exemple pour des raisons économiques). Dans la suite, nous nous intéressons uniquement à la deuxième catégorie. Les capacités de reconfiguration dynamique ouvrent de nouvelles possibilités à la conception de systèmes embarqués. Pour faire une modification du système déployé, il n’est plus nécéssaire de reconstruire, regénerer et redéployer le système, avec les conséquences comme le redémarrage (donc un temps sans service), perte de l’état actuel du système etc. Par conséquent, nous pouvons exploiter ces capacités pour mettre-à-jour une partie fautive du logiciel, introduire des algorithmes spécialisés, autoriser des extensions faites par des tiers ou adapter le système à un nouvel environment. Mises-à-jour La mise-à-jour d’une partie du système est la première application de la reconfiguration dynamique. Cette mise-à-jour peut être requise soit i) pour corriger un module défaillant dans le logiciel, soit ii) pour adapter le système à un nouvel environment. – Correction de fautes logicielles Selon le système embarqué considéré, la conception du logiciel système passe par une phase de validation importante qui élimine quasiment toute source d’erreur. Cependant, il existe toute une classe de systèmes dont la conception est dictée par le délai de mise sur le marché et où le système commercialisé comporte encore des erreurs à corriger, voire des erreurs de conception. La capacité de reconfiguration dynamique permet alors de corriger ces défauts et la finalisation peut avoir du produit peut avoir lieu même après son déploiement2 . 2 Ceci est le cas des routeurs domotiques tels la LiveBox ou la FreeBox ou des routeurs télécoms. 2 Chapitre 1. Introduction 1.3. Systèmes reconfigurables – Nouvelles versions, nouvelles spécifications Les besoins sur un système peuvent évoluer au cours du temps, donnant lieu à une nouvelle spécification. Dans ce cas, une fois la nouvelle version disponible, la reconfiguration dynamique permet de mettre à jour le système déployé avec cette nouvelle version. Par exemple une mise-à-jour du routeur domotique peut être nécéssaire pour déployer un nouveau service de l’opérateur de (ex. de la passerelle domotique LiveBox de l’opérateur Orange et des service MaLigneTV ou unik). Spécialisations, Algorithmes adaptatifs Les perfomances d’un algorithme implémenté dans le système d’exploitation peuvent varier d’un système embarqué à l’autre. La reconfiguration dynamique permet de remplacer l’algorithme (sous-système) avec une version spécialisée, pendant l’exécution du système. Le concepteur du système développe plusieurs version d’un algorithme qui peuvent être interchangées à l’aide de la reconfiguration dynamique ((Soules et al., 2003) discute l’exemple d’un file cache manager adaptable). Monitoring, debugging Dans l’exemple précédent, la décision de reconfiguration pour spécialiser un module se base sur des données récoltées pendant l’exécution du système, notamment les informations concernant les performances du système. Ces informations peuvent être obtenues en introduisant des sondes de surveillance entre les différents modules du système moyennant la reconfiguration dynamique. Le monitoring des interactions entre les différents modules du système peut s’avérer utile pendant la mise au point d’un système - par exemple comprendre l’origine d’un inter-bloquage (deadlock). Extensions Une autre application de la reconfiguration dynamique se trouve dans l’extension d’un système d’exploitation. Il peut s’agir des extensions effectuées par des tiers, en particulier par des applications (on parle alors de application specific services). Une application n’est plus obligée de se baser sur un ensemble d’interface imposé par le système, mais peut implémenter et utiliser ses propres interfaces systèmes, réalisées sous forme d’extensions. Il en résulte une meilleure efficacité de l’application. Bershad dans SPIN (Bershad et al., 1994) détaille plusieurs scénarios d’extensions système utiles à une application (extension de traitement d’un protocol (IPC, TCP/IP), ordonnancement applicatif, système de fichier applicatif ...). Un système qui autorise des extensions faites par des tiers (via la reconfiguration dynamique) est un système ouvert, et cette capacité entraı̂ne des conséquences en matière de sécurité et de sûreté d’un système ouvert, car il faut pouvoir garantir la fiabilité des extensions ou du moins les isoler en cas de dys-fonctionnement (Denys et al., 2002), (Seltzer et al., Oct. 1996), (Bershad et al., 1994). Support pour l’adaptabilité De manière générale, la reconfiguration dynamique constitue la base pour la conception de système adaptables (self-diagnosing et self-healing) – des systèmes qui évoluent en fonction des conditions de leur environment, de leur contexte (Appavoo et al., 2003). Ainsi, la conception du système évolue vers un élément de la boucle autonomique (autonomic computing) (Horn, 2001), (Horn, 2001). 1.3 Systèmes reconfigurables Un système est reconfigurable lorsqu’il permet de reconfigurer dynamiquement une de ses parties. Un système reconfigurable définit le qui, le quoi, le quand et le comment de la reconfiguration dynamique. La reconfiguration dynamique est à comprendre dans son environment qui comprend à la fois : – des mécanismes (ou algorithmes) de reconfiguration dynamiques, – des moyens (procédés, outils, ...) qui permettent de construire un système reconfigurable, et 3 Chapitre 1. Introduction spécification système reconfigurable exécution système reconfigurable ? spécification reconfiguration plate-forme d'exécution 1.4. Objectifs de la thèse reconfiguration F IG . 1.1 – Les éléments d’un modèle de programmation de systèmes reconfigurables et leur relation. – des moyens qui permettent de programmer des reconfigurations. La relation entre ces trois éléments est schématisée sur la figure 1.1. Dans ce travail, nous appelons l’ensemble de ces trois élements et leur relation un modèle de programmation de systèmes reconfigurables3 . L’élément central de ce modèle de programmation sont les mécanismes de reconfiguration dynamique. Ces mécanismes permettent de modifier le logiciel en cours d’exécution, par exemple en ajoutant de nouvelles parties, ou en modifiant le code ou les données des parties existantes. Pour construire un système reconfigurable, le concepteur détermine quelles sont les parties reconfigurables du système (par exemple application sous-partie du système, composant, une liaison entre composants etc.), avec quels mécanismes et comment ces mécanismes sont accédés depuis les programmes de reconfiguration. La problématique de la reconfiguration dynamique ne s’arrête pas à la définition des mécanismes de reconfiguration et à la construction d’un système reconfigurable, mais définit également comment programmer les reconfigurations une fois que le système est déployé. Le programme de reconfiguration va effectuer les modifications souhaitées en utilisant les mécanismes fournis par le système. 1.4 Objectifs de la thèse Cette thèse est à placer dans le contexte des systèmes embarqués à composants, construits à la carte. Les composants permettent de structurer un système et le concevoir explicitement suivant une architecture logicielle. De plus, les composants sont une unité de réutilisation et permettent ainsi la construction de systèmes à la carte. Nous investiguons ces deux aspects au chapitre suivant. Dans ce contexte, l’objectif de cette thèse est de fournir une approche homogène, permettant à la fois de construire (à la carte) des systèmes embarqués à composants reconfigurables et de programmer les reconfigurations de ces systèmes. Concrétement, nous proposons : – une approche générique pour autoriser l’utilisation de différentes implémentations des différents mécanismes de reconfiguration dynamique dans un système à composants, – une approche et des outils pour la contruction de systèmes reconfigurables à la carte, et – une approche et des outils pour programmer les reconfigurations des systèmes reconfigurables déployés. L’association de ces trois éléments et la définition de la relation entre-eux, définit ce que nous appelons un Modèle de construction de systèmes reconfigurables à la carte. 3 Le modèle de programmation est une vue conceptuelle abstraite de la structure et de l’opération d’un système 4 Chapitre 1. Introduction 1.4. Objectifs de la thèse La suite de cette section décrit plus en détail les objectifs des différents éléments constituant le modèle. 1.4.1 Mécanismes de reconfiguration dynamique Une interface de programmation générique Comme nous allons le voir dans les chapitres suivants, la reconfiguration dynamique repose sur plusieurs mécanismes de base qui peuvent avoir différentes implémentations suivant le système à construire. L’objectif premier étant alors de proposer une abstraction – une interface de programmation – pour ces différents mécanismes de base. Cette interface expose au programmeur des reconfigurations l’implémentation des mécanismes de reconfiguration, pour une partie donnée du système (i.e. chaque partie d’un système peut implémenter sa propre interface de reconfiguration). Une telle interface, permet la séparation des préoccupations entre l’implémentation des mécanismes de reconfiguration et la programmation des reconfigurations se basant exclusivement sur le contrat offert par l’interface proposée. Mécanismes et interface de reconfiguration indépendants de l’implémentation fonctionnelle Nous voulons que l’interface ainsi définie et son implémentation soient indépendantes de l’implémentation de la partie fonctionnelle d’un système (séparation des préoccupations entre l’implémentation des mécanismes de reconfiguration et la partie fonctionnelle du système). De cet objectif découle un certain nombre de propriétés : – L’implémentation d’une telle interface de reconfiguration pour une partie donnée d’un système peut être optionnelle – le système peut omettre l’implémentation des mécanismes de reconfiguration pour une partie du système non-reconfigurable. – Plusieurs implémentations de cette même interface peuvent exister et peuvent être utilisées dans un système, de façon transparente au programmeur de reconfigurations. – Par conséquent, cette indépendance offre une flexibilité pour la construction des systèmes reconfigurables personnalisés (ou à la carte). Implémentation des mécanismes respectant les contraintes de performances Nous voulons que l’implémentation de cette interface de reconfiguration, par conséquent des mécanismes de reconfiguration, puisse respecter les différentes contraintes de performances de la plate-forme cible (par exemple l’occupation mémoire ou le surcoût du temps à l’exécution). 1.4.2 Construction des systèmes reconfigurables à la carte Il existe un certain nombre de canevas à composants permettant de construire des systèmes à la carte. L’objectif est alors de proposer une extension d’un canevas pour pouvoir construire des systèmes reconfigurable à la carte, i.e. avec une interface de reconfiguration. Outils de construction Nous proposons de développer des outils (compilateur) supportant le processus de construction des systèmes reconfigurables. Nous voulons également pouvoir combiner à la reconfiguration dynamique d’autres aspects non-fonctionnels (tel la sécurité). 1.4.3 Programmation des reconfigurations Nous avons pour objectif de proposer un paradigme et des outils adaptés pour programmer les reconfigurations d’un système construit à la carte, homogènes avec le paradigme de construction de systèmes reconfigurables. 5 1.5. Organisation du document 1.5 Chapitre 1. Introduction Organisation du document Le présent document est organisé en trois parties. Etat de l’art et problématique Le chapitre 2 – Etat de l’art – analyse les systèmes reconfigurables existants par rapport à nos objectifs. Description de la contribution Cette partie décrit notre proposition du modèle de programmation de systèmes reconfigurables et les trois éléments qui le composent. Le chapitre 3 – Vers un modèle de programmation de systèmes reconfigurables – présente un aperçu global de notre proposition avec les fondations de notre approche à composants – le modèle Fractal et son implémentation Think. Le chapitre 4 – Interface de programmation pour la reconfiguration dynamique – décrit le premier élément de notre proposition, à savoir comment nous proposons d’implémenter les différents mécanismes de reconfiguration dans un systèmes à composants. Le chapitre 5 – Compilation des systèmes reconfigurables – décrit notre approche pour la construction des systèmes reconfigurables, à l’aide du compilateur Think ADL. Le chapitre 6 – Reconfiguration des architectures Think avec FScript – développe notre proposition pour programmer les reconfigurations d’un système à l’aide d’un langage dédié à la reconfiguration des architectures Fractal. Finalement, l’annexe D – Optimisations architecturales séléctives – décrit un travail en cours sur les optimisations architecturales et leurs combinaison avec l’implémentation des mécanismes de reconfiguration dynamique. Partant du constat que la notion de composant comme entité visible à l’exécution n’est pas toujours nécéssaire, nous avons développé un compilateur capable de générer des structures à composants optimisées où le coût relatif à l’utilisation d’un composant est quasi nul. Evaluation de la proposition Le chapitre 7 – Evaluations – est dédié à l’évaluation qualitative et quantitative de notre proposition. Concrétement nous avons réalisé trois prototypes de systèmes reconfigurables, à travers lesquels nous montrons les différents aspects de l’évaluation du modèle proposé. Nous concluons ce travail au chapitre 8 avec les différentes perspectives qui s’ouvrent par la suite. 6 Première partie Problématique et Etat de l’art 7 Chapitre 2 Etat de l’art Il existe une variété de systèmes d’exploitation, ainsi qu’il existe une multitude d’implémentations de mécanismes de reconfiguration dynamique. L’objectif de ce chapitre est de synthétiser un état de l’art de la reconfiguration dynamique dans les systèmes embarqués.Nous limitons notre analyse aux systèmes disposant d’une architecture. 2.1 Systèmes reconfigurables Le spectre de systèmes1 reconfigurables est large, avec autant de mécanismes pour la reconfiguration dynamique. La figure 2.1 montre la répartition de ce spectre de systèmes en plusieurs catégories suivant l’échelle de la reconfiguration, allant des systèmes d’exploitation reconfigurables, jusqu’aux systèmes répartis. A chaque niveau correspond une problématique propre de reconfiguration dynamique. Ainsi par exemple un mécanisme de reconfiguration pour les systèmes distribués n’aura pas les mêmes contraintes d’efficacité qu’un mécanisme pour les systèmes d’exploitation mono-processeur, voire sur un noeud de capteur fortement contraint en ressources matérielles. Orthogonallement à cette répartition, on trouve des approches se basant sur des techniques particulières liées à une implémentation d’un langage de programmation. programming language (implementation) application application application middleware operating system operating system operating system hardware platform hardware platform hardware platform OS level middleware level distributed operating system hardware platform ... application level hardware platform ... DistOS level language level F IG . 2.1 – Le spectre de la reconfiguration dynamique dans les systèmes. 1 Système au sens large – système informatique plus ou moins complexe, comportant une ou plusieurs plate-formes matérielles, système(s) d’exploitation, des applications etc. 9 2.2. Systèmes d’exploitation reconfigurables Chapitre 2. Etat de l’art Mécanismes de reconfiguration d’applications et approches langages Les approches langages pour la reconfiguration dynamique consistent à s’appuyer sur un langage (souvent avec une sémantique particulière permettant la vérification à l’exécution) et son environnement d’exécution (au minimum un éditeur de liens, sinon un compilateur) qui permet de remplacer des parties de codes ou de données à l’exécution, comme c’est la cas de Dynamic Software Update de Hicks qui utilise Popcorn, une extension au langage C (Hicks, 2001; Hicks & Nettles, 2005; Neamtiu et al., 2006), Dynamic C++ (Hjálmtýsson & Gray, 1998) ou Erlang (Armstrong et al., 1996). Généralement ces approches sont adaptées à la reconfiguration des applications et non des systèmes d’exploitation – dépendence à un langage particulier, nécéssité d’un environment d’exécution. Néanmoins, Dymos (Lee, 1983), un des premiers système d’exploitation reconfigurable, utilise cette technique pour la reconfiguration dynamique (basé sur le langage Modula – *Mod (Cook, 1980)). D’autres mécanismes de reconfiguration d’applications existent, comme PODUS (Segal & Frieder, 1993) ou On-Line Software Version Change (Gupta & Jalote, 1993), le dernier utilise une fonction de transfert d’état entre deux instances du même programme (la notion d’état dans ce cas conret comprend l’état d’exécution d’un programme – pile, tas et variables globales). Systèmes distribués et middleware reconfigurables Il existe un grand nombre de systèmes répartis reconfigurables, dont Argus (Bloom & Day, 1993; Bloom, 1983), PolyLith (Hofmeister, 1994), 2k (Kon et al., 1998), Djinn (Mitchell et al., 1998) ou OpenCOM (Clarke et al., 2001; Coulson et al., 2002). Le défi des mécanismes de reconfiguration dans les systèmes (d’exploitation) distribués ou les intergiciels est de proposer des mécanismes de reconfiguration répartis, qui permettent d’une part de changer la configuration distribuée du système, et d’autre part de synchroniser les modifications entre plusieurs noeuds du système de manière efficace (Ajmani, 2004). Systèmes d’exploitation reconfigurables La conception de mécanismes de reconfiguration pour les systèmes d’exploitation présente des contraintes et défis que nous détaillons par la suite. 2.2 Systèmes d’exploitation reconfigurables Le but de cette section est de présenter brièvement les différentes catégories de systèmes d’exploitation reconfigurables que nous analysons par rapport à nos objectifs dans la section suivante. Nous discutons également les aspects architecture et modèle d’exécution dans ces systèmes. Les systèmes que nous considérons tout au long de ce chapitre sont les suivants : – systèmes à usage général – K42, Synthetix – micro-noyaux et exo-noyaux – L4, Kea, Exokernel – systèmes extensibles – DYMOS, SPIN, VINO, Deimos – systèmes reflexifs – Apertos, Muse, MetaOS – systèmes pour réseaux de capteurs, et – FlexCup, Maté, Contiki, SOS – canevas de construction de systèmes – MMLite, Think 2.2.1 Classification des systèmes d’exploitation reconfigurables Le problème de la reconfiguration dynamique dans les systèmes d’exploitation est ancien. DYMOS (Lee, 1983), un des premiers systèmes reconfigurables, a été proposé il y a plus de 25 ans. Depuis, une quantité de systèmes reconfigurables ou solutions pour la reconfiguration dynamique ont vu le jour. Dans ce spectre, nous avons identifié et classifié un certain nombre de systèmes représentatifs du domaine qui répondent à des besoins différents. 10 Chapitre 2. Etat de l’art 2.2. Systèmes d’exploitation reconfigurables Systèmes à usage général La reconfiguration dynamique dans les systèmes à usage général n’a que peu d’intérêt et se limite à satisfaire le confort d’un utilisateur. Les systèmes, tels que Linux ou Windows (qui sont des systèmes modulaires), fournissant qu’un support limité pour la reconfiguration dynamique, typiquement limité à certaines fonctionnalités comme les pilotes de périphériques – il est possible de charger ou décharger un module noyau. Cependant ces mécanismes sont ad-hoc et n’offrent pas un support complet pour la reconfiguration dynamique. Dans Linux par exemple, les modules peuvent dépendre d’autres modules et pour remplacer (ou reconfigurer) un module sous-jacent, il faut décharger tous les modules qui en dépendent. Par contre, l’emploie des systèmes d’exploitation à usage général dans les serveurs ayant des requis de haute disponibilité motive l’implémentation des mécanismes de reconfiguration dynamique dans ces systèmes. Plusieurs approches existent, comme K42 (Soules et al., 2003; Baumann et al., 2005; Appavoo et al., 2002), Slic (Ghormley et al., 1998) ou Synthetix (Pu et al., 1995b; Pu et al., 1995a). K42 est un système à composants récent qui implémente l’ABI2 Linux. K42 défini un modèle d’exécution particulier, qui permet de détecter un état stable de composant en employant des intercepteurs dynamiques. L’emploie des intercepteurs dynamiques permet de limiter dans le temps l’impact des intercepteurs, introduits uniquement pendant la reconfiguration. Dans nos travaux nous avons implémenté ces mécanismes et nous les discuterons plus en détail dans les chapitres 4 et 7. Synthetix consitue une approche intéréssante en proposant des mécanismes de spécialisation (i.e. reconfiguration) par évaluation partielle d’un système à l’exécution. Micro-noyaux et exo-noyaux Historiquement, par rapport aux systèmes monolithiques, les micronoyaux, comme L4 (Liedtke, 1995), Chorus (Rozier et al., 1988), µ-Choices (Campbell & Tan, 1995; Campbell & Islam, 1993), QNX (Hildebrand, 1992), Kea (Veitch & Hutchinson, 1998) ou Pebble (Gabber et al., 1999), répondent à un besoin de modularité, flexibilité et sécurité des systèmes (Liedtke, 1996). La performance des mécanismes de communication entre les serveurs est au coeur de la conception de tous les micro-noyaux. Un micro-noyau n’est lui-même pas reconfigurable, mais le couplage faible entre les différents serveurs, implémenté par des IPC constitue un mécanisme de base pour la reconfiguration dynamique – à base de redirection de la liaison entre deux serveurs – exploité dans Kea ou Pebble. De plus Kea affine les mécanismes de communication et permet de migrer les serveurs utilisateurs vers le noyau et vice-versa. µChoices permet l’extension d’un noyau en chargeant des agents. Les exo-noyaux – Exokernel (Engler et al., 1995) – poursuivent la philosophie du minimalisme des services systèmes des micro-noyaux, néanmoins tout mécanisme de reconfiguration dynamique est à bâtir par le concepteur d’un système concret, au-dessus de ces services. Les exo-noyaux permettent de construire de systèmes d’exploitation adaptés fonctionnellement à des applications. Systèmes extensibles L’objectif des systèmes extensibles est d’autoriser les applications à personnaliser le système d’exploitation en y chargeant leur propre code permettant d’améliorer les performances et les foncationnalités du système entier (système d’exploitation et application) (Cheung & Loong, 1995). Par rapport à un système reconfigurable, un système extensible permet un ajout de fonctionalités de façon prédéfinie et non la modification du système existant. Notre approche à la reconfiguration dynamique englobe l’extensibilité du système. Les extensions dans les systèmes extensibles ont un accès privilégié au noyau, par conséquent la plupart des systèmes extensibles se focalise sur les mécanismes garantissant la sécurité et la sûreté du système. Les techniques utilisées varient suivant les systèmes et inclus l’utilisation de langages sûrs ou interprétés, l’isolation logicielle de fautes (software fault isolation ou sandboxing) (Wahbe et al., 1993; Rippert & Stefani, 2002) ou un code vérifiable (proof-carrying code) (Necula, 1997; 2 Application Binary Interface - spécification d’un niveau de compatibilité entre le systèmes d’exploitation et les applications. 11 2.2. Systèmes d’exploitation reconfigurables Chapitre 2. Etat de l’art Necula & Lee, 1996) (qui consiste à accompagner un code avec une preuve formelle de validité qui peut être vérifiée par le système au chargement). SPIN (Bershad et al., 1995; Bershad et al., 1994) utilise un langage fortement typé – Modula-3, associé à un compilateur à l’exécution qui par construction vérifie la validité des extensions, ou spindles. Les extensions sont des gestionnaires d’événements qui sont connectés au bus d’événements du système et peuvent ainsi réagir aux événements (appels de méthodes). Dans VINO (Small & Seltzer, 1995; Seltzer et al., Oct. 1996) les extensions sont appelées grafts. Dans VINO la sécurité est atteinte en combinant l’isolation logicielle de fautes (Small, 1997) et en réalisant les reconfigurations du systèmes comme des transactions, permettant de maı̂triser l’utilisation des ressources et valider les extensions. En cas de défaillance, un rollback permet de revenir à l’état avant la reconfiguration. DEIMOS (pour Dynamically Extensible Incrementally Modularised Operating System) (Clarke & Coulson, 1998) est un système extensible qui permet à chaque application de construire son propre environment d’exécution, i.e. système d’exploitation. DEIMOS ne prédéfinit pas d’entité noyau, au lieu de cela, les services systèmes sont implémentés comme modules gérés par un gestionnaire de configuration (configuration manager) – chargement ou déchargement. Les modules DEIMOS implémentent le modèle RM-ODP (Blair & Stefani, 1998) et sont conformes à notre définition de composants, à l’exception du fait que le modèle ODP est un modèle plat. De part sa technique d’implémentation, nous classons également DYMOS (pour Dynamic Modification System) (Lee, 1983) dans cette catégorie. Dymos est un des premiers systèmes reconfigurable (il y a plus de vingt ans !), basé sur une variante de Modula. A l’exécution DYMOS inclus un compilateur et un environment d’exécution (comprenant par exemple un interpréteur de commandes pour la reconfiguration). Systèmes réflexifs Les systèmes réfléxifs, tels que Apertos (Yokote, 1992; Itoh et al., 1995), Muse (Yokote et al., 1991) ou MetaOS (Horie et al., 1998), organisent le système en objets associées à des méta-objets, via un MOP – meta-object protocol. Un premier méta-méta niveau alloue les ressources au système – qui constitue les objets du méta-niveau. Les applications sont les objets du niveau de base qui sont associés à un méta-niveau. Les objets implémentent des interfaces bien définies (Kiczales et al., 1997; Maeda et al., 1997) qui permettent la reconfiguration du système en remplaçant des objets au niveau méta, donc au niveau du système. Par conséquent chaque application (objets au niveau de base) peut personnaliser le système sous-jacent (méta-niveau). Systèmes pour réseaux de capteurs Apparus récémment (Hill et al., 2000), les noeuds de réseaux de capteurs ont la particularité d’être des systèmes très contraints en ressources, nécéssitant des systèmes d’exploitation légér et efficaces. De par la nature de leur déploiement, souvent difficilement accessible physiquement, la reconfiguration dynamique des systèmes pour réseaux de capteurs est essentielle, néanmoins peu d’entre eux le sont. Pour la plupart, ces systèmes remontent des événements de capteurs de manière périodique, étant en veille la plupart du temps. Par conséquent les mécanismes de mise-à-jour se limitent souvent à une réécriture de l’image du système et redémarrage fiables du système (perdant éventuellement un état), comme Mantis (Bhatti et al., 2005). TinyOS (Hill et al., 2000) est l’un des premiers systèmes pour réseaux de capteurs. Les systèmes à composants TinyOS sont construits à la carte, à l’aide du langage nesC (Gay et al., 2003). TinyOS est un système événementiel. TinyOS n’est pas reconfigurable, néanmoins plusieurs approches existent pour permettre la reconfiguration des applications d’un système TinyOS. XNP (Jeong et al., 2003) permet de télécharger et réinstaller une nouvelle image système en effectuant un redémarrage. FlexCup (Marrón et al., 2006) permet la reconfiguration dynamique au grain des applications. Le mécanisme de FlexCip est basé sur des méta-données générées pendant la compilation du système qui permet à un éditeur de 12 Chapitre 2. Etat de l’art 2.2. Systèmes d’exploitation reconfigurables liens embarqué de modifier le système pendant l’exécution. Cette approche nécéssite le partitionnement d’une application en plusieurs composants chacun dans un segment mémoire séparé. Contiki (Dunkels et al., 2004; Dunkels et al., 2006) est un système reconfigurable modulaire pour réseaux de capteurs. Contiki est organisé en modules, une architecture plate. Un coeur non-reconfigurable permet de (télé-)charger les modules (applications ou sous-systèmes), qui constituent alors l’unité de reconfiguration dans Contiki. Un modèle d’exécution événementiel permet une implémentation efficace d’un état stable pour les modules. L’approche SOS (Han et al., 2005) est similaire à Contiki. Finallement Maté (Levis & Culler, 2002) est une machine virtuelle construite avec TinyOS. Les applications Maté sont construites avec un ensemble restreint d’instructions de la machine virtuelle, arrivant à des applications très compactes. Le système permet uniquement une mise-à-jour d’applications de la machine virtuelle. De cette façon, l’approche Maté optimise l’utilisation de la ressource la plus critique – la consommation d’énergie de l’interface radio. Par contre, dû à l’approche de machine virtuelle, Maté présente un impact sur les performances d’un système. Egalement, cette approche ne permet pas une reconfiguration à grain fin, notamment des couches de service, et ne sauvegarde pas l’état d’une application à la reconfiguration. Canevas de construction de systèmes à la carte Les canevas (framework) de construction de systèmes permettent de construire des système à la carte. Pour la plupart il s’agit de systèmes à composants – composants uniquement comme une notion à la conception par conséquent non-reconfigurables, comme OSKit (Ford et al., 1997; Reid et al., 2000) ou eCos (eCos, n.d.), ou également composants à l’exécution qui permettent une reconfiguration du système – MMLite (Helander & Forin, 1998; Forin et al., 2001) ou Think (Sénart, 2003; Charra, 2004; Senart et al., 2002). MMLite est système à composants, implémentant le modèle COM. Dans MMLite un composant implémente une interface et ses dépendences sont bien identifiés ce qui permet de bâtir des algorithmes de détection d’un état quiescent (MMLite est un système multi-threadé) à l’aide de comptage de références (read-write locks). Les travaux récents par A. Sénart et O. Charra revisitent le canevas à composants Think original pour se baser sur le modèle à composants Fractal. Ainsi, Think à travers les interface de contrôle Fractal défini une interface de programmation de reconfigurations. Cependant, ces travaux ne considèrent pas l’implémentation d’un état stable, ni la programmation pratique des reconfigurations tel que présentés dans ce travail. 2.2.2 Modèles d’architecture La notion d’architecture est relative à un état de l’art, une pratique, de construction de logiciels (ou systèmes) et dépend du niveau d’abstraction choisi. Par exemple, peut-on considérer la structure offerte par le langage C comme une architecture ? Oui à la conception, mais cette structure disparaı̂t à l’exécution. De la même manière, un micro-noyau défini également une architecture du système. Parmi l’ensemble des définitions de l’architecture logicielle, nous empruntons celle donnée par Bass et al. (Bass et al., 1998) : ”The software architecture of a program or computing system is the structure or structures of the system, which comprise software elements, the externally visible properties of those elements, and the relationships among them.” Cette définition met en évidence une structure du logiciel et la relation explicite établie entre ses éléments. La notion d’architecture permet de définir les mécanismes de la reconfiguration pour identifier une partie de l’architecture et pour la modifier. Dans les systèmes embarqués nous pouvons considérer l’architecture à plusieurs niveaux. Au premier niveau, l’architecture du système ou son organisation interne à gros grain. Au second plan, architecture 13 2.2. Systèmes d’exploitation reconfigurables Chapitre 2. Etat de l’art logicielle ou style d’architecture, notion dévelopée par Garlan et Shaw (Garlan & Shaw, 1994; Shaw & Garlan, 1996). 2.2.2.1 Architecture système L’architecture du système défini son organisation, en particulier la relation entre le système (d’exploitation) et les applications. Il existe plusieurs architectures de systèmes : – systèmes à protection unique – le système et l’application partagent le même domaine de protection (par exemple DOS ou PalmOS (aujourd’hui Garnet OS) (PalmOS, n.d.)), – noyaux monolithiques – le système est constitué d’un seul bloc sans architecture apparente (les premières versions de Unix, Linux ou Windows), – micro-noyaux – un système minimal fourni une abstraction minimale de la machine sous-jacente et un mécanisme de communication – Inter Process Communication (IPC) (Liedtke, 1996; Liedtke, 1995), le reste du système étant implémenté comme un ensemble de serveurs dans des espaces d’adressage et de protection différents, communicant via des IPCs. Les applications utilisateurs sont implémentées de la même manière et demandent service aux processus serveurs par des IPCs (par exemple L4, QNX, Chorus, µChoices et beaucoup d’autres, les hyperviseurs comme Xen (Barham et al., 2003) sont des cas particuliers de cette architecture), – exonoyaux – un exokernel élimine toutes les abstractions du noyau et se limite au multiplexage et à la protection des resources, tout les services systèmes sont implémentés sous forme de bibliothèques. 2.2.2.2 Architecture logicielle Parmi les différents styles d’architecture logicielle, tels que définies par Garlan et Shaw (Garlan & Shaw, 1994; Shaw & Garlan, 1996) nous comptons par exemple : – structuration en couches – système historique THE (Dijkstra, 1968), – structuration orientée objet – le système Choices ou µChoices, ou K42, – structuration orientée composant (ou module) – pratiquement tous les systèmes modernes tombent dans cette catégorie – SPIN, VINO ou encore MMLite. Le µ-noyau Pebble est un système à composant, tout comme les systèmes construits avec le canevas à composants Think. – structure orientée événément, – structure orientée flot de donnée – les systèmes x-Kernel (Hutchinson & Peterson, 1991) ou Click (Morris et al., 1999). 2.2.2.3 Architecture à composants Le contexte de cette thèse sont les systèmes embarqué à composants. Un composant est défini comme suit (Szyperski, 2002) : ”Un composant logiciel est une unité de composition avec des interfaces spécifiées contractuellement et des dépendances de contexte explicites. Un composant peut être déployé indépendamment et être composé par des tiers pour former des applications ou des composants composites.3 ”. Par rapport aux modules, un composant peut être instancié et les composants peuvent être assemblés de manière hiérarchique et dynamique. Par rapport aux objets, un composant définit explicitement ses dépendences, permettant de répondre aux besoins de configuration logicielle et de déploiement. Ainsi, un système à composants défini les entités architecturales (composants) et les relations entre ces entités (dépendans et liaisons). 3 A component is a unit of composition with contractually specified interfaces and fully context dependencies that can be deployed independently and is subject to third-party composition 14 Chapitre 2. Etat de l’art 2.2.2.4 2.3. Synthèse Description architecturale des systèmes L’architecture d’un système peut être décrite à l’aide de langages spécifiques comme des ADL (Architecture Description Language) – (Medvidovic et al., 2007) (xADL (Dashofy et al., 2001), AADL (SEA-AADL, n.d.), ACME (Garlan et al., 2000), Rapide (Luckham et al., 1995), Wright (Douence et al., 1997), ...) – ou d’autres abstractions comme UML (OMG, 2004; Bell, 2003), DCUP (Plásil et al., 1998). Cette description d’architecture peut avoir des fins variées – une documentation du système, ou son design indépendant de l’implémentation (le cas historique d’UML modélisant une architecture à objets), ou servir au déploiement du système (Darwin (Magee et al., 1995), Olan (Balter et al., 1998)), ou dans d’autre cas servir à construire, ou assembler, le système (le cas des canevas de construction de systèmes comme eCos avec CDL (eCos, n.d.) ou OSKit avec Knit (Ford et al., 1997; Reid et al., 2000)). 2.3 Synthèse La notion de configuration désigne la capacité de personaliser un système lgociel. Cette notion englobe à la fois une capacité de personalisation pendant la conception, aussi bien qu’à l’exécution – reconfiguration dynamique. Il existe plusieurs études de systèmes configurables, notamment Stankovic et al. (Friedrich et al., 2001) ou Denys (Denys et al., 2002), le dernier classifiant les systèmes selon deux dimensions, suivant le moment de l’adaptation et suivant l’initiateur de celle-ci. Quant à Sénart (Sénart, 2003) ou Ketfi et al. (Ketfi et al., 2002), ils proposent une classification des mécanismes de la reconfiguration dynamique suivant les capacités offertes (l’objet de la reconfiguration, le moment, l’initiateur, le type etc.). Classer les systèmes embarqués uniquement suivant les dimensions de la reconfiguration dynamique est reducteur, car souvent la conception des mécanismes de reconfiguration dynamique impacte profondément la conception du système en général. Pour analyser les systèmes reconfigurables par rapport à nos objectifs décrit au chapitre 1-1.4, nous nous sommes inspirés des critères établis d’une part par Hicks (Hicks, 2001; Hicks & Nettles, 2005) et d’autre part par Tournier (Tournier, 2005b), à savoir : – flexibilité des mécanismes de reconfiguration – granularité, les différents types de reconfiguration possibles (composants, liaisons etc.), indépendance et optionalité des mécanismes, – impact sur les performances du système, ou l’efficacité du système reconfigurable, – garanties offertes par le système reconfigurable quant à la reconfiguration dynamique (fiabilité, sûreté, sécurité, robustese etc.), – simplicité d’utilisation (subjectif) ou concrétement le degré et la compléxité de l’outillage permettant de maı̂triser la complexité de conception de systèmes reconfigurables et des reconfigurations. Les résultats de cette analyse sont reportés sur le tableau 2.3.4. 2.3.1 Flexibilité – Objet de reconfiguration ou Qu’est-ce qui peut être reconfiguré dans un système reconfigurable ? – Une partie du système à un grain élevé (comme par exemple un driver complet) ou plus finement des sous-parties plus ou moins complexes, voire des types de données C et leurs instances ? Est-ce que le système entier peut être reconfiguré ou uniquement une partie du système peut être modifiée – cas des systèmes ayant une couche minimale non-reconfigurable. Ici nous identifions deux sous-critères : le grain d’objets reconfigurables et si le système entier peut être l’objet d’une reconfiguration. – Quelles sont les différentes évolutions possibles d’une architecture ? Une reconfiguration n’est pas forcément un remplacement 1-à-1 d’une partie de l’architecture. Par exemple, dans le cas d’une architecture à composants, une reconfiguration peut remplacer un composant par un composant 15 2.3. Synthèse Chapitre 2. Etat de l’art ayant une dépendances supplémentaire. Pour satisfaire la dépendence, la reconfiguration peut alors ajouter un nouveau composant à l’architecture. – Indépendance des mécanismes de la conception du système Il n’existe pas un mécanismes universel répondant à toutes les exigeances d’efficacité, flexibilité etc. Par conséquent un système reconfigurable pourrait offrir plusieurs mécanismes, adaptés aux besoins du système (pouvant évoluer au cours du temps). – Optionalité L’implémentation des mécanismes de reconfiguration, en particulier l’implémentation de la détection d’un état stable ou quiescent, ont un impact considérable sur les performances d’un système. Un système pourrait inclure les mécanismes de reconfiguration (par exemple des intercepteurs) uniquement là où c’est réellement nécéssaire ou spécifié par l’utilisateur – les mécanismes de reconfiguration seraient alors optionnels. Objet de reconfiguration Le grain de reconfiguration qu’un système autorise dépend fortement de son architecture : – les systèmes dont les mécanismes sont conçus au niveau langage d’implémentation (i.e. fonctions, types de données etc.) offrent le grain de reconfiguration le plus fin – SPIN, VINO et DYMOS, – les systèmes qui ont une architecture à grain fin, comme les systèmes à composants ou des objets, – finalement les systèmes modulaires qui permettent une reconfiguration à gros grain - modules, applications (pour Maté ou systèmes pour réseaux de capteurs) ou serveurs systèmes pour les micro-noyaux. Pour la plupart, les systèmes analysés ne sont pas entièrement reconfigurable : – En principe, avec les mécanismes appropriés, les systèmes construits avec Deimos, MMLite et Think ne présentent pas de couche minimale non-reconfigurable et donc toute partie d’un système construit à l’aide de ces canevas peut être objet d’une reconfiguration. – Les autres systèmes sont consistitués d’une couche minimale non-reconfigurable à laquelle les mécanismes de reconfiguration ne sont pas applicables. Evolutions d’architecture Nous distinguons plusieurs catégories de systèmes qui autorisent l’évolution de leur architecture logicielle. Cette capacité est fortement lié à la définition de l’architecture dans chaque système considéré : – Les systèmes à objets ou composants – Think, MMLite, K42, Deimos, Apertors – peuvent faire évoluer leur architecture interne en autorisant la modification des liaisons entre objets (ou composants) et en autorisant l’évolution des définition d’interface des objets (ou composants). – Dans les systèmes à architecture figée à gros grain – micro-noyaux, FlexCup, Contiki, SOS – les modules implémentent des interfaces systèmes représentant des services bien définis, des contrats. L’évolution de cette architecture ne représente pas l’objecitf de ces systèmes. – Dans Maté, les reconfigurations consistent à remplacer une application complète (donc son architecture complète), par conséquent les mécanismes offerts par Maté offrent moins de flexibilité par rapport à d’autres systèmes reconfigurables. – Les systèmes extensibles, tels que SPIN ou VINO, autorisent un ajout de code dans le système. VINO et SPIN autorisent le changement d’une partie de l’architecture grâce aux extensions, l’architecture du coeur du système restant inchangée. Dans DYMOS l’architecture du système sont les éléments du langage Modula et ne peuvent être changés. DYMOS autorise la modification des fonctions existantes, mais non l’évolution de l’architecture. Cependant, les mécanismes de reconfiguration basés sur l’utilisation d’un langage, comme proposé par Hicks (Hicks & Nettles, 2005), peuvent éventuellement offrir des fonctionnalités de réécriture et restructuration du code où il n’y a aucune restriction quant à l’évolution d’une architecture. 16 Chapitre 2. Etat de l’art 2.3. Synthèse Indépendance des mécanismes de la conception du système Dans la plupart des systèmes les mécanismes pour la reconfiguration dynamique sont fortement dépendant de la conception du système et le système propose uniquement un seul ensemble de mécanismes (par example un mécanisme pour la détection d’un état stable/quiescent) : – MMLite implémente plusieurs mécanismes gérant l’état d’un composant pendant sa reconfiguration. Entre autres MMLite implémente le mécanisme de comptage de référence. – La conception des interfaces de contrôle du modèle Fractal appliqué au canevas Think laisse supposer l’indépendance des différentes implémentations. Nous confirmons cette hypothèse dans le chapitre 4. Optionalité des mécanismes Les mécanismes de reconfiguration ont un impact sur les performances du système. Par conséquent un système où les mécanismes sont implémentés sélectivement (là où besoin) permet de réduire cet impact sur les performances : – Pour détecter un état quiescent, K42 implémente des intercepteurs dynamiques, introduits uniquement pendant la reconfiguration. Ces intercepteurs sont génériques, par conséquent en dehors de la reconfiguration, un composant ne paie aucune surcoût lié à la capacité de reconfiguration et cette implémentation peut être considérée comme facultative. – Les mécanismes sont optionnels dans MMLite. – Pour les autre systèmes, les mécanismes sont figés et inclus à la conception du système, voire obligatoire pour les systèmes refléxifs, car la reconfiguration est le concept de base de ces systèmes. 2.3.2 Efficacité Les mécanismes de reconfiguration ont un impact sur les performances du système reconfigurable – sur les performances nominales, sans reconfiguration, et sur les performances pendant la reconfiguration. Chaque système prend en compte de façon diverse les critères de performance – temps d’exécution (ou le temps de calcul), l’occupation de l’espace mémoire, l’utilisation de l’énergie (en rapport avec le temps de calcul), les temps de réponses, l’utilisation de la bande passante etc. L’efficacité est un bon critère de comparaison, cependant les systèmes étudiés divergent dans leur conception (et leur but) et il est nécéssaire d’analyser chaque système en profondeur pour en tirer des éléments de comparaison. De plus, ces systèmes implémentent différents mécanismes de reconfiguration ce qui accroı̂t la dispersion. Par conséquent nous résumons uniquement quelques remarques : – DYMOS implémentent un système complexe de verrouillage à l’entrée et sortie de chaque méthode qui est une source de dégradation de performances. Les reconfigurations dans DYMOS sont compilées à l’exécution par le système lui-même. – VINO implémente modifie les grafts avec l’outil MiSFIT pour inclure des vérifications à l’exécution. – Dans SPIN, les spindles sont compilés à l’exécution par un compilateur inclus dans le système. – Maté est une machine virtuelle, donc par sa conception comprend un surcoût lié à l’exécution du byte-code. – La discussion sur l’impact d’IPCs sur les performances des micro-noyaux est ancienne (Liedtke, 1993; Haertig et al., 1997). 2.3.3 Garanties La reconfiguration dynamique affecte le code et les données d’un système d’exploitation. Une reconfiguration peut être source d’erreurs et ammener le système à défaillir : 17 2.3. Synthèse Chapitre 2. Etat de l’art – le nouveau code instantié par la reconfiguration peut être défaillant, causant des erreurs, – le nouveau code peut intéragir avec le reste du système ne respectant pas les spécifications, – un transfert d’état incomplet peut corrompre l’état du système, par exemple les nouvelles données instantiées par la reconfiguration peuvent être mal initialisées, – les mécanismes de reconfiguration peuvent être exploités par des tiers, pour faire défaillir le système (en ajoutant un code défectueux ou en corrompant les données), – le programme de reconfiguration lui-même peut être défaillant et peut abandonner le système dans un état non-cohérent, par exemple où la reconfiguration ne satisfait pas toutes les dépendances d’une partie du système, ou un nouveau module est incomptabile avec le reste du système, – ... Par conséquent, il est nécéssaire qu’un système reconfigurable garantisse un certain nombre de propriétés par rapport à la conception des mécanismes de reconfiguration et la programmation des reconfigurations, de façon à fiabiliser le système et le processus de reconfiguration. Alors que la liste exacte des garanties dépend du système, de son architecture et des capacités de reconfiguration qu’il offre, on peut dégager trois garanties majeures : – cohérence de l’architecture – l’architecture du système après reconfiguration soit cohérente par rapport à sa spécification, – le nouveau module est valide suivant une liste de critères établis par le système, en particulier le système garantit que son fonctionnement est correct (fiabilité), – les mécanismes de reconfiguration ne peuvent pas être détournés pour faire défaillir le système (sécurité). Cohérence architecturale – Les systèmes qui n’autorisent aucune évolution de l’architecture n’ont pas la nécéssité d’offrir cette garantie. – Les systèmes qui autorisent une évolution de l’architecture au cours d’une reconfiguration garantissent au moins la cohérence architecturale du système. Fiabilité – Dans SPIN, les spindles sont écrits en Modula et un compilateur garantie leur validité. – Dans VINO, les grafts sont modifiés pour inclure des vérifications à l’exécution qui empêche typiquement à une extension d’exécuter ou modifier le code système de manière non-autorisée (ce qui induit un coût). – La machine virtuelle Maté interprète un byte-code et par conséquent son exécution est soumise à validation. – Les autres systèmes ne font pas de garanties de sûreté des extensions ou du système. 2.3.4 Outils Pour être utilisés (et pour minimiser le risque d’erreurs), la construction des systèmes reconfigurables et l’écriture des reconfigurations doivent être simples à utiliser. La notion de simplicité étant intuitive et subjective4 , nous nous intéréssons au degré d’outillage des systèmes reconfigurables. Le rôle de l’outillage est d’autant plus important pour les systèmes construits à la carte. Nous avons considéré les outils qui assistent le concepteur du système dans la construction du système, mais également dans le programmation des reconfigurations. Il y a peu d’informations concernant les outils associés aux différents systèmes, par conséquent dans plusieurs cas nous avons supposé leur existance. 4 mais importante ! 18 Chapitre 2. Etat de l’art 2.3. Synthèse – Les systèmes qui inclus un compilateur d’extensions (evtl. à l’exécution) – DYMOS, SPIN ou VINO – offrent des outils nécéssaires pour à la fois construire un système et programmer sa reconfiguration. – Dans le cas de Maté, le compilateur de programmes pour la machine virtuelle est suffisant pour construire le système et pour construire les reconfigurations. – A notre connaissance les autres systèmes ne fournissent qu’un compilateur permettant de construire ces systèmes, mais non leur reconfigurations. K42 Synthetix µ-kernels, Kea DYMOS SPIN VINO Deimos Apertos, Muse MetaOS FlexCup Maté Contiki, SOS MMLite Think g ** ** * *** *** *** ** ** ** * * ** ** ** Flexibilité e i *** ** ? ** * ** ** *** *** *** ** ** √ ? *** √ *** Efficacité o √ ? √ ? - t ? - √ √ √ - √ √ ? ? - √ √ √ - ? √ √ √ Garanties a f √ √ √ √ √ √ √ √ √ √ √ √ - Outils * ** * *** *** *** * * * ** *** * * * Légende abbréviations Flexibilité abbréviations Garanties g – grain a – cohérence architecturale e – évolution architecturale f – fiabilité o – optionalité des mécanismes t – tout système reconfigurable i – indépendances des mécanismes de la conception du système évaluation des systèmes ***,**,* qualité √ la fonctionnalité existe ou la contrainte est garantie ? aucune information rien (-) fonctionalité probablement possible, mais ne représente pas le but du système, aucune information TAB . 2.1 – Les systèmes d’exploitation reconfigurables – synthèse. 19 2.4. Conclusion 2.4 Chapitre 2. Etat de l’art Conclusion Comme le présente ce chapitre, l’ensemble des choix et critères d’implémentation d’un système reconfigurable est vaste. Le choix d’une architecture pour un système détermine grandement ses capacités de reconfiguration. De plus, le choix du modèle d’exécution déterminera pour la grande partie l’implémentation d’un état stable – mécanisme coeur de la reconfiguration dynamique, mais également le mécanisme dont l’implémentation peut avoir le plus d’impact sur les performances du système. Les critères associés à l’évaluation des systèmes d’exploitation et des mécanismes de reconfiguration forment des contraintes très fortes et variées. Par rapport à nos objectifs, nous constatons que chaque système fige un mécanisme de reconfiguration. Ce mécanisme est étroitement lié à la conception du système et n’est pas optionnel, ce qui implique une dégradation de performances. Ce constat guide notre proposition d’une approche de construction de systèmes reconfigurables à la carte et leurs reconfigurations – l’objectif principal de notre approche, que nous présentons dans les chapitres suivants, est de proposer un modèle de programmation indépendant d’un système concret et d’un mécanisme concret, avec des outils sous-jacents, qui permettent de construire des systèmes reconfigurables à la carte et également leurs reconfigurations. 20 Deuxième partie Contributions 21 Chapitre 3 Vers un modèle de construction de systèmes reconfigurables La contribution de nos travaux consiste à définir un modèle pour à la fois construire à la carte des systèmes reconfigurables et programmer les reconfigurations de tels systèmes. L’élément central de notre approche est l’architecture du système, donnée par un modèle à composants qui donne une représentation manipulable du système. – L’utilisation d’un modèle à composants nous permet de construire des systèmes reconfigurables à la carte, de manière flexible. – L’analyse de l’architecture du système, telle qu’écrite par le concepteur, permet d’intégrer au système l’implémentation d’une interface de reconfiguration et donc des mécanismes de reconfiguration, portés par les composants. – Les reconfigurations dynamiques sont programmées comme les modifications de l’architecture. Par conséquent leurs validité peut être vérifiée uniquement en analysant l’architecture du système. La figure 3.1 montre cette approche. spécification système construction système système d'exploitation exécution méta-données pour la reconfiguration architecture spécification reconfiguration construction reconfiguration programme de reconfiguration reconfiguration F IG . 3.1 – Notre proposition d’un modèle de programmation de systèmes reconfigurables, articulé autour d’une architecture du système à composants. Dans ce chapitre nous décrivons notre approche. Nous nous intéressons d’abord aux concepts fondamentaux de la reconfiguration dynamique qui ont motivé notre contribution. Ensuite nous décrivons les grands traits de notre proposition et leur articulation, pour finalement terminer avec la description des fondements de l’approche – à savoir le modèle à composants F RACTAL et son implémentation appelée T HINK. 23 3.1. Concepts fondamentaux de la reconfiguration dynamique 3.1 Chapitre 3. Aperçu de la contribution Concepts fondamentaux de la reconfiguration dynamique Déroulement d’une reconfiguration Intuitivement, une reconfiguration dynamique se déroule de la manière suivante (illustré également sur la figure 3.2), il faut : – identifier et délimiter la partie du système à reconfigurer [ident], – suspendre son exécution (pour éviter de corrompre le système) [suspd] , – modifier la configuration du système (ajouter, supprimer des parties...) [modif], – transferer l’état vers les nouvelles parties [transf], et – reprendre l’exécution de la partie interrompue du système [resum]. ident B B suspd A A C C modif D D B B A' resum A' modif transf A C C F IG . 3.2 – Déroulement d’une reconfiguration : ident - identification de la partie à reconfigurer, suspd suspension de l’exécution de cette partie, modif - modification de l’architecture, transf - transfer d’état vers les instances de l’architecture, et resum - reprise d’exécution. De cette définition intuitive du déroulement de la reconfiguration découle un certain nombre d’opérations et de mécanismes de base de la reconfiguration dynamique que nous étudions dans cette partie : – un modèle d’architecture – permettant d’effectuer les opérations ident et modif, et – un modèle permettant de capturer et contrôler l’état du système – permettant d’effectuer les opérations suspd/resum et transf. 3.1.1 Architecture et la reconfiguration dynamique Identification et délimitation Une architecture logicielle du système est le fondement de la reconfiguration dynamique. Elle permet à la fois d’identifier la partie du logiciel à l’exécution, que nous appellerons composant, à reconfigurer, ainsi que de ”délimiter” ce composant (component boundaries). La représentation de l’architecture à l’exécution (et donc la capacité de pouvoir identifier un composant à l’exécution) est essentielle. 24 Chapitre 3. Aperçu de la contribution 3.1. Concepts fondamentaux Pour délimiter un composant, il est nécéssaire de localiser le code, les données et les composants qui peuvent les accéder ou modifier. En d’autres termes, un composant reconfigurable doit encapsuler ses données self-contained et il doit être possible d’identifier les points d’accés aux services (fonctions) qu’il offre (well-defined access points). Modifications architecturales Pour pouvoir modifier l’architecture d’un système, il est nécéssaire de pouvoir rediriger les appels entre les composants de l’architecture, par exemple, lors d’un remplacement de composant, il faut rediriger tous les appels vers le nouveau composant. En général, une architecture faiblement couplée permet de réaliser la redirection des appels – les modules étant liés par une indirection, il suffit de changer la liaison entre les modules (Ghormley et al., 1998; Soules et al., 2003). De plus, comme nous allons le voir dans la section suivante, la connaissance de dépendence entre les élements est fondamentale pour pouvoir contrôler l’état d’exécution d’un composant. 3.1.2 Etat et la reconfiguration dynamique Une reconfiguration, i.e. la modification de données et/ou du code d’une partie du système, ne peut avoir lieu alors que celle-ci peut être accédée de façon concurrente – il faut garantir que les données de la partie du système à modifier ne sont pas en train d’être modifiées et que le code de cette partie ne peut pas s’exécuter pendant la reconfiguration (sous risque de comportement imprévisible et corruption du système). Par conséquent il est nécéssaire de connaı̂tre l’état du système. Nous définissons l’état comme un vecteur de variables observables – un ensemble de propriétés mesurables à un instant donné qui caractérise une situation du système (?). Cette définition comprend deux notions : – l’état interne de la partie du système à reconfigurer – les données internes (variables et ressources), et – l’état de contrôle lié au modèle d’exécution du système, par exemple l’état de la pile dans les système multi-thread. L’état d’un système évolue suivant une dynamique décrite en partie dans le programme (modèle de programmation) et en partie décrite par un modèle d’exécution. Par exemple, le modèle d’exécution multi-thread spécifie que les threads évoluent de manière concurrente et peuvent se synchroniser à l’aide de points de synchronisation (sémaphores). A l’opposé, un modèle événementiel organise l’exécution comme un traitement d’événements indépendent et ne spécifie rien quant à l’évolution concurrente ou l’ordre d’exécution des événements (spécifié par une politique d’ordonnancement). L’annexe B décrit les deux modèles d’exécution événementiel et multi-thread. Cette définition d’état permet alors de définir les deux autres opérations de la reconfigurations dynamique : – la suspension de l’exécution d’une partie du système (ou sa reprise), comme la détection d’un état stable, et – le transfert d’état entre parties du systèmes. Etat stable et modèle d’exécution L’état stable est un état dans lequel le système (ou une partie du système) n’évolue pas – l’état est alors observable. Typiquement, dans un système multi-processus, une partie du système est dans un état stable, lorsqu’elle n’est accédée par aucun thread. Pour pouvoir caractériser un état stable d’un système (ou d’une partie), il faut prendre en compte sa dynamique d’exécution, définie par le modèle d’exécution. Suivant le modèle, un état stable peut être obtenu par construction (modèle événementiel décrit dans le chapitre 7) ou peut être obtenu par 25 3.2. Aperçu de la contribution Chapitre 3. Aperçu de la contribution détection à l’exécution, par exemple dans le modèle multi-thread. Dans ce dernier cas, l’état stable est un état quiescent, voir également la discussion détaillée en annexe B. Transfert d’état Le transfert d’état est l’opération qui permet de récuperer l’état interne d’une partie du système à reconfigurer et l’injecter dans une nouvelle instance. L’état interne (données) d’une partie d’un système peut être complexe, allant d’un ensemble d’attributs, jusqu’à des structures compliquées comme par exemple les listes de processus bloqués sur un sémaphore. Chaque partie du système défini la sémantique de son état et la façon à laquelle cet état est maintenu (format). Par exemple, dans le cas du sémaphore, la nouvelle implémentation peut maintenir son état dans un tableau alloué statiquement (format), avec par exemple plus d’informations concernant les processus, informations non-disponibles dans l’état actuel (sémantique). Dans ce cas, le transfert d’état n’est pas uniquement une correspondance 1-à-1 et nécéssite une intervention du programmeur de reconfiguration pour transformer l’état (format et sémantique). La transformation du format peut nécéssiter un passage par un format commun (ou générique) aux deux composants, à partir de ce format, des mécanismes complexes de correspondance de format et sémantique pourraient être bâtis (en utilisant des langages spécialisés par exemple). Par conséquent pour effectuer un transfert d’état, il est nécéssaire de : – disposer d’une spécification de ce qu’est l’état d’un composant – l’ensemble relevant des variables observables (format et sémantique), – pouvoir accéder à l’état du composant (pour le récupérer ou l’initialiser), – pouvoir accéder à cet état de manière cohérente, lorsque le composant est dans un état stable, – transformer (éventuellement) cet état pour qu’il soit conforme aux spécifications du nouveau composant (format et/ou sémantique). Un mécanisme de haut-niveau peut être bâti pour automatiser les transformations de format ou sémantiques lors d’un transfert d’état. Un tel mécanisme nécéssite uniquement une interface de bas niveau d’accès à l’état interne du composant. 3.2 Aperçu de la contribution Concrétement notre approche se base sur le modèle à composant F RACTAL (Bruneton et al., 2006; Bruneton et al., 2004) et sur son implémentation en C appelée T HINK, permettant de construire à la carte des systèmes embarqués à composants (Fassino et al., 2002; Fassino, 2001). Un systèmes à base de composants T HINK ne requièrt aucun environment d’exécution supplémentaire – tout est composant. De plus un composant F RACTAL/T HINK représente à la fois une entité de conception et une entité à l’exécution, donnant ainsi la possibilité de manipuler l’architecture à l’exécution. Par rapport à d’autres modèle existants (comme EJB (DeMichiel & Keith, 2006), CCM (OMG, 2001; OMG, 2002), OpenCOM (Clarke et al., 2001) et d’autres), Fractal présente plusieurs avantages qui ont conduit à son adoption comme base de nos travaux : – F RACTAL est un modèle à composant minimal et extensible, où tout concept est optionnel, visant à construire des systèmes flexibles. – Le concept des interfaces de contrôle permet de séparer les aspects fonctionnels des aspects nonfonctionnels, telle la reconfiguration dynamique, et permet d’implémenter différents mécanismes de reconfiguration de façon indépendente et transparente à l’utilisateur (partie fonctionnelle du composant). – Le modèle est réflexif, en particulier, un composant Fractal est une entité à l’exécution, donc identifiable lors de la reconfiguration dynamique. La réflexivité permet de retrouver et manipuler l’architecture d’un système pendant son exécution. 26 Chapitre 3. Aperçu de la contribution 3.2. Aperçu de la contribution – Le modèle est hiérarchique – les composants peuvent contenir d’autres sous-composants, permettant de construire des mécanismes de reconfiguration avec des périmètres différents (par exemple au niveau d’une application entière, construite comme un composant composé d’autres composants, ou au niveau d’un composant de base). – Il existe plusieurs implémentations de F RACTAL, en particulier le canevas de construction de systèmes embarqués flexibles appelé T HINK. T HINK n’impose aucune philosophie prédeterminée de système (micro-noyau, monolithique ou exo-noyau) et son empreinte mémoire permet de construire des systèmes pour les réseaux de capteurs (Jarboui et al., 2006a). – Le canevas T HINK est accompagné d’un compilateur extensible (Ozcan, 2007; Leclercq et al., 2007) qui constitue un embryon d’un outil permettant l’intégration des aspects liés à la reconfiguration dans le processus de construction de systèmes. – Il existe une bibliothèque de composants T HINK, appelée Kortex, pour la construction des systèmes d’exploitation. – Récemment un langage de reconfiguration des architectures F RACTAL a été défini et implémenté. Ce langage, appelé FS CRIPT permet d’écrire les reconfigurations sous forme d’un programme impératif, manipulant les éléments du modèle F RACTAL (composants, liaisons, interfaces, ...). Dans notre approche, la reconfiguration dynamique consiste à modifier l’architecture F RACTAL d’un système à l’exécution, par conséquent avec cette approche nous autorisons plusieurs types de reconfigurations architecturales : – Modification de liaisons. Dans une architecture Fractal, nous pouvons alterer les liaisons – soit mettre en place une nouvelle liaison ou la supprimer (dans le cas de liaisons optionnelles), soit rediriger la liaison vers une autre interface (d’un autre composant). – Modification d’attributs. Dans Fractal, les attributs de composants sont des éléments d’architecture. Lors d’une reconfiguration, le programmeur peut être ammené à changer un attribut d’un composant. – Création de composants. Les composants Fractal peuvent être instanciés à partir des usines de composants. Un système à l’exécution peut contenir des usines de composants (elles-mêmes des composants). Dans notre proposition, en plus des usines embarquées dans le système, une instance de composant peut être téléchargée pendant la reconfiguration. Un composant nouvellement créé doit être intégré à l’architecture. Le programmeur peut l’insérer dans un autre composant et créer (ou modifier) les liaisons de ce composant. – Suppression de composant. A l’exécution, il est possible de supprimer un composant Fractal. La suppression entraı̂ne la modification ou la suppression des liaisons du composant parent. – Remplacement de composant. Le remplacement d’un composant est la combinaison de l’ajout d’un nouveau composant et de la suppression d’un autre composant. Le nouveau composant doit fournir les mêmes interfaces serveur que le composant à remplacer. L’état encapsulé par le composant à supprimer peut être transféré vers le nouveau composant. Par conséquent, avec notre approche, il n’y a pas une reconfiguration dynamique, mais des reconfigurations dynamiques – deux reconfigurations du système peuvent différer, par exemple une reconfiguration ajoute un composant et le lie au reste de l’architecture, une autre reconfiguration remplace un composant existant, sans modifier les liaisons. Interface de reconfiguration Le modèle F RACTAL, à travers les interfaces de contrôle (voir section 3.3.1), permet de séparer les aspects fonctionnels des aspects non-fonctionnels d’une architecture. Nous définissons une interface de programmation de reconfiguration comme un ensemble d’interfaces de contrôle Fractal implémentées par les composants reconfigurables. Ces interfaces permettent d’implémenter les mécanismes de détection d’un état stable, de transfert d’état etc. 27 3.2. Aperçu de la contribution Chapitre 3. Aperçu de la contribution Construction de systèmes reconfigurables à composants Pour faire face à la complexité de construction de systèmes à la carte, en plus avec une interface de reconfiguration, nous avons implémenté un compilateur permettant de contruire des systèmes à composants reconfigurables. En réalité, nous avons enrichi le compilateur existant. L’utilisateur spécifie les composants reconfigurables et le compilteur transforme l’architecture pour y inclure l’implémentation d’une interface de reconfiguration. Programmation des reconfigurations Le code de reconfiguration utilisant notre interface de reconfiguration peut être écrit en C. Cependant, un tel code peut être complexe, répétitif et difficile à maı̂triser, car le langage C est de trop bas niveau par rapport à la programmation des reconfigurations. Afin de palier à ces problèmes nous utilisons le langage de reconfiguration appelé FScript (David & Ledoux, 2005), qui permet de manipuler une architecture à composants F RACTAL. La seule analyse de l’architecture du système permet de valider le programme de reconfiguration. Nous avons mis au point un compilateur de programmes FScript (à l’origine FScript vient avec un interprète en Java) et un support d’exécution minimal qui autorisent l’utilisation de FScript même dans les environements très contraints en ressources, tels les réseaux de capteurs. Le compilateur FScript est réalisé comme une extension du compilateur Think et réutilise l’interprète original de FScript. Rôles du cycle de vie L’utilisation d’un modèle à composants permet la séparation de préoccupations entre la conception du système et son implémentation, i.e. l’implémentation des composants. De la même manière, l’utilisation de FScript permet la séparation des préoccupations entre la programmation des reconfigurations comme modifications de l’architecture et l’implémentation du système. Par conséquent, il est possible d’identifier trois rôles différents dans le cycle de vie d’un système reconfigurable : – programmeur de bas niveau, implémentant des parties du système à partir de leur spécification, i.e. implémentant les composants, – concepteur du système, et – programmeur de reconfigurations, tous deux ayant une visions architecturale du système. 3.2.1 Limitations La reconfiguration dynamique étant un problème complexe, nous avons voulu proposer une interface de bas-niveau suffisamment riche et puissante au-dessus de laquelle il serait possible de bâtir des mécanismes plus complexes. En particulier, – nous considérons qu’un mécanisme de transfert d’état sophistiqué peut être bâti au-dessus des primitives d’accès à l’état telles que nous les avons définies, – les compilateurs conçus, à l’état de prototypes, peuvent être sujet d’améliorations visant à fiabiliser l’ensemble des processus de reconfiguration dynamique – par différentes vérifications à la compilation, l’utilisation d’un système de types, etc. Les mécanismes de reconfiguration que nous avons définis et implémentés sont minimaux afin de limiter l’impact sur les performances du système. Comparé à d’autres technologies pour les systèmes embarqués, la réflexivité de F RACTAL/T HINK induit un surcoût et pour pouvoir construire des systèmes reconfigurables à plus grande échelle, il faut non seulement disposer de mécanismes de reconfiguration minimaux, mais il faut également pouvoir maı̂triser l’impact de l’utilisation des composants T HINK. Dans ce but, nous avons initié un travail sur les optimisations architecturales séléctives qui visent à eliminer tout surcoût lié au modèle F RACTAL là, où il n’est pas justifié par un besoin de l’utilisateur. Nous décrivons ces travaux, leur impact sur les performances et l’intégration avec les mécanismes de reconfiguration en annexe D. 28 Chapitre 3. Aperçu de la contribution 3.2.2 3.3. Fondations de l’approche Mise-en-oeuvre Nous avons appliqué notre travail à trois prototypes de systèmes embarqués. Premièrement nous avons construit un prototype de système embarqué avec un contrôle d’accès très fin et reconfigurable (Jarboui et al., 2004), alliant l’intégration et la combinaison de deux aspects non-fonctionnels dans un système embarqué - la sécurité et la reconfiguration. Deuxièmement, les noeuds de réseaux de capteurs constituent un cadre idéal pour l’application de nos travaux. Pour la plupart, il s’agit de plate-formes matérielles très contraintes, à base de processeurs AVR (Atmel, n.d.), ayant 4 ou 8ko de mémoire. L’objectif étant de vérifier que le modèle de programmation proposé avec son implémentation convient aux plate-formes très contraintes. Finallement nous avons conçu un décodeur vidéo H.264 reconfigurable. Le décodeur est conçu comme un système à composants très fins (Layaida et al., 2005), l’objectif étant de quantifier l’impact sur les performances de l’implémentation des mécanismes de reconfiguration. 3.2.3 Organisation de la contribution Nous avons découpé notre proposition en trois parties que nous décrivons dans des chapitres séparées, la figure 3.3 montre ce découpage : – la construction de systèmes reconfigurables, – les mécanismes de reconfiguration dynamique et l’exécution du système et des reconfigurations, et – la programmation des reconfigurations spécification système Fractal/THINK construction du système image système d'exploitation THINK ADL compiler Chapitre 6: Construction de systèmes reconfigurables exécution méta-données pour la reconfiguration architecture spécification reconfiguration FScript construction reconfiguration programme de reconfiguration compilé THINK ADL compiler Chapitre 7: Programmation de reconfigurations reconfiguration Chapitre 5: Mécanismes de reconfiguration F IG . 3.3 – Decoupage du modèle de notre proposition pour un modèle de programmation de systèmes reconfigurables. 3.3 Fondations de l’approche Notre approche est basée sur le modèle à composant Fractal (3.3.1) et sur son implémentation en C, appelée Think (3.3.2). 29 3.3. Fondations de l’approche 3.3.1 Chapitre 3. Aperçu de la contribution Le modèle à composant Fractal Fractal (Bruneton et al., 2006; Bruneton et al., 2004) est un modèle à composant à la fois simple, dynamique et extensible. Fractal est un modèle hiérarchique et réflexif – chaque système Fractal peut retrouver à l’exécution son architecture (introspection), voire la modifier (intercession). 3.3.1.1 Anatomie d’un système Fractal La figure 3.4 montre l’anatomie d’un composant/système Fractal. Chaque composant implémente un certain nombre d’interfaces, dites serveurs, et peut être client d’autres composants via des interfaces, dites clientes. Les composants peuvent être liées entre eux par des liaisons (une interface cliente et liée à une interface serveur). Les interfaces sont décrites dans un langage de description d’interfaces (IDL) qui spécifie les méthodes d’une interface. Les interfaces sont typées, par leur description IDL. Un système Fractal est vu comme un assemblage de composants. Etant donné que Fractal est un modèle hiérarchique, les composants sont à la fois en relation de parenté, mais également reliés via des liaisons de communication : – La relation de parenté/sous-composants défini un graphe de composants 1 . Ce graphe a généralement une racine – le composant englobant le système entier. – Une liaison est un canal de communication entre une interface cliente d’un composant et une interface serveur d’un autre composant. Une laison peut être implémentée par exemple comme une référence (pointeur) vers l’interface serveur détenue par le composant client. Un appel – une communication – à une interface serveur est un appel d’une méthode de cette interface. Un composant Fractal est une entité à l’exécution, accédée par des interfaces bien définies. Un composant Fractal est logiquement vu (composé) en deux parties - la membrane (ou le contrôleur) et le contenu : – Le contenu implémente la partie fonctionnelle du composant, il peut soit contenir d’autres composants ou implémenter directement ses interfaces fonctionnelles. – La membrane contient l’implémentation des interfaces dites de contrôle. Ces interfaces implémentent les aspects non-fonctionnels d’une architecture. Concrétement le modèle défini un ensemble coeur d’interfaces de contrôle, permettant d’accéder et modifier les liaisons, l’architecture d’un composant (en termes de contenance), gérer le cycle de vie des composants etc. L’implémentation d’une interface de contrôle peut exercer un contrôle actif sur les sous-composants, i.e. l’interface de contrôle peut intercepter les appels entrans ou sortants des sous-composants. Ainsi défini, le contenu d’un composant implémente les aspects techniques ou métiers, alors que la membranes ou les contrôleurs implémentent les aspects liés à l’administration ou le contrôle de l’architecture. Ce contrôle peut être passif – l’implémentation d’un contrôleur n’altère pas l’exécution de ses sous-composants (donc l’exécution de la partie fonctionnelle), ou actif – le contrôleur intercepte les appels de ses sous-composants. Les interfaces de contrôle sont optionnelles, chaque membrane peut offrir un ensemble arbitraire d’interfaces et leur présence détermine le niveau de capacité d’introspection et d’intercession d’une architecture. On peut également définir de nouvelles interfaces de contrôle pour implémenter de nouveaux aspects, comme l’implémentation de certains mécanismes de base pour la reconfiguration dynamique, comme nous le montrerons au chapitre suivant. Parmis les interfaces de contrôle standards, définies par le modèle Fractal, nous utiliserons à des fins de reconfiguration dynamique les interfaces suivantes : – ComponentIdentity2 , cette interface est à la fois l’identifiant à l’exécution d’un composant 1 2 Dans Fractal les composants peuvent être partagés, par conséquent le graphe de composition n’est pas un arbre. Component dans la dernière spécification du modèle Fractal. 30 Chapitre 3. Aperçu de la contribution 3.3. Fondations de l’approche control interface Component A Component B Component C binding Component D content functionnal interface Component E membrane F IG . 3.4 – Le modèle à composant Fractal – les concepts. et permet également de retrouver ses interfaces visibles, par exemple pour établir une liaison vers une interface serveur du composant. – ContentController permet de retrouver et éventuellement modifier la structure interne d’un composant (ses sous-composants). – BindingController permet de modifier une liaison, en modifiant la référence cliente d’une interface détenue par le composant client. – LifeCycleController permet d’arrêter ou démarrer un composant. La sémantique de ces opérations dans la spécification Fractal est floue et son interprétation diverge dans les deux implémentations de références Julia en Java et Think en C (voir également la description de Think, 3.3.2). – AttributeController autorise l’accès et la modification des attributs d’un composant. – NameController offre une représentation de l’identifiant d’un composant sous forme de chaı̂ne de caractères. Dans le chapitre suivant, nous décrivons une extension du modèle Fractal pour la reconfiguration dynamique, en particulier nous définissons un ensemble d’interfaces de contrôle permettant de détecter un état stable et d’accéder à l’état d’un composant lors d’un transfert d’état. 3.3.2 THINK : une implémentation de Fractal en C Le terme Think désigne plusieurs projets (tous dans le Projet Think (Think, n.d.)) : – une implémentation du modèle Fractal, qui défini comment sont implémentés les concepts du modèle – le canevas Think, – afin de faciliter la construction de systèmes à base de composants Think, le canevas Think vient avec un langage de description d’architectures (Architecture Description Language, ou ADL), Think défini également un langage de description d’interfaces – IDL, – un compilateur de systèmes à base de composants Think (générant les structures de données des composants Think), et – bibliothèque de composants Think pour la construction de systèmes d’exploitation, appelée Kortex. 3.3.2.1 THink Is Not a Kernel THINK (Fassino et al., 2002; Fassino, 2001) (acronyme pour Think is not a Kernel, car le canevas Think n’est pas un système, mais permet de créer des systèmes) est un canevas de construction de 31 3.3. Fondations de l’approche Chapitre 3. Aperçu de la contribution systèmes à composant, en C. Think n’est pas un système, mais permet de construire des systèmes. Aujourd’hui, Think implémente le modèle à composant Fractal. Think est un canevas général de construction de logiciels à composants, néanmoins, nous allons décrire et discuter son application à la construction de systèmes d’exploitation. L’utilisation de composants Think ne requièrt aucun environement d’exécution supplémentaire – tout est composant. Par conséquent, il est possible de construire des systèmes d’exploitation minimaux, où uniquement les composants nécéssaires sont inclus. Par ailleurs, le canevas ne définit et n’exclut aucun type d’organisation du système (micro-noyau, exo-noyau, noyau monolithique, événementiel, multithread ...). En tant qu’implémentation du modèle Fractal, Think définit l’implémentation concrète des différents concepts du modèle - interfaces, interfaces de contrôle et leurs méta-données, liaisons, références etc. Plus spécifiquement, le canevas définit les structures de bases suivantes : – l’implémentation des interfaces fonctionnelles et des liaisons (intéraction fonctionnelle entre les composants), et – l’implémentation et méta-données des interfaces de contrôle Fractal (contrôle). Implémentation fonctionnelle La figure 3.5 montre l’implémentation standard3 d’un composant Think. Chaque interface serveur d’une instance d’un composant est matérialisée par un descripteur d’interface. Le descripteur contient un pointeur vers la table des méthodes de l’interface (meth, cf. vtable en C++) et un pointeur vers les données d’instance du composant (selfdata). La table des méthodes de l’interface contient les adresses effectives des méthodes. Dans Think, toutes les méthodes d’interface acceptent comme premier paramètre un pointeur vers les données d’instance du composant. Ces données contiennent d’une part les données privées au composant (ces variables d’instances) et les méta-données du composant – références d’interfaces clientes et éventuellement d’autres données comme attributs, noms d’interfaces etc. En particulier, l’adresse des données permet d’identifier de manière unique l’instance d’un composant quelconque dans le système. Une référence cliente est représentée par un pointeur référençant le descripteur de l’interface serveur. Ce pointeur matérialise également la liaison entre les composants, il s’agit d’une liaison dynamique (elle peut-être changé à l’exécution, en modifiant la référence). Les liaisons dynamiques offrent la flexibilité nécéssaire pour implémenter les différents mécanismes de reconfiguration dynamique, notamment les opérations de redirection des références. Les références clientes peuvent être modifiées via l’interface de contrôle BindingController. Cette interface peut également servir à leur initialisation lors du démarrage du système à composant. Pour appeler une méthode d’une interface serveur, il suffit de déréferencer la référence cliente pour retrouver le descripteur d’interface serveur qui donne accès aux méthodes et aux données du composant serveur. Par exemple, pour appeler à partir du composant A la méthode f() de l’interface cliente Itf_I, implémentée par l’interface Itf_I_impl du composant B, le code C suivant est nécéssaire : Itf_I->meth->f(Itf_I->data, arg1, arg2, ...); Pour l’utilisateur ce code est caché par la construction suivante : CALL(Itf_I, f, arg1, arg2, ...); L’implémentation de la méthode f() est une fonction qui accepte en premier paramètre un pointeur vers ses données d’instance (par conséquent dont le type est connu et déclaré au même endroit), par conséquent l’implémentation a la signature suivante : void f(struct myData* _this, int arg1, int arg2, ...); 3 Le présent chapitre montre l’implémentation standard de Think, voir annexe D pour la description des travaux en cours sur les optimisations architecturales. 32 Chapitre 3. Aperçu de la contribution 3.3. Fondations de l’approche Itf_I A B conceptual view implementation view virtual method table Itf_I_impl interface descriptor component data f() g() meth f() implem. ... data g() implem. client interface references Itf_I component data B A F IG . 3.5 – L’implémentation des interfaces dans Think. De la même manière que pour l’accès aux interfaces, Think défini un accès aux attributs d’un composant, déclarés dans son ADL. Implémentation du contrôle par défaut L’implémentation actuelle de Think n’implémente qu’un sous-ensemble des interfaces de contrôle définies par le modèle Fractal. Ces interfaces de contrôle par défaut implémentent un contrôle passif (les membranes de composants composites n’interceptent pas les appels). Concrétement le canevas Think fournit une implémentation des interfaces ComponentIdentity, BindingController, AttributeController, ContentController, LifeCycleController et Factory (qui permet d’implémenter des usines de composants – l’implémentation est générée par le compilateur avec le code d’instantiation et d’initialisation du composant). Les interfaces de contrôles sont ajoutées à un composant par le compilateur et ont le même format binaire que les interfaces fontionnelles. Le compilateur génère un certain nombre de méta-données qui permettent d’implémenter ces interfaces. Ces méta-données sont référencées par le pointeur des données d’instance dans le descripteur d’interface. Les données d’implémentation de l’interface ComponentIdentity sont particulières, car cette interface permet de retrouver toutes les autres interfaces serveurs d’un composant. Par conséquent, ces données contiennent une structure avec tous les desripteurs d’interface du composant, associée au nom de l’interface serveur correspondante. 3.3.2.2 Architecture Description Language Les composants Think peuvent être décrits en utilisant un langage de description d’architecture (ADL). L’ADL de Think est un langage déclaratif de haut niveau dans lequel le programmeur peut exprimer la configuration d’une architecture Think en utilisant les concepts du modèle Fractal (interfaces, composants, liaisons etc.). Concrétement, l’ADL de Think permet de : – définir des types abstraits de composants, – décrire les composants de base en précisant les interfaces implémentées, les interfaces requises et le fichier d’implémentation en C de ce composant, un composant peut implémenter un type de composant, – décrire des composants plus complexes, contenant d’autres composants et ayant des liaisons entre ses composants, 33 3.3. Fondations de l’approche Chapitre 3. Aperçu de la contribution – surcharger une définition de composant, typiquement une description d’architecture de haut-niveau est spécialisée pour une plate-forme matérielle donnée en surchargeant les composants dépendants de la plate-forme, – spécifier quelle membrane doit être générée pour un composant donné, et – définir et initialiser les attributs des composants La figure 3.6 montre un exemple d’un système d’exploitation sous forme d’un composant Think et sa description en ADL. CI, BC, LCC, RC, CC, NC CI, BC, LCC, NC net CI,LCC,SC driver composite SmallNetKernel { provides net.api.Net as net provides util.api.Buffer as buffer eth net contains contains contains contains CI, BC, LCC, NC CI, LCC, SC, NC buffer binds binds binds binds binds buffer allocator alloc SmallNetKernel CI: ComponentIdentity BC: BindingController CC: ContentController LCC: LifeCycleController RC: ReconfigurationController SC: StateTransferController NC: NameController net = net.lib.tcpip eth = chip.net.tulip buffer = util.lib.buffer alloc = memory.lib.malloc net.driver to eth.driver net.alloc to alloc.alloc buffer.alloc to alloc.alloc this.net to net.net this.buffer to buffer.buffer membrane Reconfig_ThreadCounting } F IG . 3.6 – Exemple d’un système à composant Think, vue conceptuelle et ADL. 3.3.2.3 Compilateur Think Les composants Think décrits en ADL sont assemblés à l’aide d’un compilateur ADL. A partir des ADL et des IDL, ce compilateur génère du code C, appelé glue code. Le code C généré, avec le code fonctionnel écrit par le programmeur de composant, est compilé avec un compilateur C standard et lié sous forme d’une image exécutable sur la plate-forme cible. La figure 3.7 montre le processus de compilation d’un système à base de composants Think. architectural code functional code .adl .idl Think compiler adl + idl .adl.c .idl.h terminal kernel .c .h C compiler .o .o .o dynamic loader linker boot loader kernel F IG . 3.7 – La chaı̂ne de compilation pour construire un système à composant Think. Le compilateur Think étant la pièce principale dans notre approche pour la construction de systèmes 34 Chapitre 3. Aperçu de la contribution 3.3. Fondations de l’approche reconfigurables, nous détaillons son architecture et le fonctionnement dans le chapitre 5. L’annexe D décrit l’évolution de ce compilateur afin de supporter des optimisations architecturales séléctives. 3.3.2.4 Kortex : une bibliothèque de composants Kortex est une bibliothèque de composants Think destinée à la construction des systèmes d’exploitation – ordonnanceurs, allocateurs, protocoles réseaux, différents pilotes matériels (réseau, disque, vidéo etc.), systèmes de fichiers etc. L’objectif de cette bibliothèque est de fournir un cadre expérimental pour de nouveaux concepts en systèmes d’exploitation. Nous avons réutilisé Kortex pour construire les prototypes de reconfiguration dynamique (qui seront détaillés au chapitre 7). Multi-programmation Kortex définit une architecture générique pour la multi-programmation, autorisant l’utilisation de différents ordonnanceurs et simplifiant l’écriture des systèmes multi-programmé. Dans Kortex un thread est matérialisé par un composant job géré par un ordonnanceur (composant scheduler). Chaque job est associé à un composant utilisateur qui contient la logique d’exécution. Uniquement ce dernier composant est écrit par l’utilisateur. A tout moment, l’identité du thread courant, donc du composant job, peut être retrouvée par la méthode getJob() de l’interface Scheduler du composant scheduler. La figure 3.8 montre un example d’un système avec trois threads, ordonnancé par un ordonnanceur à priorités. runner runner runner0 job runner runner1 job job0 runner scheduler runner2 job job1 runner scheduler job2 runner scheduler scheduler ... scheduler generic architecture application independent F IG . 3.8 – Kortex – L’architecture des composants pour le multi-threading. Interruptions Kortex définit une architecture générique pour la gestion des interruptions. Au plus bas niveau se trouve le composant trap, dépendant de la plate-forme matérielle, encapsulant le vecteur d’interruptions du processeur. Toutes les interruptions sont réifiées sous forme d’interfaces clientes optionnelles qui peuvent être liées (ou non) à un composant gestionnaire d’une interruption concrète. Une telle réification d’une interruption peut être répétée à plusieurs niveau. Lorsqu’une interruption a lieu, le composant trap appelle le composant gestionnaire lié à l’interface cliente correspondante. Par conséquent, le même composant trap peut être réutilisé dans des architectures différentes. La figure 3.9 montre le cas le plus typique d’implémentation sur un processeur ARM, où un composant trap est lié à un composant pic qui gère les interruptions des périphériques rattachés au processeur. Lors d’une interruptions d’un périphérique, le trap remonte l’événement au pic qui acquitte 35 3.3. Fondations de l’approche Chapitre 3. Aperçu de la contribution l’interruption et remonte l’événement à un gestionnaire de plus haut niveau, si celui-ci est liée à l’interface cliente correspondante (potentiellement indépendent de la plate-forme matérielle). La conséquence de ce fonctionnement est que les composants trap et pic peuvent être réutilisés dans des configurations architecturales différentes. application kbd keyboard driver serial trap pic radio radio driver pic ARM processor exceptions ... RESET UNDEF handler IRQ FIQ ARM processor keypad radio link screen ... F IG . 3.9 – Kortex – l’architecture des composants gestionnaires d’interruptions. 36 Chapitre 4 Interface de programmation pour la reconfiguration dynamique Au chapitre précédent nous avons identifié les mécanismes pour la reconfiguration dynamique. Dans ce chapitre nous décrivons une interface1 flexible de programmation des reconfigurations, permettant d’implémenter dans un système à composants les différents mécanismes de reconfiguration. L’interface de programmation que nous proposons est basée sur la notion des interfaces de contrôle de Fractal – chaque mécanisme est accessible via une interface de contrôle spécifique, chaque composant fournissant un ensemble approprié d’interfaces de contrôle et leurs implémentations. L’interface que nous définissons est indépendante du modèle d’exécution. Nous décrivons d’abord les différentes interfaces de contrôle Fractal constituant l’interface de programmation des reconfigurations. Nous discutons ensuite de l’utilisation de cette interface de programmation et montrons quelques exemple de l’utilisation. Nous montrerons également deux implémentations de la détection d’un état stable de composant. 4.1 Interface de programmation des reconfigurations Une interface de programmation des reconfigurations de bas niveau doit permettre de programmer les actions suivantes : – identifier à l’exécution la partie de l’architecture à reconfigurer - composant, liaison, l’attribut, – contrôler l’état d’un composant (détection d’un état stable ou quiescent, accès à son état interne afin de pouvoir le transférer), – modifier l’architecture (ajouter, supprimer ou remplacer des composants). Nous avons étendu le modèle F RACTAL en définissant de nouvelles interfaces de contrôle (notamment pour la détection d’un état stable et le transfert d’état) et nous avons également fourni une implémentation de ces interfaces pour le canevas Think. 4.1.1 Motivation des choix de conception L’API de reconfiguration que nous définissons répond aux objectifs que nous avons identifiés (cf. chapitre 1) de la manière suivante : 1 A distinguer interface de programmation et interface de contrôle. L’interface de programmation est l’ensemble de méthodes mises à dispositions du programmeur de reconfigurations, une interface de contrôle est un concept du modèle à composant F RACTAL. 37 4.1. Interface de programmation des reconfigurations Chapitre 4. Interface de programmation Flexibilité Dans notre approche, les composants reconfigurables implémentent un certain nombre d’interfaces de contrôle permettant de programmer les reconfigurations. Une telle interface de programmation est de bas niveau et permet de maı̂triser au grain fin la reconfiguration. En revanche, l’implémentation de cette interface garantit uniquement la sémantique des opérations de bas niveau de la reconfiguration, mais non leur ensemble. Par exemple, dans un système où l’interface de reconfiguration est donnée par une méthode replace(old, new, ...), l’implémentation de cette interface garantirait que le composant old soit dans un état stable avant sa manipulation, un transert d’état entre les composants etc. Dans notre approche, c’est au programmeur de reconfigurations de s’assurer de la validité sémantique de son programme de reconfiguration. Cette approche autorise une flexibilité dans la construction d’un système reconfigurable – pour chaque composant reconfigurable, le concepteur choisit les implémentations des mécanismes de reconfiguration. Le concepteur doit également s’assurer que le système à composants reconfigurable implémente bien une interface de reconfiguration complète. Nous allons montrer dans le chapitre 6 comment les reconfigurations peuvent être programmées en utilisant un langage de reconfiguration de haut niveau, FScript dans notre cas, qui permet la compilation et la vérification des programmes de reconfiguration. Néanmoins, la reconfiguration peut être programmée sans recours à FScript. Indépendance des implémentations des différents mécanismes Les implémentations des différentes interfaces de contrôle composant l’interface de reconfiguration sont indépendante les unes des autres et de la partie fonctionnel du système. C’est le concepteur du système qui choisit la combinaison des différentes implémentations que fournit un composant reconfigurable. Il est tout à fait possible que deux composants implémentent le même mécanisme d’accès à leurs états (càd l’interface StateTransferController) mais implémentent différentes stratégies pour détecter un état stable (interface ReconfigurationController, par example par comptage de référence ou à l’aide des intercepteurs dynamiques - décrits ci-dessous). Indépendance des implémentations vis-à-vis des programmes de reconfiguration Grâce à la séparation stricte des interfaces et leurs implémentations dans le modèle F RACTAL, les programmes de reconfigurations utilisant l’interface de programmation sont indépendants de l’implémentation des mécanismes sous-jacents (fournis par le composant). Lors de la programmation des reconfigurations, il est aisé de raisonner uniquement en termes d’architectures et d’opérations nécéssaires à la reconfiguration, sans se soucier des particularités des implémentations des interfaces de contrôle. Respect des contraintes de performances du système Toutes les interfaces de contrôle sont optionnelles, uniquement les composants reconfigurables doivent les implémenter. Dans le chapitre 7 nous évaluons l’impact de l’implémentation de l’interface de programmation en terme d’occupation mémoire et de surcoût à l’exécution. 4.1.2 Identification et introspection Dans F RACTAL l’interface ComponentIdentity identifie de façon unique un composant dans un système. Concrétement, dans T HINK, les différentes structures générées pour un composant occupent un emplacement unique en mémoire et par conséquent il est possible d’identifier le composant par le biais de ces adresses. En particulier, l’adresse mémoire du descripteur de l’interface ComponentIdentity constitue un identifiant unique d’un composant, de même que l’adresse de données d’instance d’un composant (this). 38 Chapitre 4. Interface de programmation 4.1. Interface de programmation des reconfigurations Le modèle Fractal étant hiérarchique, pour retrouver un composant quelconque dans une architecture, il faut être capable de parcourir cette dernière ou l’introspecter. L’interface ContentController, également définie dans Fractal, répond à ce besoin et permet de retrouver tous les sous composants d’un composant parent. Le programme de reconfiguration doit connaı̂tre la poignée (handle) de la racine de l’architecture et un chemin vers le composant à partir de la racine. Ensuite tout composant parent du composant recherché doit implémenter l’interface ContentController et l’interface ComponentIdentity. Les interfaces ComponentIdentity et ContentController sont définies de la manière suivante : interface ComponentIdentity { any getInterface(String name); any[] getInterfaces(); } interface ContentController { void addSubComponent(ComponentIdentity c); void removeSubComponent(ComponentIdentity c); any[] getSubComponents(); } L’interface NameController permet de retrouver le nom d’un composant. Cette interface sera utile à l’implémentation du support d’un langage de haut-niveau tel que FScript, où l’utilisateur désigne les composants par leur nom. L’interface NameController est définie de la manière suivante : interface NameController { String getName(); void setName(String name); } La figure 4.1 montre les interfaces d’introspection et comment celles-ci permettent de parcourir une architecture Fractal. Par exemple, en détenant une référence vers le composant C (donc vers l’interface CI de ce composant), il est possible d’obtenir l’interface CC de ce même composant, qui donne accès aux sous-composants A et B. De cette façon, et en utilisant le NC pour comparer les noms, nous pouvons trouver le composant désigné par le chemin absolu C.B. CI CI NC CC BC NC CI A NC B C F IG . 4.1 – Les interfaces d’introspection. 39 4.1. Interface de programmation des reconfigurations 4.1.3 Chapitre 4. Interface de programmation Contrôle d’état Comme décrit au chapitre 3, le contrôle d’état pour la reconfiguration dynamique comprend deux opérations : la suspension de l’exécution d’un composant et le transfert d’état entre des instances de composants. A ces deux opérations nous ajoutons également l’opération d’initialisation et arrêt de composants (qui correspond au constructeur et desctructeur de composants). 4.1.3.1 Initialisation et arrêt des composants Dans Think, l’interface de contrôle LifeCycleController est utilisée pour initialiser un composant (méthode start()) ou pour l’arrêter (méthode stop()), par exemple pour initialiser un driver matériel (port série, carte ethernet etc.). L’implémentation de cette interface est appelée lors de l’initialisation du système à composant, avant l’exécution propre du système. Tout composant nécéssitant d’une phase d’initialisation doit implémenter cette interface. L’appel à cette interface doit aussi être effectué lors d’une reconfiguration (si cette interface est implémentée par le composant en question), par exemple lors de la suppression d’un driver – il faut l’arrêter proprement. interface LifeCycleController { void start(); void stop(); } 4.1.3.2 Etat stable Pour implémenter un algorithme de détection d’un état stable, nous avons défini une interface de contrôle, appelée ReconfigurationController, qui permet de contrôler l’état d’un sous-composant en demandant la suspension de son exécution (état stable ou quiescent). L’interface ReconfigurationController est définie comme suit : interface ReconfigurationController { void suspend(ComponentIdentity subcomponent, Callback cb); resume(ComponentIdentity subcomponent); } La méthode suspend() de l’interface ReconfigurationController permet de déclencher la détection d’un état stable du sous-composant subcomponent et, lorsque celui-ci est atteint, suspendre son exécution. L’argument subcomponent permet à la fois d’identifier le composant à suspendre, ainsi qu’identifier la requête de détection d’état stable (cet identifiant permet de reprendre l’exécution après une reconfiguration). Afin de pouvoir détecter l’état stable de plusieurs composants en même temps, l’implémentation de la méthode suspend() est impérativement non-bloquante et c’est à l’appelant de cette méthode d’attendre jusqu’à ce que les différents composants aient atteint des état stables. Par conséquent suspend() prend en paramètre une référence d’interface, cb, qui doit être appelée lorsqu’un état stable est atteint (plus précisément la méthode notify() de cette interface). C’est à l’appelant d’implémenter cette interface et de gérer un état (compteur par exemple) qui lui permet de savoir si tous les composants à reconfigurer sont dans un état stable. Ce mode de fonctionnement est illustré sur la figure 4.2. L’interface auxiliaire Callback est définie comme suit : interface Callback { void notify(int waiting); } 40 Chapitre 4. Interface de programmation requester 4.1. Interface de programmation des reconfigurations C0 Cn C1 request() request() n times, for n components request() request done, action: return or block(), P() callback() n times, for n components callback() callbacks not necessery in order callback() request done, action: callback() or unblock(), V() F IG . 4.2 – Callbacks – fonctionnement. La méthode resume() permet de reprendre l’exécution des composants suspendus. L’argument subcomponent est l’identifiant permetant à l’implémentation de l’interface ReconfigurationController d’identifier quel composant peut reprendre l’exécution, en particulier si le composant identifié à l’origine par l’argument subcomponent a été remplacé, dans ce cas, c’est le nouveau composant qui reprend l’exécution. L’interface ReconfigurationController ainsi proposée est simple et l’implémentation d’un algorithme d’état stable est local à un composant. Par conséquent, avec le modèle multi-thread et le comptage de référence, il est possible de créer un interblocage (deadlock) à la reconfiguration. (Par example, en demandant simultanément la détection d’un état quiescent de deux composants liées. Si le composant serveur atteint un état quiescent, il bloque tout nouvel appel. Le composant client n’arrivera jamais dans un état quiescent, car en attente de terminaison des appels en cours, bloqués au composant client.) Notre approche pour la programmation de reconfigurations, cf. 6, permet d’analyser le programme de reconfiguration et détecter cette situation, voire la corriger en réordonnanceant le code. 4.1.3.3 Transfert d’état Dans ce travail, nous considérons uniquement des mécanismes de bas niveau permettant un transfert d’état complet, comme décrit au chapitre 3, section 3.1. Par conséquent, nous définissons uniquement une interface pour accéder à l’état d’un composant (lecture, écriture). Cette interface doit être implémentée par le programmeur du composant Afin d’accéder à la représentation de l’état d’un composant nous définissons une interface de contrôle appelée StateTransferController définie comme suit : interface StateTransferController { void* getState(); void setState(void* state); } Les deux méthodes permettent d’accéder à l’état du composant (getState() permet de le retrou- 41 4.2. Utilisation Chapitre 4. Interface de programmation ver, setState() permet de l’insérer). Ces deux méthodes sont suffisantes pour bâtir des mécanismes de transfert d’état plus complexes, en particulier l’interface StateTransferController ainsi définie ne fait aucune hypothèse sur la structure de l’état interne du composant. C’est au programmeur du composant d’implémenter cette interface et c’est à l’appelant de connaı̂tre la structure de l’état et de la manipuler si nécessaire (i.e. lorsque lors d’un remplacement de composant les deux formats pour maintenir l’état interne – le nouveau et l’ancien – ne sont pas identiques). 4.1.4 Modifications de l’architecture Le modèle Fractal définit déjà un certain nombre d’interfaces de contrôle qui permettent de modifier une architecture à base de composants Fractal. Nous autorisons : – les modifications de liaisons, – les modifications des attributs, et – les modifications de l’architecture, i.e. de l’assemblage des composants. 4.1.4.1 Modifications de liaisons Une liaison est matérialisée par une référence cliente du composant client vers une interface serveur du composant serveur. L’interface BindingController implémentée par le composant client permet d’accéder et modifier cette référence et par conséquent d’établir une nouvelle liaison en cas de reconfiguration (ou la supprimer). L’interface BindingController est définie de la manière suivante : interface BindingController { void bind(String name, any itf); void unbind(String name); any lookup(String name); } 4.1.4.2 Modification d’attributs Les attributs d’un composant peuvent être modifiés grâce à l’interface de contrôle AttributeController, si le composant en question implémente cette interface : interface AttributeController { any get(String name); void set(String name, any value); } 4.1.4.3 Modifications de l’assemblage des composants La composition, ou l’assemblage des composants, d’un système à composants Fractal peut être modifiée grâce aux implémentations de l’interface de contrôle ContentController (définie plus haut) des différents composants. Cette interface permet à chaque composant qui l’implémente, d’ajouter ou de supprimer ses sous-composants. 4.2 Utilisation L’API de reconfiguration ainsi définie est riche en combinaisons et en cas d’utilisation - par exemple il est possible d’ajouter un sous-composant supplémentaire à un composant composite CC (Content- 42 Chapitre 4. Interface de programmation interface de contrôle ComponentIdentity getInterface() getInterfaces() NameController getName() setName() ContentController addSubComponent() removeSubComponent() getSubComponents() BindingController bind() unbind() lookup() AttributeController set() get() 4.2. Utilisation intercession introspection sans état stable intercession avec état stable x x - - x - x - x x - x - x x x - x x - x x - x - TAB . 4.1 – L’API de reconfiguration – introspection et intercession. Controller du composant composite CC) et le lier à une interface optionnelle d’un autre composant (BindingController du composant client). Ou alors il est possible de remplacer un souscomposant d’un composant composite nécéssitant la détection d’un état stable du sous-composant à remplacer (donc implication de l’utilisation des interfaces ContentController, ReconfigurationController, StateTransferController et BindingController ...). Les interfaces de contrôle, et plus précisément les méthodes des interfaces de contrôle, tombent dans différentes catégories : – introspection uniquement, donc aucune modification de l’architecture, – intercession sans nécéssité d’un état stable du composant, – intercession avec nécéssité d’un état stable du composant, – méthodes permettant de contrôler l’état, en particulier de détecter un état stable, Les tableaux 4.1 et 4.2 détaillent les catégories de chaque méthode de l’API. L’utilisation correcte des différentes méthodes des interfaces de contrôle est dirigée par les règles suivantes : – Toute interface de contrôle est optionnelle et c’est au constructeur du système de s’assurer que le système inclut toutes les interfaces de contrôle nécéssaires à la reconfiguration dynamique. (Au chapitre suivant nous allons montrer comment on peut automatiser la construction des systèmes reconfigurables avec toutes les interfaces de contrôle nécessaires.) – Les méthodes d’introspection peuvent être utilisées sans aucune contrainte pendant la reconfiguration. – Les méthodes d’intercession ne nécéssitant pas que le composant soit dans un état stable peuvent être utilisées sans aucune contrainte pendant la reconfiguration. – Les méthodes d’intercession nécéssitant qu’un ou plusieurs composants soient dans un état stable peuvent être utilisées une fois qu’un état stable a été détecté par l’implémentaiton de l’interface ReconfigurationController. 43 4.3. Exemple d’un programme de reconfiguration Chapitre 4. Interface de programmation interface de contrôle ReconfigurationController suspend() resume() LifeCycleController start() stop() StateTransferController getState() setState() état d’exécution état interne x x - - x x - x x TAB . 4.2 – L’API de reconfiguration – contrôle d’état. – Les interfaces de contrôle d’état interne LifeCycleController et StateTransferController doivent être utilisées alors que le composant est dans un état stable2 . – Les méthodes des interfaces BindingController et AttributeController peuvent nécéssiter que le composant soit dans un état stable, selon l’implémentation du composant. C’est au programmeur de reconfiguration de connaı̂tre la sémantique du composant et décider si un état stable est nécéssaire à la manipulation des attributs ou des liaisons. 4.3 Exemple d’un programme de reconfiguration Considérons un système à composant fictif comme montré sur la figure 4.3. CI CC CI BC LCC RC LCC* CI LCC* SC alloc (server) composant lié à un composant reconfigurable alloc (client) xyz composant reconfigurable alloc kernel CI: BC: CC: LCC: ComponentIdentity RC: ReconfigurationController BindingController SC: StateTransferController ContentController LifeCycleController (optionnel) F IG . 4.3 – Un exemple de système reconfigurable. Considérons un exemple de reconfiguration qui consiste à remplacer le sous-composant alloc du composant kernel. Cette reconfiguration nécéssite la détection d’un état stable du composant alloc, l’instantiation du nouveau composant, la modification des liaisons vers ce composant et finalement la suppression de l’ancien composant alloc. Avec l’API décrite ci-dessus, nous pouvons programmer cette reconfiguration de la manière suivante (nous supposons que le programme dispose d’une poignée vers la racine de l’architecture – composant 2 Nous considérons qu’arrêter un composant, i.e. appeler la méthode stop() du LifeCycleController alors qu’il n’est pas en état stable, est une erreur. 44 Chapitre 4. Interface de programmation 4.3. Exemple d’un programme de reconfiguration kernel, et la poignée vers le composant déjà instancié) : /∗ CI t : C type CC t : C t y p e : C type BC t LCC t : C t y p e : C type RC t SC t : C type NC t : C t y p e for for for for for for for ComponentIdentity ContentController BindingController LifeCycleController ReconfigurationController StateTransferController NameController S e m t : C t y p e f o r Semaphore i n t e r f a c e I t f d e s c t : C t y p e f o r an i n t e r f a c e d e s c r i p t o r ( g e n e r i c ) ∗/ /∗ helper functions ∗/ C I t ∗ h e l p e r f i n d c o m p o n e n t ( C I t ∗ p a r e n t , char ∗ cName ) { CC t ∗ c c = p a r e n t −>g e t I n t e r f a c e ( ” c o n t e n t − c o n t r o l l e r ” ) ; i n t n = cc−>g e t S u b C o m p o n e n t s ( 0 ) ; C I t ∗ ∗ subcomps = a l l o c ( n∗ s i z e o f ( C I t ∗ ) ) ; int i = 0; cc−>g e t S u b C o m p o n e n t s ( subcomps ) ; for ( i = 0 ; i < n ; i ++) { C I t ∗ subcomp = subcomps [ i ] ; NC t ∗ nc = subcomp−>g e t I n t e r f a c e ( ” name− c o n t r o l l e r ” ) ; char ∗ name = nc−>getName ( ) ; i f ( ! s t r c m p ( name , cName ) ) r e t u r n subcomp ; } r e t u r n 0 ; / / no match } / ∗ s i m p l i f i e d p r i v a t e d a t a , s h a r e d by C a l l b a c k and main program ∗ / int counter ; Sem t ∗ sem ; /∗ Callback i n t e r f a c e impleentation , s i m p l i f i e d ∗/ void C a l l b a c k n o t i f y ( ) { c o u n t e r − −; / / i d e a l l y s h o u l d be p r o t e c t e d a g a i n s t c o n c u r r e n t a c c e s s i f ( ! counter ) sem−>V ( ) ; / / ok , l a s t q u i e s c e n t s t a t e d e t e c t e d } / ∗ main r e c o n f i g u r a t i o n program ∗ / v o i d r e c o n f i g u r a t i o n ( C I t ∗ C I k e r n e l , C I t ∗ CI new ) { /∗ i n i t ∗/ c o u n t e r = 1 ; / / o n l y one q u i e s c e n t s t a t e d e t e c t i o n r e q u e s t sem−> i n i t ( 1 ) ; 45 4.4. Exemples d’implémentation Chapitre 4. Interface de programmation CC t ∗ C C k e r n e l = C I k e r n e l −>g e t I n t e r f a c e ( ” c o n t e n t − c o n t r o l l e r ” ) ; RC t ∗ R C k e r n e l = C I k e r n e l −>g e t I n t e r f a c e ( ” r e c o n f i g u r a t i o n − c o n t r o l l e r ” ) ; CI t ∗ CI old = helper find component ( CI kernel , ” alloc ” ) ; C C k e r n e l −>add ( CI new ) ; /∗ Quiescent s t a t e request ∗/ R C k e r n e l −>s u s p e n d ( C I o l d ) ; Semaphore−>P ( ) ; / / b l o c k s /∗ Quiescence detected ∗/ / ∗ s t o p old a l l o c component ∗ / LCC t ∗ LCC old = C I o l d −>g e t I n t e r f a c e ( ” l i f e c y c l e − c o n t r o l l e r ” ) ; i f ( ! LCC old ) LCC old−>s t o p ( ) ; S C t ∗ S C o l d = C I o l d −>g e t I n t e r f a c e ( ” s t a t e −t r a n s f e r − c o n t r o l l e r ” ) ; v o i d ∗ s t a t e = SC old−>g e t S t a t e ( ) ; / / state transformation ? S C t ∗ SC new = CI new−>g e t I n t e r f a c e ( ” s t a t e −t r a n s f e r − c o n t r o l l e r ” ) ; SC new−>s e t S t a t e ( s t a t e ) ; /∗ Itf CI BC BC BC rebind ∗/ d e s c t ∗ I t f a l l o c n e w = CI new−>g e t I n t e r f a c e ( ” a l l o c ” ) ; t ∗ C I x y z = h e l p e r f i n d c o m p o n e n t ( C I k e r n e l , ” xyz ” ) ; t ∗ BC xyz = CI xyz −>g e t I n t e r f a c e ( ” b i n d i n g − c o n t r o l l e r ” ) ; xyz−>u n b i n d ( ” a l l o c ” ) ; xyz−>b i n d ( ” i t f ” , I t f a l l o c n e w ) ; / ∗ s t a r t new a l l o c c o m p o n e n t ∗ / LCC t ∗ LCC new = CI new−>g e t I n t e r f a c e ( ” l i f e c y c l e − c o n t r o l l e r ” ) ; i f ( ! LCC new ) LCC new−> s t a r t ( ) ; / ∗ resume o p e r a t i o n ∗ / R C k e r n e l −>r e s u m e ( C I o l d ) ; / / C I o l d i s an i d ! C C k e r n e l −>remove ( C I o l d ) ; } 4.4 Exemples d’implémentation Alors que l’implémentation des interfaces BindingController, LifeCycleController ou AttributeController reste relativement simple et standard dans l’implémentation Think, l’interface ReconfigurationController est la plus complexe à réaliser. Dans cette section nous décrivons deux différentes implémentations de cette interface, implémentant deux algorithmes différents de détection d’un état stable d’un composant – par comptage de références et avec les intercepteurs dynamiques. 4.4.1 Comptage de références Dans un système avec un modèle d’exécution multi-thread, pour détecter un état quiescent d’un composant (un état quiescent est la trivilialisation d’un état stable, cf. chapitre 2), le système peut compter les invocations et leurs retours. Ainsi, quand le compteur de références est à zéro, aucune invocation n’est 46 Chapitre 4. Interface de programmation 4.4. Exemples d’implémentation en cours et le composant est donc dans un état quiescent. Le composant (ou le système) bloque tout les nouvelles requêtes jusqu’à la fin de la reconfiguration – jusqu’à l’appel de resume(). Cet algorithme classique, que nous appelons comptage de référence, a l’avantage d’être simple. Il a été implémenté dans MMlite (mutation), Synthetix (read-write locks) ou d’autres systèmes. Dans THINK, les invocations de composants sont des appels de méthodes de ses interfaces. Pour compter ces appels, nous avons utilisé des composants intercepteurs spécifiques reliés et contrôllés par l’implémentation de l’interface ReconfigurationController. Ces intercepteurs sont déployés pour chaque interface d’un composant reconfigurable, comme le montre la figure 4.4. L’état stable du composant est déterminé à partir des états stables partiels – un état stable pour une interface. Le rôle d’un intercepteur est de maintenir un compteur des invocations actives de l’interface interceptée pour pouvoir détecter un état quiescent si nécéssaire (i.e. lorsqu’une requête de détection a été émise) et bloquer les nouvelles invocations. Pour cela, chaque intercepteur maintient un état relatif aux invocations en cours – un des états possibles est l’état stable partiel pour cette interface. CI RC implementation CI SC LC I-0 a client component I-1 a client component reconfigurable component I-2 F IG . 4.4 – Deploiement des intercepteurs de comptage de références. Anatomie et type d’intercepteur Les composants intercepteurs font partie de l’implémentation de la membrane d’un composant, concrètement de l’implémentation de l’interface ReconfigurationController. Un intercepteur est spécifique à un type d’interface (type au sens IDL), on parle alors de type d’intercepteur. Par example, la figure 4.5 montre un intercepteur de type I pour un type d’interface I3 . Un intercepteur de comptage de références type I fournit les interfaces suivantes : – une interface serveur du type I, – les interfaces de contrôle standard (ComponentIdentity, BindingController et LifeCycleController), – une interface de contrôle spécifique, appelée InterceptorStateController, permettant de demander un changement d’état d’un intercepteur (suite à une requête de détection d’un état stable, voir suite), Chaque intercepteur de comptage de références de type I requièrt les interfaces suivantes : – une interface cliente du type I, 3 Pour les raisons de simplicité nous confondons le type de l’intercepteur avec le type d’interface. 47 4.4. Exemples d’implémentation Chapitre 4. Interface de programmation – l’interface sf (semaphorefactory), utilisée lors de l’initialisation pour créer deux sémaphores et les lier vers les interfaces clientes mutex et sem – l’interface mutex, sert à implémenter l’exclusion mutuelle pour manipuler le compteur – l’interface sem, sert à bloquer les nouveaux threads entrant lorsque le composant est dans un état stable, – l’interface client cb (ou callback), liée au moment d’une requête de détection d’un état stable et servant à informer l’appelant lorsqu’un état stable est atteint. CI BC LCC LC I I interception logic: forward, reject, block interceptor component mutex sem cb interface type I sf F IG . 4.5 – Un intercepteur de comptage de références de type I. Etats des intercepteurs Pour décider si un appel peut être transmis à l’interface interceptée, chaque intercepteur maintient un état relatif aux appels en cours et à la présence d’une requête de détection d’un état quiescent. Les transitions entre les états sont soit implicites (déterminées à partir de la valeur du compteur) ou doivent être déclenchées explicitement en appelant la méthode change state() fournie par l’interface de contrôle InterceptorStateControl. Les différents états et les transitions associées sont décrits dans la figure 4.6. Après l’initialisation des intercepteurs (pendant l’initialisation du système – appels récursif aux interfaces LifeCycleController), les intercepteurs passent en mode de fonctionnement normal, càd où aucune requête de détection d’état quiescent n’est en cours. Dans ce mode, l’intercepteur peut se trouver dans un état actif – lorsque le compteur de référence est à zéro, donc aucune invocation de méthode ne s’exécute alors, ou dans un état busy – au moins une invocation de méthode est en train d’être exécutée. Les transitions entre ces deux états sont implicites, déterminées uniquement par la valeur du compteur. En particulier le changement entre l’état busy vers l’état actif survient lorsque le dernier thread actif retourne du composant vers l’intercepteur. Une requête de détection d’un état quiescent est traduite par un changement d’état explicite, donc par un appel à change state(). Au moment de cet appel, si l’intercepteur se trouve dans un état actif, l’état devient suspendu et l’état quiescent pour cette interface est atteint directement et tout nouvel appel sera bloqué. Si par contre l’intercepteur se trouve dans un état busy, la requête est temporairement suspendue (par l’appelant) jusqu’à ce que la dernière invocation en cours se termine et qui fait basculer l’état de l’intercepteur dans un état suspendu, donc dans un état quiescent. A ce moment, la méthode notify() de l’appelant est appelée (implementée par l’interface Callback de l’appelant). Algorithme général L’algorithme de détection d’un état quiescent (méthode suspend() de l’interface ReconfigurationController) est implémenté comme suit (pseudo-code C, l’algorithme 48 Chapitre 4. Interface de programmation 4.4. Exemples d’implémentation *) externally triggered transitions, all other transitions are triggered by the interceptor itself NeedInit initialization Activated a thread exits the component and was the last one to execute a thread enxters the component reconfiguration request (*) Busy incoming service requests (thread) proceeds reconfiguration terminated (*) Suspended Quiescent State reconfiguration request (*) InSuspendRequest last thread active in the component exited F IG . 4.6 – Les états des intercepteurs de comptage de références. suppose l’existence d’un mutex pour l’accès en exclusion mutuelle aux données et une implémentation de l’interface Callback, discuté plus bas) : v o i d s u s p e n d ( C o m p o n e n t I d e n t i t y component , C a l l b a c k cb ) { InterceptorStateController ∗ istate ; ComponentIdentity i n t e r c e p t o r s [ ] ; i n t e r c e p t o r s = . . . / / r e t r i e v e from p r i v a t e data ; . . . = cb ; / / s a v e cb t o p r i v a t e d a t a for ( i n t e r c e p t o r : i n t e r c e p t o r s ) { i s t a t e = i n t e r c e p t o r s −>g e t I n t e r f a c e ( ” s t a t e − c o n t r o l l e r ” ) ; i n t a c t i v e = i s t a t e −>c h a n g e s t a t e ( S u s p e n d ) ; if ( active ) { CALL( s e l f −>mutex , P ) ; s e l f −>w a i t i n g ++; CALL( s e l f −>mutex , V ) ; } } CALL( s e l f −>mutex , P ) ; CALL( cb , n o t i f y , s e l f −>w a i t i n g ) ; CALL( s e l f −>mutex , V ) ; } L’implémentation de la méthode suspend() demande un changement d’état aux intercepteurs et remonte à l’appelant le nombre d’intercepteurs encore en attente d’un état stable (cf. appel à l’interface cb de l’appelant). La méthode notify() de l’interface Callback, appelé depuis les intercepteurs lorsqu’un état stable est atteint, est implémentée comme suite : 49 4.4. Exemples d’implémentation Chapitre 4. Interface de programmation void n o t i f y ( ) { CALL( s e l f −>mutex , P ) ; s e l f −>w a i t i n g −−; i f ( s e l f −>w a i t i n g = = 0 ) { / / ok , no more i n t e r c e p t o r s t o w a i t f o r C a l l b a c k cb = . . . / / r e t r i e v e f r o m p r i v a t e d a t a CALL( s e l f −>mutex , V ) ; CALL( cb , n o t i f y , 0 ) ; } else { CALL( s e l f −>mutex , V ) ; } } Un composant peut avoir plusieurs interfaces, donc l’implémentation de notify() ne notifie l’appelant de suspend() uniquement losrque le dernier état stable partiel a été atteint. La figure 4.7 montre l’intéraction entre les différents éléments (implémentation de ReconfigurationController et les intercepteurs d’interfaces des deux composants) pour détecter un état quiescent, ici lors de la détection d’un état quiescent de deux composants en même temps. A noter que les phases de déclanchement de changement d’état et la notification d’un état stable partiel peuvent être entrelacées. L’implémentation utilise des callbacks à deux niveaux - entre l’implémentation de l’interface ReconfigurationController et les intercepteurs, entre le programme de reconfiguration et les différents composants à reconfigurer. Dans le premier cas le callback décrémente le compteur des interfaces encore actives, lorsqu’il est à zéro, le programme de reconfiguration est notifié par un callback. Dans le deuxième cas, le programme de reconfiguration maintient un compteur de composants encore actifs. Lorsque ce compteur tombe à zéro (appel du dernier callback), le programme de reconfiguration est débloqué (appel au sémaphore). component-0 quiescent state request (component-0) interceptor-n InterceptorLifeCycle InterceptorLifeCycle ReconfigurationController reconfiguration program change_state() change_state() suspend() block(), P() callback() ReconfigurationController blocking incoming threads suspend() n-times quiescent state request (component-1) component-1 interceptor-0 change_state() last thread exited callback() n-times callback() quescient state achieved (component-0) callback() callback() quescient state achieved (component-1) unblock(), V() reschedule (by scheduler) reconfiguration continuation F IG . 4.7 – L’intéraction des différents éléments de l’implémentation de la détection d’un état quiescent avec le comptage de références. 50 Chapitre 4. Interface de programmation 4.4.2 4.4. Exemples d’implémentation Intercepteurs dynamiques L’utilisation des intercepteurs dynamiques pour détecter un état quiescent d’un composant est directement inspirée du mécanisme de hot-swapping implémenté dans K42 (Soules et al., 2003; Baumann et al., 2005). Le mécanisme est déployé dans un modèle d’exécution multi-thread avec deux hypothèses particulières – les threads doivent être non-bloquants et de durée déterminée. De cette façon les threads peuvent être groupés dans le temps et constituent des générations de threads. Une génération est un groupement arbitraire de threads à un moment donné de l’exécution du système. Une génération prend naissance lorsqu’un changement de génération est explicitement demandé. Etant donné la nature nonbloquante et la durée limitée de ces threads, une génération se termine toujours, et ce moment est détecté et donne suite à un changement de génération implicite. Lorsqu’un intercepteur dynamique est déployé à l’exécution, il n’a aucune connaissance de l’historique de l’exécution du composant, en particulier, si un appel quelconque est encore en train de s’exécuter. Au déploiement des intercepteurs dynamiques, le système demande explicitement un changement de génération et attend la notification de la terminaison de cette dernière. De son côté, l’intercepteur enregistre l’identité des appelants à chaque invocation du composant. Au moment où la génération de threads s’est terminée, le composant intercepté n’exécute que des threads de la génération courante, enregistrés alors par l’intercepteur. Un état quiescent est en principe atteint au changement de génération – tout nouvel appel est bloqué, les appels récursifs peuvent néanmoins rentrer à nouveau dans le composant (ré-entrance). La figure 4.8 illustre le mécanisme de générations de threads et leur traçabilité par un intercepteur. Au moment où l’intercepteur est déployé les threads existants (en noirs) peuvent potentiellement être en train d’exécuter des méthodes du composant intercepté. Tous les nouveaux threads (en gris) sont enregistrés. Au changement de génération, aucun thread de l’ancienne génération n’existe, alors tous les threads qui s’exécute dans le composant intercepté sont connus. Par conséquent l’intercepteur contrôle totalement l’état d’exécution du composant et peut déterminer un état stable. thread id 0 t dynamic interceptor deployement => generation swap request generation termination => notification F IG . 4.8 – Les générations de threads. Pour gérer les générations de threads, nous avons ajouté un attribut generation aux composants job (voir chapitre 3, figure 3.8) et nous interposons un composant de gestion de générations de threads au composant scheduler (figure 4.9). Ce composant intercepte les méthodes createJob() et destroyJob() de l’interface Scheduler pour maintenir un compteur de générations de threads. C’est également ce composant qui accepte les requêtes de changement de génération et qui notifie l’ap- 51 4.4. Exemples d’implémentation Chapitre 4. Interface de programmation pelant quand une génération s’est terminée. Cette notification est implémentée comme un callback sur une interface de l’appelant. job job job0 runner scheduler job runner scheduler job1 job2 runner scheduler scheduler K42 scheduler ... thread generation management scheduler scheduler F IG . 4.9 – L’architecture modifié des composants pour le multi-threading. Anatomie et type d’intercepteur De la même façon que pour les intercepteurs de comptage de références, le type d’un intercepteur dynamique est confondu avec le type de l’interface qu’il peut intercepter. La figure 4.10 montre un intercepteur dynamique de type I pour un type d’interface I. Un intercepteur dynamique de type I fourni les interfaces suivantes : – une interface serveur du type I, – les interfaces de contrôle standard ComponentIdentity, BindingController et LifeCycleController, – une interface de contrôle spécifique, appelée DynamicInterceptorStateController, permettant de demander un changement d’état d’un intercepteur (suite à une requête de détection d’un état stable, voir suite). Chaque intercepteur dynamique de type I requière les interfaces suivantes : – interface client du type I, – une interface (mutex) pour implémenter l’exclusion mutuelle pour manipuler les données privées de l’intercepteur, – une interface sem (sémaphore), sert à bloquer les appels entrant lors d’un état stable, – l’interface scheduler, sert à retrouver l’identifiant du thread courant lors d’un appel et son enregistrement. Déploiement des intercepteurs Contrairement aux intercepteurs de comptage de références, les intercepteurs dynamiques sont déployés à l’exécution. Lors du déploiement, les composants suivants doivent être instanciés et liés (en utilisant des usines à composants) : – pour chaque interface (de type I) du composant, un composant intercepteur (de type I), – un composant sémaphore, dont l’interface semaphore sera liée à l’interface mutex de l’intercepteur nouvellement installé, – un composant sémaphore, dont l’interface semaphore sera liée à l’interface sem de l’intercepteur nouvellement installé. Par conséquent un système reconfigurable avec les intercepteurs dynamiques doit inclure les usines de composants pour chaque type d’interface et une usine de sémaphores. C’est l’implémentation de 52 Chapitre 4. Interface de programmation CI 4.4. Exemples d’implémentation BC LCC LC interception logic: forward, reject, block I interceptor component I mutex sem scheduler interface type I F IG . 4.10 – Un intercepteur dynamique de type I. l’interface ReconfigurationController qui instancie ces composant et qui est liée aux usines respectives. La figure 4.11 montre un composant reconfigurable avec les intercepteurs dynamiques. Les usines de composants (intercepteurs et sémaphores) sont des composants partagés. CI RC implementation CI SC interceptor factory (shared) a client component reconfigurable component semaphore factory (shared) a client component F IG . 4.11 – Déploiement des intercepteurs dynamiques. Etats des intercepteurs Les intercepteurs dynamiques peuvent être dans trois états - Forward, Block, ou Transfert (cf. (Soules et al., 2003)). Dans état Forward un intercepteur enregistre l’identifiant du thread des appels entrants et les fait suivre au composant intercepté. Dans l’état Block, seuls les appels récursifs peuvent poursuivre l’exécution, les autres threads étant bloqués. L’état Transfert correspond à un état quiescent. Algorithme général De la même manière que pour le comptage de référence, l’algorithme de détection d’un état quiescent est éclaté dans la méthode suspend() de l’interface ReconfigurationController et la méthode notify() de l’interface Callback : 53 4.4. Exemples d’implémentation Chapitre 4. Interface de programmation v o i d s u s p e n d ( C o m p o n e n t I d e n t i t y component , C a l l b a c k cb ) { ComponentIdentity i n t e r c e p t o r s [ ] ; / / in p r i v a t e data . . . = cb ; / / s a v e cb t o p r i v a t e d a t a DynamicInterceptorStateController ∗ istate ; /∗ deploy i n t e r c e p t o r s ∗/ i t f s = CALL( component , g e t I n t e r f a c e s ( ) ) ; for ( i t f : i t f s ) { i t o r = CALL( i t o r f a c t o r y , new , i t f . t y p e ) ; sem = CALL( s e m f a c t o r y , new ) ; mutex = CALL( s e m f a c t o r y , new ) ; B C i t o r = i t o r −>g e t I n t e r f a c e ( ” b i n d i n g − c o n t r o l l e r ” ) ; CALL( B C i t o r , b i n d , ” sem ” , sem ) ; CALL( B C i t o r , b i n d , ” mutex ” , mutex ) ; / / s c h e d u l e r w e l l −known CALL( B C i t o r , b i n d , ” s c h e d u l e r ” , s c h e d u l e r ) ; i n t e r c e p t o r s . add ( i t o r ) ; } /∗ request a suspend ∗/ c h a n g e i n t e r c e p t o r s s t a t e s ( Forward ) ; } void c h a n g e i n t e r c e p t o r s s t a t e s ( i n t s t a t e ) . . = s t a t e ; / / save s t a t e in p r i v a t e data for ( i n t e r c e p t o r : i n t e r c e p t o r s ) { i s t a t e = i n t e r c e p t o r s −>g e t I n t e r f a c e ( ” s t a t e − c o n t r o l l e r ” ) ; CALL( i s t a t e , c h a n g e s t a t e , s t a t e ) ; CALL( s e l f −>mutex , P ) ; s e l f −>w a i t i n g ++; CALL( s e l f −>mutex , V ) ; } L’implémentation de la méthode suspend() instancie les intercepteurs et les sémaphores, établit les liaisons nécéssaires et demande un changement d’état aux intercepteurs. Le reste de l’algorithme est implémenté dans la méthode notify() appelée à partir des intercepteurs. La méthode notify() de l’interface Callback, appelée depuis le mécanisme de génération de threads, est implémentée comme suit : void n o t i f y ( ) { int c u r r e n t s t a t e = s t a t e ; / / in p r i v a t e data ; switch ( c u r r e n t s t a t e ) { case Forward : c h a n g e i n t e r c e p t o r s s t a t e s ( Block ) ; case Block : change interceptors states ( Transfer ); case Transfer : C a l l b a c k cb = . . . / / r e t r i e v e f r o m p r i v a t e d a t a CALL( cb , n o t i f y , 0 ) ; } } A chaque changement de génération, cette implémentation fait évoluer l’état de l’intercepteur, quand 54 Chapitre 4. Interface de programmation 4.5. Conclusion l’état Transfer est atteint, un état quiescent du composant est atteint – l’implémentation notifie l’appelant (appel du callback de l’appelant). La figure 4.12 montre l’intéraction entre les différents éléments d’un composant reconfigurable avec le mécanisme des intercepteurs dynamiques – l’implémentation de ReconfigurationController, les usines des intercepteurs et les intercepteurs eux-mêmes. La figure montre uniquement l’interaction pour un intercepteur d’une interface du composant reconfigurable. Lors d’un changement de génération, le mécanisme de génération de threads notifie l’appelant – méthode notify() décrite ci-dessus. Ce motif d’interaction est répeté pour tous les composants à suspendre et par conséquent à tous les intercepteurs d’interface déployés. En particulier, le programme de reconfiguration est débloqué lorsque tous les états quiescents sont atteints – le dernier callback du programme de reconfiguration est exécuté et débloque le programme en appelant V() sur le sémaphore associé. component-0 interceptor-0 reconfiguration program quiescent state request component-0 ReconfigurationController thread generation management suspend(callback) dynamic interceptor factory semaphore factory BindingController LifeCycle instanciate() 2x instanciate() generation_swap request(callback) bind(sem) bind(mutex) n-times, for n-interceptors change_state (Forward) n-times, for n-interceptors callback() quiescent state request component-i suspend() generation_swap request(callback) last thread of current generation change_state (Block) n-times, for n-interceptors block(), P() callback() last thread of current generation generation_swap request(callback) quiescent state achieved component-0 change_state (Transfer) n-times, for n-interceptors callback() F IG . 4.12 – L’interaction des différents éléments de l’implémentation de la détéction d’un état quiescent avec les intercepteurs dynamiques. 4.5 Conclusion Dans ce chapitre, nous avons présenté une interface de programmation flexible et de bas-niveau, permettant d’ajouter aux composants d’un système Fractal les différents mécanismes pour la reconfiguration dynamique. Implémentation de l’interface de programmation Nous pouvons implémenter une variété de mécanismes de reconfiguration uniquement en combinant du contrôle actif (intercepteurs) et du contrôle passif (implémentation des interfaces de contrôle en général). Par conséquent il est possible d’implémenter l’interface de reconfiguration de façon orthogonale à la partie fonctionnelle du système, voire l’ajouter de façon quasi-automatique par un compilateur. 55 4.5. Conclusion Chapitre 4. Interface de programmation Difficultés Ce chapitre révèle néanmoins les difficultés liées soit à l’implémentation des interfaces de contrôle, soit à la programmation des reconfigurations : – Les interfaces de contrôle étant indépendentes, il faut s’assurer que le système soit construit correctement, i.e. que toutes les interfaces nécéssaires soient incluses. – La conception de l’interface de programmation ne garantit pas son utilisation correcte (par exemple le bon ordre dans les invocations). – Lors de la détection d’un état stable, suivant l’implémentation de l’interface ReconfigurationController, il est possible de créer un interblocage. – Les exemples d’implémentation montrent une dépendance entre l’implémentation des interfaces de contrôle et la partie fonctionnelle du système (sémaphores ou scheduler par exemple), par conséquent il faut s’assurer que le système soit construit correctement, i.e. que toutes les dépendances soient satisfaites. – Le programme de reconfiguration en C (pseudo-C) peut devenir complexe et reste de trop basniveau pour pouvoir bâtir une analyse du programme en vue d’une validation ou optimisation de l’exécution. Un tel programme est également source d’erreur. Vers la compilation de systèmes reconfigurables Les difficultés énumérées peuvent être levées en adoptant une approche de construction de systèmes reconfigurables par compilation d’une part, et de la compilation de programmes de reconfiguration d’autre part. Nous décrivons notre approche dans les deux chapitres suivants : Compilation de systèmes reconfigurables et Compilation des programmes de reconfiguration. 56 Chapitre 5 Compilation des systèmes reconfigurables Dans le chapitre précédent nous avons présenté une interface de programmation flexible donnant un accès de bas-niveau aux différents mécanismes de reconfiguration dynamique. Cette interface est constituée de l’ensemble des interfaces de contrôle du système à l’exécution. Ces interfaces de contrôle sont indépendantes de la partie fonctionnelle du système (ou des composants) et par conséquent il est possible de les ajouter à l’architecture du système après la conception de cette dernière. Dans ce chapitre nous décrivons comment cette interface de programmation de reconfigurations peut être ajoutée automatiquement, par le compilateur de systèmes reconfigurables. L’automatisation de ce processus présente l’intérêt de à la fois simplifier la construction des systèmes reconfigurables, mais également à rendre ce procesus plus robuste dans la mesure où le compilateur vérifie la cohérence de la spécification du système. L’idée principale du compilateur de systèmes reconfigurables étant de transformer l’architecture conçue par le concepteur du système en y ajoutant l’implémentation d’une interface de programmation de reconfigurations. Dans ce chapitre nous décrivons notre approche pour la compilation de systèmes reconfigurables. Pour cela nous avons enrichi le compilateur Think ADL existant (décrit déjà dans (Ozcan, 2007; Leclercq et al., 2007)). Plus particulièrement, nous discutons : – le principe de la compilation des systèmes reconfigurables dans le contexte de la construction de systèmes reconfigurables et de la programmations des reconfigurations, – la structure du compilateur Think ADL existant, – notre contribution au compilateur – les extensions pour spécifier les composants reconfigurables, les transformations de l’architecture et génération des intercepteurs, – un exemple de compilation d’un système reconfigurable. 5.1 Le principe de la compilation des systèmes reconfigurables La compilation des systèmes reconfigurables, une des trois parties de notre proposition d’un modèle de programmation de systèmes reconfigurables, est l’étape clé de ce processus – c’est l’étape qui détermine à la fois la constitution finale du système (i.e. les différents mécanismes de reconfiguration pour chaque composant) et les méta-informations à transmettre au compilateur de programmes de reconfiguration. Le support de compilation que nous avons réalisé peut être généralisé généralisées pour pouvoir être réutilisé pour ajouter au compilateur le support de compilation d’autres aspects non-fonctionnels (comme les composants sécurisés, comme décrit au chapitre 7). 57 5.2. Le compilateur Think ADL Chapitre 5. Compilation des systèmes reconfigurables Définition Sous la notion de compilation ou construction d’un système reconfigurable, nous comprenons le procédé suivant : A partir d’une description architecturale d’un système, de l’implémentation des composants et d’une spécification de composants reconfigurables, le compilateur produit à la fois une image exécutable de ce système et des méta-données. Ainsi, suivant cette définition, le concepteur spécifie les composants reconfigurables du système et le compilateur en produit une image binaire, exécutable. L’ajout des différentes interfaces de contrôle et de leurs implémentations est automatique et reste transparent au dévelopeur du système. L’image exécutable du système peut être chargée et exécutée sur la plate-forme cible (load and boot). Les méta-données peuvent être exploitées pendant la compilation des programmes de reconfiguration, décrite au chapitre suivant. De plus cette définition dissocie la description architecturale du système et la spécification des parties reconfigurables. Les principes de compilation sont les suivants : – Automatisation et simplification de la construction des systèmes reconfigurables. Le compilteur assiste le concepteur du système dans l’ajout des mécanismes de reconfiguration, laissant au concepteur le focus sur la conception du système lui-même. En effet, les éléments (interface de contrôle, intercepteurs et autres composants) requis par les différents mécanismes de reconfiguration complexifient l’architecture du composant (ou du système entier). L’utilisation d’un compilateur rend la construction des composants reconfigurables plus simple et moins sujet à l’erreur – le compilateur transforme l’architecture. – Vérification de la cohérence. Le compilateur complète et vérifie que tous les éléments nécéssaires aux composants reconfigurables soient bien inclus et liés correctement : implémentations des interfaces de contrôle, intercepteurs, usines d’intercepteurs, liaisons, présence des interfaces de transfert d’état etc. En particulier, comme les implémentations d’interfaces de contrôle peuvent dépendre des composants fonctionnels (e.g. dépendance vers les usines de sémaphores), le compilateur vérifie les liaisons des implémentations d’interfaces de contrôle vers les composants fonctionnels. – Génération des méta-données de reconfiguration. Le compilateur est capable de générer des métadonnées d’architecture, comme la description de l’architecture sous forme d’ADL, des informations sur l’endroit des symboles binaires associés à un composant données etc. Ces méta-données, pouvant prendre des formes différentes, sont utilisées pour compiler et vérifier les programmes de reconfiguration. Dans le chapitre 6, nous décrivons un compilateur de programmes de reconfiguration, utilisant uniquement les descriptions architecturales (ADL) et la spécification des composants reconfigurables (décrites dans ce chapitre). – Indépendance de la spécification de composants reconfigurables et la description d’architecture. La spécification des systèmes reconfigurables est indépendante de la description architecturale du système et constitue une entrée supplémentaire au compilateur. Cette indépendance permet la réutilisation des composants dans des contextes différents - reconfigurables ou nonreconfigurables. Pour les mêmes raisons, il est simple de faire plusieurs versions d’un système, reconfigurable ou non, selon les requis dans le temps, sans changer son architecture. 5.2 Le compilateur Think ADL Pour mettre en oeuvre un compilateur de systèmes reconfigurables, nous avons étendu le compilateur Think ADL existant, capable de compiler des images exécutables de systèmes à composants T HINK. A cela, nous avons ajoutés des extensions qui ont pour but de permettre une mise-en-oeuvre d’une interface de programmation de reconfigurations, telle que spécifiée par le concepteur du système reconfigurable. 58 Chapitre 5. Compilation des systèmes reconfigurables 5.2. Le compilateur Think ADL Le compilateur actuel est décrit dans les travaux d’Erdem Özcan (Ozcan, 2007; Leclercq et al., 2007) (voir également l’historique du compilateur Think ADL en annexe C). Ce compilateur est extensible – peut être étendu par des tiers, à moindre effort – ce qui a permis de réaliser un ensemble d’extensions (optionnelles) afin de construire des systèmes reconfigurables. Ici nous donnons uniquement un aperçu de fonctionnement du compilateur pertinent à la description des extensions pour la construction de systèmes reconfigurables. Fonctionnalités du compilateur Le compilateur a les fonctionnalités suivantes : – Support en entrée des langages ADL et IDL (concrètement des parseurs vers un format interne du compilateur), – Vérification de la cohérence architecturale, – Génération du code C (ou glue code) pour un composant à partir de sa description architecturale en format interne, en particulier la membrane du composant, – Interface avec un compilateur C standard, tel que gcc pour produire une image binaire à partir des sources C générés. – Le compilateur est extensible à tout niveau d’architecture, par un mécanisme de plugin. Architecture La figure 5.1 montre une architecture à gros grain simplifié du compilateur Think ADL. Quatre composants sont le coeur du compilateur, chaque composant est lui-même découpé en composants à grain fin : – Le module de chargement est responsable de construire un AST (abstract syntax tree) complet du système à compiler à partir de sa description ADL et IDL pour les interfaces. L’architecture interne de ce composant est une chaı̂ne de composant de chargement, chacun responsable que d’une tâche spécifique, par exemple construire un noeud AST pour une interface, pour un composant ou pour une liaison. – Le module de traitement d’AST parcours l’AST (visiteur) et pour chaque noeud d’un type enregistre auprès de l’ordonnanceur un certain nombre de tâches de compilation à effectuer. Les tâches peuvent avoir des dépendences vers d’autres tâches (par exemple pour générer une instance d’un composant, il est nécéssaire de générer la définition du composant). – Le module de génération de code contient l’implémentation des tâches de génération de code, sous forme de composants de grain fin, par exemple le descripteur d’une interface est généré par un composant, la définition du composant par un autre et son instance par un troisième composant. – L’ordonnanceur exécute les tâches de compilation, après avoir ordonnée l’arbre de tâches enregistrées. Le point essentiel de cette architecture est le découplage des dépendances entre les différentes tâches de génération de code, via l’ordonnanceur, et la capacité de charger un composant (de chargement, de traitement ou de génération de code) dynamiquement par le mécanisme de plugin, ce qui permet de réaliser des extensions à un niveau arbitraire. Flot d’execution, flot de données La figure 5.2 montre le flot d’exécution dans le compilateur. D’abord un AST complet est construit par le module de chargement (vérification syntaxique, puis sémantique). Ensuite cet AST est parcouru par le module de traitement qui est lié au module de génération de code et par conséquent peut enregistrer les tâches de génération de code en parcourant l’AST. Finalement, l’ordonnanceur exécute ces tâches de génération de code et les tâches liée à l’appel d’un compilateur C pour produire l’exécutable final. 59 5.3. Mise-en-oeuvre avec Think ADL Chapitre 5. Compilation des systèmes reconfigurables module de chargement module de traitement d'AST module de génération de code et driver compil. C ordonnanceur Compilateur Think ADL F IG . 5.1 – Architecture du compilateur Think ADL. Formats internes L’architecture du système à compiler est représentée sous forme d’un AST. Cet AST, dont la syntaxe est explicitée dans une grammaire explicite (sous forme de DTD), comprend à la fois la représentation de l’ADL et de l’IDL. L’AST est construit pas les modules de chargement. 5.3 Mise-en-oeuvre avec Think ADL Les extensions du compilateur pour ajouter un support de compilation d’aspects non-fonctionnels, en particulier une interface de programmation de reconfigurations, sont au nombre de trois : – la spécification des composants reconfigurables, indépendante de la description architecturale du système, dans un langage dédié1 , – un support pour les transformations architecturale (d’AST), afin de d’étendre l’architecture de l’utilisateur avec l’interface de reconfiguration, telle que spécifiée, en particulier l’inclusion des différentes implémentations des interfaces de contrôle, et – un support pour la génération des intercepteurs d’interfaces à partir de leurs descriptions IDL, nécéssaire à l’implémentation des interfaces de contrôle. Flot d’exécution La figure 5.3 complète le flot d’exécution de la compilation d’un système reconfigurable : – L’utilisateur soumet au compilateur une spécification du système – description d’architecture et spécification des composants reconfigurables. – La spécification est évaluée et le support de FlexProp ajoute les propriétés aux noeuds AST des composants reconfigurables. – Le chargeur des plugins pour les transformations architecturales charge le plugin de la reconfiguration, tel que spécifié dans la partie definition de la propriété du noeud AST. – La transformation architecturale est réalisée, modifiant l’architecture en incluant une interface de reconfiguration, nécessitant la génération des intercepteurs. – A partir de cet instant le processus de compilation de l’architecture est identique au compilateur original – l’AST est vérifié sémantiquement et ensuite le module de traitement d’AST crée les tâches de génération de code qui seront exécutées suivant leurs dépendances par l’ordonnanceur. 1 Dans les travaux en cours, (Think, n.d.), nous avons étendu l’expressivité de l’ADL et il est désormais possible d’appliquer à un système des extensions écrites également en ADL, rendant inutile l’emploie d’un langage dédié. 60 Chapitre 5. Compilation des systèmes reconfigurables .fractalA .fractalA DSL DL DL .fractalA .fractalA IDL DL DL .fractal Implém .fractal .c, cpp, ADL ADL DSL L ADL ... .java ID .fractal ADL .fractal ADL ADL 5.3. Mise-en-oeuvre avec Think ADL Analyseurs syntaxiques AST Analyseurs sémantiques AST Correct Module de traitement d'AST Module de chargement Graphe de tâches Génération de code Ex ec Vérification uti on Execution Moteur d'exécution Liste de tâches Résolution de dépendences n tio cu e Ex Tâche 1 Ordonnanceur Graphe de tâches Tâche 2 Tâche 3 Tâche 4 Déploiement F IG . 5.2 – Flot de données et flot d’exécution dans le compilateur Think ADL (figure reproduite avec l’aimable autorisation d’A. E. Özcan). – A la fin, le compilateur aura généré une image binaire d’un système reconfigurable. 5.3.1 Spécification des composants reconfigurables Les composants reconfigurables d’un système concret sont spécifiés dans un fichier indépendant de sa description architecturale. Ce fichier contient la spécification des composants que le concepteur veut reconfigurables et des paramètres nécessaires à la transformation de l’architecture. Les spécifications sont écrites dans un langage déclaratif, appelé FlexProp. Une spécification permet d’associer des propriétés à un composant, en particulier la propriété que le composant est reconfigurable2 . Le compilateur lit et interprète ce fichier, en attachant les propriétés spécifiées aux noeuds de l’AST concernés. 5.3.1.1 Un langage déclaratif Spécifications comme propriétés La spécification d’un composant reconfigurable est réalisée comme une propriété particulière attachée au noeud AST représentant le composant. La conception du langage FlexProp est fortement liée au format des noeuds AST des propriétés : properties ::= (property)* property ::= definition (parameter)* parameter ::= name, value definition ::= <identifier> Chaque propriété est identifiée par un champ, nommé definition. Le noeud propriété est un conteneur de paramètres. Un paramètre est un couple associant un nom à une valeur. Ces paramètres se2 Dans le chapitre 7, nous montrons comment les extensions du compilateur, et donc en particulier le langage FlexProp, sont utilisées pour construire des architectures sécurisées 61 5.3. Mise-en-oeuvre avec Think ADL .fractalA .fractalA Implém DL DL C .fractal .fractal FlexProp ADL ADL SL D L ADL Analyseurs syntaxiques AST AST génération intercepteurs AST ID .fractal ADL .fractal ADL ADL extension Reconfiguration AST .fractalA .fractalA IDL DL DL Chapitre 5. Compilation des systèmes reconfigurables Transformations d'architecture AST Analyseurs sémantiques AST Correct Module de traitement d'AST Module de chargement Graphe de tâches Tâche 1 Ordonnanceur Génération de code Execution Moteur d'exécution Liste de tâches Résolution de dépendences Graphe de tâches Tâche 3 Tâche 2 Tâche 4 extensions au compilateur existant F IG . 5.3 – Flot de données et d’exécution dans le compilateur Think ADL étendu pour compiler des architectures reconfigurables (figure modifiée à partir de l’original d’A. E. Özcan). ront passés au plugin de transformation d’architecture et par conséquent sont spécifiques à une propriété et plugin donnés. Syntaxe du langage – A chaque système reconfigurable est associé un fichier FlexProp, de spécification de propriétés. – Un fichier FlexProp est constitué d’une suite de spécifications de propriétés. – Chaque spécification est composée d’une partie droite – séléction de composants – et d’une partie gauche – spécification de propriétés. La partie droite est affectée à la partie gauche ( :). – La séléction des composants (partie droite) permet de spécifier à quel composant dans l’architecture du système doit être appliquée la propriété. Il s’agit d’une expression de chemin dans le système à composant, les différents éléments du chemin sont séparés par un ’slash’ (/). – La spécification des propriétés est une expression s’apparentant à un appel de fonction – le nom correspond à l’identifification de la propriété (champ definition), les arguments étant des paires de paramètres (nom = valeur). Le langage FlexProp sert à spécifier toute propriété non-fonctionnelle liée à un composant. A titre d’exemple, à part la spécification des composants reconfigurables, nous l’utilisons aussi afin de spécifier les composants sécurisés, les optimisations architecturalles souhaitées etc. Dans sa forme déclarative actuelle, l’expressivité du langage reste très restreinte et verbeuse, nous envisageons des évolutions, permettant notamment de spécifier de façon cohérente plusieurs propriétés qui donnent lieu à des transformations d’architecture. Exemple Pour spécifier que le sous-composant counter du composant kernel est reconfigurable utilisant le mécanisme de comptage de référence, on écrirait : kernel/counter : reconfigurable(type=thread-counting, semaphore-reconfig=sem_reconfig.semaphore, mutex-reconfig=sem_mutex.semaphore, semaphore-factory=sf.factory) 62 Chapitre 5. Compilation des systèmes reconfigurables 5.3. Mise-en-oeuvre avec Think ADL La propriété reconfigurable accepte un premier paramètre qui détermine le type de mécanisme d’état stable à inclure au composant. Les paramètres supplémentaires (semaphore-reconfig, mutex-reconfig, semaphore-factory) sont spécifiques au type de transformation et dans ce cas permettent de résoudre les dépendences des implémentations des interfaces de contrôle vers les composants fonctionnels du système. 5.3.1.2 Mise-en-oeuvre Nous avons dévelopé un support pour le langage FlexProp dans le compilateur Think ADL. Ce support intègre un parseur du langage capable de trouver les noeuds représentant les composants à sélectionner et leur attacher un noeud AST de propriétés. Ce support, implémenté sous forme d’un composant, est intégré dans la phase de load, dans le module de chargement. 5.3.2 Transformations architecturales La transformation architecturale d’un système a pour but d’ajouter à ce dernier une interface de reconfiguration spécifiée avec le langage FlexProp. Plusieurs types de modification d’architecture sont nécéssaires pour pouvoir ajouter une interface de reconfiguration telle que décrite dans le chapitre précédent : – ajout des interfaces de contrôle et de leurs impléméntations, – ajout des intercepteurs et des usines d’intercepteurs, – modification des liaisons de composants, et – ajout éventuel des composants fonctionnels à l’architecture. Ces transformations d’architecture ne sont pas locales au composant reconfigurable ou au composant qui l’englobe, mais selon le mécanisme de reconfiguration la transformation impacte potentiellement tout le système (tel est le cas par exemple pour les intercepteurs dynamiques où une usine d’intercepteurs de différents types doit être générée et partagée dans tous les composants reconfigurables). Il est alors nécéssaire de manipuler l’architecture dans son ensemble. Vérifications Avant la transformation de l’architecture, le compilateur peut vérifier sa cohérence par rapport aux requis des différents mécanismes de reconfiguration : – Les composants reconfigurables avec état doivent fournir une implémentation de l’interface StateTransferController. C’est au programmeur du composant de la fournir et son existance de cette interface est vérifiée pendant la transformation. – Le compilateur peut vérifier la conformité des interfaces de contrôle fournies dans l’architecture, conformité par rapport aux requis définies dans le chapitre 4. Ces requis peuvent différer suivant les mécanismes de reconfiguration spécifiés. – Les implémentations de l’interface de reconfigurations peuvent avoir des dépendances vers des composants fonctionnels. Il faut s’assurer de l’existance de ces composants. – ... 5.3.2.1 Mise-en-oeuvre Deux choix d’implémentation sont possibles pour les transformations d’architecture : – Générer une nouvelle description d’architecture reconfigurable Cette approche consiste à générer une description d’architecture reconfigurable à partir d’une description d’architecture initiale, écrite par l’utilisateur. Cette génération serait faite avant la compilation du système (par un frontend ad-hoc). 63 5.3. Mise-en-oeuvre avec Think ADL Chapitre 5. Compilation des systèmes reconfigurables – Modifier l’architecture pendant la compilation L’approche consiste à modifier la représentation interne de l’architecture pendant la compilation du système, l’AST dans notre cas. Nous avons opté pour la modification pendant la compilation, car cette solution présente à longtermes des perspectives d’intégration intéréssantes, comme la combinaison des différents aspects nonfonctionnels ou la combinaison de la reconfiguration et des optimisations architecturales. Nous proposons une première implémentation des transformations architecturales ne visant pas à lever les problèmes de combinaisons des différentes transformations qui ne font pas l’objet de ces travaux. Néanmoins, dans la partie évaluation nous décrivons un prototype d’un système à composants sécurisé, dont les politiques de sécurité peuvent être reconfigurées dynamiquement. Ce système a été compilé en appliquant deux transformations architecturales - la reconfigurabilité et la sécurité. Les transformations d’architectures sont réalisées après la construction de l’AST du système à construire (i.e. à la fin de la chaı̂ne de composants dans le module de chargement). A la chaı̂ne des composants constituant la chaı̂ne de chargement, nous avons ajouté un composant supplémentaire – un chargeur de plugins, qui permet de charger et exécuter des plugins de transformation d’AST. De cette façon plusieurs plugins (i.e. transformations) peuvent être chargés et exécutés. Le plugin de transformation est exécuté une fois qu’une représentation complète de l’architecture est construite, mais avant que celle-ci soit vérifiée. Ainsi, toute transformation est soumise aux vérifications standards faites par le compilateur. Le plugin à charger est lui-même un composant, écrit en Java, implémentant une interface bien définie. L’AST complet est passé au plugin de transformation qui peut le modifier à sa guise, en particulier, appliquer de façon programmatique une transformation aux composants – noeuds AST – ayant des propriétés particulières (e.g. la reconfigurabilité). 5.3.3 Intercepteurs et usines d’intercepteurs Les intercepteurs sont des composants Think dont le but est de faire un pré-traitement, suivi d’un éventuel appel à l’interface interceptée, et un post-traitement. Un intercepteur a un type - le type de l’interface qu’il intercèpte, et une fonctionalité spécifique (par exemple un intercepteur de contrôle d’accès, cf. chapitre 7 ou un intercepteur de comptage de références pour la reconfiguration dynamique). Le concept d’intercepteur n’est pas exclusif à la reconfiguration et de façon générale les intercepteurs permettent de construire un contrôle actif (i.e. pendant l’exécution, à chaque invocation du composant intercepté) lors d’un accès au composant. Comme expliqué au chapitre précédent, les mécanismes de reconfigurations sont implémentés en partie avec des intercepteurs. De plus, pour certains mécanismes (comme les intercepteurs dynamiques décrits au chapitre précédent), il est nécéssaire de disposer à l’exécution d’une usine d’intercepteurs. La fonctionnalité de cette usine est d’instancier un intercepteur dynamique, de type I, correspondant au type d’interface. Une telle usine doit pouvoir être générée automatiquement de façon transparente à l’utilisateur. Les usines d’intercepteurs sont des composants composites qui regroupent les usines particulières d’intercepteurs. Ces usines ne nécéssitent pas de support particulier, elle peuvent être créés pendant la transformation de l’architecture comme un composant composite regroupant plusieurs composants. 5.3.3.1 Mise-en-oeuvre Nous avons intégré au compilateur un support générique de génération d’intercepteurs et de leurs usines. Ce support est réalisé sous forme d’un composant inclus dans la chaı̂ne des composants du module de chargement, capable de générer une représentation interne (AST) d’un intercepteur spécifique et son code C. Ce composant est générique et la génération d’un intercepteur est paramétrée avec deux fichiers templates : 64 Chapitre 5. Compilation des systèmes reconfigurables 5.3. Mise-en-oeuvre avec Think ADL – Un fichier template sert à instancier la représentation interne d’un intercepteur. Ce fichier est en réalité une définition ADL paramétrable (en XML). Pour chaque composant intercepteur, cette définition est instanciée (par la chaı̂ne du load du compilateur) avec les bons arguments. Par exemple, la figure 5.3.3.1 montre une définition ADL paramétrable pour les intercepteurs de comptage de référence. – Le deuxième template sert à instancier le code d’une méthode d’un intercepteur. Il s’agit d’un code C générique qui est personnalisé pour chaque méthode de l’intercepteur. La figure 5.3.3.1 montre un exemple simple d’un tel template, qui a pour l’unique fonctionnalité de faire suivre les appels vers l’interface interceptée. Les mots-clés du template (__type__, SRV INTERCEPTOR METHOD et CLT_INTERCEPTOR_METHOD) sont remplacés par le code réel de l’intercepteur3 . La génération des intercepteurs est donc automatisée, l’utilisateur doit uniquement écrire le template de définition et le template de code C. <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE definition PUBLIC "-//objectweb.org//DTD Fractal ADL 2.0//EN" "classpath://org/objectweb/think/adl/parser/xml/think.dtd"> <definition name="fractal.lib.reconfig.StaticStub" arguments="name,signature,implem"> <component name="${name}"> <interface name="stub" role="server" signature="${signature}" /> <interface name="target" role="client" signature="${signature}" /> <interface name="lifecycle" role="server" signature="fractal.api.reconfig.StaticStub" /> <interface name="lifecycle-controller" role="server" signature="fractal.api.LifeCycleController" /> <interface name="mutex" role="client" contingency="optional" signature="activity.api.Semaphore" /> <interface name="sem" role="client" contingency="optional" signature="activity.api.Semaphore" /> <interface name="cb" role="client" contingency="optional" signature="fractal.api.reconfig.Callback" /> <interface name="sf" role="client" signature="activity.api.SemaphoreFactory" /> <content class="${implem}" language="thinkC" > <directive language="C"> <include file="fractal.lib.reconfig.static_stubs_helper" /> </directive> </content> </component> </definition> F IG . 5.4 – Template de construction ADL des intercepteurs de comptage de références. 3 Pour des raisons de syntaxe il existe une déclinaison de ce template pour les fonctions void. 65 5.4. Conclusion Chapitre 5. Compilation des systèmes reconfigurables type SRV INTERCEPTOR METHOD ( ) { type val ; v a l = CLT INTERCEPTOR METHOD ( ) ; return val ; } F IG . 5.5 – Template de construction C des intercpteurs génériques. 5.4 Conclusion Les extensions que nous avons apportées au compilateur du canevas Think permettent d’assister le développeur d’un système reconfigurable, en automatisant les tâches liées à la génération d’une interface de reconfiguration. De plus, ces extensions sont génériques et peuvent être réutilisées pour ajouter un support d’autres aspects non-fonctionnels dans le compilateur (comme nous le montrons au chapitre 7. Nous avons décrit dans ce chapitre un prototype de compilateur de systèmes reconfigurable. Techniquement loin d’être parfait, ce prototype a néanmoins permis de démontrer le concept et a permis de dégager les perspectives de ce travail, abordées ultérieurement. 66 Chapitre 6 Reconfiguration des architectures Think avec FScript L’interface de reconfiguration que nous avons conçue est riche, mais reste de très bas niveau pour un programmeur de reconfiguration, raisonnant idéalement au niveau architectural. Alors qu’un langage de reconfiguration d’architectures Fractal (un langage dédié, domain specific language ou DSL) appelé FScript existe, l’utilisation d’un tel langage dans les architectures systèmes pose un certain nombre de problèmes que nous abordons dans ce chapitre, notamment la conception d’un support d’exécution efficace. Nous avons dévelopé un support de compilation et d’exécution des programmes FScript pour les architectures systèmes à base de composants Think. Ce support – un compilateur FScript, générant des composants de reconfiguration en C – est le dernier maillon de notre proposition pour la construction de systèmes reconfigurables et la programmation des reconfigurations, articulée autour de l’architecture. L’utilisation de ce support pour reconfigurer un système est facultative – un système qui fourni une interface de programmation de reconfigurations est autonomne par rapport à la reconfiguration dynamique et peut être reconfiguré sans l’utilisation de FScript. Ce chapitre donne un aperçu du langage FScript. Ensuite, nous discutons les différentes possibilités d’implémentation d’un support pour un tel langage, en particulier la distribution de ce support. Finallement nous décrivons la solution choisie pour réaliser ce support – un compilateur FScript générant un code de reconfiguration, ainsi qu’un support minimal nécéssaire pour exécuter le code de reconfiguration compilé. 6.1 Le langage FScript FScript est un langage dédié à la reconfiguration des architectures Fractal, conçu par Pierre-Charles David (David, 2005; David & Ledoux, 2006). L’interface de programmation des reconfigurations, décrite au chapitre 4, réalisée avec les contrôleurs Fractal est puissante et de bas niveau, néanmoins programmer les reconfigurations à ce niveau d’abstraction, i.e. en C dans le cas de Think, est verbeux, difficile à comprendre et difficile à analyser. Pour s’en convaincre, il suffit d’analyser l’exemple du code de reconfiguration du chapitre 4, section 4.3. Et encore, ce code ne fait aucune vérification lors de la reconfiguration, comme par exemple la vérification de la compatibilité des types des interfaces. FScript lève ces limitations et permet de programmer les reconfigurations au niveau d’abstraction de l’architecture, tout en préservant la puissance de l’interface de programmation offerte par les interfaces de contrôle de Fractal. 67 6.1. Le langage FScript 6.1.1 Chapitre 6. Reconfiguration avec FScript Eléments du langage FScript est un langage impératif, avec comme particularité i) un sous-langage spécifique pour des expressions, appelé FPath, permettant de naviguer dans une architecture Fractal et en séléctionner des éléments, et ii) des actions permettant d’effectuer des modifications d’une architecture Fractal. Expressions FPath FScript utilise une grammaire particulière pour les expressions, FPath (inspirée de XPath pour les documents XML). FPath supporte d’une part les expressions classiques (les littéreaux – chaı̂ne de caractères et nombre flottans, arithmétique, logique, comparaison, référencement de variables (déclarées dans FScript) et fonctions) et d’autre part une syntaxe particulière qui permet de naviguer dans une architecture Fractal et en séléctionner des élements. Une architecture Fractal est vue comme un graphe où les noeuds représentent les éléments de l’architecture. Les noeuds sont connéctés par des arcs étiquetés, représentant une relation entre les éléments. Par exemple, un composant avec plusieurs interfaces sera représenté comme un noeud composant, connecté à tous ces noeuds interfaces par les arcs annotés interface. Les éléments d’une architecture, i.e. les noeuds du graphe, sont séléctionnés avec une expression FPath qui représente leur(s) chemin(s). Chaque pas dans un chemin commence à un ensemble initial de noeuds et séléctionne un nouveau pas en suivant une des relations. L’ensemble des noeuds finaux peut être filtré suivant un nom ou par un prédicat (expression), incluant d’autres expressions de chemins. Par exemple l’expression suivante permet de séléctionner toutes les interfaces alloc des sous-composants du composants kernel, lui-même sous composant de l’ensemble racine (la variable $root$ étant bien-connue) : $root/child::kernel/child::*/interface::alloc Actions et fonctions Un programme FScript est constitué d’un ensemble de définitions de procédures - fonctions et actions. Les fonctions sont sans effets de bord sur l’architecture, alors qu’une action modifie une architecture Fractal. Le corps d’une définition de procédure est constitué d’une séquence d’instructions. FScript n’autorise pas la définition de procédures récursives. Structures de contrôle FScript offre un ensemble classique de structures de contrôle : – blocs d’instructions - un groupement d’instructions délimité par { et }, – alternative - if/then/else, – itération - foreach i in path do { body }, et – retour explicite - return. L’utilisation d’un ensemble restreint de structures de contrôles et l’interdiction de définitions récursives, permet à l’implémentation de l’interprète FScript de guarantir la terminaison des reconfigurations (David, 2005). Variables et affectations FScript supporte trois types de variables avec des portées différentes - variables globales, locales et paramètres passés lors d’une invocation de procédure. Par exemple l’instruction suivante, à l’intérieur d’une définition d’action, défini une variable locale : foo := <expression>; 68 Chapitre 6. Reconfiguration avec FScript 6.1.2 6.1. Le langage FScript Bibiolothèque d’actions et de fonctions FScript fourni une librairie extensible de fonctions primitives et d’actions qui donne à l’utilisateur l’accès à l’API Fractal. Ces primitives peuvent être combinées de façon à créer des scripts de reconfiguration complexes. A la bibliothèque standard, nous avons ajouté les actions suspend(parent, sub-component) et resume(parent, sub-component) qui correspondent à l’invocation de l’interface ReconfigurationController définie dans le chapitre 4. Le tableau 6.1 récapitule les actions standard de FScript et leur correspondance avec l’API Fractal, i.e. les interfaces de contrôle. Paramétrage des composants – AttributeController set-value(attribute, value) Renommage d’un composant – NameController set-name(component, name) Ajout et suppression de sous-composants – ContentController add(composite, sub-component) remove(composite, sub-component) Manipulation des liaisons – BindingController bind(client-itf, server-itf) unbind(client-itf) Detéction d’un état quiescent – ReconfigurationController suspend(parent, sub-component) resume(parent, id) Cycle de vie – LifeCycleController start(component) stop(component) Instantiation de composants create(component-definition) TAB . 6.1 – La bibliothèque des actions FScript. 6.1.3 Exemples Voici un exemple d’un programme FScript simple : 1 2 3 4 5 6 7 8 9 10 11 12 action rebind(parent, itfname, srvitf) = { // Select all sub-components of $parent subcomponents := $parent/child::*; foreach comp in $subcomponents do { // Select the interface $itfname of $comp cltitf = $comp/interface::$itfname[bound(.)] [required(.)] // Modify the binding bind($cltitf, $srvitf); } 69 6.2. Supports d’exécution pour FScript 13 Chapitre 6. Reconfiguration avec FScript } Cet exemple défini une nouvelle action de reconfiguration, appelée rebind. Cette action est utilisée lors d’un remplacement d’un composant pour rediriger les références clientes vers le nouveau composant. La première expression FPath (ligne 3) peut être lue ainsi : en commençant au composant $parent, séléctionner dans l’architecture tous ses sous-composants (child::*). Ensuite, pour tous ces composants (foreach, ligne 5), retrouver l’interface cliente dont le nom est $itfname (ligne 7). Nous voulons modifier uniquement les liaisons existantes, donc affecter uniquement les interfaces liées (prédicat bound(.)). De plus, le programme suppose que le composant ne peut avoir plusieurs interfaces du même nom. Une fois l’interface cliente retrouvée, le programme modifie sa valeur pour référencer l’interface $srvitf (ligne 11). L’exemple de reconfiguration du chapitre 4 (section 4.3), sans le transfert d’état, pourrait1 être programmé en FScript de la façon suivante (en utilisant l’action rebind(), ligne 7) : 1 2 3 4 5 6 7 8 9 10 6.2 k = $root/child::kernel; n := new(’new_alloc’); o := $k/child::alloc; add($k, $n); suspend($k, $o); stop($o); rebind($k, alloc, $n/interface::alloc); start($n); resume($k); remove($k, $o); Supports d’exécution pour FScript La définition du langage FScript est étroitement liée au modèle Fractal. De la même façon que le modèle Fractal est implémenté par le canevas Think, il est nécéssaire de concevoir un support d’exécution pour le langage FScript, pour pouvoir l’utiliser avec les systèmes à base de composants Think. Cette relation est montrée sur le schéma de la figure 6.1. Langage FScript définition (programmation) reconfigurations réalisation (implémentation) ? support FScript Modèle à composant Fractal implémentation exécution reconfiguration Think composants "natifs" en C F IG . 6.1 – Quel support pour FScript ? Il existe plusieurs solutions pour concevoir un support d’exécution pour FScript, suivant la répartition de ce support entre la système cible de la reconfiguration et d’autres plate-formes de support. Chaque 1 Nous faisons l’hypothèse que le compilateur (ou interprète) FScript est capable de générer l’interface Callback et gérer la suspension du programme de reconfiguration en attente de la détection d’un état stable 70 Chapitre 6. Reconfiguration avec FScript 6.2. Supports d’exécution pour FScript implémentation aura des propriétés différentes – complexité, l’utilisation des ressources, garanties offertes (terminaison, atomicité etc.), ... 6.2.1 Répartition du support FScript La figure 6.2 montre les deux répartitions possibles du support pour FScript. La première consiste à fournir un support d’exécution sur la plate-forme cible, en d’autres termes un interprète FScript réalisé en C. La deuxième solution consiste à répartir le support de FScript sur plusieurs plate-formes, afin d’alléger le support nécéssaire sur la plate-forme cible. Typiquement, cette solution est intéréssante pour les architectures embarquées contraintes en ressources matérielles, comme les noeuds de capteurs. FScript Fractal support FScript Think FScript Fractal support FScript Think compilateur, ou interprète distribué interprète natif a) support FScript centralisé, natif b) support FScript distribué plate-forme matérielle distincte F IG . 6.2 – Distribution du support pour FScript. Le choix de répartition du support FScript conduit à trois différents types d’implémentation, que nous décrivons ici : – un interprète FScript, – un compilateur FScript, et – un système ”proxy”. 6.2.1.1 Interprète FScript FScript Reconfiguration Program + new components Device + Reconfigurable Device-OS + FScript interpreter communication link reconfiguration Un interprète FScript en natif (càd en C) est un programme embarqué sur le terminal, capable d’exécuter la reconfiguration dynamique à partir du code de reconfiguration écrit en FScript. La figure 6.3 illustre le déploiement d’un interprète en natif. F IG . 6.3 – Interprète FScript. Un interprète natif analyse syntaxiquement le programme FScript (parsing), le vérifie éventuellement et exécute les actions spécifiées. La complexité des vérifications et les garanties offertes ont un impact sur la complexité de l’implémentation de l’interprète et aussi sur l’utilisation des ressources matérielles sous-jacentes. Par exemple, l’interprète de FScript pour Julia (l’interprète original, en Java, pour les 71 6.2. Supports d’exécution pour FScript Chapitre 6. Reconfiguration avec FScript composants en Java) offre des garanties d’atomicité, d’isolement et de terminaison des reconfigurations. Ceci en implémentant un système transactionnel. De plus, un interprète FScript pour instancier de nouveaux composants (dont les usines ne font pas forcément partie du système à l’exécution) l’interprète requière que le système fourni un chargeur dynamique pour charger le code et les données des nouveaux composants. 6.2.1.2 Compilateur FScript La deuxième approche pour implémenter un support pour FScript consiste à réaliser un compilateur FScript sur une machine différente de la plate-forme cible. Le compilateur analyse le programme FScript et génère le code de reconfiguration sous une forme binaire, directement téléchargeable et exécutable par le système cible. Toute la compléxité liée au support de FScript est sur la machine de compilation et le système cible ne fournit qu’un support minimal pour charger et exécuter le code binaire de reconfiguration et des nouveaux composants. La compilation des programmes FScript est basée sur une représentation de l’architecture du système à l’exécution. Plusieurs variantes d’implémentation sont possibles pour garantir que la représentation de l’architecture reflète l’architecture actuelle à l’exécution. Les figures 6.4 et 6.5 illustrent deux exemples d’implémentation. Dans le premier cas, figure 6.4, un protocol de synchronisation de représentation architecturale est mis en place afin de retrouver l’architecture actuelle juste avant la compilation d’une reconfiguration. Dans ce cas il est nécéssaire d’interdire toute modification du système entre le moment de synchronisation et la reconfiguration (hypothèse de non-évolution de l’architecture). FScript Reconfiguration Compilation Host FScript Reconfiguration Program device configuration ADL/AST reconfiguration component communication link Device + Reconfigurable Device-OS reconfiguration F IG . 6.4 – Un compilateur FScript – synchronisation des vues architecturales avant la compilation. Le deuxième cas, figure 6.5, est une simplification du cas précédent où l’implémentation fait l’hypothèse que l’architecture du système est identique à sa description initiale (en ADL) – l’architecture du système n’évolue que par des reconfigurations externes (ou les parties pouvant évoluer sont identifiées, par exemple des usines à composants). Dans ce cas aucune synchronisation n’est nécéssaire avant la compilation d’une reconfiguration et à chaque reconfiguration, la machine de compilation maintient un ADL qui reflète l’architecture actuelle de la plate-forme cible. (Cette solution nécéssite quand même un protocol qui confirmerait le succès d’une reconfiguration, afin de mettre effectivement à jour la représentation de l’architecture vue par le compilateur). L’enjeu dans une approche compilée est la maı̂trise du code généré en terme de complexité et d’efficacité. Dans la section suivante nous allons décrire notre implémentation d’un compilateur FScript, les propriétés garanties et le compromis de l’utilisation des ressources. 72 Chapitre 6. Reconfiguration avec FScript 6.3. Un compilateur FScript external device configuration (meta-data: ADL + binary image) FScript Reconfiguration Compilation Host reconfiguration component FScript Reconfiguration Program Device + Reconfigurable Device-OS reconfiguration communication link F IG . 6.5 – Un compilateur FScript – la plate-forme cible évolue uniquement via les reconfigurations FScript (aucune synchronisation des vues architecturales nécéssaire). 6.2.1.3 Utilisation d’un système ”proxy” Il existe une troisième alternative d’implémentation d’un support de FScript où la machine de compilation exécute la reconfiguration sur un système à composant local, dont l’architecture est identique à l’architecture de la plate-forme cible, figure 6.6. Le système à composant local joue le rôle d’un proxy et toute opération de modification de l’architecture locale (donc tout appel à une interface de contrôle) est répercutée sur la plate-forme cible – les interfaces de contrôle sont implémentées sous forme de proxy. Cette approche fait également une hypothèse forte sur les capacités de l’évolution de la configuration du système distant. FScript Reconfiguration Compilation Host (Re)Configuration API proxy Device + Reconfigurable Device-OS FScript Reconfiguration Program communication link F IG . 6.6 – Utilisation d’un système de proxy pour exécuter un programme de reconfiguration FScript. 6.3 Un compilateur FScript Nous avons conçu un prototype de compilateur FScript pour un usage dans le contexte des réseaux de capteurs. Dans la suite de ce chapitre nous décrivons l’implémentation de ce compilateur, le chapitre suivant 7 évaluera cette approche dans le domaine ciblé. Le compilateur répartit le support de FScript entre la plate-forme cible et une plate-forme appelée hôte de compilation. Cette dernière compile un composant binaire à partir d’un programme FScript, contentant à la fois un composant de reconfiguration et éventuellement les nouveaux composants à instantier. Nous supposons l’existance d’un lien de communication entre la plate-forme cible et l’hôte de compilation. 73 6.3. Un compilateur FScript 6.3.1 Chapitre 6. Reconfiguration avec FScript Défis de l’implémentation Plusieurs défis sont liées à une implémentation d’un compilateur FScript pour les architectures Think. – Comment assurer la cohérence des vues de l’architecture ? L’hôte de compilation qui va compiler un programme FScript doit se baser sur une représentation de l’architecture du système cible. Il faut alors s’assurer que cette représentation reflète réellement l’architecture du système à l’exécution. Typiquement, la description en ADL d’un système reflète son état lors de son déploiement. Néanmoins, si on fait l’hypothèse que le système évolue uniquement par des reconfigurations depuis l’hôte de compilation, ce dernier peut maintenir sa propre représentation du système cible. – Comment retrouver les composants sur le système cible ? Lors de la compilation, les entités manipulées dans les programmes FScript (composants, liaisons, interfaces ...) doivent être translatées vers des références réelles du système à l’exécution. En principe les capacités d’introspection d’une architecture Fractal permettent de retrouver les éléments d’une architecture à partir de leur description textuelle telle qu’écrite dans un programme FScript. – Quelles sont les propriétés garanties par le compilateur ? Par exemple, l’implémentation originale de FScript (un interprète en Java) garantie les propriétés telle que l’atomicité des reconfigurations ou leur isolation. – Quel compromis entre les propriétés garanties et les différentes ressources utilisées ? La complexité de la compilation (l’implémentation des garanties) est en principe contrainte uniquement par les capacités de l’hôte de compilation, néanmoins, pour garantir certaines propriétés (par exemple, un degré de gestion d’erreur), il peut être nécéssaire de générer un code de reconfiguration plus ou moins complexe. Par conséquent, l’implémentation des garanties peut avoir un impact sur l’utilisation des ressources de la plate-forme cible (mémoire, temps de calcul, bande passante etc.). Pour un usage sur des plate-forme matérielles contraintes en ressources, comme les réseaux de capteurs, il est nécéssaire de trouver un compromis entre les propriétés qui peuvent être garanties et les l’utilisation des ressources sur la plate-forme cible. 6.3.2 Caractéristiques du prototype Le principe du compilateur FScript, que nous avons conçu, repose sur la simulation d’une reconfiguration sur une architecture système locale, équivalente à l’architecture du système cible. Pour cela, nous réutilisons l’interprète FScript original. Le résultat de la simulation est un ensemble de modifications d’architecture en terme d’opérations sur les interfaces de contrôle Fractal – le log. Ce log est traduit en code C constituant le code du composant de reconfiguration qui, avec les nouveaux composants, est compilé sous forme binaire pour être envoyé au système cible. Le système cible implémente un support minimal pour charger ce code de reconfiguration et l’exécuter. Pour assurer la cohérence des vues architecturales entre le système cible et le système de compilation, nous faisons l’hypothèse que l’architecture du système cible évolue exclusivement via des reconfigurations explicites, faites par la machine de compilation et uniquement par cell-ci. Ainsi, la machine de compilation peut maintenir une représentation cohérente de l’architecture cible qui est mis à jour après chaque reconfiguration (nécéssité d’un protocole de confirmation du succès de la reconfiguration). La représentation initiale du système est identique à la description ADL initiale du système. Dans le code de reconfiguration généré (détaillé dans la section 6.3.4, les noms des éléments de l’architectures sont sous représentés sous leur forme textuelle, i.e. chaı̂ne de caractère. La résolution de ce nom vers une référence réelle a lieu à l’exécution, en utilisant un composant spécialement dédié à l’introspection de l’architecture Think. Nous allons décrire ce support dans la section 6.3.5. Le prototype du compilateur n’effectue pas une analyse sémantique, néanmoins vérifie et garantie les propriétés suivantes : 74 Chapitre 6. Reconfiguration avec FScript 6.3. Un compilateur FScript – L’existance des implémentations des différents mécanismes pour la reconfiguration dynamique, i.e. l’existance des différentes interfaces de contrôle. Cette vérification est faite en vérifiant la spécification des composants reconfigurables, telle que spécifiée lors de la construction du système (voir chapitre 5). – L’équivalence des types des interfaces lors d’une reconfiguration, en particulier lorsque de nouvelles liaisons sont établies (lors d’un remplacement de composant ou simplement lors d’une modification de liaison). – La résolution des dépendences des nouveaux composants à instancier. Dans l’état du prototype, le compilateur actuel optimise le code généré pour une utilisation dans un contexte de réseau de capteurs. Ainsi le compilateur privilégie la compacticité du code par rapport aux garanties susceptibles d’avoir un impact sur le code. En perspectives, le compilateur pourrait générer une implémentation de l’interface Callback et pourrait générer automatiquement le code responsable de la suspension du programme de reconfiguration (avec des sémaphores) pour attendre que les composants à reconfigurer soient dans un état stable. Le compilateur pourrait également réorganiser le code de reconfiguration afin de minimiser le temps de suspension de service etc. Une alternative consisterait à proposer une construction au niveau du langage FScript qui rendrait explicite l’attente du programme de reconfiguration sur les état stables. 6.3.3 Mise-en-oeuvre avec Think ADL Le compilateur FScript que nous avons conçu reprend l’architecture du compilateur Think ADL et l’organise à sa guise pour pouvoir compiler un programme de reconfiguration. Nous avons réutilisé sans changement la chaı̂ne de chargement d’un AST pour pouvoir à la fois de charger la description architecturale du système à reconfigurer, mais également la description architecturale des éventuels nouveaux composants. Nous avons également réutilisé, sans changements, la chaı̂ne terminale du compilateur Think ADL, à savoir le module de chargement avec la génération de code. En réalité, nous avons insérer l’interprète FScript qui dirige le flot d’exécution (contrairement au compilateur ADL, où le flot est linéaire, cf. figure 5.2 au chapitre 5). La figure 6.7 montre le flot d’exécution du compilateur FScript. Les étapes suivantes sont exécutées : – L’ADL du système à reconfigurer est chargé par le module de chargement du compilateur. L’AST ainsi obtenue est également annoté avec les propriétés de reconfigurations, telles que spécifiées dans le fichier FlexProp, puis transformé. On obtient ainsi un AST du système reconfigurable à l’exécution. – Cet AST permet d’instancier un système à composants en Java, équivalent à l’architecture du système à l’exécution. Ces composants Java implémentent uniquement les différentes interfaces de contrôle et serviront à obtenir le log de reconfiguration. – A ce stage, l’interprète FScript peut être exécuté sur le système Fractal en Java, avec en entrée le programme de reconfiguration. Un log de reconfiguration est obtenu lors de cette phase. – Ce log est passé au module génère le code de reconfiguration, encapsulé dans un composant (représentation AST), appelé driver. Ce module charge également les AST des composants à instancier par le programme de reconfiguration. Finalement, ce module construit (AST) un composant, appelé composant de reconfiguration, qui contient à la fois le driver et les nouveaux composants. – L’AST ainsi obtenu est passé à la chaı̂ne de génération de code standard, i. e. au module de traitement d’AST. – A la sortie de ce traitement, on obtient une image binaire du composant de reconfiguration. – Le module de compilation de reconfiguration génère également la nouvelle représentation du système sous forme d’une description ADL. 75 6.3. Un compilateur FScript .fractalA .fractalA FlexProp DL DL .fractalA .fractalA IDL DL DL .fractal Implém .fractal .c, cpp, ADL ADL .java ... Analyseurs syntaxiques ADL extension Reconfiguration AST DSL L ID .fractal ADL .fractal ADL ADL Chapitre 6. Reconfiguration avec FScript Transformations d'architecture AST Analyseurs sémantiques AST LOG list of new components if ack on reconfig new ADL Java Fractal Components Factory reconfiguration AST - new components FScript Interpreter FScript AST kernel Module de chargement Reconfiguration Component Compiler ADL Module de traitement d'AST AST Graphe de tâches Génération de code Ex e cu Vérification n Execution t cu e Ex Tâche 1 Ordonnanceur tio Moteur d'exécution Liste de tâches Résolution de dépendences ion Graphe de tâches Tâche 2 Tâche 3 Tâche 4 Déploiement F IG . 6.7 – Compilateur FScript - flot de données et d’exécution. 6.3.4 Le composant de reconfiguration généré Le compilateur génère un code de reconfiguration encapsulé dans une structure à composant. Nous détaillons d’abord le code de reconfiguration généré à partir d’un programme FScript, ensuite son encapsulation en composants et finalement le format binaire du composant de reconfiguration. Le code C généré Le code de reconfiguration en C est généré à partir du log de simulation de la reconfiguration par le compilateur. Ce code a les charactéristiques suivantes : – Code séquentiel, sans instructions de contrôle – le log de simulation est une suite d’opérations sur les interfaces de contrôle de l’architecture, en particulier les instructions conditionnelles sont évaluées (if/then ou boucles while etc.). Par conséquent le code C à générer ne contient pas d’instructions de contrôle et le générateur génère uniquement les différentes variables C et les appels aux interfaces de contrôle Fractal du système à l’exécution. – Références textuelles et résolution – Les références du programme FScript sont préservées sous leur forme textuelle et leur résolution est faite à l’exécution en appelant un composant d’introspection fourni par le support d’exécution de FScript embarqué sur la plate-forme. Ce composant est accédé par l’interface client introspection du composant de reconfiguration. 76 Chapitre 6. Reconfiguration avec FScript 6.3. Un compilateur FScript – Déploiement de nouveaux composants – Si lors de la reconfiguration de nouveaux composants doivent être déployés, leur code est également générée par le compilateur et peut être accédée par le code de reconfiguration via l’interface container. – Gestion d’erreurs – Un code de gestion d’erreur minimal est également généré. Ce code vérifie uniquement l’initialisation des différentes variables. En exemple, voici un extrait de code généré pour l’exemple de programme FScript de la section précédente (repris dans les commentaires du code C) : typedef Rfractal_api_ComponentIdentity CI_t; void reconfiguration__run (void* _this) { ... /* 1 k = $root/child::kernel; */ CI_t *kernelCI = CALL(REQUIRED.introspection, get, "kernel"); /* 2 n := new(’new_alloc’); */ CI_t *comp_0 = fscript_new(_this, "counter_reconfig"); /* 4 add($k, $n); */ fscript_add(_this, kernelCI, comp_0); ... } Cet exemple illustre l’utilisation de l’interface introspection pour retrouver les références réelles à partir des références textuelles. Les fonctions utilitaires fscript_new() et fscript_add() sont génériques et sont implémentés dans le support embarqué sur la plate-forme cible. Les deux fonctions encapsulent les appels vers les interfaces de contrôle Fractal et seront détaillées dans la section suivante. L’architecture interne du composant généré Le compilateur FScript génère un composant, dit de reconfiguration, qui contient à la fois le code C de reconfiguration et éventuellement les nouveaux composants à déployer lors de la reconfiguration. Ces nouvelles instances sont déduites à partir de la fonction fscript_new(). Le code C de reconfiguration est encapsulé dans un sous-composant appelé driver. La figure 6.8 montre cette architecture. Le composant driver fourni l’interface reconfiguration qui sera appelée sur la plate-forme cible pour exécuter le programme de reconfiguration. Ce composant requière deux interfaces – une interface introspection et une interface container. La première est utilisée pour traduire les références textuelles du programme FScript vers des références réelles. La deuxième sert à retrouver les nouvelles instances. Format binaire du composant de reconfiguration - ELF Le composant de reconfiguration compilé est en format binaire ELF (ELF, n.d.), relogeable (relocatable). Dans le format ELF relogeable, un objet binaire est embarqué avec des informations permettant de le charger à n’importe quelle adresse. De plus, le fichier contient également une liste de symbols nonrésolus qui doivent l’être lors de l’édition de liens sur la plate-forme cible – dans ce cas concret, ce sont uniquement les fonctions utilitaires (fscript_add(), fscript_new(), etc.) 77 Reconfiguration Component driver container (FScript code in C) new new new components components components implements reconfiguration interface introspection interfaface (required) Chapitre 6. Reconfiguration avec FScript reconfiguration interface 6.4. Conclusion F IG . 6.8 – Le composant de reconfiguration généré par le compilateur FScript. 6.3.5 Support du système à l’exécution Pour pouvoir charger et exécuter un composant binaire tel que décrit dans la section précédente, il est nécéssaire que la plate-forme cible fournisse un support minimal à l’exécution. Ce support est propre à l’implémentation du compilateur FScript, en particulier il est indépendant des mécanismes de reconfiguration tel que décrits dans le chapitre 4. Concrétement, la plate-forme cible doit fournir les composants suivants : – Chargeur et éditeur de liens La plate-forme cible doit implémenter un chargeur et éditeur de lien dynamiques pour le format ELF, capable charger le composant de reconfiguration binaire et d’éditer les liens (symbols). – Fonctions utilitaires Pour minimiser le code de reconfiguration transmis à la plate-forme, quelques fonctions utilitaires doivent être embarquées dans le système reconfigurable. Il s’agit de fonctions génériques qui encapsulent les actions des programmes FScript. Ainsi par exemple l’action add() d’un programme FScript aura une fonction utilitaire associée, appelée fscript add(). – Composant d’introspection Afin de résoudre les références textuelles d’un programme FScript vers les références réelles, la plate-forme cible doit fournir un composant permettant de retrouver à l’aide des interfaces d’introspection Fractal le composant réel. 6.4 Conclusion Le support pour un langage de programmation de reconfigurations, tel que FScript, est la dernière pièce de notre proposition pour un modèle de construction de systèmes reconfigurables. Le prototype du compilateur est bien évidemment sujet aux améliorations, néanmoins comme tout compilateur, il permet une analyse de l’architecture à reconfigurer et du programme de reconfiguration et permet de valider le programme de reconfiguration, ainsi que d’envisager des optimisations de ce dernier (réorganisation du code utilisateur) etc. Cette solution nécessite une synchronisation de la représentation de l’architecture vue par l’hôte de compilation avec l’architecture actuelle s’exécutant sur la plate-forme cible. Par ailleurs le chargement d’objets relogeables au format ELF implique un surcoût d’utilisation mémoire qui est prohibitoire dans certains cas, comme dans les noeuds de réseaux de capteurs. Néanmoins, au chapitre suivant (Evaluation) nous montrons une solution où sous certaines hypothèses la relocation des composants peut avoir lieu sur la plate-forme de compilation, optimisant ainsi l’utilisation mémoire du système cible. 78 Troisième partie Evaluation 79 Chapitre 7 Evaluations Ce chapitre est dévoué à l’évaluation de notre approche, quantitative et qualitative. Pour cela, nous avons réalisé trois prototypes de systèmes reconfigurables à travers lesquels nous allons montrer les différents éléments de l’évaluation. 7.1 7.1.1 Méthode d’évaluation Sujet d’évaluation Nous nous intéressons à évaluer notre modèle de construction de systèmes reconfigurables et leurs reconfigurations dans le contexte d’utilisation des systèmes embarqués. Nous avons découpé l’évaluation de notre proposition en trois parties, chacune correspondante à une partie du modèle, avec des critères d’évaluation différents. 7.1.1.1 Interface de reconfiguration et les implémentations des mécanismes de reconfiguration L’interface de reconfiguration que fournit Think/Fractal est l’élément clé de notre proposition. Deux aspects en sont déterminants dans le contexte des systèmes embarqués : – Flexibilité de l’interface de reconfiguration. La conception de l’interface de reconfiguration détermine sa flexibilité ou la possibilité d’implémenter différents mécanismes, à des grains d’architecture différents, dans des systèmes aux contraintes d’utilisation différentes (comme les réseaux de capteurs ayant des ressources matérielles limitées). – Performances de l’implémentation. L’implémentation d’une interface de reconfiguration a un impact sur les performances du systèmes - utilisation de la mémoire, temps de calcul... Or les performances d’un système embarqué, ou le respect des différentes contraintes du système, sont un élément déterminant dans ce contexte. 7.1.1.2 Compilation des systèmes reconfigurables Si la conception et l’implémentation d’une interface de reconfiguration est l’élément central de notre approche, le compilateur des systèmes reconfigurables englobe toute l’intelligence et la complexité de la mise-en-place d’une telle interface dans un système réel. Le compilateur étant un prototype logiciel, qu’il est toujours possible d’améliorer, nous avons cherché à évaluer les aspects liés à sa conception et à l’approche que nous proposons (en particulier la qualité ”logicielle” n’a que peu d’intérêt) : 81 7.1. Méthode d’évaluation Chapitre 7. Evaluations – Expressivité et simplicité. Les entrées du compilateur constituant une interface avec l’utilisatuer, la spécification des parties reconfigurables, son pouvoir d’expression et la simplicité constituent des éléments clés de l’adoption de notre approche. – Automatisation des transformations architecturales. Le compilateur construit un système reconfigurable à partir des spécifications utilisateur en transformant son architecture. Cette transformation nécéssite divers paramètres spécifiés par l’utilisateur (sous forme des paramètres des propriétés, comme expliqué au chapitre 5). Le compilateur réalise un compromis entre la simplicité de la spécification et la capacité à automatiser la transformation de l’architecture. – Analyse et validation. Dans la forme actuelle du modèle que nous proposons, quel est le degré d’analyse et de validation que le compilateur peut faire lors de la construction d’un système reconfigurable ? Par exemple si deux spécifications de composants reconfigurables sont compatibles au sein du même système. Cette évaluation du compilateur de systèmes reconfigurables est à mettre en lien avec les optimisations architecturales séléctives, décrites en annexe D, qui sont réalisées grâce à un support avancé du compilateur. 7.1.1.3 Programmation et compilation des reconfigurations FScript L’utilisation de FScript pour la programmation des reconfigurations est optionnelle, néanmoins son utilisation simplifie l’écriture des reconfigurations et leur validation. Le compilateur FScript étant un prototype logiciel, qu’il est toujours possible d’améliorer, nous avons cherché à évaluer les aspects liés à sa conception et à l’approche que nous proposons (en particulier la qualité ”logicielle” n’a que peu d’intérêt) : – Langage FScript - simplicité, expressivité Le langage FScript constitue une entrée utilisateur et pour son adoption, il est essentiel que le langage permette d’écrire de manière simple des programmes de reconfiguration équivalents au langage C. – Analyse et validation Quel est le type et le degré des vérifications que le compilateur peut entreprendre, avec les éléments du modèle de programmation que nous proposons ? Par exemple, dans notre proposition il est toujours nécéssaire de vérifier si le composant à reconfigurer a effectivement été construit comme un composant reconfigurable, càd si le composant à reconfigure implémente les mécanismes de reconfiguration. – Adaptation aux différents contextes opérationnels. Contrairement au compilateur de systèmes reconfigurables (qui prépare une image exécutable), le compilateur FScript intéragit avec le système à l’exécution et pour cela requière un support minimal de ce dernier. Comme énoncé, nous avons l’ambition de construire des systèmes reconfigurables pour des plate-formes variées, allant jusqu’aux réseaux de capteurs disposant de peu de ressources matérielles. Il est alors nécéssaire d’évaluer si l’utilisation de FScript s’adapte correctement à des contextes variés. 7.1.2 Critères d’évaluation La nature de l’évaluation diffère selon l’aspect à évaluer, néanmoins, nous avons divisé l’évaluation en deux parties : – évaluation qualitative, comprend l’appréciation des différents aspects liés à la compilation d’un système reconfigurable et à la programmation des reconfigurations avec FScript. – évaluation quantitative, comprend l’évaluation des différents surcoûts lié à l’implémentation d’un système reconfigurable. En particulier, nous avons séparé la mesure de performances en deux parties : 82 Chapitre 7. Evaluations 7.2. Evaluation qualitative – micro-benchmarks – mesure de performances localisée, portant sur l’évaluation de performances des intercepteurs pour la reconfiguration dynamique. – benchmarks applicatifs – évaluation de performances et comparaison d’un système reconfigurable à un système équivalent non-reconfigurable. 7.1.3 Prototypes d’évaluation Pour évaluer les différents éléments cités précédement, nous avons construit plusieurs prototypes de systèmes, permetant de montrer les différents aspects de l’évaluation. Construction d’un système sécurisé reconfigurable Nous avons contruit un prototype d’un système à composants reconfigurables et sécurisés (contrôle d’accès). Ce prototype illustre l’aspect utilisateur de notre proposition (évaluation qualitative) et l’articulation entre les phases de construction d’un système reconfigurable et sa reconfiguration. Décodeur H.264 reconfigurable Dans le but de procéder à une analyse des perfomances d’un système reconfigurable, nous avons construit un décodeur vidéo H.264 reconfigurable. En particulier, ce système nous a permis de confronter et analyser deux implémentations de détection d’un état quiescent de composants – comptage de références et les intercepteurs dynamiques. Systèmes reconfigurables pour noeuds de capteurs Notre approche a pour ambition de cibler également les plate-formes limitées en ressources matérielles, comme les noeuds de réseaux de capteurs. Comme preuve de concept, nous avons construit un système reconfigurable minimal pour un noeud de capteur. Ce prototype nous a également permis d’évaluer l’impact sur les performances du système de deux implémentations du support FScript, présentants différents compromis entre l’efficacité et la flexibilité. 7.2 Evaluation qualitative Dans cette partie nous montrons à travers le prototype d’un système sécurisé reconfigurable une évaluation qualitative de notre contribution. Concrétement nous décrivons l’articulation entre les éléments qui permettent la construction du système et sa reconfiguration. 7.2.1 Description du système Dans les systèmes mobiles, au-delà de la 3G, l’architecture de sécurité1 doit pouvoir prendre en compte les conditions d’opération très dynamiques. Il s’agit en particulier de terminaux nomades, pouvant évoluer dans des réseaux d’accès hétérogènes (GSM, WLAN, ...), ayant des requis différents sur la sécurité. Dans ce contexte, les capacités de reconfiguration dynamique permettent de construire un système où les différents aspects de sécurité sont flexibles et peuvent évoluer au cours de l’exécution, de façon non-anticipée à la conception du système. Dans le cadre d’une initiative plus large (Saxena et al., 2007) visant à construire des terminaux reconfigurables de bout-en-bout (projet E2R (E2R, n.d.)), nous avons co-dévelopé un prototype du framework de contrôle d’accès reconfigurable appelé CRACKER (Jarboui et al., 2006b) (Component-Based Reconfigurable Access Control for Kernels). Ce prototype a été construit en utilisant le compilateur Think ADL étendu, tel que décrit dans le chapitre 5. En particulier un plugin de transformation architecturale pour 1 security en anglais – sûreté et sécurité 83 7.2. Evaluation qualitative Chapitre 7. Evaluations la sécurité (autorisation d’accès) a été rajouté. Dans cette partie de l’évaluation, nous nous intéressons à l’aspect utilisateur de notre modèle – sa simplicité et son efficacité. 7.2.1.1 L’architecture CRACKER pour le contrôle d’accès CRACKER est une architecture de contrôle d’accès basée sur une architecture à composants, indépendante de la politique d’autorisation utilisée. Dans CRACKER, toute ressource, ou objet, est sous la forme de composant. L’accès à un objet par un sujet – entité active du système, typiquement un thread – est soumise à autorisation par un contrôleur spécifique de l’objet. Cette autorisation est fonction d’un contexte et des identifiant de l’objet et du sujet en question. La mise-en-place de l’architecture CRACKER pour un objet (composant) est montrée sur la figure 7.1. Elle consiste de deux éléments – un reference monitor (RM) et un policy manager (PM). ChangeAuthorizationPolicy Policy Manager (PM) Reference Monitor (RM) Check Check Decision (DC) I I J Admin Resource to protect Administration (AC) J Compute Compute Permissions (CPC) F IG . 7.1 – Architecture CRACKER pour le contrôle d’accès aux composants. Reference Monitor Un moniteur de référence est attaché à tout composant qui nécéssite un contrôle d’accès. Dans Think, ce moniteur est implémenté dans la membrane du composant de façon incountournable grâce à des mécanismes matériels basé sur une utilisation de la MMU (?). Le RM intercepte les appels entrant et apelle le policy manager du système pour autoriser ou refuser l’accès au composant contrôlé. Policy Manager Un gestionnaire de politique d’accès est une implémentation de la matrice de contrôle d’accès (sujets/objets). Concrétement il s’agit d’un composant Think qui fourni une interface utilisée par des RM lors de l’autorisation d’un accès et des interfaces permettant l’administration du système. Le PM a trois sous-composants ayant des rôles distincts : – Administration Component (AC) Ce composant maintient un contexte de sécurité et la matrice de contrôle d’accès - associant des droits à la combinaison sujet/objet. Une interface d’administration est également implémentée permettant de modifier ou réinitialiser cette matrice. – Decision Component (DC) Le composant de décision forme les requêtes d’autorisation destinées au composant d’administration, en retrouvant l’identifiant de l’objet accédé et du sujet (à l’aide du composant scheduler du système). – Compute Permissions Component (CPC) Ce composant défini la politique d’autorisation d’accès. Il implémente une fonction qui permet de calculer la matrice d’autorisation maintenue par le composant d’administration. (Saxena et al., 2007) détaille la manière de spécifier la fonction d’autorisation dans Prolog. A l’encontre des composants d’administration et de décision, ce composant dépend de la politique d’accès, changer la politique d’accès revient alors à reconfigurer ce composant et réinitialiser le composant d’administration. 84 Chapitre 7. Evaluations 7.2.1.2 7.2. Evaluation qualitative Reconfiguration des politiques d’accès de CRACKER Reconfigurer la politique de contrôle d’accès revient à remplacer le composant CPC du gestionnaire de politique CP et réinitialiser la matrice maintenue par le composant administration. Suite aux précédents travaux, deux composants CPC sont disponibles, implémentant deux politiques de contrôle d’accès - Domain and Type Enforcement (DTE) (Badger et al., 1995) et Multi-Level Security (MLS). Le protoype que nous avons construit, simule un terminal mobile qui change de contexte de sécurité, nécéssitant une mise-à-jour de la politique de contrôle d’accès. Cette politique, non-anticipée lors de la conception du système, est téléchargée avant la reconfiguration. 7.2.1.3 Architecture du système L’architecture du système, telle qu’elle est vue par l’utilisateur, comprend quatre éléments : – le système fonctionnel, ici un ensemble de threads accédant à un écran pour afficher des informations, – l’implémentation d’un protocol de communucation (TPC/IP sur ethernet, ymodem sur une ligne série, ...), – un (ou plusieurs) gestionnaire(s) de politiques de contrôle d’accès, par défaut implémentant la politique DTE, – le support pour FScript (chargeur dynamique, introspection et sémaphores). Pour plus de clarté, le système est construit en trois étapes. Le premier système, screenOS, contient uniquement la partie fonctionnelle du système. composite screenOS { provides activity.api.Main as main contains main = main contains contains contains contains contains contains contains binds binds binds binds binds binds binds binds thread1 = activity.lib.job thread2 = activity.lib.job scheduler : activity.lib.scheduler allocator : memory.lib.allocator screen : video.lib.screenType drawer = video.lib.tdrawer link : net.lib.comm_link //CHECK main.drawer to drawer.drawer main.console to screen.console main.framebuffer to screen.framebuffer main.thread1 to thread1.job main.thread2 to thread2.job main.allocator to allocator.allocator main.scheduler to scheduler.scheduler main.link to link.link binds drawer.framebuffer to screen.framebuffer binds drawer.character to screen.character binds binds binds binds thread1.scheduler thread2.scheduler thread1.allocator thread2.allocator to to to to scheduler.scheduler scheduler.scheduler allocator.allocator allocator.allocator 85 7.2. Evaluation qualitative Chapitre 7. Evaluations binds thread1.runner to main.runner binds thread2.runner to main.runner binds this.main to main.main } Le composant screenOS est étendu avec le support d’un gestionnaire de politiques de contrôle d’accès. Le nouveau composant s’appelle alors screenAcOS : composite screenAcOS extends screenOS { contains irqsafe : irq.lib.irqsafeType contains policy = security.lib.dtepolicy binds policy.scheduler to scheduler.scheduler binds policy.allocator to allocator.allocator binds policy.irqsafe to irqsafe.irqsafe } Finallement, le système est étendu en y ajoutant un support pour FScript : composite screenReconfAcOS extends screenAcOS { contains loader = loader.lib.loader contains registry = loader.lib.registry contains lowloader : loader.lib.lowloader contains introspection = introspection binds binds binds binds binds introspection.allocator to allocator.allocator loader.registry to registry.registry loader.lowloader to lowloader.lowloader loader.allocator to allocator.allocator loader.cache to cache.cache binds main.introspection to introspection.introspection binds main.loader to loader.loader binds main.registry to registry.registry } 7.2.2 Compilation du système Ce système a été compilé avec le compilateur Think ADL étendu, en particulier nous avons utilisées deux extensions de transformation d’architecture. La première pour ajouter le contrôle d’accès au composant écran avec l’infrastructure de gestion de politiques de sécurité, la deuxième pour ajouter une interface de reconfiguration au système, plus précisément au composant PM. 7.2.2.1 Spécification des propriétés Contrôle d’accès Pour spécifier le contrôle d’accès, l’utilisateur utilise le langage FlexProp introduit au chapitre 5. La spécification suivante définit l’architecture CRACKER pour le système décrit dans le paragraphe précédent. kernel/drawer_sec : security(type=accesscontrol, RM=RM, PM=kernel/policy) 86 Chapitre 7. Evaluations 7.2. Evaluation qualitative kernel/thread1 : security(type=accesscontrol, RM=RMthread, PM=kernel/policy) kernel/thread2 : security(type=accesscontrol, RM=RMthread, PM=kernel/policy) Cette spécification définit le composant drawer_sec comme un composant dont l’accès est soumis à un contrôle par le gestionnaire de politique, implémenté dans le composant kernel/policy inclus dans l’architecture conçue par l’utilisateur2 . L’argument RM spécifie le type de moniteur de référence standard pour les objets ou RMthread pour les sujets. Le moniteur pour les sujets, ici thread1 et thread2, enregistre les derniers auprès du policy manager lors du démarrage du système. Interface de reconfiguration La spécification suivante définit une interface de reconfiguration pour pouvoir remplacer à l’exécution le composant cpc. Une implémentation de type comptage de référence est spécifiée (avec les différents paramètres spécifiant les sémaphores à utiliser). kernel/policy/cpc : reconfigurable(type=thread-counting, semaphore-reconfig=sem_reconfig.semaphore, mutex-reconfig=sem_mutex.semaphore, semaphore-factory=sf.factory) Remarques En observant la spécification des différentes parties du systèmes, nous pouvons avancer deux critiques : – La spécification est relativement verbeuse, le mini-langage ne dispose pas de blocs syntaxiques (délimité par { et }) qui permetterait par exemple d’exprimer que les trois entités sont toutes liées au même gestionnaire – kernel/policy. Par conséquent il est nécéssaire de répéter cette information individuellement dans chaque spécification. – La combinaison des différentes transformations architecturales n’a pas posé de problème dans cet exemple, car l’interface de reconfiguration est appliquée à un composant existant dans l’architecture conçue par l’utilisateur. Néanmoins, l’utilisateur ne peut pas appliquer une transformation à un composant résultat d’une transformation précédente, car celui-ci n’existe pas dans le système lors du traitement de spécifications FlexProp. Concrétement, si la transformation de contrôle d’accès ajoutait également un gestionnaire de politique d’accès par défaut, il serait impossible de spécifier une interface de reconfiguration pour ce dernier. 7.2.2.2 Transformations architecturales Lors de la compilation de ce système, deux plugins sont chargés par le compilateur pour effectuer deux transformations architecturales successives (les mêmes que sur les figures 4.4 et 7.1) Le premier est le plugin de contrôle d’accès et le second le plugin pour la reconfiguration dynamique. L’ordre des plugins n’est pas important dans ce cas, néanmoins il est spécifié dans la configuration de compilation du système3 . 7.2.3 Programmation et compilation d’une reconfiguration Pour remplacer la politique de contrôle d’accès du système conçu, nous utilisons un programme FScript qui vérifie quelle politique est actuellement utilisée et suivant le cas, reconfigure le gestionnaire de politiques : 2 3 Il peut bien y avoir plusieurs gestionnaires différents dans un systèmes fichier build.properties 87 7.3. Micro-benchmarks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Chapitre 7. Evaluations k = $root/child::kernel/::child/policy; l = get-name($k/child::seccompute) if ($l != "mls") { n := new(’cpc_mls’); o := $k/child::cpc; add($k, $n); suspend($k, $o); stop($o); rebind($k, seccompute, $n/interface::seccompute); bind($o/interface::allocator, $k/interface::alloc) bind($o/interface::secadmin, $k/child::admin/interface::secadmin) start($n); resume($k); remove($k, $o); } Le code généré pour ce programme, à partir de la simulation de reconfiguration sur le système conçu, comporte deux blocs de code linéaire, en particulier la vérification de la ligne 4 a été supprimée4 (cette expérimentation a été effectué avec une sémantique bloquante de la méthode suspend()) : – une déclaration et initialisation des variables pour les lignes 1, 2, 5 et 6, – un appel aux fonctions utilitaires (fscript_add(), fscript_suspend(), ..) pour les lignes 8-17. Finallement, le compilateur FScript génère un composant de reconfiguration, qui contient le composant driver et le nouveau composant mls (contenu dans le composant container), comme le montre la figure 6.8 au chapitre 6. Ce composant de reconfiguration est envoyé sous sa forme binaire (ELF) au système s’exécutant sur le terminal qui va exécuter la reconfiguration. 7.3 Micro-benchmarks Les micro-benchmarks ont pour but d’évaluer le coût des différents intercepteurs pour la reconfiguration dynamique qui servent à implémenter la détection d’un état stable. L’implémentation de ces interepteurs diffère suivant le modèle d’exécution. Par conséquent, pour pouvoir comparer les intercepteurs du modèle à threads, nous avons réalisé un prototype d’un modèle d’exécution événementielle (prototype à sémantique pauvre) comme une alternative au modèle multi-thread pour la reconfiguration dynamique. Nous évaluons d’abord les intercepteurs pour le modèle d’exécution à threads (intercepteurs dynamiques et intercepteurs de comptage de références, décrits au chapitre 4). Dans un deuxième temps nous décrivons le modèle d’exécution événementiel et son impact sur les performances d’un système. 7.3.1 Intercepteurs dans un modèle à threads Le tableau 7.3.1 compare les différents surcoûts liés à l’utilisation du canevas Think et à l’utilisation des différents intercepteurs de reconfiguration. Le surcoût lié à un appel de composant est comparé à un 4 Le compilateur n’effectue pas une analyse sémantique, voir chapitre 6, et par conséquent générera toujours un code de reconfiguration pour ce programme, même si la condition à la ligne 4 est évaluée négative (i.e. la politique de contrôle d’accès est déjà mls). Dans ce cas, le compilateur génère un code de reconfiguration, comportant uniquement du code C pour les deux premières lignes. 88 Chapitre 7. Evaluations 7.3. Micro-benchmarks appel de fonction en C. Ces mesures ont été obtenues sur un processeur ARM XScale (Intel PXA255 sur iPAQ h2200), à une fréquence de 400MHz et un bus mémoire de 100MHz. type d’appel appel de fonction C appel de méthode T HINK appel de méthode T HINK avec intercepteurs de comptage de références appel de méthode T HINK avec intercepteurs dynamiques temps (µs) 0, 03 0, 06 0, 6 (0, 27) 0, 4 (0, 27) TAB . 7.1 – Les performances des différentes implémentations d’intercepteurs. Un appel de composant Think implique plusieurs déréférencement de pointeurs (cf. figure 3.5 au chapitre 3). Chaque implémentation d’une méthode a au moins un argument (le pointeur this), donc au total quatre déréferencements sont nécéssaires. Les intercepteurs de comptage de références utilisent un composant sémaphore pour accéder le compteur d’accès en exclusion mutuelle. Au total, six appels de composants sont effectués lors d’un appel intercepté (deux appels au composant sémaphore qui à son tour appelle le composant qui bloque les interruptions du système). Le retour d’un appel est implémenté de la même manière, d’où le facteur 10 par rapport à un appel classique de composant Think. Nous avons optimisé l’implémentation des intercepteurs, en implémentant l’exclusion mutuelle directement en bloquant les interruptions – les mesures entre parenthèse. Néanmoins cette implémentation dépend de l’architecture matérielle ciblée. L’algorithme qu’implémentent les intercepteurs dynamiques est plus complexe et nécéssite de tracer l’identité des différents appelants (avec les composants Kortex ceci nécéssite un appel au composant scheduler). Contrairement au comptage de références, les intercepteurs dynamiques ne font aucun traitement pour le retour d’appel du composant. Le tableau 7.2 indique la taille mémoire occupée par les différents intercepteurs : – Un intercepteur (que ce soit comptage de référence ou dynamique) est un composant Think qui implémente une interface fonctionnelle et deux interfaces de contrôle (LifeCycleController et InterceptorStateController). Les méta-données associées à un intercepteur représentent cette information. Les méta-données contiennent également les noms des différentes interfaces, étant la différence de taille des noms des interfaces fonctionnelles. – Pour chaque méthode de l’interface interceptée le même traitement est effectué. Pour minimiser la taille de la méthode d’interception, nous avons factorisé ce traitement en plusieurs fonctions et la méthode se résume alors à l’appel d’une ou plusieurs fonctions. – L’implémentation des interfaces de contrôle des intercepteurs, ainsi que le code de traitement sont partagés par tous les intercepteurs du système. composant intercepteur chaı̂nes de caractères stub de méthode comptage de références implémentation intercepteurs dynamiques implémentation taille code 144 + 5 100b 1280b taille données 200b - 4200b 160b TAB . 7.2 – L’occupation mémoire des différentes implémentation des intercepteurs. 89 7.3. Micro-benchmarks Chapitre 7. Evaluations Par exemple pour l’interface video.api.Console qui a six méthodes, le composant d’interception occupe 100.6 + 144 = 744b de code et 200b de données. Pour un système reconfigurable, avec l’algorithme de comptage de références, ayant i interfaces interceptées et m méthodes interceptées, la mémoire occupée par l’implémentation de l’algorithme de comptage de référence serait la suivante : 200.i + 144.i + + 100.m + 1280 7.3.2 (7.1) Un modèle d’exécution événementielle Dans un modèle à threads, la détection d’un état quiescent du composant engendre un coût et une infrastructure non-négligeable. Nous avons conçu un modèle événementiel dans lequel, par construction les composants sont dans un état stable entre chaque traitement d’événement. Ainsi, il n’y a pas de coût associé à détecter cet état. Cependant, un modèle événementiel présente deux inconvéniants. D’abord, en terme de modèle de programmation, car ce modèle est moins intuitif et plus difficile à comprendre dans son ensemble que le modèle classique. Ensuite, en terme de performances, dans la mesure, où toute intéraction entre les composants du système est implémentée comme un événement et donc implique un surcoût par rapport à un simple appel de composant. Le modèle que nous présentons ici est simple et son objectif est uniquement une analyse de performances pour la reconfiguration dynamique. 7.3.2.1 Modèle de base Le modèle événementiel mis en place est simple : – Tout appel d’une méthode d’interface est un événement. Par extension (ou abus de langage), on désigne également par événement l’interface ou son implémentantion, donc le composant. – Chaque événement s’exécute jusqu’à sa terminaison. – Les événements sont gérés par un ordonnanceur d’événements, simple dans notre modèle – une FIFO. – Le modèle d’architecture est un modèle plat – il n’y a pas de composition hiérarchique, tous les composants événementiels sont sous-composants du même composant parent. L’architecture peut néanmoins contenir des composants composite, mais leur exécution ne sera pas événementielle. – Les interruptions matérielles sont transformées en événements logiciels, associés à une interface. 7.3.2.2 Construction d’un système événementiel L’implémentation du modèle présenté est simple, toute liaison entre composant est interceptée par un intercepteur relié à un ordonnanceur. C’est l’ordonnanceur qui expédie les événements, en appelant le composant (plus précisément la méthode de l’interface). A chaque appel d’une méthode cliente, l’intercepteur met l’événement correspondant dans la file de l’ordonnanceur et l’exécution se poursuit dans le composant appelant. Lorsque le composant a terminé son traitement, l’exécution retourne à l’ordonnanceur qui élit l’événement suivant. Un système événementiel est construit en utilisant les mêmes mécanismes de compilation qui ont été utilisés pour contruire des systèmes reconfigurables ou sécurisés – spécifications et transformations d’architecture. L’utilisateur construit une architecture à composants Think et spécifie une transformation ”événementielle”. L’architecture du système doit néanmoins respecter les contraintes imposées par le modèle – modèle d’architecture plat. 90 Chapitre 7. Evaluations 7.3.2.3 7.3. Micro-benchmarks Implémentation de la reconfiguration Une reconfiguration dynamique est implémentée comme un événement. Lorsque celui-ci s’exécute, tous les autres composants sont dans un état stable. Par conséquent, l’implémentation de l’interface de reconfiguration dynamique pour un modèle d’exécution événementiel va diverger uniquement dans l’implémentation de l’état stable, donc l’implémentation de l’interface ReconfigurationController – l’implémentation de l’introspection, des modifications architecturales et les autres aspects du contrôle d’état reste inchangés. Par conséquent, les méthodes de l’interface ReconfigurationController sont simplement vides, car non-nécéssaires, mais présentes dans le programme FScript, qui lui n’a aucune information sur le modèle d’exécution sous-jacent6 . 7.3.2.4 Evaluation de performances Evénement Le tableau 7.3 montre la comparaison des temps d’exécution des différents types d’appel sur la plate-forme Cognichip – fonction C, appel synchrone de méthode d’un composant (appel classique de composant) ou appel asynchrone (intéraction par événement, cet appel est intercepté et géré par l’ordonnanceur). Le coût d’un appel asynchrone comprend à la fois le temps d’appel de l’intercepteur, qui à son tour appelle l’ordonnanceur pour insérer l’événement dans une file, également le temps que met l’ordonnanceur à exécuter l’événement. Par ailleurs, nous retrouvons le facteur 2 d’un appel de composant classique par rapport à un appel d’une fonction C. type d’appel appel fonction C appel méthode T HINK événement temps (µs) 7, 7(12, 6) 16, 8 215, 5 TAB . 7.3 – Les performances d’un appel événementiel. Evenementiel vs. Multi-thread Le tableau 7.4 compare le coût d’un évnémenent au coût du contextswitch dans un modèle multi-thread. Lors d’un context-switch, il est nécéssaire de sauvegarder et restaurer un ensemble de registres (32x8bits sur AVR), le coût de l’événement comprend l’appel du composant intercepteur, l’appel du composant scheduler et également la gestion de la file (naı̈ve dans cette implémentation). changement de contexte de threads événement (queue, dispatch) temps (µs) 262, 7 215, 5 TAB . 7.4 – Performances comparatives entre un modèle événementiel et un modèle multi-thread. Un modèle d’exécution événementiel présente un avantage en termes de performances pour implémenter un état stable dans un système reconfigurable, néanmoins cette évaluation ne donne aucun renseignement sur la comparaison des deux modèles pour une application ou un système concrèt. 6 Ces instructions pourraient être supprimées par le compilateur FScript, comme une optimisation. 91 7.4. Evaluation de performances de systèmes reconfigurables 7.4 Chapitre 7. Evaluations Evaluation de performances de systèmes reconfigurables L’évaluation de performances applicatives s’appuie sur deux prototypes d’application. Tout d’abord nous étudions les performances d’un système reconfigurable en utilisant le prototype du décodeur H.264. Ensuite, nous évaluatons le support d’exécution de programmes FScript sur le prototype d’un système pour noeuds de capteurs. 7.4.1 Décodeur vidéo H.264 reconfigurable Dans un système reconfigurable ayant un modèle d’exécution multi-thread, l’implémentation du contrôle actif – détection d’un état quiescent – a un impact considérable sur les performances du systèmes. Afin d’évaluer cet impact, nous avons contruit un décodeur vidéo H.264 reconfigurable. Au cours des travaux précédents, le décodeur H.264 de référence (JN/TML, n.d.) a été rearchitecturé sous forme de composants Think (Layaida et al., 2005). Ce système est conçu comme un système d’exploitation applicatif (application et système dans le même espace d’adressage) et permet de modifier le modèle de concurrence et son degré. Nous avons mis à l’épreuve notre conception de l’interface de reconfiguration et son implémentation en rendant reconfigurables les services systèmes, en particulier le composant d’affichage (framebuffer et screen) – le composant le plus critique en terme de performances de ce système. Nous avons construit et comparé deux versions de ce système, une première avec des threads ”classiques”, donc utilisable avec l’algorithme de comptage de référence ; et une deuxième avec des threads ”à la K42”, où les intercepteurs dynamiques peuvent être utilisés pour détecter un état quiescent, mais également l’algorithme de comptage de référence. 7.4.1.1 Caractéristiques du système La figure 7.2 montre l’architecture du système de base qui a servi à construire le décodeur complet. Cette figure montre l’architecture utilisateur, en particulier les intercepteurs liés à l’implémentation de l’interface de reconfiguration sont omis. FileSystem Allocator Scheduler FrameBuffer Console E2FS Idle Thread e2fs Console PriorityScheduler PriorityBased Scheduler FrameBuffer Driver DiskDriver Trap Manager Timer Handler Screen FlatAllocator Flat Memory Manager dlmalloc SpecificOSKernel_H264 F IG . 7.2 – Système applicatif du décodeur H.264. L’architecture du décodeur, construite au-dessus du système décrit précédemment, est composée de quatre composants complexes qui ont un traitement indépendant : – Input Reader (IR) - Composant responsable de lire le flux à décoder (réseaux, disque etc.) et le présenter au composant coeur du décodage. 92 Chapitre 7. Evaluations 7.4. Evaluation de performances de systèmes reconfigurables – Core Decoder (CD) - Le composant coeur du décodage vidéo H.264. – Video Displayer (VD) - Ce composant encapsule la logique d’affichage du flux décodé sur un support donnée, dans notre cas l’écran. – Subtitle Displayer (SD) - Gestion des sous-titres, décodage et affichage. Accès concurrent au service/composant d’affichage du système. L’exécution de chacun des quatre composants est indépendante, chaque composant est associé à un fil d’exécution – thread – et les composants s’échangent des messages et des données dans des tampons prévus à cet effet. Le flot de données, et par conséquent l’algorithme de décodage, est le suivant : i) lecture par IR, ii) décodage par CD et iii) en parallèle affichage par VD et SD. Dans cette architecture le modèle de concurrence est implémenté par le composant scheduler et les composants threads qui sont associés aux quatre composants de traitement. Les figures 7.3 et 7.4 montrent les architectures concrètes du décodeur H.264 avec les deux modèles de concurrence. En particulier, la deuxième architecture utilise des threads dynamiques et par conséquent contient des usines de composants threads. Subtitled Video Decoder Thread 1 Input Reader Thread 2 Core Decoder Subtitle Reader Thread 3 Video Displayer Subtitle Displayer Thread 4 Console Frame Buffer Memory Allocator File System Scheduler SpecificOSKernel_H264 F IG . 7.3 – Architecture du système H.264 utilisant un modèle de concurrence classique. 7.4.1.2 Benchmarks applicatifs Le benchmark applicatif a pour but de déterminer l’impact des mécanismes pour la reconfiguration dynamique sur une application réelle. Pour cela, nous avons réalisé un décodage de 200 frames vidéo avec le décodeur H.264 décrit précédemment. Le tableau 7.5 présente les temps de décodage pour les deux modèles de concurrence. Dans chaque cas nous avons comparé le temps de décodage d’une architecture non-reconfigurable à une architecture reconfigurable, avec aucune ou une seule reconfiguration (le frame-buffer). Performances initiales La comparaison des performances initiales – système non-reconfigurable et système reconfigurable (sans reconfiguration), dépend du choix du mécanisme de détection de l’état quiescent et par conséquent du choix de modèle de concurrence. Comme prévu, l’utilisation des intercepteurs dynamiques pour détecter un état stable n’altère pas les performances du système reconfigurable (sans reconfiguration) par rapport à un système non-reconfigurable. 93 7.4. Evaluation de performances de systèmes reconfigurables Chapitre 7. Evaluations Subtitled Video Decoder Execution Controller Thread 0 Thread Factory Thread n+1 Input Reader Thread n+2 Core Decoder Subtitle Reader Thread n+3 Video Displayer Subtitle Displayer Thread n+4 Console Frame Buffer Memory Allocator File System Scheduler SpecificOSKernel_H264 F IG . 7.4 – Architecture du système H.264 utilisant un modèle de concurrence avec des threads de durée déterminée et non-bloquants. L’utilisation du mécanisme de comptage de référence implique un surcoût lié à l’utilisation massive des intercepteurs. Ce surcoût est fonction de la combinaison suivante : i) la complexité de l’implémentation des intercepteurs, étroitement liée à la complexité de l’algorithme de détection d’un état quiescent, ii) le pattern d’accès au composant reconfigurable et sa complexité, donc le pattern d’exécution de l’intercepteur, ceci étant une donnée de l’application. Dans notre système, le composant frame-buffer est accédé de façon massive et donc le système paie le surcoût de l’intercepteur à chaque appel. Impact d’une reconfiguration sur les performances Une reconfiguration impacte les performances de deux façons. La première est le surcoût lié à la détection d’un état quiescent. La seconde est le surcoût lié à la reconfiguration elle-même sous forme de code non-fonctionnel qui s’exécute une fois l’état quiescent atteint. Le surcoût total varie en fonction de la complexité de la reconfiguration et de la complexité des mécanismes de détection d’état quiescent. L’impact sur les performances de la détection d’un état stable est déterminé par i) la complexité de l’intercepteur utilisé et ii) le pattern d’accès au composant reconfigurable, donc la fréquence d’exécution de l’intercepteur. Ce dernier varie selon l’application. Durée de détection d’un état quiescent Pendant le détection d’un état quiescent, les performances du systèmes sont dégradées, car dans le cas des deux mécanismes, les appels aux composants reconfigurables sont temporairement bloqués. La durée de détection d’un état stable est alors un paramètre à minimiser, néanmoins elle dépend fortement de l’application. La durée varie également selon le nombre de composants en état quiescent : – Avec les intercepteurs dynamiques la durée de détection dépend de la durée de vie d’une génération de threads, donc du pattern de création/destruction de threads et de la complexité des composants que le thread exécute. 94 Chapitre 7. Evaluations 7.4. Evaluation de performances de systèmes reconfigurables modèle à threads classique système non reconfigurable système reconfigurable avec comptage de références short-lived non-blocking threads système non reconfigurable système reconfigurable avec intercepteurs dynamiques système reconfigurable avec comptage de références pas de reconfiguration 4, 74s une reconfiguration - 4, 95s (4, 4%) 4.99s (5, 2%) pas de reconfiguration 7, 14s une reconfiguration - 7, 14s 7, 42 (3, 9%) 7, 24s (1, 4%) 7.32s (2, 5%) TAB . 7.5 – Durée de décodage de 200 frame vidéo pour les différents systèmes reconfigurables (utilisant différentes implémentations de la détection d’un état quiescent). La détection d’un état quiescent de plusieurs composants a lieu en principe en même temps, à la fin de la même génération de threads. – Avec le comptage de référence, la durée de détection dépend de la complexité du composant reconfigurable (et eventuellement des composants qu’il appelle). Contrairement aux intercepteurs dynamiques, la détection d’un état quiescent de plusieurs composants est complétement indépendente. Comptage de référence vs. Intercepteurs dynamiques Au vu des résultats il n’existe pas de mécanisme de détection d’état stable optimal. Certes, les intercepteurs dynamiques ne présentent pas de surcoût pendant l’exécution, néanmoins, le modèle d’exécution n’est pas universel et nous avons du modifié l’algorithme de décodage pour paramétrer la création et destruction de threads et cette version modifiée présente un surcoût initiale plus important que la simple version utilisant des threads ”classiques”. Le choix d’un mécanisme de détection d’état stable va dépendre de l’application, du modèle de concurrence souhaité, des performances initiales souhaitées, de la flexibilité du mécanisme (typiquement, par défaut tout composant est reconfigurable avec les intercepteurs dynamiques, alors qu’avec le comptage de référence, il faut anticiper lors de la conception du système et spécifier les composants comme reconfigurables), la durée de reconfiguration etc. Surcoût mémoire vidéo H.264. Le tableau 7.6 compare l’occupation mémoire des différentes versions du décodeur type de système reconfigurable système non reconfigurable système reconfigurable avec comptage de références système reconfigurable avec intercepteurs dynamiques occupation mémoire 907.3kB 913.2kB 990.3kB TAB . 7.6 – Occupation mémoire des systèmes reconfigurables. Le surcoût des intercepteurs dynamiques est lié à notre implémentation des usines d’intercepteurs – pour chaque type d’interface du système, le compilateur ADL génère un composant usine. Nous n’utili- 95 7.4. Evaluation de performances de systèmes reconfigurables Chapitre 7. Evaluations sons pas d’intercepteurs génériques. Pour le comptage de référence, les intercepteurs occupent moins de mémoire et l’implémentation partagée est également plus compacte. 7.4.2 Reconfiguration dynamique des noeuds de capteurs Les sytèmes pour des noeuds de réseaux de capteurs présentent des défis liés à leur construction et efficacité (de par leur limitation de resources, les systèmes sont construits ”à la carte”) et à leur mise-àjour (du fait du nombre de noeuds déployés et leur potentielle inacessibilité physique). Dans le chapitre 6, nous avons décrit le support nécessaire pour charger et exécuter un programme FScript compilé. Ce support est générique et flexible – la résolution des adresses des interfaces est faite à l’exécution par introspection et le format binaire du composant permet de le charger dynamiquement à n’importe quelle adresse mémoire. Ainsi, à part l’ADL du système, le compilateur FScript ne nécéssite aucune information supplémentaire pour compiler un programme de reconfiguration. Alors que ce fonctionnement peut convenir à des plate-formes avec suffisamment de resources, pour une utilisation dans les réseaux de capteurs, il est nécéssaire d’optimiser l’utilisation de la mémoire et de la bande passante. En effet, la transmission radio étant la resource critique qui consomme le plus d’énergie. De ce point de vue, notre approche de la reconfiguration est à l’opposé d’une approche comme la machine virtuelle Maté (Levis & Culler, 2002) où tout programme est constitué de byte-code compact et efficace à transmettre. Dans un premier temps, nous présentons une version générique et flexible du support pour FScript, ensuite nous présenterons les optimisations apportées pour une utilisation efficace dans les réseaux de capteurs. Le compilateur FScript, génère un code de reconfiguration en accord avec le support présent (ou en accord avec la spécification utilisateur). Le compilateur de systèmes reconfigurables, pourrait inclure de façon automatique un support FScript approprié au système construit. 7.4.2.1 Description du système Nous avons construit un prototype de noeud de capteur pour la radio cognivite opportuniste (Mitola, 2000), destiné à un usage domotique. Cette plate-forme est appelée Cognichip. Le Cognichip est basé sur une architecture AVR, architecture Harvard avec les bus mémoire séparés pour le code et les donées, concrètement le micro-processeur ATmega2561 (Atmel, n.d.), un RISC 8-bit à 16MHz, avec 8kb de RAM (données) et 256kb de mémoire flash (code). La plate-forme est équipée d’un circuit éméteur/recepteur radio (le cc1000, (Chipcon, n.d.)) opérant sur les fréquence radio allouées aux chaı̂nes télévisées. Le Cognichip peut accueillir des capteurs et des actionneurs domotiques (capteurs de température, détecteurs de fumée, ...). Les Cognichips sont reliés à une station de base (lien radio), servant de passerelle. Cette dernière est connectée à une infrastructure réseaux, permettant de gérer le réseau de capteurs déployé. Au vu des resources matérielles de la plate-forme, la reconfiguration logicielle est une capacité intéréssante, voire nécéssaire. Pour effectuer des mis-à-jour du système, sans le redémarrer (et perdre son état qui est associé au traitement de l’information), pour installer de nouveaux drivers permettant de piloter de nouveaux actionneurs ou capteurs etc. Avec le canevas Think, nous avons construit un prototype d’un système reconfigurable sur le Cognichip. Pour des raisons de capacité matérielle et d’efficacité à l’exécution, le modèle d’exécution classique, multi-thread, est prohibitif pour construire un tel système. En effet, la détection d’un état quiescent du composant implique un surcoût non négligeable sur une plate-forme comme le Cognichip. Par conséquent, nous avons construit un système événementiel, avec une sémantique particulière d’état stable, permettant de reconfigurer le système à moindre coût. De plus, les optimisations architecturales décrites dans l’annexe D sont spécialement conçues pour s’appliquer à ce système. 96 Chapitre 7. Evaluations 7.4.2.2 7.4. Evaluation de performances de systèmes reconfigurables Support flexible pour FScript A partir d’un ADL et d’un programme de reconfiguration, le compilateur FScript génère un code de reconfiguration (ainsi que les composants associés). La flexibilité de l’approche est dûe à l’utilisation de l’introspection dans le code généré, les références textuelles étant suffisantes, et à l’utilisation du format ELF qui permet de charger le composant à n’importe quelle adresse en mémoire. Le tableau 7.4.2.2 compare les tailles des différentes parties du fichier ELF du composant de reconfiguration généré : – L’en-tête ELF contient la table de symboles (donc chaı̂nes des caractères) et deux tables de relocation (pour les sections .text et .data). – La quantité de symboles dans la table de symboles est proportionnelle au nombre de composants. Pour chaque composant, le compilateur Think génère un nombre de variables globales (donc symboles), en particulier, l’implémentation du composant est transformée et les variables globales ”utilisateurs” sont encapsulées dans une seule structure globale. – La taille des tables de relocation est liée également à la nature du code C généré. Le compilateur génère des variables globales ”cross-référencées”, chaque référence nécéssitant une entrée dans la table de relocation. La complexité de l’implémentation du composant, en terme de découpage en fonctions, joue également un rôle - chaque appel d’une fonction nécéssite une entrée dans la table de relocation. Taille programme FScript Code de reconfiguration Nouveau composant (mls) Méta-données (structures T HINK) Total contenu En-tête ELF dont chaı̂nes de caractères Total fichier ELF 400b 1.5kb 4.7kb 0.2kb 6.4kb 2kb ∼ 1kb 8.4kb TAB . 7.7 – Composant de reconfiguration – occupation mémoire. La table 7.8 montre la taille des différents éléments du support d’exécution des composants de reconfiguration. Nous avons séparé la taille du code (section .text) de la taille des données (section .data). Dans les architectures AVR, utilisées dans des réseaux de capteurs par exemple, le code est écrit dans la mémoire flash (de l’ordre de 128kB à 256kB) et les données dans la RAM (de l’ordre de 4kB à 16kB). Les données commprennent les variables globales, en particulier, cette information ne donne aucun renseignement sur l’utilisation de la pile ou du tas par une implémentation donnée. L’éditeur de liens a des fonctionnalités minimales pour charger le composant de reconfiguration. Le support de FScript que nous avons conçu est indépendant de la méthode de communication entre l’hôte de compilation et le terminal, nous faisons l’hypothèse que le système à reconfigurer est un système ”connecté”, communiquant à l’aide d’une pile protocolaire quelconque qui accepte des requêtes de reconfiguration. 7.4.2.3 Support optimisé pour FScript L’utilisation de la mémoire et et de la bande passante de la solution précédente est liée au format de fichier ELF – le chargement d’un tel fichier nécéssite son écriture et sa modification en mémoire, le code de reconfiguration étant relogeable, son en-tête ELF contient les informations de relocations, augmentant par conséquent la quantité de données à télécharger. 97 7.5. Conclusion Chapitre 7. Evaluations Introspection Chargeur et éditeur de liens FScript fonctions utilitaires Total support à l’exécution taille code 2.7kb 4.9kb 1.3kb 8.9kb taille données 100b 300b 0b 400b total 2.8kb 5.2kb 1.3kb 9.3kb TAB . 7.8 – Support FScript, occupation mémoire. Afin d’optimiser l’efficacité d’utilisation de la mémoire et de la bande passante, nous avons implémenté un support pour FScript utilisant un format de fichier binaire où l’édition de liens est faite sur la plate-forme de compilation FScript. L’avantage est de réduire considérablement la quantité de données transmises et l’utilisation d’un chargeur dynamique n’est plus nécéssaire - le code est écrit directement dans la mémoire flash, les données en mémoire. Les addresses mémoires (donc l’image binaire du système à l’exécution) doivent être connues lors de la compilation de programmes FScript. Il est nécéssaire de les sauvegarder en tant que méta-données architecturales lors de la compilation du système. Le tableau 7.4.2.3 détaille les tailles des différentes parties du composant de reconfiguration transmises lors d’une reconfiguration. La partie code des composants à télécharger peut être directement écrite dans la flash (sans relocation nécéssaire) donc sans nécéssité d’allocation temporaire d’autant de mémoire RAM pour la relocation. taille code 1112 334 1446 Code de reconfiguration Nouveau composant (xyz) Total transféré taille données 135 57 192 TAB . 7.9 – Composant de reconfiguration, occupation mémoire. 7.5 Conclusion A travers les trois prototypes de systèmes reconfigurables, nous avons évalué les trois points clefs de notre approche i) l’interface utilisateur, ii) l’efficacité de son implémentation, et iii) l’efficacité du support pour le langage FScript compilé. 7.5.1 Evaluation qualitative L’évaluation qualitative doit montrer que notre approche satisfait aux objectifs de flexibilité, expressivité et simplicité (l’objectif d’efficacité étant traité dans la section suivante). Les raisons suivantes permettent d’affirmer cela : – La variété des implémentations de mécanismes de reconfiguration pouvant être abritées par l’interface de reconfiguration conçue, démontrant la simplicité et la flexibilité. – La variété des impléméntations de l’interface de reconfiguration pouvant être générés par le compilateur de manière uniforme, par transformations architecturales, s’adaptant à différents modèles d’exécution. Simplicité. – La généralisation des transformations architecturales, appliquées à la construction des systèmes sécurisés et des systèmes événementiels, permet de confirmer le généricité de cette approche, proposant à l’utilisateur un mécanisme polyvalent et simple pour ajouter des extensions à son système. Flexibilité. 98 Chapitre 7. Evaluations 7.5. Conclusion – L’implémentation des mécanismes de reconfiguration sur des plate-formes hétérogènes, en particulier sur des plate-formes contraintes en ressources matérielles comme les noeuds de réseaux de capteurs, montrant la flexibilité. – L’implémentation du support FScript sur des plate-formes hétérogènes, s’adaptant aux différents contextes opérationnels, en particulier sur des plate-formes contraintes en ressources matérielles comme les noeuds de réseaux de capteurs, montrant la flexibilité. Ici, l’évaluation montre le potentiel des méta-données architecturalles pour la reconfiguration dynamique. 7.5.2 Evaluation quantitative L’évaluation quantitative de notre approche met en évidence le surcoût de notre approche pour la reconfiguration dynamique, d’une part de l’implémentation d’une interface de reconfiguration, d’autre part de l’implémentation d’un support pour les programmes FScript compilés. – L’implémentation de la détection d’un état quiescent est coûteuse. Elle peut être optimisée suivant le modèle d’exécution utilisé par le système (événementiel, threads ”à la K42”), néanmoins, comme le montre la comparaison sur le décodeur H.264, il n’existe pas de mécanisme optimal – à chaque système un mécanisme adapté. – Il n’existe pas de modèle de concurrence optimal, le modèle événementiel implémente de façon efficace la détection d’un état stable, néanmoins engeandre un surcoût dû au traitement d’événements. – Les mécanismes de détection d’état quiescent n’offrent pas le même degré de flexibilité (avec comptage de référence, le composant doit être conçu pour être reconfigurable avec ce mécanisme) – Notre approche permet d’implémenter un support FScript qui réduit au maximum la quantité de données transmises et nécéssite pas de mémoire supplémentaire pour exécuter le code de reconfiguration (pas de chargeur). Pour réduire la quantité de données transmises au-delà de nos résultats, il faudrait utiliser une approche de machine virtuelle, comme Maté (Levis & Culler, 2002). 99 7.5. Conclusion Chapitre 7. Evaluations 100 Chapitre 8 Conclusion et perspectives 8.1 Bilan Dans cette thèse nous avons décrit un modèle de construction de systèmes embarqués qui offre une flexibilité pour construire à la carte des systèmes embarqués reconfigurables et autorise la programmation de leurs reconfigurations. Le modèle de construction que nous avons proposé est fondé sur la notion de l’architecture du système et comprend les éléments suivants : – Une interface de programmation des reconfigurations de bas-niveau permettant l’implémentation à grain fin des différents mécanismes de reconfiguration dynamique – chaque composant implémente les mécanismes appropriés. Les reconfigurations sont programmées en termes de cette interface de programmation. L’interface telle que nous l’avons définie est indépendante de la partie fonctionnelle d’un composant et du modèle d’exécution du système et par conséquent offre une flexibilité de construction de systèmes reconfigurables. Dans ce même esprit, l’interface pour la reconfiguration offre que des méthodes d’accès de basniveau à l’état d’un composant, permettant la construction de mécanismes de transfert d’état complexes. – Un environment de construction de systèmes reconfigurables, supporté par des outils aidant le concepteur du système à créer et valider une architecture reconfigurable, i.e. avec une implémentation concrète d’une interface de reconfiguration. Le concepteur du système spécifie les parties reconfigurables du système de façon orthogonale à l’architecture elle-même du système. – Un environment de programmation de reconfigurations, basé sur l’utilisation d’un langage dédié à la reconfiguration, ciblant jusqu’à des architectures matérielles contraintes en ressources matérielles. Pour valider notre approche, nous avons réalisé plusieurs prototypes de systèmes reconfigurables qui ont eu pour but de démontrer la flexibilité de l’approche ainsi que l’efficacité de son implémentation. En particulier nous avons réalisé un prototype d’un système applicatif reconfigurable pour noeuds de réseau de capteurs, fortement contraints en ressources matérielles. Son évaluation quantitative montre l’impact du choix du modèle d’exécution sur les performances d’un système reconfigurable. 8.2 Limitations La conception du modèle proposé présente plusieurs limitations. Généricité de l’interface de reconfiguration Tout d’abord, nous avons implémenté plusieurs mécanismes de reconfiguration (concrétement la détection d’un état stable) avec plusieurs modèles d’exécution. 101 8.2. Limitations Chapitre 8. Conclusion et perspectives Néanmoins, nous ne disposons d’aucune preuve que l’interface telle que nous l’avons conçue est suffisamment générique pour satisfaire toutes les utilisations possibles. Inter-blocages Le programmeur de reconfiguration peut ammener le système dans un état d’interblocage dans le cas précis où dans un modèle à thread l’état quiescent de plusieurs composants doit être détécté à l’aide du mécanisme de comptage de références. En effet notre implémentation se base sur l’utilisation de sémaphores et losrqu’un état quiescent pour un composant est détecté, les threads entrants sont mis en attente sur un sémaphore. L’inter-blocage intervient lorsque le programmeur de reconfiguration demande la détection d’un état quiescent de deux composants – client et serveur et lorsque l’état stable du serveur est détecté en premier lieu. Dans ce cas, les threads venant du client sont mis en attente à l’entrée du composant serveur et le composant client n’atteindre jamais un état quiescent (car les appels au serveur ne progressent pas). Cette limitation est dûe à la volonté de concevoir une interface où les différents éléments sont indépendants et aucune implémentation n’a une vue globale de l’état des reconfigurations. Une solution consiste à réordonnancer le programme de reconfiguration pendant la phase de compilation en faisant une analyse architecturale de façon à exclure toute possibilité de deadlock et insérer des points de synchronisation dans le programme de reconfiguration. Limites des transformations transparentes à l’utilisateur L’approche qui consiste à transformer l’architecture du système de façon transparente à l’utilisateur présente deux inconvéniants. D’abord, l’implémentation de la membrane d’un composant Think peut dépendre des composants fonctionnels qui doivent se trouver dans l’architecture utilisateur (dépendance cachée). Nous avons résolu ce problème en introduisant les paramètres dans les propriétés attachées à des noeuds reconfigurables lors de la compilation. Ces propriétés permettent de remplir les dépendances de la membrane vers la partie fonctionnelle. De plus, lorsque plusieurs transformations de l’architecture utilisateur sont nécessaires, comme le montre le prototype du système sécurisé reconfigurable (cf. 7), un décallage d’architecture est créé à chaque transformation, alors que celles-ci sont spécifiées par rapport à l’architecture initiale (la transformation n + 1 ne voit pas le résultat de la transformation n). Une solution consiste à introduire une représentation intermédiaire entre les transformations (l’ADL en est une) et spécifier les transformations de façon itérative par rapport à description précédente (et non initiale). Synchronisation des vues architecturales pour la compilation FScript La compilation FScript quant à elle, présente une difficulté liée à la synchronisation des vues architecturales - celle du système de compilation avec l’architecture réelle du système cible. Dans le chapitre 6 nous avons étudié plusieurs possibilités de synchronisation, faisant différentes hypothèse sur la dynamique du système et offrant différents compromis entre la flexibilité et l’efficacité du support pour la reconfiguration. En ce qui concerne l’efficacité d’un programme de reconfiguration compilé, nous avons rencontré deux problèmes liés à son utilisation efficace dans les noeuds de réseaux de capteurs : – Dans les réseaux de capteurs, une des ressources critiques est l’utilisation de l’énergie, la transmission radio étant une des opération des plus coûteuse. De ce point de vue, notre solution transmettant des composants binaires (i.e. en langage objet cible) est moins efficace en termes d’informations à transmettre qu’une solution telle que la machine virtuelle Maté où le code à transmettre est sous forme d’un ensemble restreint d’instructions de la machine virtuelle. – Pour offrir un maximum de flexibilité à un système reconfigurable, le composant de reconfiguration binaire est sous la forme d’un fichier ELF où le code et les données sont relogeables. Ceci est au détriment de la taille du fichier à transmettre, car l’en-tête ELF contient une quantité d’informations de relocation. 102 Chapitre 8. Conclusion et perspectives 8.3. Perspectives A ce problème, nous avons décrit au chapitre 7 une solution partielle qui consiste à faire l’édition de liens sur la machine de compilation à une adresse fixe, prédéterminée pendant la construction de la plate-forme cible. Après cette édition de liens, le volume des données à transmettre à la plate-forme cible est réduit au code et aux données du composant de reconfiguration (éliminant l’en-tête ELF). Cependant, la flexibilité de cette solution est réduite, car l’éditeur de liens doit connaı̂tre l’adresse d’édition (par un protocole quelconque entre la plate-forme cible et l’hôte de compilation). 8.3 Perspectives Ce travail ouvre de nouvelles perspectives sur la reconfiguration dynamique des systèmes embarqués. Le compilateur Think ADL Dans l’annexe D nous décrivons une évolution du compilateur Think ADL pour supporter des optimisations architecturales séléctives. En perspectives, il est nécéssaire d’explorer l’articulation des optimisations et des mécanismes pour la reconfiguration dynamique. En effet, les deux approches sont contradictoires – la reconfiguration dynamique repose sur des liaisons dynamiques et sur l’implémentation du contrôle, alors que les optimisations les éliminent. Il serait intéréssant de concevoir une solution d’optimisations réversibles en s’inspirant des techniques d’implémentation des spécialisations dans l’évaluation partielle (Consel et al., 1998; Consel et al., 1996; Volanschi, 1998). Boucle autonomique La reconfiguration dynamique constitue un élément de base de la boucle autonomique. Ces systèmes évoluent en fonction des conditions de leur environment, de leur contexte (Appavoo et al., 2003), (Horn, 2001), (Horn, 2001). Dans ce cas, le système détecte la nécéssité de reconfiguration suivant des critères et déclenche sa reconfiguration lui-même. Il serait alors intéressant d’investiguer la conception d’une gestion de contexte efficace dans le cadre des systèmes embarqués restreints en ressources matérielles, comme le Cognichip. Transfert d’état L’interface que nous avons conçue offre un accès bas-niveau à l’état du composant – set() et get(). Cette interface ne précise volontairement pas la sémantique de l’implémentation attendue. De cette façon, il est possible de bâtir différents mécanismes ou stratégies pour transférer l’état d’un composant. Cependant, c’est au programmeur du composant reconfigurable de spécifier la sémantique de l’implémentation de cette interface et de l’implémenter. Par exemple, un composant peut accepter d’exhiber ses données directement via un pointeur. Lors d’une reconfiguration le nouveau composant peut accepter un tel pointeur. Cela suppose à la fois que les formats de données du nouveau composant et de l’ancien soient compatibles, et que le programmeur ait cette information de compatibilité. Dans un autre cas, par exemple où les formats des données sont incompatibles, le programmeur de reconfiguration peut écrire une fonction de transformation de données (ou celle-ci peut être générée par des outils tiers), appelée pendant la phase de transfert d’état. Modèle d’exécution événementiel Dans la partie évaluation de notre proposition, nous avons montré un modèle d’exécution événementiel simplifié. L’avantage d’un modèle événementiel par rapport à un modèle multi-thread est controversé, 103 8.3. Perspectives Chapitre 8. Conclusion et perspectives néanmoins, ce modèle d’exécution présente des avantages en terme de vérification du comportement des systèmes (analyse temporelle) ou de la distribution du système entre plusieurs coeurs d’exécution (sur un chip ou sur plusieurs machines). Pour la reconfiguration dynamique, le modèle événementiel présente l’avantage de pouvoir implémenter de façon très efficace les différents mécanismes, en particulier l’état stable d’un composant est donné par construction du système. La difficulté dans un modèle événementiel est dans la programmation et compréhension du système (le flot d’exécution ne suit pas le code). Dans notre proposition, la sémantique que nous avons donnée est suffisamment simple et nous avons pu automatiser la construction du système événementiel uniquement en analysant l’architecture du système (ADL). Cependant, les liaisons architecturales ne sont pas suffisantes pour spécifier les dépendances des événéments dans un système, comme c’est déjà le cas avec le langage ThinkJoin (Ozcan, 2007). Le défi consiste à allier la reconfiguration dynamique à une telle description et en réaliser une implémentation effiface sur le plan des performances. La suite logique dans la définition d’un modèle d’exécution à sémantique plus riche, serait de pouvoir extraire un comportement du système afin de le vérifier. Un certain nombre de travaux existent déjà autour du couplage de Think et de BIP (Basu et al., 2006; Basu et al., 2007) afin de permettre la vérification des systèmes à sémantique opérationnelle particulière. Ces modèles ne prennent pas en compte une évolution quelconque du système. Il serait alors intéréssant d’explorer quelles sont les propriétés et garanties qui peuvent être vérifiées si le système évolue via la reconfiguration dynamique. Le compilation FScript Nous avons implémenté un prototype d’un compilateur FScript. Pour des raisons d’efficacité sur plate-formes contraintes en ressources matérielles, il serait intéréssant de pouvoir optimiser le code de reconfiguration généré. Par exemple, on pourrait envisager d’analyser la séquence des requêtes de détection d’un état stable et de modifier leur ordre afin de minimiser le temps de suspension de service. Une telle analyse pourrait également prévenir le cas d’inter-blocage décrit plus haut. Rollback Le prototype du compilateur FScript ne génère pas un code de reconfiguration avec une gestion consistante d’erreurs. Dans le code généré, il faut pouvoir gérer les erreurs liés au programme de reconfiguration. Ces erreurs sont manifestées par les erreurs bas-niveau, i.e. au niveau du langage C (pointeurs nulls etc.) Se basant sur une gestion consistante d’erreurs, il serait intéressant dans le cadre des systèmes embarqués restraints en ressources matérielles de pouvoir effectuer un rollback de la reconfiguration en cours en cas d’erreur. Persistance des reconfigurations Les reconfigurations dynamiques telles que nous les avons décrites se basent toujours sur un système qui a un état initial connu, qui correspond à sa configuration de construction, C0 . Or lorsqu’une ou plusieurs reconfigurations sont appliquées à ce système et si pour une raison quelconque le système redémarre, il est souhaitable de redémarrer directement dans la configuration Ci . Il faut alors s’assurer de la persistance des reconfigurations qui ont menés à la dernière configuration. Egalement, il faut être capable de regénerer l’architecture du sysètme qui correspond à la dernière configuration. 104 Chapitre 8. Conclusion et perspectives 8.3. Perspectives Console d’administration La représentation de l’architecture du système en ADL et les programmes en FScript fournissent une abstraction pour pouvoir construire une console d’administration de systèmes reconfigurables capable de gérer un parc de systèmes, potentiellement hétérogènes. Typiquement, un opérateur mobile gère non loin de milles types de plate-formes différentes (un type est la configuration logicielle sur une plateforme matérielle), mais avec quasiment les mêmes services. Une console d’administration pourrait offrir une gestion transparente des différentes versions de configuration logicielle, résolvant les problèmes de versionnement de systèmes et en déployant les composants adaptés à chaque plate-forme. 105 8.3. Perspectives Chapitre 8. Conclusion et perspectives 106 Annexe A Caractéristiques variées des systèmes embarqués Les caractéristiques de ces systèmes embarqués sont très variées (Lee, 2000; Sztipanovits & Sastry, n.d.). Pour la plupart, il s’agit de systèmes connectés, spécifiques à un domaine d’application, s’exécutant sur des plate-formes matérielles hétérogènes, ayant différents requis ”temps réel” et d’autres qualités non-fonctionnelles (sécurité, maintenabilité, ...). La durée de vie de ces systèmes peut être de l’ordre de quelques (dizaines d’) années et alors il peut s’avérer nécéssaire de mettre à jour soit la partie logicielle ou/et matérielle du système déployé. Spécificité à un domaine d’application Contrairement aux ordinateurs personnels qui sont des systèmes de calcul généraux, les systèmes embarqués tel que nous les décrivons sont des systèmes hautement spécialisés à un domaine applicatif. Par exemple, dans un téléphone portable nous trouvons un système spécialisé dans le traitement du signal audio et vidéo. Les systèmes embarqués dans les avions ou dans les voitures sont spécialisés dans le contrôle, les noeuds de réseaux de capteurs dans la remontée d’informations etc. Hétérogénité des plate-formes et variété des environments Chaque domaine d’application nécéssite différentes ressources matérielles et par conséquent un système d’exploitation adapté. Par exemple, les noeuds des réseaux de capteurs peuvent être réalisés avec des architectures AVR à basse consommation, mais à faible fréquence de calcul. Les téléphones portables, plus gourmands en calcul, peuvent être constitués d’un coeur ARM maı̂tre et des DSP spécialisés, dédiés au traitement du signal. De plus, ces systèmes embarqués évoluent dans des environments physiques et logiques très variés – un noeud de réseaux de capteurs déployé dans la nature pour des expérimentations biologiques n’aura pas le même environement qu’un routeur télécom (le premier par exemple est physiquement difficilement accessible, alors que le second évolue dans un système complexe de routage). Durée de vie Une fois déployé, un systèmes embarqué peut avoir une durée de vie importante, voire correspondante à la durée de vie du produit dans lequel il est intégré – voiture, équipements multi-média etc. Une mise-à-jour du logiciel est possible pour une classes de systèmes où tout simplement cette fonctionalité aurait été prévue pendant la conception du système (par exemple le cas de l’électronique grand public). Cette mis-à-jour peut être effectuée de façon manuelle (par un service spécialisé ou l’utilisateur) ou de façon automatique, à distance (par exemple téléphones portables ou les routeurs domotiques tel la LiveBox). 107 Chapitre A. Caractéristiques variées des systèmes embarqués Cycle de vie complexe Aujourd’hui, le cycle de vie d’un système embarqué est complexe et souvent plusieurs acteurs interviennent dans le processus. En général, la partie matérielle est conçue par un fabricant, indépendamment du système d’exploitation et indépendamment de la partie applicative. Souvent le commenditaire d’un système embarqué va jouer le rôle de l’intégrateur pour l’intégrer dans l’environement cible (ex. industrie télécom et des services proposés par un opérateur). Les spécifications des différentes parties (matérielles, système d’exploitation etc.) qui servent à la coopération entre les différents acteurs sont souvent standardisées (par exemple OSEK (OSEK/VDX, n.d.)). Par exemple, un opérateur mobile achète des téléphones à des fournisseurs qui fournissent donc le matériel et une partie du logiciel. Ceux-ci achètent souvent le logiciel (au moins le système d’exploitation) à des tiers. Une fois le téléphone déployé (i.e. intégré à l’infrastructure de l’opérateur), l’opérateur va vouloir déployer ses propres services (ou l’ouvrir à des services tiers) ou événetuellement mettre à jour la partie logicielle du système. De plus, une fois le système embarqué déployé, il n’est pas forcément dans la possession du concepteur, ni sous le contrôle de ce dernier. Par exemple un réseau de capteurs géologiques déployé par l’état peut être dans la possession de l’état et sous son contrôle. Par contre un téléphone portable peut être dans la possession de l’utilisateur qui en prend contrôle (il a la liberté de l’allumer, l’éteindre et téléphoner à sa guise, par contre l’opérateur peut toujours détenir un certain contrôle sur le logiciel du téléphone). Ouverture Dans certains domaines, comme sur les téléphones portables, les systèmes embarqués sont de plus en plus ouverts, c’est à dire accessibles à des acteurs tiers non-présents lors du dévelopment ou déploiement du système (par exemple possibilité d’installer des applications Java sur des téléphones portables). Cette ouverture modifie les exigences sur le système et complexifie la conception du système, notamment en matière de sûreté et sécurité. La standardisation des différentes interfaces (matérielles, logicielles, réseau ...) joue un rôle important. Parmi les standards système nous trouvons POSIX (POSIX, n.d.), standard généraliste pour l’interface entre le système et les applications, OSEK (OSEK/VDX, n.d.) également pour l’interface avec le système (fortement utilisé dans les automobiles), CAN (Bosch, 1991) standard de bus de terrain (ex. automobile), . Connectivité Pour la plupart, les systèmes embarqués d’aujourd’hui sont des systèmes inter-connectés que ce soit de manière classique par un réseau filaire (ex. Ethernet, bus de terrain CAN (Bosch, 1991), etc.), par radio (ex. réseaux de capteurs (Hill et al., 2000)) ou par bus d’interconnexion sur silicium dans les cas de plusieurs noeuds sur une puce matérielle. Nous distinguons deux degrés de connectivité. Tout d’abord les systèmes isolés réalisés à partir de plusieurs noeuds communiquant entre-eux. C’est le cas des systèmes critiques comme les centrales nucléaires, avions ou voitures. Dans la deuxième catégorie nous plaçons les systèmes connectés à plus grande échelle, comme les téléphones portables ou des réseaux de capteurs et passerelles domotiques, tous connectés sur Internet. Exigences temps-réel et prédictabilité Dans tous les systèmes l’aspect temps est important. Suivant l’importance accordée, les exigences en termes de prédictabilité et satisfaction des échéances (deadlines) peuvent déterminer toute la conception du système embarqué. Les besoins en temps réel peuvent déterminer le paradigme de programmation du système, en adoptant par exemple une approche synchrone avec les langages Lustre (Halbwachs et al., 1991) ou Esterel (Berry, 2000). Par conséquent, les exigences temps-réel constituent une contrainte forte pour le choix d’un paradigme de programmation (car toutes les technologies ne permettent pas de maı̂triser l’aspect temps du système conçu). 108 Chapitre A. Caractéristiques variées des systèmes embarqués Dans les systèmes où les contraintes liées au temps ne sont pas primordiales (comme par exemple sur les téléphones portables où un retard dans le décodage n’est probablement même pas perceptible par l’utilisateur), celles-ci sont satisfaites dans la conception globale du système qui n’est en particulier pas subordonnée à cette unique contrainte, typiquement en dimensionnant correctement le matériel ou en implémentant une politique de qualité de service. Aspects non-fonctionnels Notre définition de systèmes embarqués embrasse une large gamme de systèmes ayant des nécéssités très variées. Suivant le contexte, une panoplie de qualités non-fonctionnelles doit être considérée lors de la conception du système, ces qualités constituent souvent une valeur ajoutée importante d’un système. Parmi les différentes qualités non-fonctionnelles, nous ne citons que quelques exemples : – efficacité par rapport au matériel utilisé (temps calcul, mémoire ou consommation d’énergie, bande passante, ...), – sécurité, – fiabilité, disponibilité, sûreté (Avizienis et al., ?), – maintenabilité, – ”certifiabilité” (ou la qualité d’un système à pouvoir être certifié, plus ou moins facilement), – documentation, – adaptabilité et extensibilité, – qualité de service (QoS) (Tournier, 2005a), – peristance, – ... Compléxité Les systèmes embarqués peuvent varier d’un simple noeud d’un réseau de capteur qui implémente l’acquisition des données du capteur et un protocole permettant de remonter cette information, jusqu’aux systèmes de contrôle complexes comme dans les voitures ou dans les avions (Sztipanovits & Sastry, n.d.). De plus, cette complexité a tendance à augmenter avec le temps (Damm, 2006a; Damm, 2006b). 109 Chapitre A. Caractéristiques variées des systèmes embarqués 110 Annexe B Modèles d’exécution Deux modèles d’exécution sont répandus dans les systèmes d’exploitation – le modèle à threads et le modèle evénementiel. Leurs avantages et inconvéniants font partie de guerres religieuses – Lauer et Needham (Lauer & Needham, 1979) en relèvent la dualité, Ousterhout (Ousterhout, 1996), Dabek et al. (Dabek et al., 2002) ou Lee (Lee, 2006) identifient le modèle à threads comme source de problèmes et réservent son utilisation uniquement aux parties à performances critiques. Plus tard Behren et al. argumentent le contraire (von Behren et al., 2003a; von Behren et al., 2003b)... B.1 Threads Un thread présente deux aspects à la fois. D’un côté un thread est une abstraction de l’état de l’exécution du système d’exploitation et des ressources de la machine sous-jacente (registres par exemple). D’un autre coté, un thread est un outil de programmation et de structuration de code – un fils d’exécution – et défini un modèle de calcul concurrentiel et constitue une solution simple pour la multi-programmation d’un processeur (exécution simultanée de plusieurs threads/applications). Historiquement, le concept de thread est issu d’un besoin de maximiser l’utilisation du processeur, même lorsque le programme actif est en attente d’une opération d’entrée/sortie. Lors d’une opération d’entrée/sortie, le thread actif est mis en attente et le système – l’ordonnanceur (scheduler) – continue l’exécution avec un autre thread – changement de contexte (context-switch). Aujourd’hui le thread est devenu un outil pour la programmation parallèle. L’avantage d’un modèle d’exécution à base de threads est la simplicité d’écriture de programmes et leur compréhension - l’exécution d’un programme donne l’impression d’être séquentielle, il suffit de suivre les appels de fonctions. L’algorithme d’ordonnancement n’altère pas l’ordre d’exécution à l’intérieur d’un thread. Néanmoins, le modèle à threads présente plusieurs inconvéniants : – La mémoire peut être partagée entre plusieurs threads, par conséquent il est nécéssaire de protéger son accès en synchronisant les threads. La programmation de cette synchronisation est la source d’erreurs de synchronisation difficilement détéctables et fatales, comme les inter-bloquages (deadlock ou le livelock). – Pour chaque thread le système doit maintenir le contexte de son exécution, le contexte étant l’état de la machine (registres) et du système à un moment donné. La conséquence est double : le système doit allouer de la mémoire pour gérer le contexte (en particulier le système alloue une pile d’exécution pour chaque thread) et lorsque le système choisit un autre thread pour s’exécuter, le contexte courant doit être sauvegardé pour restaurer le contexte du thread qui va s’exécuter (entraı̂nant en particulier des effets sur le cache et la TLB). – Finalement, l’exécution concurrente de plusieurs threads est non-déterministe. 111 B.2. Modèle d’exécution événementiel Chapitre B. Modèles d’exécution Etat stable – Détection d’un état quiescent Dans un système à base de threads, un état quiescent permet de trivialiser l’état de contrôle lié à une partie du système. L’état quiescent est défini comme un état qui garanti la quiescence, donc l’absence d’activité. L’état de contrôle (la pile (stack) dans ce cas) est alors vide (ou non-relevant pour cette partie du système). Dans ce cas, l’état de la partie du système se résume uniquement à son état interne. Cette partie (code et donnée) peut alors être manipulée avant de reprendre l’exécution. Un état quiescent est un état stable comme défini auparavant. Dans un modèle à threads, une partie du système peut être accédée de manière concurrente et son état quiescent doit être détecté à l’aide d’un algorithme. Au chapitre 4 nous décrivons deux algorithmes de détection d’un état quiescent. B.2 Modèle d’exécution événementiel Les systèmes dits événementiels sont organisés autour de traitement d’événements. Un événement correspond à une intéraction entre les différentes entités qui composent le système, mais également à une interruption du processeur convertie en événement (et par conséquent à toute intéraction avec l’environement physique du système). Un système événementiel défini la sémantique d’exécution d’un événement, e.g. non-réentrant (peut être interrompu par un autre événement) ou plus fort, non-préemtible (appelé run to completion dans TinyOS (Hill et al., 2000)). Par exemple, un système événementiel simple est constitué d’un ensemble de traitants d’événements atomiques1 et un ordonnanceur qui exécute les événements selon une politique d’ordonnancement prédéfinie (FIFO par exemple). Comparé au modèle d’exécution multi-thread, l’approche événementielle ne nécéssite pas de sauvegarde de contexte. Les implémentations utilisent souvent des continuations (Adya et al., 2002; Draves et al., 1991). De plus, sur mono-processeur, la synchronisation de l’accès concurrent à une ressource est simplifié par la sémantique d’exécution de l’événement – dans le cas des événements atomiques, aucune synchronisation n’est nécéssaire, tout événement s’exécute en exclusion mutuelle. Comparé aux threads, l’exécution d’un système événementiel est déterministe et un modèle événementiel permet une analyse de la prédictabilité et une implémentation pour les systèmes temps réel. Le modèle événementiel présente plusieurs inconvéniants : – la non-réentrance ou la non-préemtion (l’atomicité) des événements a une influence sur la réactivité du système et le programmeur doit en tenir compte dans la conception du système (au lieu d’assigner une priorité plus élevée à un thread), – par rapport aux threads, le concepteur du système doit avoir une démarche plus structurante pour éclater le système en évéments de granularité arbitraire, – le déroulement d’une exécution n’est pas aussi intuitif que dans un système à threads, – les systèmes événementiels manquent d’outils répandus généralement comme les compilateurs et les ”debuggers”. Il existe plusieurs systèmes d’exploitation que nous qualifions d’événementiels, parmi lesquels TinyOS (Hill et al., 2000) (conception événementiel) ou SEDA (Welsh et al., 2001). Le système extensible SPIN est également un système événementiel. Etat stable dans un système événementiel Dans un système événementiel sur uni-processeur (événements atomiques, non-préemtifs), par construction un composant est dans un état stable entre chaque réaction à un événement (donc lorsque l’ordonnanceur s’exécute). Contrairement aux systèmes multithreadés, où la détection d’un état quiescent implique un surcoût (voir à ce sujet les chapitres 4 et 7), l’obtention d’un état stable dans un système événementiel n’implique pas de surcoût à l’exécution. 1 L’atomicité dans ce cas suppose la non-préemption et donc la non-réentrance 112 Annexe C Historique du compilateur Think ADL L’historique du compilateur Think ADL est étroitement lié à l’évolution du modèle sous-jacent au canevas Think. Canevas Think à l’origine A l’origine, le canevas Think avait son propre modèle à composants et un ADL en XML (différent de l’ADL existant, typiquement la notion de liaison explicite était absente). Par conséquent le premier compilateur avait pour but d’interpréter cette ADL et de générer du code ”glue” en C adapté aux composants écrits également en C. Le modèle et le compilateur (la chaı̂ne de génération) sont décrits dans (Fassino, 2001). Adoption du modèle Fractal Avec l’adoption du modèle à composant Fractal, le canevas Think a été doté d’un nouveau ADL et par conséquent d’un nouveau compilateur. Cette version est designé comme la deuxième version du canevas. L’ADL est celui présenté dans ce travail, le compilateur ayant pour but d’interpréter cet ADL et générer du ”glue” code en C. Un compilateur à composants La dernière évolution n’a touché que le compilateur Think qui a luimême été construit avec des composants Fractal (en Java), décrit dans (Ozcan, 2007). En réalité il s’agit d’une convergence du compilateur FractalADL pour les composants écrit en Java et du compilateur Think précédent, dans la mesure où le compilateur FractalADl existant a été adapté pour produire un code identique au compilateur précédent. Le modèle de programmation de composants primitifs et l’ADL sont resté inchangés. Cette version est communément designée comme la troisième version du canevas ou think-v3. 113 Chapitre C. Historique du compilateur Think ADL 114 Annexe D Optimisations architecturales sélectives En tant qu’implémentation du modèle à composants F RACTAL, le framework T HINK actuel1 fixe un certain nombre de choix d’implémentation des concepts du modèle F RACTAL. En particulier, l’implémentation spécifie un format binaire standard d’un composant qui fixe les structures des méta-données associées à un composant. Ce format offre une flexibilité pour la réutilisation des composants (Fassino, 2001). Cependant ces choix d’implémentation flexible ne sont pas obligatoirement pertinents pour tous les systèmes pouvant être construits avec le framework T HINK. Nous avons identifié les lacunes de l’implémentation actuelle et nous avons réalisé une implémentation flexible (Lobry & Polakovic, 2008), autorisant des optimisations architecturales sélectives : – Sous optimisations nous comprenons une implémentation alternative des méta-données d’un système à composant F RACTAL, offrant un compromis différent entre la flexibilité et des critères de performance du système (comme l’occupation de l’espace mémoire ou le surcoût à l’exécution etc.). – Ces optimisations concernent l’implémentation de l’architecture du système, donc liaisons, métadonnées, attributs . . .(contrairement à une optimisation du code fonctionnel). – Les optimisations sont séléctives, spécifiées par l’utilisateur, et s’appliquent localement à un composant ou liaison (contrairement à tout système, même si cette possibilité existe). D.1 Motivation D.1.1 Historique L’origine de la réflexion sur les optimisations est double. Surcoût lié aux intercepteurs pour la reconfiguration dynamique L’implémentation d’un algorithme de détection d’un état quiescent pour la reconfiguration utilise des composants intercepteur (voir chapitre 4) qui ont un impact non-négligeable sur les performances du système. Dans une architecture reconfigurable, ce composant intercepteur est étroitement lié au composant dont l’interface est interceptée. Par conséquent, il est intéressant d’investiguer quel degré de flexibilité est nécessaire et si il est possible de supprimer tout surcoût lié à ce composant intercepteur (par exemple en court-circuitant ces liaisons, en supprimant ces interfaces de contrôle, etc.). L’implémentation efficace des composants sur AVR Pour être réutilisable dans différentes architectures matérielle, le composant uart dans la bibliothèque Kortex – pilote de port série – exporte des attri1 début 2007 115 D.1. Motivation Chapitre D. Optimisations architecturales sélectives buts qui permettent de le configurer pour une architecture donnée. Ces attributs sont par exemple les mappings mémoire des registres de contrôle du port série. Les attributs dans T HINK sont implémentés dans les méta-données dont l’implémentation est fixée et pour accéder la valeur de cet attribut le code du composant doit passer par une indirection. Or sur l’architecture AVR, l’exécution d’une telle implémentation prend trop de temps et la communication sur le port série déborde (overflow). Cependant, pour une architecture donnée, la valeur de ces attributs est constante, donc l’attribut pourrait être implémenté comme une variable constante ou une macro. D.1.2 Objectif L’objectif du travail consiste à pouvoir construire des architectures à composants avec différents compromis entre la flexibilité et l’efficacité (suivant des critères de performances établis). Ce compromis peut varier d’un système flexible (l’existant) à l’élimintation de tout surcoût lié à l’implémentation du modèle à composant, dans quel cas, le composant serait une entité de conception, mais disparaı̂trait à l’exécution. L’intérêt de pouvoir varier le compromis entre flexibilité et l’efficacité d’implémentation est de pouvoir construire des systèmes embarqués à composants sur des plate-formes matérielles contraintes en ressources (comme les systèmes sur architecture AVR, par exemple le Cognichip (Jarboui et al., 2006a)). Les types d’optimisations du framework actuel que nous avons identifiés sont au nombre de cinq : – Attributs constants – Liaisons statiques – Implémentation optionnelle du contrôle – Implémentations alternatives des méta-données – ”Inlining” des composants D.1.2.1 Attributs constants Un attribut d’un composant qui ne change pas au cours de l’exécution du système est dit constant. L’implémentation actuelle génère systématiquement des méta-données (i.e. une variable) pour chaque attribut. Cet attribut pourrait bien être implémenté comme une constante (i.e. variable const ou comme une macro de préprocesseur). D.1.2.2 Liaisons statiques Dans la version actuelle de T HINK, une liaison est implémentée avec un pointeur dans les métadonnées du composant client qui référence le descripteur de l’interface serveur. Ce descripteur est un couple de deux pointeurs qui référencent les données privées du composant serveur et une table virtuelle de méthode (cf. figure 3.5 au chapitre 3) Cette implémentation offre la flexbilité à l’exécution – il est possible de modifier la liaison et lié une interface cliente vers une autre interface serveur d’un autre composant. Dans le cas où cette flexibilité n’est pas nécessaire (composants fortement couplés ou architecture qui ne va pas évoluer), la liaison, dite alors statique, peut être supprimée dans le système à l’exécution et pour l’appel d’une méthode serveur pourrait s’effectuer directement en appelant la fonction C de l’implémentation. Le code suivant (cf. chapitre 3, section 3.3.2) : ItfI->meth->f(ItfI->data, arg1, arg2, ...); deviendrait alors : f(data, arg1, arg2, ...); 116 Chapitre D. Optimisations architecturales sélectives D.2. Les problèmes ou idéalment, l’utilisateur l’écrirait de la façon suivante (l’argument data, qui permet de retrouver les données d’instance du composant, est invisible à l’utilisateur) : ItfI_f(arg1, arg2, ...); D.1.2.3 Implémentation optionnelle du contrôle Le compilateur actuel génère pour tout composant un ensemble d’interfaces de contrôle par défaut. Les interfaces de contrôle permettent d’introspecter l’architecture du système ou la modifier. Dans le cas où telles fonctionalités ne sont pas souhaitées dans une architecture concrète (introspection ou modification), le compilateur pourrait omettre la génération de ces interfaces de contrôle. D.1.2.4 Implementations alternatives des méta-données Les méta-données générées pour un composant sont fixes. Il s’agit d’un certain nombre de structures C prédéfinies. Par exemple les identifiants d’interfaces clientes sont implémentés comme pointeurs stockés dans un tableau. Sur une architecture 32-bit, ce pointeur pourrait potentiellement identifier une interface parmi 232 − 1. Pour optimiser l’occupation mémoire, un identifiant d’interface pourrait être implémenté comme un index dans un tableau d’identifiants. Pour un système sur AVR, ce tableau ne dépasserait guère 256 interfaces (i.e. entrées) et l’identifiant pourrait être codé sur 8 bits. L’objectif est alors de proposer des implémentations alternatives des méta-données d’un composant ou du système entier. D.1.2.5 Inline des composants Dans Kortex, pour une meilleure réutilisation de composants pilotes de périphériques, le code bas niveau dépendant d’une architecture matérielle concrète est implémenté dans des composants séparés. Ainsi par exemple, les pilotes utilisent tous un composant irqsafe implémentant le masquage d’interruption sur une architecture donnée, composant appelé fréquemment. En général il s’agit d’un quelques instructions en assembleur. Cependant, le système paie un surcoût lié au composant – surcoût d’exécution lié à la liaison et surcoût mémoire lié aux méta-données. L’objectif est alors de pouvoir ”inliner” (analogie avec les fonctions inline de C) le code des composants (ou interfaces) serveurs dans les composants clients. D.2 Les problèmes D.2.1 Problèmes identifiés de l’implémentation actuelle Pour implémenter les optimisations telles que décrites, nous avons identifiés plusieurs problèmes dans l’implémentation actuelle de T HINK. Le langage d’implémentation Le langage d’implémentation de composants (ou le dialect C) est dépendant d’une structure de méta-données – le dialect impose une structure. Par exemple un attribut est accédé avec la construction ATT.myAttribute. Le point, ’.’, dans cette syntaxe impose que les méta-données d’attributs sont une structure avec des champs qui représentent les attributs. Le compilateur Le compilateur actuel est peu flexible et génère uniquement une structure unique de méta-données de composants (et par conséquent une seule représentation binaire du composant). Cependant, la flexibilité du compilateur est dans le support de la génération des interfaces de contrôle – différentes implémentations d’interfaces de contrôle peuvent être générées. 117 D.3. Vers une implémentation D.2.2 Chapitre D. Optimisations architecturales sélectives Problèmes liés à l’implémentation des optimisations Mise à part les problèmes de l’implémentation actuelle, les optimisations architecturales présentent plusieurs difficultés pour construire des systèmes à composants de manière flexible. Ces problèmes sont discutés plus en détail dans (Lobry & Polakovic, 2008). Spécification des optimisations Pour un système donné, l’utilisateur doit pouvoir spécifier les différentes informations d’optimisation de l’architecture. De plus, cette spécification doit se faire de manière indépendante de la spécification des composants de façon à pouvoir réutiliser les composants dans une autre architecture, optimisée différemment. Ce support pour la spécification doit pouvoir être suffisamment expressif pour permettre les différentes combinaisons possibles. Analyse de l’architecture Comme évoqué au chapitre 5 nous abordons le compilateur avec un soucis de pouvoir intégrer des aspects non-fonctionnels à l’architecture, comme nous considérons l’interface de reconfiguration. Le compilateur doit garantir une cohérence des différentes spécifications d’un système – son architecture, les optimisations et les différents aspects non-fonctionnels appliqués. Par exemple, dans une architecture reconfigurable, toutes les liaisons ne peuvent pas être statiques. Représentations internes du compilateur Le compilateur T HINK génère du code C, glue, à partir d’une représentation interne de l’architecture du système à construire. Pour chaque élément de la représentation de l’architecture, une partie du code est générée, de manière prédéfinie, de façon à maintenir la cohérence de l’ensemble du code C glue généré. Le flot de génération de code est déterminé de manière fixe par un pattern visiteur. Pour pouvoir implémenter les optimisations il est nécessaire de disposer d’une représentation interne du code C à générer de façon à pouvoir le modifier à plusieurs moment du flot de génération de code. En effet, pour implémenter les optimisations, il est nécessaire de coordonner la structure du code glue généré par les différents modules. D.3 Vers une implémentation Nous avons implémentés un premier prototype de compilateur pouvant générés des structures optimisées à composants. Nous avons appelé ce compilateur N UPTSE2 Ce prototype se base sur trois modifications du framework T HINK. Ces modifications ne sont décrites que brièvement, car récemment N UPTSE a vu une réingéniérie importante qui a modifié la spécification des différents éléments, néanmoins sans altérer leur rôle dans l’implémentation des optimisations. D.3.1 Un nouveau langage d’implémentation de composants Nous avons défini un nouveau langage d’implémentation de composants, appelé N UP C. Ce langage est une extension du langage C qui définit des mots-clés représentant les différents concepts présents dans la description ADL d’un composant. Ainsi, le code d’implémentation de composants ne fait aucune supposition sur la struture des méta-données, uniquement sur leur existance. Par exemple un attribut myAttr peut être accédé dans le code source par le mot-clé ATTR myAttr. Dans la dernière version du framework, la langage N UPT C est du C pur (ne définissant pas de nouveaux mot-clés) avec des annotations dans les commentaires. Ces annotations permettent au parseur C du compilateur N UPTSE d’associer les différents éléments du code (appel de méthode, définition de fonctions ou accès aux variables) à la génération de méta-données et de code glue. 2 Du nom du sommet himalayan Nuptse (7861m). 118 Chapitre D. Optimisations architecturales sélectives D.3.2 D.3. Reconfiguration dynamique Spécification des optimisations La spécification des optimisations architecturales est dissociée de la description architecturale d’un composant, de façon à pouvoir réutiliser un composant dans des contextes d’optimisations différents. Nous avons réutilisé le mécanisme de propriétés pouvant être associées à un composant, décrites au chapitre 5. A l’aide du langage FlexProp, il est possible d’associer des optimisations à tout élément de l’architecture. Dans la dernière version du framework, le langage FlexProp a été remplacé par des extensions ADL. A la compilation, une extension ADL permet d’étendre l’architecture du système à compiler et ajouter des éléments à cette architecture (comme par exemple des interface de contrôles ou des propriétés). D.3.3 Support de compilation Le compilateur N UPTSE se base sur le compilateur décrit au chapitre 5. De plus, il définit un format interne représentant le code C glue à générer – C ODE G EN. La structure du compilateur N UPTSE fait objet de l’article (Lobry & Polakovic, 2008). D.4 Optimisations architecturales et reconfiguration dynamique Les différentes implémentation de la reconfiguration dynamique peuvent avoir un impact sur les performances du système, par exemple causé par l’utilisation des intercepteurs. Pour minimiser l’impact des différentes implémentations, il serait intéréssant d’appliquer les optimisations architecturales à un système reconfigurable. Cependant une analyse du système à construire est nécessaire pour déterminer le compromis entre l’efficacité et la flexibilité, par exemple, les liaisons d’un composant reconfigurable doivent être flexibles de façon à pouvoir être modifiées pendant la reconfiguration. D.5 En conclusion Nous avons implémenté un prototype de compilateur pouvant générer différentes structures de métadonnées pour un système composant. L’intérêt d’une telle approche étant de pouvoir construire des systèmes à composants très flexibles, ou des systèmes n’ayant pas de surcoût lié à l’utilisation de composants (le système est équivalent à un système écrit en C pur), ou de pouvoir satisfaire un compromis entre efficacité et flexibilité entre les deux extrêmes. Les optimisations architecturales, telles que présentées, sont un mécanisme de bas niveau du compilateur N UPTSE. Afin de satisfaire les différents besoin en construction de systèmes à composants, une phase d’analyse des spécifications (architecturales, aspects non-fonctionnels et optimisations) et de leur cohérence doit précéder la génération de code. 119 D.5. En conclusion Chapitre D. Optimisations architecturales sélectives 120 References A DYA , ATUL , H OWELL , J ON , T HEIMER , M ARVIN , B OLOSKY, W ILLIAM J., & D OUCEUR , J OHN R. 2002. Cooperative task management without manual stack management. Pages 289–302 of : Proceedings of the general track : 2002 usenix annual technical conference. Berkeley, CA, USA : USENIX Association. A JMANI , S. 2004. Automatic software upgrades for distributed systems. Ph.D., MIT. A PPAVOO , J., AUSLANDER , M., S ILVA , D. DA , E DELSOHN , D., K RIEGER , O., O STROWSKI , M., ROSENBURG , B., W ISNIEWSKI , R.W., & X ENIDIS , J. 2002. K42 overview. A PPAVOO , J., H UI , K., S OULES , C. A. N., W ISNIEWSKI , R. W., S ILVA , D. M. DA , K RIEGER , O., AUSLANDER , M. A., E DELSOHN , D. J., G AMSA , B., G ANGER , G. R., M C K ENNEY, P., O S TROWSKI , M., ROSENBURG , B., S TUMM , M., & X ENIDIS , J. 2003. Enabling autonomic behavior in systems software with hot swapping. Ibm syst. j., 42(1), 60–76. A RMSTRONG , J OE , V IRDING , ROBERT, W IKSTR ÖM , C LAES , & W ILLIAMS , M IKE. 1996. Concurrent programming in erlang. Second edn. Prentice-Hall. A RTIST 2. http ://www.artist-embedded.org – network of excellence on embedded systems design. ATMEL. Atmel avr 8-bit risc microcontrollers. http ://www.atmel.com. AVIZIENIS , A LGIRDAS , L APRIE , J EAN -C LAUDE , & R ANDELL , B RIAN. ?. Dependability and its threats : A taxonomy. BADGER , L., S TERNE , D. F., S HERMAN , D. L., WALKER , K. M., & H AGHIGHAT, S. A. 1995. Practical domain and type enforcement for unix. Page 66 of : Sp ’95 : Proceedings of the 1995 ieee symposium on security and privacy. Washington, DC, USA : IEEE Computer Society. BALTER , R., B ELLISSARD , L., B OYER , F., R IVEILL , M., & V ION -D URY, J. 1998. Architecturing and configuring distributed applications with olan. BARHAM , PAUL , D RAGOVIC , B ORIS , F RASER , K EIR , H AND , S TEVEN , H ARRIS , T IM , H O , A LEX , N EUGEBAUER , ROLF, P RATT, I AN , & WARFIELD , A NDREW. 2003. Xen and the art of virtualization. Pages 164–177 of : Sosp ’03 : Proceedings of the nineteenth acm symposium on operating systems principles. New York, NY, USA : ACM. BASS , L EN , C LEMENTS , PAUL , & K AZMAN , R ICK. 1998. Software architecture in practice. Boston, MA, USA : Addison-Wesley Longman Publishing Co., Inc. BASU , A NANDA , B OZGA , M ARIUS , & S IFAKIS , J OSEPH. 2006. Modeling heterogeneous real-time components in bip. Pages 3–12 of : Sefm ’06 : Proceedings of the fourth ieee international conference on software engineering and formal methods. Washington, DC, USA : IEEE Computer Society. BASU , A NANDA , M OUNIER , L AURENT, P OULHI ÈS , M ARC , P ULOU , JACQUES , & S IFAKIS , J OSEPH. 2007. Using bip for modeling and verification of networked systems – a case study on tinyos-based networks. Pages 257–260 of : Nca. 121 REFERENCES REFERENCES BAUMANN , A., H EISER , G., A PPAVOO , J., DA S ILVA , D., K RIEGER , O., W ISNIEWSKI , R.W., & K ERR , J. 2005. Providing dynamic update in an operating system. In : Proceedings of the 2005 USENIX annual technical conference. B ELL , D ONALD. 2003 (june). UML basics : An introduction to the Unified Modeling Language. B ERRY, G ÉRARD. 2000. The foundations of esterel. Proof, language, and interaction : essays in honour of robin milner, 425–454. B ERSHAD , B.N., S AVAGE , S., PARDYAK , P., S IRER , E.G., F IUCZYNSKI , M. E., B ECKER , D., C HAM BERS , C., & E GGERS , S. 1995. Extensibility safety and performance in the SPIN operating system. Pages 267–283 of : Proceedings of the 15th acm symposium on operating systems principles. ACM Press. B ERSHAD , B RIAN , C HAMBERS , C., E GGERS , S., M AEDA , C., M C NAMEE , D., PARDYAK , P., S A VAGE , S., & S IRER , E. G. 1994. SPIN - An Extensible Microkernel for Application-specific Operating System Services, Technical Report TR-94-03-03. Tech. rept. University of Washington. B HATTI , S., C ARLSON , J., DAI , H., D ENG , J., ROSE , J., S HETH , A., S HUCKER , B., G RUENWALD , C., T ORGERSON , A., & H AN , R. 2005. MANTIS OS : An Embedded Multithreaded Operating System for Wireless Micro Sensor Platforms. Monet, 10(4), 563–579. B LAIR , G ORDEN S., & S TEFANI , J EAN -B ERNARD. 1998. Open distributed processing and multimedia. Boston, MA, USA : Addison-Wesley Longman Publishing Co., Inc. B LOOM , T. 1983. Dynamic module replacement in a distributed programming system. Ph.D., MIT. Also as MIT LCS Tech. Report 303. B LOOM , T., & DAY, M. 1993. Reconfiguration and module replacement in Argus : Theory and Practice. Iee software engineering journal, 8(2). B OSCH. 1991. Can specification, version 2.0. http ://www.semiconductors.bosch.de/pdf/can2spec.pdf. B RUNETON , E., C OUPAYE , T., & S TEFANI , J.-B. 2004. The Fractal Component Model. http ://fractal.objectweb.org. B RUNETON , E., C OUPAYE , T HIERRY, L ECLERCQ , M., Q U ÉMA , V., & S TEFANI , J.-B. 2006. The Fractal Component Model and its Support in Java. Software - practice and experience, special issue on experiences with auto-adaptive and reconfigurable systems, 36(11–12), pp. 1257–1284. C AMPBELL , R. H., & TAN , S EE -M ONG. 1995. /spl mu/choices : an object-oriented multimedia operating system. Page 90 of : Hotos ’95 : Proceedings of the fifth workshop on hot topics in operating systems (hotos-v). Washington, DC, USA : IEEE Computer Society. C AMPBELL , ROY H., & I SLAM , NAYEEM. 1993. Choices : a parallel object-oriented operating system. Research directions in concurrent object-oriented programming, 393–451. C HARRA , O. 2004. Conception de noyaux de systèmes embarqués reconfigurables. Ph.D., Université Joseph Fourier – Grenoble 1. C HEUNG , W. H., & L OONG , A NTHONY H. S. 1995. Exploring issues of operating systems structuring : from microkernel to extensible systems. Sigops oper. syst. rev., 29(4), 4–16. C HIPCON. Cc1000 – single chip very low power rf transceiver, focus.ti.com/lit/ds/symlink/cc1000.pdf. http ://www.chipcon.com. C LARKE , M., & C OULSON , G. 1998. An architecture for dynamically extensible operating systems. Page 145 of : Cds ’98 : Proceedings of the international conference on configurable distributed systems. Washington, DC, USA : IEEE Computer Society. 122 REFERENCES REFERENCES C LARKE , M ICHAEL , B LAIR , G ORDON S., C OULSON , G EOFF , & PARLAVANTZAS , N IKOS. 2001. An efficient component model for the construction of adaptive middleware. Pages 160–178 of : Middleware ’01 : Proceedings of the ifip/acm international conference on distributed systems platforms heidelberg. London, UK : Springer-Verlag. C ONSEL , C., H ORNOF, L., M ARLET, R., M ULLER , G., T HIBAULT, S., VOLANSCHI , E.-N., L AWALL , J., & N OY É , J. 1998. Tempo : specializing systems applications and beyond. Acm comput. surv., 30(3es), 19. C ONSEL , C HARLES , H ORNOF, L UKE , N O ËL , F RANÇOIS , N OY É , JACQUES , & VOLANSCHE , N ICO LAE . 1996. A uniform approach for compile-time and run-time specialization. Pages 54–72 of : Selected papers from the internaltional seminar on partial evaluation. London, UK : SpringerVerlag. C OOK , R. P. 1980. *mod a language for distributed programming. Ieee trans. softw. eng., 6(6), 563–571. C OULSON , G EOFF , B LAIR , G ORDON S., C LARKE , M ICHAEL , & PARLAVANTZAS , N IKOS. 2002. The design of a configurable and reconfigurable middleware platform. Distrib. comput., 15(2), 109–126. DABEK , F RANK , Z ELDOVICH , N ICKOLAI , K AASHOEK , F RANKS , M AZI ÈRES , DAVID , , & M ORRIS , ROBERT. 2002. Event-driven programming for robust software. In : Proceedings of the 2002 sigops european workshop. DAMM , W ERNER. 2006a. Embedded system development for automotive applications : trends and challenges. Pages 1–1 of : Emsoft ’06 : Proceedings of the 6th acm & ieee international conference on embedded software. New York, NY, USA : ACM. DAMM , W ERNER. 2006b. Embedded system development for automotive applications : trends and challenges. http ://www.artist-embedded.org/docs/PositionPapers/Damm EmSoft06.pdf. DASHOFY, E RIC M., DER H OEK , A NDR É ; VAN , & TAYLOR , R ICHARD N. 2001. A highly-extensible, xml-based architecture description language. Page 103 of : Wicsa ’01 : Proceedings of the working ieee/ifip conference on sof tware architecture (wicsa’01). Washington, DC, USA : IEEE Computer Society. DAVID , P.-C., & L EDOUX , T. 2005. Une approche par aspects pour le développement de composants fractal adaptatifs. Pages 91–108 of : 2ème journée francophone sur le développement de logiciels par aspects (JFDLPA 2005). Lille, France : Hermès. DAVID , P IERRE -C HARLES. 2005 (July). Développement de composants fractal adaptatifs : un langage dédié à l’aspect d’adaptation. Phd thesis, Université de Nantes / École des Mines de Nantes. DAVID , P IERRE -C HARLES , & L EDOUX , T HOMAS. 2006 (July). Safe dynamic reconfigurations of fractal architectures with fscript. In : Proceedings of the 5th fractal workshop at ecoop 2006. D E M ICHIEL , L INDA , & K EITH , M ICHAEL. 2006. Jsr 220 : Enterprise javabeans, version 3.0. Tech. rept. Sun Microsystems. D ENYS , G., P IESSENS , F., & M ATTHIJS , F. 2002. A survey of customizability in operating systems research. Acm comput. surv., 34(4), 450–468. D IJKSTRA , E DSGER W. 1968. The structure of the ”the”-multiprogramming system. Commun. acm, 11(5), 341–346. D OUENCE , R ÉMI , A LLEN , ROBERT, & G ARLAN , DAVID. 1997. Specifying dynamism in software architectures. Pages 11–22 of : L EAVENS , G ARY T., & S ITARAMAN , M URALI (eds), Proceedings of the first workshop on the foundations of compone nt-based systems, zurich, switzerland, september 26 1997. 123 REFERENCES REFERENCES D RAVES , R ICHARD P., B ERSHAD , B RIAN N., R ASHID , R ICHARD F., & D EAN , R ANDALL W. 1991. Using continuations to implement thread management and communication in operating systems. Pages 122–136 of : Proceedings of the13th ACM symposium on operating systems principle. Association for Computing Machinery SIGOPS. D UNKELS , A., G RONVALL , B., & VOIGT, T. 2004. Contiki - A Lightweight and Flexible Operating System for Tiny Networked Sensors. Pages 455–462 of : Lcn ’04 : Proceedings of the 29th annual ieee international conference on local computer networks (lcn’04). D UNKELS , A., F INNE , N., E RIKSSON , J., & VOIGT, T. 2006. Run-time dynamic linking for reprogramming wireless sensor networks. In : Proceedings of the 4th acm conference on embedded networked sensor systems (sensys 2006). E2R. End-to-End Reconfigurability. http ://e2r2.motlabs.com. E C OS . http ://sources.redhat.com/ecos. ELF. System v application binary interface specification, tool interface standard, executable and linking format. http ://www.caldera.com/developers/devspecs/gabi41.pdf. E NGLER , D.R., K AASHOEK , M.F., & O’T OOLE , J R ., J. 1995. Exokernel : an operating system architecture for application-level resource management. Pages 251–266 of : Proceedings of the 15th acm symposium on operating systems principles. ACM Press. FASSINO , J.-F. 2001. Think : vers une architecture de systèmes flexibles. Ph.D. thesis, École Nationale Supérieure des Télécommunications. FASSINO , J.-P., S TEFANI , J.-B., L AWALL , J., & M ULLER , G. 2002. Think : a software framework for component-based operating system kernels. Pages 73–86 of : Proceedings of the 2002 USENIX annual technical conference. USENIX, Monterey, CA. F ORD , B., L EPREAU , J., C LAWSON , S., M AREN , K. VAN , ROBINSON , B., & T URNER , J. 1997. The Flux OS Toolkit : Reusable Components for OS Implementation. Page 14 of : Hotos ’97 : Proceedings of the 6th workshop on hot topics in operating systems (hotos-vi). Washington, DC, USA : IEEE Computer Society. F ORIN , A., H ELANDER , J., P HAM , P., & R AJENDIRAN , J. 2001. Component based invisible computing. F RIEDRICH , L. F ERNANDO , S TANKOVIC , J OHN , H UMPHREY, M ARTY, M ARLEY, M ICHAEL , & H AS KINS , J OHN . 2001. A survey of configurable, component-based operating systems for embedded applications. Ieee micro, 21(3), 54–68. G ABBER , E., S MALL , C., B RUNO , J., B RUSTOLONI , J., & S ILBERSCHATZ , A. 1999. The Pebble Component-Based Operating System. Pages 267–282 of : Proceedings of the USENIX annual technical conference. G ARLAN , D., M ONROE , R. T., & W ILE , D. 2000. ACME : Architectural Description of ComponentBased System s. Pages 47–68 of : L EAVENS , G ARY T., & S ITARAMAN , M URALI (eds), Foundations of component-based systems. Cambridge University Press. G ARLAN , DAVID , & S HAW, M ARY. 1994. An introduction to software architecture. Tech. rept. Carnegie Mellon University, Pittsburgh, PA, USA. G AY, D., L EVIS , P., VON B EHREN , R., W ELSH , M., B REWER , E., & C ULLER , D. 2003. The nesC language : A holistic approach to networked embedded systems. Pages 1–11 of : Proceedings of the acm sigplan 2003 conference on programming language design and implementation (plid’03). 124 REFERENCES REFERENCES G HORMLEY, D OUGLAS P., P ETROU , DAVID , RODRIGUES , S TEVEN H., & A NDERSON , T HOMAS E. 1998. Slic : an extensibility system for commodity operating systems. Pages 4–4 of : Atec’98 : Proceedings of the annual technical conference on usenix annual technical conference, 1998. Berkeley, CA, USA : USENIX Association. G UPTA , D EEPAK , & JALOTE , PANKAJ. 1993. On line software version change using state transfer between processes. Softw. pract. exper., 23(9), 949–964. H AERTIG , H., H OHMUTH , M., L IEDTKE , J., & S CHOENBERG , S. 1997. The performance of microkernel-based systems. Pages 66–77 of : Proceedings of the sixteenth acm symposium on operating systems principles. ACM Press. H ALBWACHS , N., C ASPI , P., R AYMOND , P., & P ILAUD , D. 1991. The synchronous data-flow programming language LUSTRE. Proceedings of the ieee, 79(9), 1305–1320. H AN , C.-C., K UMAR , R., S HEA , R., KOHLER , E., & S RIVASTAVA , M. 2005. A dynamic operating system for sensor nodes. Pages 163–176 of : Mobisys ’05 : Proceedings of the 3rd international conference on mobile systems, applications, and services. H ELANDER , J., & F ORIN , A. 1998. MMLite : a highly componentized system architecture. Pages 96– 103 of : Ew 8 : Proceedings of the 8th acm sigops european workshop on support for composing distributed applications. Sintra, Portugal : ACM Press. H ICKS , M. W. 2001. Dynamic software updating. Ph.D., The University of Pennsylvania. H ICKS , M ICHAEL W., & N ETTLES , S COTT. 2005. Dynamic software updating. Acm trans. program. lang. syst., 27(6), 1049–1096. H ILDEBRAND , DAN. 1992. An architectural overview of qnx. Pages 113–126 of : Proceedings of the workshop on micro-kernels and other kernel architectures. Berkeley, CA, USA : USENIX Association. H ILL , J., S ZEWCZYK , R., W OO , A., H OLLAR , S., C ULLER , D., & P ISTER , K. 2000. System architecture directions for networked sensors. Pages 93–104 of : Proceedings of the ninth international conference on architectural support for programming languages and operating systems (asplos). H J ÁLMT ÝSSON , G ÍSLI , & G RAY, ROBERT. 1998. Dynamic c++ classes : a lightweight mechanism to update code in a running program. Pages 6–6 of : Atec’98 : Proceedings of the annual technical conference on usenix annual technical conference, 1998. Berkeley, CA, USA : USENIX Association. H OFMEISTER , C HRISTINE R. 1994. Dynamic reconfiguration of distributed applications. Ph.D., University of Maryland. H ORIE , M ICHAEL , PANG , JAMES C., M ANNING , E RIC G., & S HOJA , G HOLAMALI C. 1998. Using meta-interfaces to support secure dynamic system reconfiguration. In : Proceedings of the 4th international conference on configurable distributed systems (iccds). H ORN , PAUL. 2001 (Oct). Autonomic Computing — IBM’s Perspective on the State of Information Technology. IBM Corporation. H UTCHINSON , N ORMAN C., & P ETERSON , L ARRY L. 1991. The x-kernel : An architecture for implementing network protocols. Ieee trans. softw. eng., 17(1), 64–76. I TOH , J., YOKOTE , Y., & L EA , R. 1995. Using Meta-Objects to Support Optimisation in the Apertos Operating System. Pages 147–158 of : Proceedings of the usenix conference on object-oriented technologies (coots). JARBOUI , T., FASSINO , J.-P., & L ACOSTE , M. 2004. Applying components to access control design : Towards a framework for os kernels. In : International conference on dependable systems and networks (dsn 2004). 125 REFERENCES REFERENCES JARBOUI , T., M ARX , F., L AVAL , J.-P., G HOZZI , M., G ERMAIN , F., & L OBRY, O. 2006a. The Cognichip : A flexible, lightweight spectrum monitor. In : COGnitive systems with interactive sensors (COGIS). JARBOUI , TAHAR , L ACOSTE , M ARC , & WADIER , P IERRE. 2006b. A component-based policy-neutral authorization architecture. In : Cfse’06 : Conférence française sur les systèmes d’exploitation. J EONG , J., K IM , S., & B ROAD , A. 2003. Network reprogramming. TinyOS documentation. 2003. http ://www.tinyos.net/tinyos-1.x/doc/ NetworkReprogramming.pdf. JN/TML. JM/TML Software Coordination JVT software page. http ://bs.hhi.de/suehring/tml. K ETFI , A., B ELKHATIR , N., & C UNIN , P.-Y. 2002. Automatic adaptation of component-based software : Issues and experiences. Pages 1365–1371 of : Proceedings of the international conference on parallel and distributed processing techniques and applications. CSREA Press. K ICZALES , G REGOR , L AMPING , J OHN , L OPES , C HRISTINA V IDEIRA , M AEDA , C HRIS , M ENDHE KAR , A NURAG , & M URPHY, G AIL . 1997. Open implementation design guidelines. Pages 481–490 of : Icse ’97 : Proceedings of the 19th international conference on software engineering. New York, NY, USA : ACM. KON , FABIO , S INGHAI , A SHISH , C AMPBELL , ROY H., C ARVALHO , D ULCINEIA , M OORE , ROBERT, & BALLESTEROS , F RANCISCO J. 1998. 2k : A reflective, component-based operating system for rapidly changing environments. Pages 388–389 of : Ecoop ’98 : Workshop ion on object-oriented technology. London, UK : Springer-Verlag. K RAMER , J., & M AGEE , J. 1990. The evolving philosophers problem : Dynamic change management. Ieee trans. software eng., 16(11). L AUER , H UGH C., & N EEDHAM , ROGER M. 1979. On the duality of operating system structures. Operating systems review, 13(2), 3–19. L AYAIDA , O., Ö ZCAN , A. E., & S TEFANI , J.-B. 2005. A component-based approach for MPSoC SW design : Experience with OS customization for H.264 decoding. In : 3rd workshop on embedded systems for real-time multimedia. L ECLERCQ , M., O ZCAN , A.-E., Q UEMA , V., & S TEFANI , J.-B. 2007. Supporting heterogeneous architecture descriptions in an extensible toolset. In : Icse ’07 : Proceedings of the 29th international conference on software engineering. L EE , E DWARD A. 2000. What’s ahead for embedded software ? Computer, 33(9), 18–26. L EE , E DWARD A. 2006. The problem with threads. Computer, 39(5), 33–42. L EE , I NSUP. 1983. Dymos : a dynamic modification system. Ph.D., The University of Wisconsin Madison. L EVIS , P., & C ULLER , D. 2002. Maté : A tiny virtual machine for sensor networks. In : Proceedings of the 10th international conference on architectural support for programming languages and operating systems (asplos). L IEDTKE , J. 1993. Improving IPC by kernel design. Pages 175–188 of : Proceedings of the 14th acm symposium on operating systems principles. Asheville, NC, USA : ACM Press. L IEDTKE , J. 1995. On micro-kernel construction. Pages 237–250 of : Proceedings of the 15th acm symposium on operating systems principles. Copper Mountain, Colorado, USA : ACM Press. L IEDTKE , J. 1996. Toward real microkernels. Commun. acm, 39(9), 70–77. L OBRY, O LIVIER , & P OLAKOVIC , J URAJ. 2008. Controlling the performance overhead of componentbased systems. In : Software Composition, 7th International Symposium, SC 2008. 126 REFERENCES REFERENCES L UCKHAM , DAVID C., K ENNEY, J OHN L., AUGUSTIN , L ARRY M., S V ERA , JAME , B RYAN , D OUG , & M ANN , WALTER. 1995. Specification and analysis of system architecture using rapide. IEEE transactions on software engineering, 21(4), 336–355. M AEDA , C HRIS , L EE , A RTHUR , M URPHY, G AIL , & K ICZALES , G REGOR. 1997. Open implementation analysis and design. Pages 44–52 of : Ssr ’97 : Proceedings of the 1997 symposium on software reusability. New York, NY, USA : ACM. M AGEE , J., D ULAY, N., E ISENBACH , S., & K RAMER , J. 1995. Specifying Distributed Software Architectures. Pages 137–153 of : S CHAFER , W., & B OTELLA , P. (eds), Proc. 5th european software engineering conf. (ESEC 95), vol. 989. Sitges, Spain : Springer-Verlag, Berlin. M ARR ÓN , P. J., G AUGER , M., L ACHENMANN , A., M INDER , D., S AUKH , O., & ROTHERMEL , K. 2006. FlexCup : A Flexible and Efficient Code Update Mechanism for Sensor Networks. Pages 212–227 of : European workshop on wireless sensor networks. M EDVIDOVIC , N ENAD , DASHOFY, E RIC M., & TAYLOR , R ICHARD N. 2007. Moving architectural description from under the technology lamppost. Inf. softw. technol., 49(1), 12–31. M ITCHELL , S COTT, NAGUIB , H ANI , C OULOURIS , G EORGE , & K INDBERG , T IM. 1998. Dynamically reconfiguring multimedia components : a model-based approach. Pages 40–47 of : Ew 8 : Proceedings of the 8th acm sigops european workshop on support for composing distributed applications. New York, NY, USA : ACM. M ITOLA , J OSEPH. 2000. Cognitive radio : An integrated agent architecture for software defined radio. Ph.D. thesis, Royal Institute of Technology (KTH). M ORRIS , ROBERT, KOHLER , E DDIE , JANNOTTI , J OHN , & K AASHOEK , M. F RANS. 1999. The click modular router. Sigops oper. syst. rev., 33(5), 217–231. N EAMTIU , I ULIAN , H ICKS , M ICHAEL , S TOYLE , G ARETH , & O RIOL , M ANUEL. 2006. Practical dynamic software updating for c. Pages 72–83 of : Pldi ’06 : Proceedings of the 2006 acm sigplan conference on programming language design and implementation. New York, NY, USA : ACM. N ECULA , G EORGE C. 1997. Proof-carrying code. Pages 106–119 of : Popl ’97 : Proceedings of the 24th acm sigplan-sigact symposium on principles of programming languages. New York, NY, USA : ACM. N ECULA , G EORGE C., & L EE , P ETER. 1996. Safe kernel extensions without run-time checking. Pages 229–243 of : Osdi ’96 : Proceedings of the second usenix symposium on operating systems design and implementation. New York, NY, USA : ACM. OMG. 2001. CORBA 3.0 New Components Chapters. Tech. rept. OMG TC Document otc/2001-11-03. Object Managment Group. OMG. 2002. Lightweight CORBA component model. Tech. rept. Lightweight CCM 2003 FTF ptc/200406-10. Object Management Group. OMG. 2004. Unified Modeling Language (UML), version 2.0, Object Management Group. OSEK/VDX. Offene systeme und deren schnittstellen für die elektronik in kraftfahrzeugen (eng., ”open systems and their interfaces for the electronics in motor vehicles”). http ://www.osek-vdx.org. O USTERHOUT, J OHN. 1996 (Jan.). Why threads are a bad idea (for most purposes). In : Usenix winter technical conference. O ZCAN , A.-E. 2007. Conception et implantation d’un environnement de développement de logiciels à base de composants, applications aux systèmes multiprocesseurs sur puce. Ph.D. thesis, Institut National Polytechnique de Grenoble. PALM OS. Palmos by palm inc., currently garnet os by access. http ://www.palmos.com. 127 REFERENCES REFERENCES P L ÁSIL , F., B ÁLEK , D., & JANECEK , R. 1998. Sofa/dcup : Architecture for component trading and dynamic updating. Page 43 of : Cds ’98 : Proceedings of the international conference on configurable distributed systems. Washington, DC, USA : IEEE Computer Society. POSIX. Portable operating system http ://www.opengroup.org/onlinepubs/009695399. interface, ieee std 1003.1. P U , C., AUTREY, T., B LACK , A., C ONSEL , C., C OWAN , C., I NOUYE , J., K ETHANA , L., WALPOLE , J., & Z HANG , K. 1995a. Optimistic incremental specialization : streamlining a commercial operating system. Sigops oper. syst. rev., 29(5), 314–321. P U , C ALTON , AUTREY, T ITO , B LACK , A NDREW P., C ONSEL , C HARLES , C OWAN , C RISPIN , I N OUYE , J ON , K ETHANA , L AKSHMI , WALPOLE , J ONATHAN , & Z HANG , K E . 1995b. Optimistic incremental specialization : Streamlining a commercial operating system. Pages 314–324 of : Proceedings of the 15th acm symposium on operating system principles. R EID , A., F LATT, M., S TOLLER , L., L EPREAU , J., & E IDE , E. 2000 (Oct.). Knit : Component Composition for Systems Software. Pages 347–360 of : Proceedings of the 4th usenix symposium on operating systems design and implementation (osdi). R IPPERT, C HRISTOPHE , & S TEFANI , J EAN -B ERNARD. 2002. Think : a secure distributed systems architecture. Pages 243–246 of : Ew10 : Proceedings of the 10th workshop on acm sigops european workshop. New York, NY, USA : ACM. ROZIER , M., A BROSSIMOV, V., A RMAND , F., B OULE , I., G IEN , M., G UILLEMONT, M., H ERRMANN , F., K AISER , C., L ANGLOIS , S., L EONARD , P., & N EUHAUSER , W. 1988. CHORUS distributed operating system. In computing systems, Vol. 1(4), 305–370. S AXENA , A NSHUMAN , L ACOSTE , M ARC , JARBOUI , TAHAR , L UCKING , U LF, & S TEINKE , B ERND. 2007. A software framework for autonomic security in pervasive environments. In : Third international conference on information systems security (iciss 2007). SEA-AADL. Architecture analysis and design language (formerly avionics architecture description language). http ://www.aadl.info/. S EGAL , M ARK E., & F RIEDER , O PHIR. 1993. On-the-fly program modification : Systems for dynamic updating. Ieee softw., 10(2), 53–65. S ELTZER , M.I., E NDO , Y., S MALL , C., & S MITH , K.A. Oct. 1996. Dealing with disaster : Surviving misbehaved kernel extensions. Pages 213–227 of : Proceedings of the 2nd usenix symposium on operating systems design and implementation (osdi). S ENART, A., C HARRA , O., & S TEFANI , J. 2002. Developing dynamically reconfigurable operating system kernels with the think component architecture. In : In proceedings of the workshop on engineering context-aware object-oriented systems and environments. S HAW, M ARY, & G ARLAN , DAVID. 1996. Software architecture : perspectives on an emerging discipline. Upper Saddle River, NJ, USA : Prentice-Hall, Inc. S MALL , C. 1997. MiSFIT : A tool for constructing safe extensible C++ systems. In : Proceedings of the 3rd usenix conference on object-oriented technologies (coots). USENIX. S MALL , C., & S ELTZER , M. 1995. Structuring the kernel as a toolkit of extensible, reusable components. Pages 134–137 of : Proceedings of the 4th international workshop on object-orientation in operating systems. S OULES , C.A.N., A PPAVOO , J., H UI , K., W ISNIEWSKI , R.W., S ILVA , D. DA , G ANGER , G.R., K RIE GER , O., S TUMM , M., AUSLANDER , M., O STROWSKI , M., ROSENBURG , B., & X ENIDIS , J. 2003. System support for online reconfiguration. Pages 141–154 of : Proceedings of the 2003 USENIX annual technical conference. 128 REFERENCES REFERENCES S ZTIPANOVITS , JANOS , & S ASTRY, S HANKAR. Embedded software : Opportunities and challenges. University of California, Berkeley and DARPA/ITO, http ://www.nitrd.gov/subcommittee/sdp/planning/presentations/UCBerkeley-Sastry.pdf. S ZYPERSKI , C LEMENS. 2002. Component software : Beyond object-oriented programming. Boston, MA, USA : Addison-Wesley Longman Publishing Co., Inc. S ÉNART, A. 2003. Canevas logiciel pour la construction d’infrastructures logicielles dynamiquement adaptables. Ph.D., Institut National Polytechnique de Grenoble. T HINK. The THINK Project Homepage. T OURNIER , J.-C H . 2005a. Qinna : Une architecture à base de composants pour la gestion de la qualité de service dans les systèmes embarqués mobiles. Ph.D. thesis, Institut National des Sciences Appliquées de Lyon. T OURNIER , J.-C H . 2005b. A survey of configurable operating systems, tr-cs-2005-43. Tech. rept. University of New Mexico. V EITCH , A., & H UTCHINSON , N. 1998. Dynamic Service Reconfiguration and Migration in the Kea Kernel. Page 156 of : Proceedings of the international conference on configurable distributed systems (iccds). Washington, DC, USA : IEEE Computer Society. VOLANSCHI , E UGEN N. 1998. Une approche automatique à la spécialisation de composants système. Ph.D., Université de Rennes 1. VON B EHREN , J. ROBERT, C ONDIT, J EREMY, & B REWER , E RIC A. 2003a. Why events are a bad idea (for high-concurrency servers). Pages 19–24 of : Hotos. VON B EHREN , ROB , C ONDIT, J EREMY, Z HOU , F ENG , N ECULA , G EORGE C., & B REWER , E RIC. 2003b. Capriccio : scalable threads for internet services. Pages 268–281 of : Sosp ’03 : Proceedings of the nineteenth acm symposium on operating systems principles. ACM Press. WAHBE , R., L UCCO , S., A NDERSON , T.E., & G RAHAM , S.L. 1993. Efficient software-based fault isolation. Pages 203–216 of : Proceedings of the 14th acm symposium on operating systems principles. Asheville, NC, USA : ACM Press. W EISER , M ARK. 1991. The computer for the twenty-first century. Scientific american. W ELSH , M ATT, C ULLER , DAVID E., & B REWER , E RIC A. 2001. SEDA : An architecture for wellconditioned, scalable internet services. Pages 230–243 of : Symposium on operating systems principles. YOKOTE , Y. 1992. The Apertos reflective operating system : the concept and its implementation. Pages 414–434 of : Proceedings on object-oriented programming systems, languages, and applications (oopsla). ACM Press. YOKOTE , YASUHIKO , M ITSUZAWA , ATSUSHI , F UJINAMI , N OBUHISA , & T OKORO , M ARIO. 1991. Reflective object management in the muse operating system. Pages 16–23 of : Proceedings of the 1991 international workshop on object-orientation in operating systems (IWOOOS). Washington, DC : IEEE Computer Society. 129