Ce didacticiel est destiné aux nouveaux utilisateurs de la Simple Virtual Machine.
Dans ce didacticiel, vous allez améliorer les capacités du code à accéder à la mémoire de la machine virtuelle.
Le temps de lecture de ce didacticiel est estimé à 30 minutes si l'écriture d'une application simple a déjà été étudiée ainsi que les manipulations basiques de la mémoire.
Pour commencer, créez le canevas de l'application dans le fichier exécutable pointeurs.svm en utilisant ce code :
#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
CODE "main" INLINE
:debug BREAK
END
MEMORY ".*"
END
Lancez l'application en mode débugueur :
./pointeurs.svm -d 8080 -i 3 -i 1 -i 4 -i 1 -i 5 -s a -s b -s c
Dans le débugueur, en utilisant la fenêtre des noyaux (Kernels), sélectionnez le noyau puis affichez la fenêtre de la mémoire :
Les valeurs créées à partir des options de la ligne de commande sont placées à différentes adresses mémoire : à chaque valeur est associé un nombre qui sert d'identifiant unique pour accéder à la valeur depuis les instructions exécutées.
Par exemple, à l'adresse &6
se trouve la valeur de type chaine de caractères "b"
. Vous pouvez entrer le numéro de l'adresse que vous voulez mettre en surbrillance dans la petite zone de texte en bas de la fenêtre de mémoire du débugueur, et cliquer sur le bouton "Display".
Dans une application, il peut être utile de pouvoir indiquer où se situent dans la mémoire un certain nombre de valeurs ayant des adresses contigües.
Pour cela, il faut un objet qui contienne une adresse de départ et le nombre d'adresses après cette première adresse : un tel objet existe et s'appelle un pointeur.
Dans le débugueur de l'application exemple, en dessous des adresses et leurs valeurs associées se trouvent un petit tableau dont la deuxième colonne contient des pointeurs. Vous pouvez cliquer sur le bouton ">" à droite de chaque pointeur pour afficher les adresses indiquées par chaque pointeur.
Un pointeur permet de référencer une zone mémoire continue, à partir d'une adresse de départ et sur un certain nombre d'adresses.
Les pointeurs sont notés par une adresse et un entier séparés par le symbole *
.
Ce système pourtant très rustique d'adresses et de pointeurs possède une vraie puissance pour accéder aux différentes valeurs de la mémoire, au travers d'opérations spécifiques.
La première possibilité est de construire un pointeur à partir de son adresse de départ et de sa taille. Modifiez le code pour obtenir (la signification de la première instruction sera donnée plus bas dans ce didacticiel) :
#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
CODE "main" INLINE
:memory PTR/p
&3*4 -> &p
:debug BREAK
END
MEMORY ".*"
END
Puis relancez l'application comme précédemment et ouvrez le débugueur sur la fenêtre mémoire :
Le pointeur commençant à l'adresse 3 sur 4 adresses a tout simplement été créé en écrivant dans le code &3*4
. Dans la fenêtre de la mémoire, vous pouvez cliquer sur le bouton ">" à droite de ce pointeur pour mettre en surbrillance la zone mémoire qu'il représente.
Dans une application, certaines zones mémoire sont privilégiées, et doivent pouvoir être accessibles facilement depuis n'importe quel endroit du code.
Pour cela, la machine virtuelle offre la possibilité de nommer certains pointeurs. Ces noms, définis de manière unique, portent le nom d'alias et peuvent être utilisés comme des pointeurs.
Par ailleurs, les valeurs associées aux options et arguments de la ligne de commande sont disponibles au travers d'alias définis automatiquement par la machine virtuelle au démarrage de l'application.
Modifiez le code de l'application pour obtenir :
#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
CODE "main" INLINE
:memory PTR/p
integers -> &p
:debug BREAK
END
MEMORY ".*"
END
Puis, encore une fois, relancez l'application avec les mêmes options dans le débugueur. Cette fois, la fenêtre mémoire montre :
Le pointeur placé à l'adresse &8
contient cette fois le pointeur qui se trouve à droite de l'alias "integers".
L'opération inverse peut être faite sur un pointeur : il peut être déconstruit pour récupérer son adresse de départ ou sa taille. Modifiez une nouvelle fois le code :
#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
CODE "main" INLINE
:debug EXPLAIN &integers*SIZE strings
END
MEMORY ".*"
END
Et une nouvelle fois, tel un running gag, relancez l'application avec les mêmes options dans le débugueur. En revanche, cette fois, ouvrez plutôt la liste des points d'arrêt :
Le pointeur calculé par l'expression du point d'arrêt est constitué de l'adresse de départ du pointeur de l'alias "integers" (obtenue avec l'opérateur &
) et de la taille du pointeur de l'alias "strings" (obtenue avec l'opérateur SIZE
).
Indiquer une zone mémoire grâce à un pointeur a l'avantage d'être plutôt compact pour désigner dans la mémoire plusieurs adresses mémoire. Cependant, lorsque le code de l'application doit accéder aux différentes valeurs du pointeur, il va falloir lui fournir les adresses contenues dans le pointeur.
Une nouvelle fois, suivez la procédure décrite précédemment avec le code :
#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
CODE "main" INLINE
:debug EXPLAIN (integers/0)
:debug EXPLAIN (integers/2)
:debug EXPLAIN (integers/5)
:debug EXPLAIN (integers/-1)
:debug EXPLAIN (strings/1)
END
MEMORY ".*"
END
Après avoir passé tous les points d'arrêt, la fenêtre des points d'arrêt indique :
Cette succession de calculs d'adresses à l'intérieur de pointeurs montre que :
Cette dernière propriété est très importante, car à long terme, elle assure de l'intégrité des accès en mémoire à travers les différents pointeurs manipulés par le code.
Notez enfin que cette notation utilise un caractère division (/) qui n'a pas la même signification que celui présent sur l'instruction :memory
et qui sera détaillé ultérieurement.
Il est possible d'écrire en mémoire plusieurs valeurs d'un seul coup en utilisant une autre forme de l'instruction ->
.
Modifiez le code de l'application pour obtenir :
#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
CODE "main" INLINE
:debug BREAK
[ 17 , 42 , , "A", "B" ] -> &2*5
END
MEMORY ".*"
END
Puis utilisez le débugueur pour voir l'action de cette instruction : toutes les valeurs écrites entre les crochets sont écrites d'un seul coup dans la mémoire à l'endroit indiqué par le pointeur, à condition que le nombre de valeur corresponde à la taille du pointeur et que les types correspondent avec ceux de la mémoire. En cas de tentative d'écriture mal formée, aucune valeur n'est écrite en mémoire et une erreur est générée.
Si une valeur est manquante, cela enlève la valeur précédemment écrite en mémoire.
*
et enfin sa taille.&
suivi du pointeur lui-même.SIZE
suivi du pointeur lui-même.(pointeur/indice)
où l'indice doit être un entier entre zéro et la taille du pointeur moins un. Cet opérateur permet d'utiliser les pointeurs comme des tableaux de valeurs.[ valeur 1 , ... , valeur N ] -> pointeur
, et permet d'enlever une valeur à une ou plusieurs adresses.Vous venez de voir comment accéder plus finement à la mémoire de la machine virtuelle.
La puissance du langage de la machine virtuelle repose en grande partie sur la capacité à accéder à des adresses grâce à une syntaxe compacte et versatile. En effet, les différents opérateurs présentés dans ce didacticiel peuvent être combinés pour atteindre des adresses après plusieurs indirections depuis un pointeur initial.