Ce didacticiel est destiné aux utilisateurs maîtrisant la programmation en C ou C++, ainsi que l'architecture de la Simple Virtual Machine.
Dans ce didacticiel, vous allez manipuler la mémoire depuis une extension.
Le temps de lecture de ce didacticiel est estimé à 25 minutes.
Pour commencer, créez le canevas de l'extension dans le fichier memoire.svm_plugin en utilisant ce code :
PLUGIN ram
DEFINE
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.read INT:address -> VALUE
%{
auto a = ARGV_VALUE(0,integer);
return ::svm_memory_read_address(svm,CURRENT(kernel),a);
%}
INSTRUCTION ram.read_internal INT:address INT:type -> VALUE
%{
auto a = ARGV_VALUE(0,integer);
auto t = ARGV_VALUE(1,integer);
if((t<0) or (t>=PLUGIN_TYPE))
{
ERROR_INTERNAL(FAILURE,"Wrong type");
}
return ::svm_memory_read_address_type_internal(svm,CURRENT(kernel),a,static_cast<SVM_Type>(t));
%}
INSTRUCTION ram.read_external INT:address PEP:type -> VALUE
%{
auto a = ARGV_VALUE(0,integer);
SVM_Value_PluginEntryPoint t = ::svm_parameter_value_get(svm,argv[1]);
return ::svm_memory_read_address_type_external(svm,CURRENT(kernel),a,t);
%}
Générez et compilez l'extension. Puis écrivez une petite application de test qui utilise ces trois instructions.
Vous pouvez constater que la première instruction permet de réaliser une lecture sans test de type. Les deux autres instructions vérifient le type en même temps.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.write INT:address VALUE
%{
auto a = ARGV_VALUE(0,integer);
SVM_Value v = ::svm_parameter_value_get(svm,argv[1]);
::svm_memory_write_address(svm,CURRENT(kernel),a,v);
%}
Générez et compilez l'extension. Puis écrivez une petite application de test qui utilise cette instruction pour écrire une valeur en mémoire.
Ici, la valeur réellement écrite en mémoire n'est pas celle donnée en paramètre de la fonction svm_memory_write_address
, mais une copie de cette valeur.
La copie peut ne pas être voulue lors d'une écriture en mémoire. Soit parce que la valeur ne peut pas être copiée, soit parce que cette copie est inutile.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.write INT:address VALUE
%{
auto a = ARGV_VALUE(0,integer);
SVM_Value v = ::svm_parameter_value_get(svm,argv[1]);
SVM_String s = ::svm_value_print(svm,v);
SVM_Value_String vs = ::svm_value_string_new(svm,s);
::svm_value_state_set_movable(svm,vs);
::svm_memory_write_address(svm,CURRENT(kernel),a,vs);
%}
Générez et compilez l'extension. Puis écrivez une petite application de test qui utilise cette instruction. Ici, la représentation textuelle de la valeur est écrite en mémoire.
Cette représentation textuelle, créée dans le code de l'instruction, peut être déplacée en mémoire pour éviter une copie. La valeur est marquée comme déplaçable juste avant d'être écrite en mémoire.
Pour vous en convaincre, modifiez le code de l'extension :
PLUGIN ram
code:
%{
SVM_Value_String _s;
%}
DEFINE
INSTRUCTION ram.write INT:address VALUE
%{
auto a = ARGV_VALUE(0,integer);
SVM_Value v = ::svm_parameter_value_get(svm,argv[1]);
SVM_String s = ::svm_value_print(svm,v);
_s = ::svm_value_string_new(svm,s);
::svm_variable_scope_set_global(svm,_s);
::svm_value_state_set_movable(svm,_s);
::svm_memory_write_address(svm,CURRENT(kernel),a,_s);
%}
INSTRUCTION ram.test
%{
::svm_value_string_set__raw(svm,_s,"");
::svm_variable_scope_set_local(svm,_s);
%}
Générez et compilez l'extension. Puis écrivez une application contenant ce code :
#!/usr/bin/env svm
LOG
LOCAL PLUGIN "svmpluginram/libsvmram.so"
PLUGIN "svmcom.so"
PROCESS "ram"
CODE "main" INLINE
:memory STR/s
:ram.write 0 17
:com.message "[" @&s "]"
:ram.test
:com.message "[" @&s "]"
END
END
Lancez l'application :
./memoire.svm
[17]
[]
La deuxième ligne montre une chaine vide ! Cela est dû au fait que la fonction svm_value_string_set__raw
a agit sur la valeur qui avait été déplacée en mémoire et conservée comme variable Simple Virtual Machine globale.
Enlevez l'appel de la fonction svm_value_state_set_movable
puis regénérez et compilez l'extension. Relancez l'application :
./memoire.svm
[17]
[17]
Cette fois, la modification de la chaine n'a pas altéré la mémoire, car la valeur y a été copiée durant l'écriture en mémoire.
svm_memory_read_address
et ses variantes permettent d'écrire des valeurs en mémoire.svm_memory_write_address
permet d'écrire une valeur en mémoire.Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.new INT:size INT:address ? -> PTR ?
%{
SVM_Value_Integer s = ::svm_parameter_value_get(svm,argv[0]);
SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
::svm_memory_zone_append_internal(svm,z,AUTOMATIC,s);
if(argc==1)
{
return ::svm_memory_allocate(svm,CURRENT(kernel),z);
}
else
{
auto a = ARGV_VALUE(1,integer);
return ::svm_memory_allocate_address(svm,CURRENT(kernel),z,a);
}
%}
Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction pour créer un tableau.
Vous pouvez noter que :
SVM_Memory_Zone
est créée. Cette variable contient la description du bloc mémoire à allouer,svm_memory_allocate
alloue la mémoire comme l'instruction :memory
le fait, en choisissant une adresse convenable pour loger la mémoire allouée,svm_memory_allocate_address
alloue la mémoire à une adresse donnée. Si l'allocation échoue, un pointeur nul est retourné par la fonction. Cette allocation est utile lorsqu'une contrainte forte d'adresse existeEn plus des retours de fonctions, l'interface programmatique permet de libérer la mémoire explicitement.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.delete PTR
%{
SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
::svm_memory_free(svm,CURRENT(kernel),p);
%}
Générez et compilez l'extension. Puis écrivez une application avec ce code :
#!/usr/bin/env svm
LOG
LOCAL PLUGIN "svmpluginram/libsvmram.so"
PLUGIN "svmcom.so"
PROCESS "ram"
CODE "main" INLINE
:debug BREAK
:memory INT*10
:ram.delete &2*5
END
END
Lancez cette application dans le débugueur. Après l'exécution de l'instruction :ram.delete
, vous pouvez constater que :
&2*5
ne sont plus définies.svm_memory_allocate
, ou svm_memory_allocate_address
quand la mémoire doit être allouée à une adresse précise.SVM_Memory_Zone
qui se construit avec la fonction svm_memory_zone_new
, puis en ajoutant des types internes avec la fonction svm_memory_zone_append_internal
et des types extensions avec la fonction svm_memory_zone_append_external
.svm_memory_free
. Cette fonction n'altère pas la portée de la mémoire supprimée.Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.new STR PTR
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[1]);
::svm_memory_add_alias(svm,CURRENT(kernel),n,p);
%}
INSTRUCTION ram.delete STR
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
::svm_memory_remove_alias(svm,CURRENT(kernel),n);
%}
INSTRUCTION ram.get STR -> PTR ?
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
if(::svm_memory_has_alias(svm,CURRENT(kernel),n))
{
return ::svm_memory_alias_get_pointer(svm,CURRENT(kernel),n);
}
return NEW_NULL_VALUE(pointer);
%}
Générez et compilez l'extension. Puis écrivez une application manipulant les alias. Notez que l'instruction permettant de retourner le pointeur associé à un alias ne lève pas d'interruption quand l'alias n'existe pas.
svm_memory_add_alias
de l'interface programmatique permet de créer un alias.svm_memory_remove_alias
de l'interface programmatique permet de supprimer un alias.svm_memory_has_alias
de l'interface programmatique permet de détecter si un alias est défini.svm_memory_alias_get_pointer
de l'interface programmatique permet de récupérer le pointeur associé à un alias.Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.local PTR
%{
SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
::svm_memory_scope_set_local(svm,CURRENT(kernel),p);
%}
INSTRUCTION ram.global PTR
%{
SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
::svm_memory_scope_set_global(svm,CURRENT(kernel),p);
%}
Générez et compilez l'extension. Puis écrivez une application manipulant la portée de la mémoire, et lancez la dans le débugueur.
Notez que ces fonctions, même si elles sont placées dans le module de mémoire de l'interface programmatique, modifient le processeur et non la mémoire elle-même. En effet, elles indiquent au processeur comment gérer la mémoire lors d'un retour de fonction.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.local STR
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
::svm_memory_scope_set_local_alias(svm,CURRENT(kernel),n);
%}
INSTRUCTION ram.global STR
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
::svm_memory_scope_set_global_alias(svm,CURRENT(kernel),n);
%}
Générez et compilez l'extension. Puis écrivez une application manipulant la portée de la mémoire, et lancez la dans le débugueur. Cette fois encore, ces fonctions modifient le processeur.
svm_memory_scope_set_local
et svm_memory_scope_set_global
modifient la portée de la mémoire elle-même, sans toucher aux alias.svm_memory_scope_set_local_alias
et svm_memory_scope_set_global_alias
modifient la portée des alias, sans toucher à la portée de la mémoire.Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.defined INT -> BLN
%{
auto a = ARGV_VALUE(0,integer);
return NEW_VALUE(boolean,::svm_memory_address_is_defined(svm,CURRENT(kernel),a));
%}
INSTRUCTION ram.initialised INT -> BLN
%{
auto a = ARGV_VALUE(0,integer);
return NEW_VALUE(boolean,::svm_memory_address_is_initialised(svm,CURRENT(kernel),a));
%}
Générez et compilez l'extension. Puis écrivez une application utilisant ces deux instructions.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.type INT -> STR
%{
auto a = ARGV_VALUE(0,integer);
SVM_Type t = ::svm_memory_address_get_type(svm,CURRENT(kernel),a);
return NEW_VALUE(string,::svm_type_print(svm,t));
%}
Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.
Lorsqu'une écriture doit être faite en mémoire, il peut être utile de vérifier que cette écriture ne va pas échouer. Avec les fonctions de l'interface programmatique que nous venons de voir, il faut s'assurer que :
svm_memory_address_is_defined
,svm_memory_address_get_type
.Ce test peut devenir rapidement fastidieux à écrire. Pour éviter ce code fastidieux, il est possible d'utiliser la fonction svm_memory_address_is_writable
avec les mêmes paramètres que la fonction d'écriture. Cette fonction réalise tous les tests nécessaires, et indique si l'écriture est réalisable.
svm_memory_address_is_defined
pour savoir si l'adresse est définie, svm_memory_address_is_initialised
pour savoir si elle est initialisée, svm_memory_address_get_type
pour récupérer son type lorsqu'elle est définie.svm_memory_address_is_writable
.Les instructions, par exemple, peuvent admettre un nombre quelconque d'arguments, en fonction d'expression rationnelle de ses arguments. Parfois, ces arguments peuvent être considérés comme un tableau d'argument. Cependant, il est parfois plus pratique d'utiliser un tableau en mémoire, et de le lire depuis un pointeur passé en argument.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.read_array PTR -> STR
%{
SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
SVM_Address a = ::svm_value_pointer_get_address(svm,p);
SVM_Size s = ::svm_value_pointer_get_size(svm,p);
std::ostringstream oss;
oss << "[";
for(SVM_Address i=a; i<(a+s) ; ++i)
{
SVM_Value v = ::svm_memory_read_address(svm,CURRENT(kernel),i);
SVM_String vs = ::svm_value_print(svm,v);
oss << " " << RAW_STRING(vs);
}
oss << " ]";
return NEW_VALUE(string,NEW_STRING(oss.str()));
%}
Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.
La lecture du tableau se fait en deux étapes :
Notez qu'il est également possible de lire un tableau d'un seul coup avec la fonction svm_memory_read_pointer
et ses variantes. Dans ce cas, la valeur retournée est un tableau de valeurs terminé par un pointeur nul.
Il est aussi parfois très pratique pour une instruction de retourner un tableau. Dans ce cas, les valeurs sont écrites en mémoire, puis le pointeur vers les données écrites en mémoire est retourné par l'instruction.
Modifiez le code de l'extension :
PLUGIN ram
includes:
%{
#include <vector>
%}
DEFINE
INSTRUCTION ram.write_array INT -> PTR
%{
auto s = ARGV_VALUE(0,integer);
std::vector<long long int> v;
for(size_t i=0 ; i<s ; ++i)
{
v.push_back(i);
}
SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
::svm_memory_zone_append_internal__raw(svm,z,INTEGER,v.size());
SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
SVM_Address pa = ::svm_value_pointer_get_address(svm,p);
SVM_Size ps = ::svm_value_pointer_get_size(svm,p);
auto it = v.begin();
for(SVM_Address i=pa ; i<(pa+ps) ; ++i)
{
SVM_Value_Integer vi = ::svm_value_integer_new(svm,*it);
::svm_value_state_set_movable(svm,vi);
::svm_memory_write_address(svm,CURRENT(kernel),i,vi);
++it;
}
return p;
%}
Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.
L'instruction ici commence par construire un vecteur d'entiers, puis :
Une alternative est possible lorsque le tableau à écrire en mémoire provient d'un tableau généré par l'interface programmatique.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.write_array -> PTR
%{
SVM_Value_Interruption *irq = ::svm_machine_list_interruption(svm);
SVM_Size s = 0;
for(SVM_Value_Interruption *i=irq ; *i ; ++i)
{
::svm_value_state_set_movable(svm,*i);
++s;
}
SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
::svm_memory_zone_append_internal__raw(svm,z,INTERRUPTION,s);
SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
::svm_memory_write_pointer(svm,CURRENT(kernel),p,irq);
return p;
%}
Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.
Ici, l'instruction alloue toujours la mémoire et écrit les valeurs en mémoire, mais différemment :
svm_memory_write_pointer
. De plus, cette fonction écrit l'ensemble des valeurs de manière atomique : soit l'écriture réussit et toutes les valeurs sont écrites, soit l'écriture échoue, et aucune valeur n'est écrite en mémoire.svm_memory_read_pointer
et svm_memory_write_pointer
permettent d'accéder à un tableau en mémoire de manière atomique.Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.type_auto INT
%{
auto a = ARGV_VALUE(0,integer);
::svm_memory_address_set_type(svm,CURRENT(kernel),a,::svm_type_new_internal(svm,AUTOMATIC));
%}
INSTRUCTION ram.type_plugin INT PEP
%{
auto a = ARGV_VALUE(0,integer);
SVM_Value_PluginEntryPoint p = ::svm_parameter_value_get(svm,argv[1]);
::svm_memory_address_set_type(svm,CURRENT(kernel),a,::svm_type_new_external(svm,p));
%}
Générez et compilez l'extension. Puis écrivez une application utilisant ces instructions.
Notez qu'en plus de changer le type associé à une adresse, ces deux fonctions retirent la valeur éventuelle associée à l'adresse modifiée.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.shift INT INT
%{
auto a = ARGV_VALUE(0,integer);
SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[1]);
::svm_memory_address_shift(svm,CURRENT(kernel),a,i);
%}
Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.
La fonction svm_memory_address_shift
ne fonctionne que sur des adresses ayant le type entier, et modifie la valeur associée à une adresse.
Il est parfois utile de réaliser un traitement sur toute la mémoire qui peut être accédée depuis un pointeur, en suivant récursivement tous les pointeurs.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.expand PTR -> PTR
%{
SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_Pointer *t = ::svm_memory_pointer_list_accessible(svm,CURRENT(kernel),p);
SVM_Size s = 0;
for(SVM_Value_Pointer *it=t ; *it ; ++it)
{
::svm_value_state_set_movable(svm,*it);
++s;
}
SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
::svm_memory_zone_append_internal__raw(svm,z,POINTER,s);
SVM_Value_Pointer r = ::svm_memory_allocate(svm,CURRENT(kernel),z);
::svm_memory_write_pointer(svm,CURRENT(kernel),r,t);
return r;
%}
Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.
La fonction svm_memory_pointer_list_accessible
renverra toujours en premier le pointeur passé en paramètre. Ensuite, elle renvoie une liste de pointeurs qui ne tient pas compte de la topologie réelle de la mémoire, mais une représentation à plat où chaque adresse accessible n'est présente qu'une fois.
De la même manière, il est parfois important de récupérer la liste des alias définis sur une zone mémoire.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.names PTR -> PTR
%{
SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_String *t = ::svm_memory_pointer_list_alias(svm,CURRENT(kernel),p);
SVM_Size s = 0;
for(SVM_Value_Pointer *it=t ; *it ; ++it)
{
::svm_value_state_set_movable(svm,*it);
++s;
}
SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
::svm_memory_zone_append_internal__raw(svm,z,STRING,s);
SVM_Value_Pointer r = ::svm_memory_allocate(svm,CURRENT(kernel),z);
::svm_memory_write_pointer(svm,CURRENT(kernel),r,t);
return r;
%}
Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.
Si vous tentez de fournir un pointeur à cheval sur un alias, cet alias ne sera pas retourné : la fonction svm_memory_pointer_list_alias
ne retourne que les alias inclus entièrement dans le pointeur fourni en paramètre.
Copier un tableau entier depuis une zone mémoire vers une autre peut être une tâche fastidieuse. Heureusement, l'interface programmatique propose une fonction pour réaliser cette opération.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.copy PTR PTR
%{
SVM_Value_Pointer s = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_Pointer d = ::svm_parameter_value_get(svm,argv[1]);
::svm_memory_copy(svm,CURRENT(kernel),s,CURRENT(kernel),d);
%}
Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.
Vous pouvez remarquer que :
svm_memory_copy
, l'expression CURRENT(kernel)
apparaît deux fois. En réalité, cette fonction est capable de copier des valeurs d'une mémoire à une autre mémoire : il suffit d'indiquer un noyau différent à la place d'une de ces deux expressions ! D'une manière plus générale, nous avons dans ce didacticiel manipulé que la mémoire du noyau courant, mais il est possible de modifier n'importe quelle mémoire avec ces fonctions.De la même manière, il est possible de déplacer des valeurs.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.move PTR PTR
%{
SVM_Value_Pointer s = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_Pointer d = ::svm_parameter_value_get(svm,argv[1]);
::svm_memory_move(svm,CURRENT(kernel),s,CURRENT(kernel),d);
%}
Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.
Les mêmes remarques vues pour la copie s'appliquent au déplacement de valeurs.
Enfin, il est également possible de partager les valeurs entre plusieurs adresses. Cela est intéressant dans certains cas, notamment lorsqu'une partie des paramètres d'une fonction peut provenir d'une structure de données, et que la fonction peut intervenir dessus : cela donne une vue supplémentaire sur les valeurs.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.share PTR PTR
%{
SVM_Value_Pointer s = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_Pointer d = ::svm_parameter_value_get(svm,argv[1]);
::svm_memory_share(svm,CURRENT(kernel),s,CURRENT(kernel),d);
%}
Générez et compilez l'extension. Puis écrivez une application avec ce code :
#!/usr/bin/env svm
LOG
LOCAL PLUGIN "svmpluginram/libsvmram.so"
PLUGIN "svmcom.so"
PROCESS "ram"
CODE "main" INLINE
:debug BREAK
:memory INT*10/t
[ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ] -> t
:memory (PTR, INT*3)/p
:ram.share (&t+3)*3 (&p+1)*3
:call decrease p
:memory INT/i
0 -> &i
:label display
:com.message @(t/@&i)
:shift &i
:goto display :when @&i IN t
:shutdown
:label decrease
:memory INT -> &P
1 -> &@&P
:label decrease_loop
:shift -1 (P/@&@&P)
:shift &@&P
:goto decrease_loop :when @&@&P IN P
:return
END
END
Lancez cette application dans le débugueur. Ouvrez les fenêtres de la mémoire, du processeur et du code, puis exécutez l'application instruction par instruction.
Notez que lorsque l'instruction :ram.share
s'exécute, rien de visible ne se produit. En revanche, lorsque la fonction "decrease" modifie un de ses paramètres entiers, le tableau d'entiers est également modifié : et pour cause, les valeurs sont physiquement les mêmes grâce à l'instruction :ram.share
!
svm_memory_address_set_type
permet de modifier le type associé à une adresse.svm_memory_address_shift
permet de modifier un entier en mémoire.svm_memory_pointer_list_accessible
et svm_memory_pointer_list_alias
permettent de récupérer des informations sur une zone mémoire, comme toute la mémoire accessible depuis une zone mémoire, ou tous les alias contenus dans une zone mémoire.svm_memory_copy
, svm_memory_move
et svm_memory_share
permettent respectivement de copier, déplacer et de partager des zones mémoire.Les adresses mémoires, lorsqu'elles sont partagées entre différentes mémoires, peuvent être accédées de manière concurente. Pour protéger la mémoire de tels accès concurents, chaque adresse peut être synchronisée : dans ce cas, les accès en lecture sont toujours possibles en parallèle, mais sont exclusifs avec les écritures, qui sont mutuellement exclusives entre elles.
Notez que seul les accès à la mémoire sont synchronisés. Les valeurs doivent à leur tour être synchronisées si elles sont modifiées.
Les adresses sont naturellement synchronisées lorsqu'elles sont partagées entre deux mémoires par la fonction svm_memory_share
, mais cette synchronisation peut être activée ou désactivée à volonté.
Modifiez le code de l'extension :
PLUGIN ram
DEFINE
INSTRUCTION ram.sync PTR
%{
SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
::svm_memory_synchronisation_enable(svm,CURRENT(kernel),p);
%}
INSTRUCTION ram.free PTR
%{
SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
::svm_memory_synchronisation_disable(svm,CURRENT(kernel),p);
%}
La synchronisation des adresses n'étant pas particulièrement visible, il n'est pas nécessaire de tester ces instructions. Ce code est donné pour illustrer comment la synchronisation se manipule.
svm_memory_synchronisation_enable
et svm_memory_synchronisation_disable
.Vous venez de voir comment manipuler les mémoires de la machine virtuelle depuis les extensions.
La mémoire de la machine virtuelle est riche en opérations possibles. Cependant, il convient de maîtriser en particulier la lecture, l'écriture, l'allocation et la portée de la mémoire pour un usage courant dans les instructions.