PROCEDE DE POSITIONNEMENT DE DONNEES ELEMENTAIRES D'UNE STRUCTURE DE DONNEES DANS UNE MEMOIRE
La présente invention porte sur les logiciels et environnements d'exécution embarqués dans un appareil électronique portatif doté d'un microcontrôleur réunissant un processeur et des mémoires, tel qu'une carte à puce par exemple.
Plus particulièrement, l'invention concerne un procédé de positionnement de données élémentaires d'une structure de données dans une mémoire.
On utilise en effet, pour organiser le contenu d'une mémoire, des structures de données représentant une organisation des données élémentaires d'un code dans une structure logique particulière. Ces structures de données sont alors prévues pour être allouées en mémoire, c'est-à-dire qu'une zone mémoire est réservée pour stocker cette structure de données, lors du chargement et de l'installation du code en mémoire par l'environnement d'exécution.
Le code logiciel en lui-même ne contient pas d' information sur la façon de placer les données élémentaires de la structure en mémoire (ordre, disposition) , mais fournit généralement des informations sur le type et la taille de ces données . Les données élémentaires sont typiquement codées soit sur 1 octet, soit sur 2 octets, soit sur des multiples 4 octets. Lors de l'affectation des données élémentaires d'une structure de données en mémoire, il est nécessaire de respecter les contraintes d'alignement pour la représentation des données en
mémoire, qui sont imposées par l'architecture matérielle du processeur. Ainsi, les données codées sur un octet peuvent être positionnées à n'importe quelle adresse mémoire, alors que les données codées sur deux octets ne peuvent être positionnées qu'à une adresse mémoire multiple de 2, et les données codées sur des multiples de quatre octets ne peuvent être positionnées qu'à une adresse mémoire multiple de 4.
Aujourd'hui, les architectures matérielles reposent principalement sur l'adressage de mémoire 32 bits, la taille des mots mémoire étant alors limitée à 4 octets. La description qui suit sera faite en référence à cette taille de mot mémoire bien que l'invention ne se limite pas à de telles structures de mémoire.
Plusieurs approches classiques peuvent alors être envisagées pour calculer le positionnement de données élémentaires d'une structure de données en mémoire. Les exemples ci-après sont basés sur une structure de données de type classe et vont ainsi décrire le chargement d'une classe par une machine virtuelle embarquée, qui constitue l'interface permettant de traduire en instructions exécutables par le processeur de l'appareil hôte, les langages intermédiaires orientés objet tels que le pseudo-code Java (désigné par bytecode en langue anglaise) , obtenu après compilation du langage source Java.
Pour rappel, un logiciel Java est classiquement diffusé sous forme d'un ensemble de modules constitué de fichiers .class. Ces fichiers correspondent donc à une forme compilée du logiciel. Chaque fichier compilé
correspond à une structure de données de type classe et comprend en tant que telle la description des éléments de la classe, notamment ses champs, qui constituent dans ce contexte les données élémentaires à placer en mémoire pour l'exécution du pseudo-code Java.
Le chargeur de la machine virtuelle est alors prévu pour charger et transformer les informations du fichier .Class en des structures de données en mémoire de travail, qui sont propres à la machine virtuelle et qui permettent à cette machine virtuelle d'exécuter le logiciel.
Pour illustrer le processus de positionnement des champs d'une classe lors de son chargement dans une structure de mémoire selon l'état de la technique, soit le morceau de pseudo-code suivant, décrivant une classe comportant trois champs de tailles différentes.
Class C { //la classe est nommée ΛC
Byte bl ; //un champ nommé Λbl' de taille 1 octet Int il ; // un champ nommé Λil/ de taille 4 octets
Short si ; // un champ nommé Λsl' de taille 2 octets
} La taille optimale des instances de la classe est égale à la somme de la taille de chacun de ses champs, soit 7 octets pour les instances de la classe ΛC .
La figure 1 illustre une zone 10 dans une structure de mémoire 32 bits, telle qu'elle se présente à l'issue d'un processus de stockage des champs du pseudo-code considéré, où chaque champ est placé à la
suite de l'autre, en tenant compte des contraintes d'alignement décrites plus haut. Ainsi, pour la classe ΛC considérée, le champ bl, codé sur 1 octet, est positionné à l'adresse mémoire 0, le champ il, codé sur 4 octets, est positionné nécessairement à l'adresse mémoire 4 du fait des contraintes d'alignement du processeur, et le champ si, codé sur 2 octets, est alors positionné à l'adresse mémoire 8.
Chaque champ de la classe occupe donc la même place en mémoire. On s'aperçoit que cette solution n'est pas du tout optimale en ce qui concerne la taille totale d'une instance de cette classe, qui dans cet exemple occupe 12 octets, alors que 5 octets restent inoccupés (octets apparaissant en blanc sur la figure) , correspondant à autant de place perdue en mémoire.
Une solution permettant d'optimiser l'occupation de la mémoire lors du positionnement des champs d'une structure de données type classe a alors été développée et est présentée à la figure 2. Cette solution consiste à établir une carte de la mémoire 20, permettant de mémoriser pour chaque adresse de la mémoire 10, si elle est occupée ou non. Au départ, tous les bits de la carte dont initialisés à 0. Selon cette méthode, le premier champ bl, codé sur 1 octet, est positionné à l'adresse mémoire 0 et le bit correspondant de la carte mémoire pour cet octet est alors placé à 1, indiquant que cet octet est désormais occupé.
Pour positionner le champ suivant il, codé sur 4 octets, on va parcourir la carte mémoire depuis son début jusqu'à trouver le prochain emplacement bien aligné (c'est-à-dire à une adresse multiple de 4), de
taille 4 octets, qui soit disponible. En conséquence, le champ il est positionné à l'adresse mémoire de départ 4 et la carte mémoire est mise à jour. Pour ce faire, les bits correspondant de la carte mémoire pour les octets des adresses mémoires 4 à 7 sont placés à 1, indiquant que ces octets sont désormais occupés.
Puis, pour positionner le champ suivant si, codé sur 2 octets, on va à nouveau parcourir la carte mémoire depuis son début jusqu'à trouver le prochain emplacement bien aligné (c'est-à-dire à une adresse multiple de 2), de taille 2 octets, qui soit disponible. Cet emplacement correspond aux adresses mémoires 2 et 3. Le champ si est alors positionné à l'adresse mémoire 2 et la carte mémoire est mise à jour. Pour ce faire, les bits correspondant de la carte mémoire pour les octets des adresses mémoires 4 à 7 sont placés à 1, indiquant que ces octets sont désormais occupés. La carte mémoire permet ainsi de fournir lors du processus de positionnement des champs, une description de la structure de données en mémoire.
On obtient alors un positionnement optimal des champs de la classe dans la zone mémoire qui lui est affectée. En effet, dans cet exemple les instances de la classe ΛC auraient une taille de 8 octets, et on a uniquement un octet de perdu au niveau de l'occupation de la mémoire pour une instance de cette classe.
Cette solution présente néanmoins deux inconvénients majeurs. Tout d'abord, elle est consommatrice en mémoire. En effet, elle nécessite, lors du calcul du placement des champs, de stocker la carte mémoire, cette dernière occupant une quantité de
mémoire non négligeable, puisque proportionnelle à la taille de la structure de mémoire dont elle reproduit la cartographie. Cette méthode pourrait donc s'avérer inadaptée à une implémentation dans des environnements contraints en mémoire, tels que les cartes à puce par exemple, où une utilisation optimale de la ressource mémoire est une exigence forte.
En outre, dans ce même contexte, pour pouvoir assurer la notion d'héritage de classe, la cartographie de la mémoire doit être maintenue en mémoire. En effet, lorsque la machine virtuelle reçoit une classe fille d'une classe mère déjà traitée, elle doit tenir compte, pour le calcul du positionnement des champs de la classe fille, de la position des champs de la classe mère : ils existent aussi dans la classe fille et sont positionnés au même endroit.
Un autre inconvénient de cette solution concerne l'inefficacité en termes de temps d'exécution du calcul de positionnement des champs. En effet, cette méthode impose de parcourir la carte mémoire depuis son début autant de fois qu'il y a de champs à placer dans la structure mémoire. Ce processus peut devenir fortement pénalisant en présence d'un grand nombre de champs à placer, notamment dans les environnements embarquées, disposant généralement de faibles ressources de calcul.
L'invention vise à résoudre un ou plusieurs de ces inconvénients, en proposant un procédé permettant d'obtenir un positionnement optimal en mémoire de données élémentaires d'une structure de données, tout en étant efficace en termes de consommation de mémoire et de temps de calcul.
L'invention a ainsi pour objet un procédé de positionnement de données élémentaires d'une structure de données dans une mémoire organisée en mots d'au moins quatre octets, comprenant, suivant la taille de la prochaine donnée à positionner, la détermination de la position à affecter en mémoire à ladite donnée élémentaire en fonction d'une description de ladite structure en mémoire, et la mise à jour de ladite description de ladite structure en mémoire, caractérisé en ce que ladite description est basée sur au moins trois indices indiquant respectivement la prochaine position en mémoire disponible pour placer une donnée de taille un octet, la prochaine position en mémoire disponible pour placer une donnée de taille deux octets et la prochaine position en mémoire disponible pour placer une donnée de taille au moins trois octets.
Selon un mode de réalisation, la détermination de la position en mémoire de la donnée élémentaire à positionner en fonction de la description de la structure de données en mémoire, consiste à affecter à ladite position la valeur courante de l'indice correspondant à la taille de la donnée considérée.
Selon un mode de réalisation, la mise à jour de la description de la structure de données en mémoire consiste à affecter de nouvelles valeurs aux dits indices en fonction de leurs positions relatives, de sorte que lesdites valeurs mises à jour décrivent respectivement la nouvelle position en mémoire la plus petite disponible où une prochaine donnée élémentaire de taille un octet, deux octets, ou au moins trois octets pourra être positionnée.
Le procédé selon l'invention s'applique avantageusement au positionnement en mémoire des champs d'une classe d'un code logiciel compilé en langage intermédiaire orienté objet, la classe pouvant être une classe héritant d'une autre classe.
Le procédé selon l'invention s'applique aussi avantageusement au positionnement en mémoire dans une pile d'exécution, des variables d'un ensemble de variables locales d'une fonction.
L'invention concerne également un chargeur d'un code logiciel compilé en langage intermédiaire orienté objet destiné à être chargé dans un appareil électronique portatif, caractérisé en ce qu'il est susceptible d'être stocké dans une mémoire non volatile de l'appareil pour la mise en œuvre du procédé selon 1' invention.
L'invention concerne encore un appareil électronique portatif comprenant une mémoire non volatile mémorisant le chargeur tel qu'il vient d'être décrit.
De préférence, cet appareil est une carte à puce.
D'autres caractéristiques et avantages de la présente invention apparaîtront plus clairement à la lecture de la description suivante donnée à titre d'exemple illustratif et non limitatif et faite en référence aux figures annexées dans lesquelles :
- la figure 1 est une représentation schématique d'une zone mémoire dans une structure mémoire 32 bits, dans laquelle ont été positionnés les champs d'une classe selon un procédé de l'art antérieur ;
- la figure 2 est une représentation schématique de ladite zone mémoire, dans laquelle ont été positionnés les champs d'une classe selon un procédé optimisée de l'art antérieur ; - la figure 3 est un organigramme illustrant la mise en oeuvre du procédé selon la présente invention.
Selon l'invention, le procédé de positionnement de données élémentaires d'une structure de données en mémoire est basé sur une description particulière de la structure en mémoire. Cette description repose sur le calcul et la mise à jour de trois indices, vl, v2 et v4, en lieu et place du maintient en mémoire de la carte de la mémoire de la solution de l'art antérieur décrite plus haut. Les indices vl, v2 et v4 sont prévus pour décrire respectivement la prochaine position ou adresse mémoire disponible (et donc bien alignée) où une donnée codée sur 1 octet pourra être placée, la prochaine adresse mémoire disponible où une donnée codée sur 2 octets pourra être placée et la prochaine adresse mémoire disponible où une donnée codée sur 3 octets, 4 octets ou plus (dans ce cas, on arrondira la taille de la donnée au multiple de 4 supérieur) pourra être placée. Du fait des contraintes d'alignement, des données codées sur 3, 4, 5 octets et plus ne peuvent être alignées que sur des multiples de 4 en adresse mémoire.
Les trois indices vl, v2 et v4 nécessaires à la mise en œuvre du procédé de positionnement selon l'invention, peuvent par exemple être codés chacun sur 2 ou 4 octets. Ils occupent alors au maximum 6 ou 12 octets en mémoire. L'utilisation de tels indices
présente donc l'avantage de ne consommer qu'une quantité fixe de mémoire, indépendante de la taille de la structure de mémoire à décrire.
L'organigramme de la figure 3 illustre la mise en œuvre du procédé de l'invention basé sur l'utilisation des trois indices vl, v2 et v4.
Soient des données élémentaires d'une structure de données de tailles 1, 2 et multiples de 4 octets à positionner en mémoire. Les trois indices vl, v2 et v4 sont initialisés à zéro au début du processus et on donne en entrée la taille de la prochaine donnée à positionner en mémoire.
Plus précisément, on initialise les indices à zéro si on est en adressage relativement au début de la structure de données, sinon les indices sont initialisés avec l'adresse de début de structure dans la mémoire. L'initialisation des indices n'intervient que la première fois où l'on a à positionner une donnée élémentaire dans la structure. Par la suite, les indices conservent leurs valeurs courantes au fur et à mesure de l'évolution de la structure de données en mémoire.
En El, on regarde si la taille de la donnée est égale à 1. Dans cette hypothèse, en E2, la position (nommée « Res ») qui est allouée à cette donnée en mémoire est donnée par la valeur courante de l'indice vl . La position des données en mémoire est en effet déterminée par la valeur courante de l'indice correspondant à la taille de la donnée considérée. Les valeurs des trois indices doivent alors être mises à jour selon le processus suivant.
En E3, on regarde si vl est égal à v2. Si ce n'est pas le cas, on passe en E4 où la nouvelle valeur de l'indice vl mise à jour, nommée nvl, prend la valeur courante v2, la nouvelle valeur de l'indice v2 mise à jour, nommée nv2, reste égale à la valeur courante v2, et la nouvelle valeur de l'indice v4 mise à jour, nommée nv4, reste égale à la valeur courante v4.
Par contre, dans le cas où l'égalité testée en E3 est vérifiée, on passe en E5, où la valeur de l'indice vl est mise à jour. Cette nouvelle valeur nvl de l'indice vl mise à jour prend dans ce cas la valeur vl+1. Pour la mise à jour des valeurs des autres indices v2 et v4, on regarde alors en E6 si v2 est égal à v4. Si ce n'est pas le cas, on passe en E7, où la nouvelle valeur nv2 de l'indice v2 mise à jour prend la valeur courante v4, et la nouvelle valeur nv4 de l'indice v4 mise à jour, reste égale à la valeur courante v4. Par contre, dans le cas où l'égalité testée en E6 est vérifiée, on passe en E8, où dans ce cas, la nouvelle valeur nv4 de l'indice v4 mise à jour prend la valeur v4+4, et la nouvelle valeur nv2 de l'indice v2 mise à jour prend la valeur v2+2.
Les trois indices sont donc mis à jour de sorte qu'ils correspondent chacun à la nouvelle adresse mémoire la plus petite disponible où une prochaine donnée respectivement de taille 1, 2, ou multiple de 4 octets pourra être positionnée. Cette mise à jour des indices est avantageusement déduite d'une simple analyse de leurs positions relatives. De façon similaire à ce qui vient d'être décrit pour une donnée de taille 1, si, en E9, la prochaine
donnée à placer est de taille 2 octets, la position Res qui est allouée en ElO à cette donnée en mémoire est donnée, selon le principe de l'invention, par la valeur courante de l'indice v2. La position allouée à une donnée de taille 2 en mémoire est en effet déterminée par la valeur courante de l'indice correspondant à la taille de la donnée considérée, à savoir dans ce cas, v2.
Une fois le positionnement de la donnée considérée déterminée, les valeurs des trois indices vl, v2 et v4 doivent alors être mises à jour.
Pour ce faire, on regarde en ElI si v2 est égal à v4. Si ce n'est pas le cas, on passe en E12, où la nouvelle valeur nv2 de l'indice v2 mise à jour prend la valeur courante v4, et la nouvelle valeur nv4 de l'indice v4 mise à jour, reste égale à la valeur courante v4. Par contre, dans le cas où l'égalité testée en ElI est vérifiée, on passe en E13, où dans ce cas, la nouvelle valeur nv4 de l'indice v4 mise à jour prend la valeur v2+4, et la nouvelle valeur nv2 de l'indice v2 mise à jour prend la valeur v2+2.
Pour ce qui est de la mise à jour de l'indice vl, on passe en E14, où on vérifie l'égalité vl = v2. Si c'est le cas, en E15, la nouvelle valeur nvl de l'indice vl mise à jour prend la valeur nv2 de l'indice v2 qui vient d'être mis à jour, sinon, en E16, la nouvelle valeur nvl de l'indice vl mise à jour garde la valeur courante vl .
Enfin, si la prochaine donnée à placer a une taille de 3 octets ou plus, le placement Res qui est allouée en E17 à cette donnée en mémoire est
donnée, selon le principe de l'invention, par la valeur courante de l'indice v4. Il convient de noter que si la taille de la donnée considérée est supérieure à 4 octets, on arrondira sa taille pour les calculs de positionnement et de mise à jour des indices à la valeur multiple de 4 immédiatement supérieure ou égale
(ainsi, une donnée codée sur 3 octets verra sa taille arrondie à 4 octets, une donnée codée sur 4 octets verra sa taille demeurée à 4 octets, une donnée codée sur 5 ou 6 ou 7 ou 8 octets verra sa taille arrondie à 8 octets, etc. ) .
Ainsi, la nouvelle valeur nv4 est mise à jour en E18 et prend la valeur courante v4 à laquelle on ajoute la taille de la donnée considérée, arrondie à la valeur multiple de 4 immédiatement supérieure ou égale.
Pour ce qui est de la mise à jour des indices vl et v2, on passe en E19, où on vérifie l'égalité v4 = v2. Si les deux indices v4 et v2 ont des valeurs différentes, la mise à jour est effectuée de la façon suivante en E20 où la nouvelle valeur nv2 mise à jour demeure inchangée et garde la valeur courante v2, et la nouvelle valeur nvl mise à jour demeure inchangée et garde la valeur courante vl . Par contre, si l'égalité en E19 est vérifiée, la nouvelle valeur nv2 de l'indice v2 est mise à jour en E21 et prend la valeur nv4 qui vient d'être mis à jour. Puis, la mise à jour de l'indice vl consiste à vérifier en E22 l'égalité vl=v2. Si c'est le cas, en E23, la nouvelle valeur nvl de l'indice vl mise à jour prend la nouvelle valeur nv2 de l'indice v2, sinon, en E24, la nouvelle valeur nvl de l'indice vl mise à jour garde la valeur courante vl .
Ainsi, le procédé de l'invention a simplement besoin en entrée de la taille de la prochaine donnée élémentaire de la structure de données à placer dans la mémoire et de la description de cette structure en mémoire, qui est fournie par les trois indices vl, v2 et v4, indiquant respectivement la prochaine adresse mémoire disponible et bien alignée pour placer une donnée de taille 1, la prochaine adresse mémoire disponible et bien alignée pour placer une donnée de taille 2 et la prochaine adresse mémoire disponible et bien alignée pour placer une donnée de taille multiple de 4. On obtient alors en sortie la position en mémoire
(Res) qui doit être allouée à la donnée à placer et la description mise à jour de la structure en mémoire, qui est déduite d'une simple analyse des positions relatives des indices .
Le procédé de positionnement d'une donnée en mémoire basé sur les trois indices vl, v2 et v4, qui vient d'être décrit est plus particulièrement prévu pour répondre aux contraintes d'alignement inhérentes aux structures de mémoires de type 32 bits, où la taille des mots mémoire est donc de 4 octets . Toutefois, il pourrait être envisagé, sans pour autant sortir du cadre de l'invention, d'utiliser plus d'indices si les contraintes d'alignement matériel étaient différentes. Cela pourra être le cas par exemple avec une mémoire organisée en mots de 8 octets (64 bits) où les règles d'alignement imposent que les données de 1 octet ne peuvent être positionnées qu'à des adresses multiples de 1, les données de 2 octets ne peuvent être positionnées qu'à des adresses multiples
de 2, les données de 3 et 4 octets ne peuvent être positionnées qu'à des adresses multiples de 4 et les données de taille autre ne peuvent être positionnées qu'à des adresses multiples de 8. Dans cet exemple, il faudrait alors prévoir 4 indices pour pouvoir décrire complètement la structure en mémoire et modifier en conséquence les règles de mise à jour des indices dans l'algorithme décrit en figure 3.
De manière générale, le procédé de positionnement de données en mémoire selon l'invention est prévu pour s'appliquer à tous les environnements d'exécution où il faut organiser la mémoire et stocker des données élémentaires de tailles variables dans la mémoire de façon optimisée en termes d'occupation de la mémoire, en tenant compte des contraintes d'alignement matériel.
Ainsi, le procédé selon l'invention s'applique au calcul du positionnement des champs d'une structure de données en général et, par exemple, au calcul du positionnement des champs d'une classe d'un code logiciel compilé en langage intermédiaire orienté objet, lors du chargement de la classe en mémoire de travail par un chargeur d'une machine virtuelle embarquée. Le procédé de l'invention est de plus particulièrement avantageux dans le cas où la classe à charger est une classe qui hérite d'une autre classe. En effet, lorsque la machine virtuelle reçoit une classe fille d'une classe mère déjà traitée, elle doit tenir compte, pour le calcul du positionnement des champs de la classe fille, de la position des champs de la classe mère dont la classe fille hérite. A cet effet, les trois indices vl, v2 et v4 déjà utilisés
lors du calcul du positionnement des champs de la classe mère sont ajoutés à l'information de la classe, de sorte qu'ils peuvent être réutilisés pour calculer les positions des champs de la classe fille lors de son chargement, qui sont alors ajoutés aux champs de la classe mère déjà positionnés.
Le procédé de l'invention s'applique ainsi typiquement à la plate-forme Java Card (marque déposée) ou .Net (marque déposée) . Egalement, une autre application du procédé selon l'invention concerne le positionnement d'un ensemble de variables locales d'une fonction dans une pile d'exécution, cet ensemble de variables locales formant une structure de données. En effet, de la même manière que chaque champ se voit affecter une position dans une instance de classe en mémoire, les variables locales d'une fonction stockées dans la pile doivent se voir affecter une position par rapport à la position de la pile à l'entrée de la fonction.