Brevet FR2883083A1 — publié le 20060915

Procédé d'exécution d'une application dans un conteneur virtuel formant une session d'environnement virtualisé La présente invention concerne un procédé d'insertion dynamique d'un traitement informatique au sein d'une tâche informatique en cours d'exécution, de façon transparente pour cette tâche. Elle concerne aussi un procédé utilisant une telle insertion dynamique pour exécuter une application dans un conteneur virtuel formant une session d'environnement virtualisé. Au sein d'un ordinateur ou d'un réseau d'ordinateurs, ces procédés sont utiles en particulier pour gérer la façon dont sont exécutées certaines applications informatiques, même lorsqu'elles n'ont pas été conçues en vue d'une telle gestion. Le domaine de l'invention est celui des réseaux ou clusters d'ordinateurs formés de plusieurs ordinateurs collaborant entre eux. Ces clusters sont utilisés pour exécuter des applications logicielles apportant un ou des services à des utilisateurs. Ces applications peuvent être mono-processus ou multi-processus, et être exécutées sur un seul ordinateur ou répartie sur plusieurs ordinateurs en réseau collaborant entre eux pour former un cluster . Il peut s'agir en particulier d'applications transactionnelles, par exemple sous la forme d'une application distribuée, et communicant par échange de messages ( Message Passing Interface ) ou à travers des zones de mémoire partagée ( Shared Memory ). L'invention s'applique particulièrement à la gestion du fonctionnement, au sein de l'ordinateur ou du cluster, d'une telle application dite maître ou primaire et exécutée sur un ordinateur dit noeud primaire ou opérationnel. Cette gestion de fonctionnement peut en particulier être réalisée par une autre application logicielle dite application intermédiaire, par exemple une application de type middleware . Cette gestion de fonctionnement peut comprendre en particulier des opérations de réplication, de redistribution, de fiabilisation, ou de traçage ( debugging ) de tout ou partie de cette application, au sein du noeud primaire ou en collaboration avec d'autres noeuds dits secondaires. Pour réaliser une telle gestion de fonctionnement, il est utile de pouvoir intervenir dans l'exécution de l'application gérée, par exemple pour analyser ou modifier son fonctionnement. Il est en particulier utile de disposer de techniques d'enregistrement des évènements survenant au sein de cette application, pour pouvoir les rejouer, c'est-à-dire les réexécuter ou les faire se produire de la même façon, à un autre moment ou sur un autre noeud ou ordinateur. Or, lorsqu'une application utilisée en exploitation n'a pas été prévue dès sa conception pour réaliser une telle gestion, il est difficile et coûteux de lui ajouter de telles fonctionnalités ultérieurement, et cela constitue un risque important d'erreurs. Pour toutes les applications existantes, il est particulièrement utile de pouvoir implémenter une telle gestion de fonctionnement sans modifier leurs fichiers exécutables, et en les gardant dans leur version d'origine ( Iegacy applications ). Des méthodes existent déjà pour permettre le chargement dynamique de code dans un exécutable, qui constitue un élément critique du procédé. Nous les listons ci-dessous, et indiquons certaines de leurs limitations. - Pré-chargement de bibliothèque par l'éditeur de lien dynamique. Lors de l'éxécution d'un exécutable à édition de liens dynamique, il est possible de précharger, par la spécification d'une variable d'environnement, une bibliothèque non requise dans l'espace mémoire adressable du programme. En particulier, cette méthode ne s'applique pas aux exécutables à édition de lien statique. - Chargement de bibliothèque par utilisation de l'appel système existant ptrace depuis un processus tiers. Ce procédé est celui utilisé par les débuggueurs pour altérer l'exécution d'un processus. Il s'appuie sur la présence de symboles de debug dans le binaire de l'exécutable pour permettre l'invocation de fonctions. La fonction de chargement de bibliothèque, appelée dlopen dans Linux, doit dèjà être présente dans l'exécutable, ce qui n'est pas systématique (et rare dans le cas d' exécutables statiques). Ces méthodes, en plus des limitations qu'elles comportent pour l'insertion de code dans des processus en cours d'exécution, ne permettent pas seules de garantir un fonctionnement correct de l'exécutable si le code inséré doit être plus tard supprimé. De telles méthodes reviennent souvent à considérer que les parties de codes ainsi insérées le sont de manière permanente, ce qui pose alors des problèmes potentiels d'interférence entre le code inséré et l'exécutable original. Certaines méthodes d'interposition dynamique sont aussi décrites dans le brevet n FR 2 843 210 des mêmes inventeurs, mais permettent surtout d'intervenir lors de l'exécution d'un appel système. Ces techniques peuvent être insuffisantes pour intervenir dans des instructions ne réalisant pas d'appel système. Un but de l'invention est de pallier certains des inconvénients de l'art antérieur, et en particulier de permettre d'insérer un traitement informatique au sein de l'exécution d'une tâche appartenant à une telle application. Dans cet esprit, l'invention propose un procédé d'insertion dynamique d'une ou plusieurs instructions de programme constituant un traitement inséré, par une première tâche informatique, dite tâche gestionnaire, dans une deuxième tâche informatique, dite tâche cible, utilisant une mémoire de travail pour son exécution dans un système informatique géré par un logiciel système, ce procédé comprenant les étapes suivantes - une mémorisation, au sein d'une zone de mémoire externe à la mémoire de travail de la tâche cible, d'au moins une donnée d'accrochage représentant des informations de déclenchement du traitement à insérer; - une exécution, au sein du déroulement d'une routine système appelée ou contrôlée par la tâche cible, d'une instruction dite d'accrochage déclenchant un test de la donnée d'accrochage; - exécution du traitement inséré, en fonction du résultat dudit test et en lieu et place de l'instruction de programme suivant l'instruction d'accrochage dans le déroulement de ladite routine système. En particulier, à l'issue de l'exécution du traitement inséré, l'exécution de la routine système reprend là où elle s'était interrompue lors de l'exécution du traitement inséré. Dans le cas où la tâche cible est liée au moins une autre tâche par le partage d'un même espace mémoire d'exécution, le procédé comprend en outre un blocage temporaire de la tâche liée pendant au moins une opération comprenant le chargement, dans ledit espace mémoire d'exécution, des instructions du traitement à insérer. De façon avantageuse, le traitement inséré comprend une gestion 10 d'au moins une donnée identifiant la tâche cible. Plus particulièrement, le traitement inséré réalise ou déclenche au moins une opération de gestion de fonctionnement d'une application dite application gérée. Cette opération de gestion de fonctionnement peut comprendre au moins une opération de suivi ou d'enregistrement de l'exécution de la tâche cible, ou de capture de l'état de ladite tâche cible ou d'au moins une ressource qu'elle utilise. En particulier, cette opération de gestion de fonctionnement comprend au moins une opération de mémorisation d'une donnée associée à la tâche cible et représentant une barrière de synchronisation avec au moins une autre tâche. Plus particulièrement, cette opération de gestion de fonctionnement comprend au moins une opération de réplication de la tâche cible dans un autre système informatique, ou de libération dans son système d'origine de ladite tâche cible ou d'au moins une ressource qu'elle utilise ses ressources, après une telle réplication. Le traitement inséré peut aussi réaliser un chargement dynamique d'un élément d'une bibliothèque logicielle, ladite bibliothèque logicielle étant accessible ou modifiable au cours de l'exécution de la tâche cible ou d'une application à laquelle elle appartient. Le traitement inséré peut aussi comprendre une mémorisation, de façon associée à la tâche cible, d'une donnée constituant un identifiant virtuel commun entre la tâche cible et au moins une autre tâche en cours d'exécution, constituant ainsi un identifiant d'un conteneur virtuel englobant lesdites tâches. L'identifiant de conteneur virtuel peut être mémorisé de façon associée à une première tâche, par un traitement inséré exécuté lors d'une routine système appelée par ladite première tâche, cet identifiant de conteneur étant propagé à au moins une deuxième tâche par héritage à partir de la première tâche. Cet identifiant de conteneur virtuel peut aussi être propagé par la caractéristique qu'au moins une troisième tâche est crée par une opération comprenant une instruction d'accrochage utilisée par une tâche gestionnaire pour mémoriser avec ladite troisième tâche un identifiant de conteneur correspondant à conteneur virtuel existant et devant contenir ladite troisième tâche. L'invention permet qu'au moins une opération logicielle soit appliquée à l'ensemble des tâches appartenant à un même conteneur virtuel. Le conteneur virtuel peut regrouper un ensemble de tâches exécutées au sein d'une même application dite application cible. Il peut aussi être utilisé pour qu'au moins une tâche exécutée dans le système informatique subit une opération de gestion de fonctionnement 20 dépendant de son appartenance au conteneur virtuel. Selon une particularité, la gestion de fonctionnement des tâches du conteneur comprend un traitement de suivi ou de contrôle de l'exécution ou de capture d'état d'au moins une tâche informatique ou d'au moins une ressource qu'elle utilise. Selon une autre particularité, la gestion de fonctionnement des tâches du conteneur comprend un traitement de réplication dans une autre système informatique d'au moins une tâche informatique, ou de libération de cette tâche ou d'au moins une ressource qu'elle utilise, après une telle réplication. D'autres particularités et avantages de l'invention ressortiront de la description détaillée d'un mode de mise en oeuvre nullement limitatif, et des dessins annexés. Cette description présente une méthode de mutation dynamique, temporaire et réversible du code binaire de processus en cours d'exécution. Cette méthode est utilisée, entre autres, pour mettre en oeuvre la capture d'état (checkpoint) d'applications sans avoir à les modifier. En résumé, il s'agit entre autres d'une problématique d'amorçage: nous décrivons un procédé de chargement de module simple (le bootstrap), qui lui-même sera capable de charger puis d'exécuter dans le processus des bibliothèques complexes. Une autre manière d'obtenir cette fonction pour une application donnée, serait que le programme de cette application dispose en lui-même de fonctionalités de gestions de sa propre mémoire interne, et notamment des parties de code exécutable, ce qui nécessite soit que le programme dispose ait accès à une bibliothèque de telles fonctions (ce qui n'est pas le cas pour les programmes statiques), soit que le programme ait été conçu à l'origine pour cela, soit qu'il faille reconcevoir le programme. Un objectif important de la méthode présentée est pouvoir appliquer ce procédé, ainsi qu'une fonction donnée (telle que le checkpoint), à la totalité des programmes existants, sans avoir à les modifier ni à les reconcevoir. Les caractéristiques de la nouvelle méthode sont: - Compatibilité avec les exécutables à édition de liens statique aussi bien que dynamique. - Ne dépend pas de la présence de symboles de debug. - Ne dépend pas de la présence dans l'exécutable des fonctions de chargement dynamique d'objet binaires. - Capacité d'instrumentation d'éxécutables sous controle de ptrace. - Cycle de vie court. Réversibilité complète. Le chargement de code est effectué non pas au démarrage du process, mais juste avant l'opération à réaliser (par exemple un checkpoint). A la fin de l'opération, tout le nouveau code est inséré - Prise en compte de plusieurs taches concurrentes, simultanées, pouvant ou non partager le même espace d'adressage mémoire (threads). Dans le processus d'instrumentation, on s'intéresse à la première étape, qui consiste à charger dans un exécutable une bibliothèque de fonctions dont la présence n'est pas requise à l'origine par l'exécutable. Une telle bibliothèque peut contenir du code permettant par exemple le profiling (mesure de performances à grain fin), ou bien l'activation de traces de diagnostic, ou encore la capture d'état des processus, à des fins de haute disponibilité ou de migration d'application entre machines appartenant à un cluster. Le procédé de mutation décrit ici, afin d'être réversible, et de préserver le code existant des programmes cibles, consiste en l'ajout d'un nouveau segments de code dans l'espace mémoire adressable du programme. Cet ajout, la mise en oeuvre de ce code, ainsi que sa suppression, sont pilotées par un processus tiers, extérieur à l'application. Le principe de cette méthode consiste à implémenter dans le système d'exploitation le service de chargement de bibliothèque. Le service luimême est fourni par un module noyau à chargement dynamique. Ce service peut-être déclenché via un appel système d'entrée-sortie (ioctl) sur un pilote de périphérique (device driver) mis à disposition par le module noyau. Il peut aussi être déclenché de manière automatique par accrochage sur l'appel système exec, afin de propager automatiquement En préambule à la description détaillée de la méthode de chargement, 20 nous décrivons les aménagements apportés au système d'exploitation permettant la mise en oeuvre du procédé. Ces aménagements consistent d'une part dans l'ajout d'un champ particulier dans la structure de données décrivant une tache au niveau du système d'exploitation (structure task_struct dans linux), ainsi que dans l'insertion de hooks dans l'implémentation internes de cetains appels systèmes stratégiques comme fork , exec , getpid , etc. Ces hooks sont des points de branchement conditionnels permettant de déclencher des traitements supplémentaires dans le contexte de l'appel système pour réaliser des taches de virtualisation, que nous ne détaillerons pas ici. La condition de déclenchement est associé à la valeur du champ rajouté dans la structure task_struct . L'intérêt du nouveau champ de task_struct est de pouvoir définir, et à tout instant identifier, un ensemble de taches (processus ID ou thread ID) , que nous regroupons sur le concept de session , et sur lesquelles il faut agir de manière consistente et simultanée afin de maintenir la cohérence du système. Par exemple, plusieurs threads partagent le même espace mémoire adressable. Le chargement d'un module dans l'une affecte donc les autres. De même, des ressources telles que les descripteurs de fichiers sont héritées lors de la création de nouvelles taches, et font apparaitre des relations d'interdépendence entres les taches d'une même filiation. La définition d'une session consiste à affecter un identifiant unique de session à la tache initiale d'une application cible (par exemple, le shell script de lancement). Ce numéro de session sera propagé, par héritage, et grâce aux hooks, à toutes les taches affiliées à cette tache initiale, ou pour laquelle une nouvelle relation d'interdépendence apparait (ex: transmission d'un descripteur de fichier entre 2 processus non affiliés). La mise du module de bootstrap se fait pour une liste de taches données, appartenant à une session, telle que décrite ci-dessus. La procédure de mise en oeuvre, telle qu'illustrée dans la figure 1 consiste en: - 1 Ordre de chargement par le processus controleur, via un appel système IOCTL (contrôle d'entrée/sortie) au pilote de périphérique bootstrap. - 2 Pour les processus multi-thread (plusieurs tâches concurrentes partageant le même espace mémoire), une barrière de synchronisation implémentée dans le module bootstrap permet de n'activer - 3 Insertion du module de chargement dans l'espace adressable du process. Le module est en 2 parties, un segment de code compilé de manière relogeable (tous les déplacements et sauts sont calculés en relatif par rapport au début du segment), et un segment de données, par nature non relogeable (les données constituant un segment de mémoire non exécutable). - 4 Insertion d'un handler de signal sur le signal STOP dans le process. Cette opération est réalisée par le module de bootstrap. Le signal STOP ne pouvant être intercepté par le process lui-même, il n'y a pas d'interférence possible avec d'autres handlers de signaux. D'autre part cela garantie l'exécution du module de chargement sur réception du signal. Le handler de signal est paramétré de manière à exécuter le point d'entrée du segment de texte (le module de chargement lui-même), et à propager comme paramètre d'exécution l'adresse de chargement du segment de données lors de l'émission du signal, par le champ SIGINFO. - 5 Un compteur de références permet de suivre le nombres de taches ayant accès au module de bootstrap après son chargement à une adresse mémoire donnée. Cela permet de vérifier notamment, qu'aucune tache n'est en train de pointer sur le module lors de sa suppression. - 6 Envoi du signal STOP aux tâches partie de la session. Sur réception du signal STOP, les traitements suivants sont activés: - 7 Résolution de la relocation des données internes aux module bootstrap, grâce à l'adresse de chargement du segment de données passée en paramètre à l'émission du signal. Il s'agit ici d'un processus extrèmement simplifié de la phase de relocation des symboles de données. Cela rend le fonctionnement du module de chargement indépendant de sa position dans l'espace mémoire du processus. - 8 La fonction principale du module de bootstrap est de permettre le chargement dans l'espace mémoire des taches de la session de la bibliothèque fonctionnelle complexe (checkpoint/restart, mesure de performances, ou autres). - 9 Execution de la bibliothèque fonctionnelle Suppression du code en 2 temps, avec vérification de non dépendence des taches sur le code supprimé: d'abord la bibliothèque fonctionnelle, puis le module de bootstrap lui-même. Pour la réplication ou la restauration d'une application de reprise, à partir d'une application maître, dans un autre ordinateur ou dans un environnement différent de celui de l'application maître, il existe certaines techniques de virtualisation des ressources et de l'environnement d'origine permettant à l'application de reprise de travailler comme si elle était encore dans son environnement d'origine. - 10 - La suite de cette description a pour but de détailler et de perfectionner la description des procédés de virtualisation et de réplication d'application dans le brevet Réplication Holistique . Un autre objectif est de permettre la virtualisation de ressources dépendantes de la localisation, par insertion de fonctions dans l'implémentation noyau de certains appels systèmes. Ces aménagements consistent d'une part dans l'ajout d'un champ particulier dans la structure de données décrivant une tache au niveau du système d'exploitation (structure task_struct dans linux), ainsi que dans l'insertion de hooks qui seront invoqués lors de l'exécution en contexte noyau de certains appels systèmes stratégiques comme fork , exec , getpid , etc. Ces hooks peuvent être insérés dans ces appels, ou dans une routine de gestion des identifiants (PID ou TID) qui est elle- même appellé par ces appels systèmes. Ces hooks peuvent aussi être inisérés dans toute autre routine utilisée par le système pour gérer une ressource correspondant à la tâche cible, même à des moments où celle-ci ne comporte pas d'appel système. Ces hooks sont des points de branchement conditionnels insérés au coeur de routines stratégiques manipulant des ressources à virtualiser (tels que création, suppression et identification de processus-id). Ces routines sont elles-même appelée par plusieurs appels systèmes. l'ajout d'un hook dans ces routines permet de réaliser la virtualisation des pid de manière simultanée pour tous les appels systèmes utilisant ces routines. La présence à un niveau de granularité plus fin que l'appel système permet aussi d'étendre la virtualisation pour des opérations qui ne mettent pas en jeu un appel système, comme le déclenchement d'une exception par le noyau, notifiée au processus sous forme de signal. Le hook lui-même est inséré de manière permanente dans le code du noyau. Il permet le chargement dynamique et reversible de services pouvant être combinés entre eux (par exemple un service de virtualisation et un service de trace). L'intérêt du nouveau champ de task_struct est de pouvoir définir, et à tout instant identifier, un ensemble de taches (processus ID ou thread ID) , que nous regroupons sur le concept de session , et sur lesquelles il 11 - faut agir de manière consistente et simultanée afin de maintenir la cohérence du système. Par exemple, plusieurs threads partagent le même espace mémoire adressable. Le chargement d'un module dans l'une affecte donc les autres. De même, des ressources telles que les descripteurs de fichiers sont héritées lors de la création de nouvelles taches, et font apparaitre des relations d'interdépendence entres les taches d'une même filiation. La définition d'une session consiste à affecter un identifiant unique de session à la tache initiale d'une application cible (par exemple, le shell script de lancement). Ce numéro de session sera propagé, par héritage, et grâce aux hooks, à toutes les taches affiliées à cette tache initiale, ou pour laquelle une nouvelle relation d'interdépendence apparait (ex: transmission d'un descripteur de fichier entre 2 processus non affiliés). Si on considère les process-id (PID), chaque tache possède une double identitée: son PID global au niveau système, et son PID virtuel, alloué par le mécanisme de virtualisation. Une tache faisant partie de l'espace global n'est pas virtualisée. Elle ne peut intéragir avec une tache que par ses identifiants globaux, et non virtuels.Cela constitue une limitation, et empêche l'emploi de programmes de monitoring ou de debugging. Nous introduisons donc la notion de processus spectateur d'une session: le processus a accès aux fonctions de virtualisation d'une session, mais ne fait pas partie de la session en cas de checkpoint. Les canaux de communications établis avec les taches de la session sont éphémères. Les ressources relatives aux taches spectatrices sont ignorées lors du checkpoint et du redémarrage. Il est introduit à cet effet un flag spectateur à la structure task_struct décrivant la tache au niveau système. Alors que toutes les taches appartenant normalement à une session sont démarrées par une tache faisant partie de la hiérarchie des taches de la session, une tache spectatrice provient de l'espace global et est insérée dans la session lors de son démarrage. Un exemple d'utilisation de tache spectatrice est le lancement d'un débuggueur s'attachant à chaud sur un programme multi-thread virtualisé. - 12 - Le débuggueur doit retrouver la liste de toutes les threads auxquelles s'attacher à partir du PID de la thread cible, ce qui ne sera possible que si le debuggueur est lui-même virtualisé. Un autre exemple est un programme de supervision qui doit interagir avec une application virtualisée par le biais de signaux. Le programme de supervision surveillant les ressources systèmes locales n'a pas de raison d'être migré, mais il a besoin de la virtualisation pour interagir de manière consistente avec l'application. Cette section fournit une description détaillée du fonctionnement de la barrière de synchronisation des processus, permettant la réalisation du checkpoint holistique. Difficultés: - garantir la possibilité de bloquer l'ensemble des processus faisant partie de l'application à checkpointer, ce quelque soit la charge de la machine ou le degré de priorité de ces processus. - Obtenir de manière fiable la liste complète des processus et threads à bloquer, sachant que cette liste peut être en constante modification, du à l'apparition ou disparition soudaine de processus en dehors du controle du système de checkpoint. - Minimiser le temps de blocage, ainsi que le surcoût induit en performances, notamment par l'ajout de basculement de contextes. Pour garantir l'identification complète des taches (processus et threads) faisant partie d'une session, on procède par propagation d'un marqueur de session dans le descripteur de tache (structure interne du noyau décrivant une tache). Cette propagation est activée par déclenchement d'un module spécifique chaque fois que la routine d'allocation ou de libération d'un identifiant unique de processus (PID), de thread (TID), ou de groupe (GID) est invoquée. Ces routines ont été instrumentées par hooks, tels que décrits précedemment. Le module de virtualisation, lorsqu'invoqué par le hook, examine si le processus père fait lui même partie d'une session. Si c'est le cas, il propage le numéro de session dans le contexte du processus courant. Une référence croisée est établie dans une table du module de synchronisation. Cela permet de garantir l'identification complète à la volée des taches faisant partie de l'application. La synchronisation elle-même est réalisée par le module de synchronisation en mode noyau, ce qui garantit la capacité d'interrompre les taches en cours d'exécution quelque soit leur état, ainsi que l'atomicité de l'opération. Le blocage des processus est effectué par envoi du signal STOP à chacune des taches, de manière atomique. L'atomicité est garantie par la non préemptivité du traitement dans le noyau. La liste des taches à stopper est obtenue à partir du numéro de session. Le handler de signal, installé de même par le module de synchronisation dans le contexte utilisateur des processus, permet de réaliser la phase finale de rendez-vous, par attente d'un évènement du module de synchronisation, qui sera émis lorsque le signal aura été délivré à la totalité des taches. On peut noter aussi que pendant cette phase de synchronisation, la possibilité de modifier la liste des taches, par ajout ou suppression de processus, est désactivée au niveau des hooks, par test d'une variable positionnée par le module de synchronisation. Afin de permettre la prise en compte des signaux émis et utilisés par les taches virtualisées, d'effectuer la capture de leur état ainsi que leur restauration, les opérations suivantes doivent être faites: -Virtualiser le destinataire du signal. - Virtualiser l'émetteur du signal. Problème, l'information n'est pas un paramètre ou une valeur de retour d'appel système, mais un champ d'une structure interne au noyau transmise au processus au moment du traitement du signal. - Virtualiser des informations liées au signal. Là encore, il s'agit de champs dans des structures internes du noyau, sur lesquels l'utilisateur n'a pas de controle. - Au moment du checkpoint: capturer l'état des signaux émis à destination du processus courant, et non encore consommés, de manière non destructive:permettre au processus de consommer les signaux de manière après le checkpoint. - A la restauration: génerer les données internes relatives au signal. - 14 - Le mécanisme de virtualisation des PIDs par les hooks est à un niveau suffisamment bas pour permettre la prise en compte des identifiants de processus manipulés par le traitement des signaux. Un service noyau supplémentaire est introduit, permettant la génération de signal avec control fin par l'utilisateur des champs normalement restreints à l'usage interne du noyau. Alors que la seule information accessible à l'utilisateur pour l'émission d'un signal est le numéro de celui-ci, le service de génération permet de spécifier chacun des attributs définis par la structure SIG_INFO . Ce service est mis en oeuvre d'une part au checkpoint, pour restaurer avant reprise les signaux reçus par la tache mais non encore consommés (dans l'hypothèse ou la tache continue ses traitements après le checkpoint), et d'autre part lors du redémarrage à partir d'un checkpoint, pour regénérer ces même signaux. Sous Linux, et d'autres systèmes Unix, /proc est un pseudo système de fichier utilisé comme interface d'accès à des données internes du système, plutot que d'avoir à accéder directement à l'image mémoire du noyau (/dev/kmem). Le premier niveau de répertoire sous /proc est constitué de tous les identifiants de process visibles du système. Ces identifiants doivent être virtualisés lorsqu'utilisés par une application elle- même virtualisée. Le mécanisme de virtualisation réside dans la routine de résolution des nuoms de fichier (name lookup) en objet fichier (inode), dont l'instance est spécifique au pseudo système de fichier. Lorsqu'une opération de lookup est effectuée (c'est à dire pour toute opération de manipulation d'un fichier par son nom), et que le nom canonique réside sous /proc, alors les opérations de translation entre PID virtuel et systéme global (et inverse) sont activées pour les taches faisant partie d'une session, soit en tant que tache active, ou bien en tant que tache spectatrice. Les descripteurs de fichiers sont des objets du noyau qui offrent un niveau d'abstraction générique de ressources système, afin de permettre leur manipulation par les processus. Un descripteur de fichier permet à un processus d'effectuer de manière uniforme des opérations d'entréessorties (lecture-écriture) vers des fichiers de stockage, mais aussi avec des - 15 - gestionnaires de périphériques divers, des connections réseaux, ou des mécanismes de communication inter-processus (pipes, sockets Unix, streams, ...). Quelque soit le type de ressource système, les descripteurs de fichiers ont des propriétés génériques: - Plusieurs descripteurs peuvent être ouverts sur une même ressource physique (lorsque celle-ci le permet: oui pour un fichier, non pour une connection réseau). Le changement d'un attribut du descripteur n'affecte pas les autres pointant sur la même ressource. - Un descripteur de fichier ouvert peut être "cloné" (appel système dup ) . En résultat, on obtient plusieurs instances d'un même descripteur, chacune avec sa propre identité (numéro de descripteur). Le changement de certains attributs d'un descripteur (ex: pointeur de lecture-écriture dans un fichier), est répercuté tous dans ses clones. - Les descripteurs de fichiers ouverts par un processus père sont hérités (par clonage) par un processus fils lors de la création de celui-ci (appel système fork ). Le père peut aussi spécifier par avance la liste des descripteurs à fermer lorsque le fils exécute un nouveau programme (appel système execve ). - Un descripteur de fichier peut-être transmis par un processus vers un autre, sans qu'il y ait de lien hiérarchique entre ces processus. Or, il s'avère que le système d'exploitation n'offre pas, à postériori, de moyen de déterminer pour les descripteurs de fichiers référencés par un ensemble de processus, lesquels ont été hérités ou clonés, et lesquels ont été ouverts séparément. Nous introduisons donc un mécanisme permettant: - D'identifier les attributs internes aux descripteurs de fichier rendus opaques par le système d'exploitation. - De capturer l'état de ces attributs dans le checkpoint. - De restaurer l'état de ces attributs au redémarrage de l'application. La figure 4 présente l'architecture de traitement des descripteurs de fichier au moment du checkpoint et du restart. Au checkpoint, les opérations suivantes sont réalisées: - 16 - -le processus controleur de checkpoint, appelé mcr duplique un processus appelé fd_helper , dédié à la prise en compte des descripteurs de fichiers. - les descripteurs de fichiers sont transmis par send_msg par le code embarqué dans les processus au fd_helper, de manière à fournir un accès au contexte sans fermeture de la ressource. Le système décrit ici à pour principal bénéfice une grande portabilité, malgré la dépendence sur des données internes au système d'exploitation. Ce traitement s'applique aux méthodes de communication inter- processus suivantes: - les pipes (ou fifo ) - les sockets (notamment socketpairs ). - les messages queues - les streams Comme pour les signaux pending (reçus mais non consommés), des données de canaux de communication interprocessus peuvent résider dans les tampons mémoires (buffers) de réception lors du checkpoint. Le mécanisme de checkpoint de ces données consiste alors à consommer ces données pendant la phase de checkpoint, de manière à les sauver, puis à réinjecter ces données juste avant le redémarrage, soit parce que la tache reprend ses traitements après le checkpoint, soit parce qu'il s'agit d'un redémarrage de la tache d'après un checkpoint. La sauvegarde des données est effectuée par le processus du coté recepteur. Si il y a plusieurs processus en réception sur un même descripteur de fichier, seul un processus effectue la sauvegarde (puis la réinjection), grâce au mécanisme décrit auparavant de fd helper. Il se peut aussi qu'au moment du checkpoint, le canal de communication inter-processus soit dans un état transitoire. Par exemple, le process émetteur peut avoir déjà terminé et avoir disparu alors que le récepteur n'a pas encore consommé toutes les données. Après la sauvegarde des données par le récepteur, celui-ci doit restaurer l'état du canal, et pour cela recréer et simuler la mort de l'émetteur, ce qui est fait à l'aide du fd helper. - 17 - Dans le cas ou il s'agit du récepteur qui a disparu avant la consommation des données du canal, il n'est pas nécessaire d'effectuer la sauvegarde et la restauration des données si le canal n'est plus accessible par une nouvelle tache (cas des socketpairs, ou des pipes nonnommés). Si par contre le canal est accessible en réception par une tache future (cas des pipes ou sockets nommés, la tache émettrice doit alors effectuer la sauvegarde et la restauration de ces données. La distinction entre ces différents cas de figure est effectuée grâce au fd helper, qui permet d'établir de graphe des dépendences de chacun des canaux de communication inter-processus. Le système de communication inter-processus à base de sockets utilise un mécanisme d'établissement de connection de type bind, listen, accept qui met en oeuvre des ressources globales, ce qui empêche une application, virtualisée ou non, d'utiliser une addresse (ou nom de pipe) si celle-ci est déjà utilisée. Pour pallier cette limitation, les ressources globales liées aux communications inter-processus, telles que les addresses de socket du domaine Unix, ou les noms de fichier de type pipe, ou bien encore les addresses de socket sur une interface de loopback, sont virtualisées afin de permettre une isolation des espaces de nommage par session. La séparation de l'espace de nommage sur les noms de pipe ou addresses de sockets UNIX se fait par adjonction d'une racine contenant le numéro unique de session, et préfixant de manière transparente le nom affecté par l'application. Par exemple, un pipe nommé par l'application /tmp/pipel sera nommé au niveau du système /proc/mcr/123/paths/tmp/pipel (123 étant le numéro unique de session). Bien sûr, l'invention n'est pas limitée aux exemples qui viennent d'être décrits et de nombreux aménagements peuvent être apportés à ces exemples sans sortir du cadre de l'invention. - 18 - REVENDICATIONS 1. Procédé d'insertion dynamique d'une ou plusieurs instructions de programme constituant un traitement inséré, par une première tâche informatique, dite tâche gestionnaire, dans une deuxième tâche informatique, dite tâche cible, utilisant une mémoire de travail pour son exécution dans un système informatique géré par un logiciel système, ce procédé comprenant les étapes suivantes - une mémorisation, au sein d'une zone de mémoire externe à la mémoire de travail de la tâche cible, d'au moins une donnée d'accrochage représentant des informations de déclenchement du traitement à insérer; - une exécution, au sein du déroulement d'une routine système appelée ou contrôlée par la tâche cible, d'une instruction dite d'accrochage déclenchant un test de la donnée d'accrochage; exécution du traitement inséré, en fonction du résultat dudit test et en lieu et place de l'instruction de programme suivant l'instruction d'accrochage dans le déroulement de ladite routine système. 2. Procédé selon la revendication précédente, caractérisé en ce que, à l'issue de l'exécution du traitement inséré, l'exécution de la routine système reprend là où elle s'était interrompue lors de l'exécution du traitement inséré. 3. Procédé selon l'une des revendications précédentes, caractérisé en ce que la tâche cible est liée au moins une autre tâche par le partage d'un même espace mémoire d'exécution, le procédé comprenant en outre un blocage temporaire de la tâche liée pendant au moins une opération comprenant le chargement, dans ledit espace mémoire d'exécution, des instructions du traitement à insérer. - 19 - 4. Procédé selon l'une des revendications précédentes, caractérisé en ce que le traitement inséré comprend une gestion d'au moins une donnée identifiant la tâche cible. 5. Procédé selon l'une des revendications 1 à 3, caractérisé en ce que le traitement inséré réalise ou déclenche au moins une opération de gestion de fonctionnement d'une application dite application gérée. 6. Procédé selon la revendication 5, caractérisé en ce que cette opération de gestion de fonctionnement comprend au moins une opération de suivi ou d'enregistrement de l'exécution de la tâche cible, ou de capture de l'état de ladite tâche cible ou d'au moins une ressource qu'elle utilise. 7. Procédé selon la revendication 5, caractérisé en ce que cette opération de gestion de fonctionnement comprend au moins une opération de mémorisation d'une donnée associée à la tâche cible et représentant une barrière de synchronisation avec au moins une autre tâche. 8. Procédé selon la revendication 5, caractérisé en ce que cette opération de gestion de fonctionnement comprend au moins une opération de réplication de la tâche cible dans un autre système informatique, ou de libération dans son système d'origine de ladite tâche cible ou d'au moins une ressource qu'elle utilise ses ressources, après une telle réplication. 9. Procédé selon l'une des revendications précédentes, caractérisé en ce que le traitement inséré réalise un chargement dynamique d'un élément d'une bibliothèque logicielle, ladite bibliothèque logicielle étant accessible ou modifiable au cours de l'exécution de la tâche cible ou d'une application à laquelle elle appartient. 10. Procédé selon l'une des revendications 1 à 5, caractérisé en ce que le traitement inséré comprend une mémorisation, de façon associée à la tâche cible, d'une donnée constituant un identifiant virtuel commun entre la tâche - 20 - cible et au moins une autre tâche en cours d'exécution, constituant ainsi un identifiant d'un conteneur virtuel englobant lesdites tâches. 11. Procédé selon la revendication précédente, caractérisé en ce que l'identifiant de conteneur virtuel est mémorisé de façon associée à une première tâche, par un traitement inséré exécuté lors d'une routine système appelée par ladite première tâche, cet identifiant de conteneur étant propagé à au moins une deuxième tâche par héritage à partir de la première tâche. 12. Procédé selon l'une des revendications 10 ou 11, caractérisé en ce qu'au moins une troisième tâche est crée par une opération comprenant une instruction d'accrochage utilisée par une tâche gestionnaire pour mémoriser avec ladite troisième tâche un identifiant de conteneur correspondant à conteneur virtuel existant et devant contenir ladite troisième tâche. 13. Procédé selon l'une des revendications 10 à 12, caractérisé en ce qu'au moins une opération logicielle est appliquée à l'ensemble des tâches 20 appartenant à un même conteneur virtuel. 14. Procédé selon l'une des revendications 9 à 12, caractérisé en ce que le conteneur virtuel regroupe un ensemble de tâches exécutées au sein d'une même application dite application cible. 15. Procédé selon l'une des revendications 10 à 14, caractérisé en ce qu'au moins une tâche exécutée dans le système informatique subit une opération de gestion de fonctionnement dépendant de son appartenance au conteneur virtuel. 16. Procédé selon la revendication 15, caractérisé en ce que la gestion de fonctionnement des tâches du conteneur comprend un traitement de suivi ou de contrôle de l'exécution ou de capture d'état d'au moins une tâche informatique ou d'au moins une ressource qu'elle utilise. - 21 - 17. Procédé selon la revendication 15, caractérisé en ce que la gestion de fonctionnement des tâches du conteneur comprend un traitement de réplication dans une autre système informatique d'au moins une tâche 5 informatique, ou de libération de cette tâche ou d'au moins une ressource qu'elle utilise, après une telle réplication.