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 créer et manipuler des processus de la machine virtuelle.
Le temps de lecture de ce didacticiel est estimé à 25 minutes.
Pour commencer, créez le canevas de l'extension dans le fichier processus.svm_plugin en utilisant ce code :
PLUGIN process
DEFINE
Les processus peuvent être créés statiquement en utilisant le fichier d'application. Cela s'avère rapidement insuffisant lorsque les processus doivent être créés dynamiquement en fonction de données.
Modifiez le code de l'extension :
PLUGIN process
DEFINE
TYPE process.instance
%{
SVM_Process _process;
operator std::string() const { return "process"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_process);
%}
print default: %{}
INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_PluginEntryPoint s = nullptr;
if(::svm_parameter_type_is_value(svm,argv[1]))
{
s = ::svm_parameter_value_get(svm,argv[1]);
}
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Process p = nullptr;
if(::svm_value_type_is_library(svm,v))
{
SVM_Code c = ::svm_value_library_get_code(svm,v);
p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
}
else
{
p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
}
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
Vous pouvez noter ici :
svm_process_new_code
et svm_process_new_symbol
,Vous pouvez générer et compiler l'extension, même si elle n'est encore pas très utile.
Il est également possible de récupérer le processus courant. Modifiez le code de l'extension :
PLUGIN process
DEFINE
TYPE process.instance
%{
SVM_Process _process;
operator std::string() const { return "process"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_process);
%}
print default: %{}
INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_PluginEntryPoint s = nullptr;
if(::svm_parameter_type_is_value(svm,argv[1]))
{
s = ::svm_parameter_value_get(svm,argv[1]);
}
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Process p = nullptr;
if(::svm_value_type_is_library(svm,v))
{
SVM_Code c = ::svm_value_library_get_code(svm,v);
p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
}
else
{
p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
}
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.current -> process.instance
%{
SVM_Process p = ::svm_process_get_current(svm);
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
Vous pouvez générer et compiler l'extension, mais à nouveau, elle n'est pas exploitable en l'état.
svm_process_new_code
et svm_process_new_symbol
.svm_process_get_current
. L'appel à cette fonction peut même être remplacé par l'utilisation de la macro C/C++ CURRENT(process)
.Pour pouvoir modifier un processus, il est nécessaire que ce processus appartienne au processus courant : en effet, les processus sont des unités d'exécution du code totalement indépendantes et concurrentes et modifier un processus en cours d'exécution pourrait occasionner des plantages de la machine virtuelle.
Dans le cas où le code d'une instruction doit modifier un processus ou le noyau d'un processus, elle doit s'assurer que le processus appartient bien au processus courant.
Modifiez le code de l'extension :
PLUGIN process
DEFINE
TYPE process.instance
%{
SVM_Process _process;
operator std::string() const { return "process"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_process);
%}
print default: %{}
INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_PluginEntryPoint s = nullptr;
if(::svm_parameter_type_is_value(svm,argv[1]))
{
s = ::svm_parameter_value_get(svm,argv[1]);
}
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Process p = nullptr;
if(::svm_value_type_is_library(svm,v))
{
SVM_Code c = ::svm_value_library_get_code(svm,v);
p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
}
else
{
p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
}
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.current -> process.instance
%{
SVM_Process p = ::svm_process_get_current(svm);
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.owned process.instance -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
return NEW_VALUE(boolean,b);
%}
Générez et compilez l'extension. Puis créez une application contenant ce code :
#!/usr/bin/env svm
PLUGIN "svmcom.so"
LOCAL PLUGIN "svmpluginprocess/libsvmprocess.so"
PROCESS "process"
CODE "main" INLINE
:memory process.instance*2/p, BLN*2/b
:process.new "s" DEFAULT $"s" -> (p/0)
:process.current -> (p/1)
:process.owned @(p/0) -> (b/0)
:process.owned @(p/1) -> (b/1)
:com.message @(b/0) " " @(b/1)
:shutdown
:symbol s
:com.message "s"
END
END
Puis lancez l'application :
./processus.svm
FALSE TRUE
Vous pouvez constater que :
Pour qu'un autre processus appartienne au processus courant, il existe un seul moyen : verrouiller le processus distant depuis le processus courant.
Modifiez le code de l'extension :
PLUGIN process
DEFINE
TYPE process.instance
%{
SVM_Process _process;
operator std::string() const { return "process"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_process);
%}
print default: %{}
INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_PluginEntryPoint s = nullptr;
if(::svm_parameter_type_is_value(svm,argv[1]))
{
s = ::svm_parameter_value_get(svm,argv[1]);
}
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Process p = nullptr;
if(::svm_value_type_is_library(svm,v))
{
SVM_Code c = ::svm_value_library_get_code(svm,v);
p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
}
else
{
p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
}
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.current -> process.instance
%{
SVM_Process p = ::svm_process_get_current(svm);
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.owned process.instance -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
return NEW_VALUE(boolean,b);
%}
TYPE process.lock
%{
SVM_Process_Lock _lock;
operator std::string() const { return "lock"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_lock);
%}
print default: %{}
WAITING INSTRUCTION process.lock process.instance -> process.lock
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
VARIABLE_GLOBAL(l);
return NEW_PLUGIN(process,lock,new type_lock {l});
%}
WAITING INSTRUCTION process.critical_lock -> process.lock
%{
SVM_Process_Lock l = ::svm_process_ownership_lock_critical(svm);
VARIABLE_GLOBAL(l);
return NEW_PLUGIN(process,lock,new type_lock {l});
%}
Générez et compilez l'extension. Puis modifiez l'application avec ce code :
#!/usr/bin/env svm
PLUGIN "svmcom.so"
LOCAL PLUGIN "svmpluginprocess/libsvmprocess.so"
PROCESS "process"
CODE "main" INLINE
:memory process.instance*2/p, BLN*2/b, process.lock/l
:process.new "s" DEFAULT $"s" -> (p/0)
:process.current -> (p/1)
:call owner P
:process.lock @(p/0) -> &l
:call owner P
[ ] -> l
:call owner P
:shutdown
:label owner
:process.owned @(p/0) -> (b/0)
:process.owned @(p/1) -> (b/1)
:com.message @(b/0) " " @(b/1)
:return
:symbol s
:com.message "s"
END
END
Cette fois, l'exécution devient :
./processus.svm
FALSE TRUE
TRUE TRUE
FALSE TRUE
Cette fois, entre le moment où le verrou depuis le processus distant est créé et où il est détruit, ce processus appartient au processus courant !
Notez la décorrelation entre l'appartenance et le verrouillage : en effet, la fonction qui teste l'appartenance ne demande pas de verrou en paramètre. Seule l'existence quelque part d'au moins un verrou suffit à garantir l'appartenance.
L'exemple ci-dessus utilise le verrou simple, qui permet de verrouiller un seul processus distant. Cependant, il existe un autre verrou dont l'utilisation est plutôt rare : le verrou critique qui verrouille tous les processus sauf le processus courant. Un tel verrouillage, même extrême, peut être utile lorsque l'exécution d'une instruction a un effet de bord pouvant être néfaste si d'autres processus s'exécutent en même temps.
Enfin, il existe une fonction de l'interface programmatique qui simplifie l'obtention de l'appartenance d'un processus distant : la fonction svm_process_ownership_get_local
permettant d'obtenir l'appartenance localement à la fonction extension courante. Un exemple d'utilisation de cette fonction sera donné juste après dans ce didacticiel.
svm_process_ownership_lock
permet d'obtenir l'appartenance d'un processus distant au processus courant.svm_process_ownership_lock_critical
permet d'obtenir l'appartenance de tous les autres processus au processus courant, pour des cas spécifiques.svm_process_ownership_get_local
permet d'obtenir l'appartenance d'un autre processus durant la fonction courante.Modifiez le code de l'extension :
PLUGIN process
DEFINE
TYPE process.instance
%{
SVM_Process _process;
operator std::string() const { return "process"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_process);
%}
print default: %{}
INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_PluginEntryPoint s = nullptr;
if(::svm_parameter_type_is_value(svm,argv[1]))
{
s = ::svm_parameter_value_get(svm,argv[1]);
}
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Process p = nullptr;
if(::svm_value_type_is_library(svm,v))
{
SVM_Code c = ::svm_value_library_get_code(svm,v);
p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
}
else
{
p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
}
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.current -> process.instance
%{
SVM_Process p = ::svm_process_get_current(svm);
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.owned process.instance -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
return NEW_VALUE(boolean,b);
%}
TYPE process.lock
%{
SVM_Process_Lock _lock;
operator std::string() const { return "lock"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_lock);
%}
print default: %{}
WAITING INSTRUCTION process.lock process.instance -> process.lock
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
VARIABLE_GLOBAL(l);
return NEW_PLUGIN(process,lock,new type_lock {l});
%}
INSTRUCTION process.write MUTABLE process.instance INT VALUE
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Address a = ARGV_VALUE(1,integer);
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,1);
SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
SVM_Value_Pointer pm = ::svm_memory_allocate_address(svm,k,z,a);
if(not pm)
{
ERROR_INTERNAL(MEMORY,"Invalid address");
}
::svm_memory_write_address(svm,k,a,v);
%}
Générez et compilez l'extension. Puis modifiez l'application avec ce code :
#!/usr/bin/env svm
LOG
PLUGIN "svmcom.so"
LOCAL PLUGIN "svmpluginprocess/libsvmprocess.so"
PROCESS "process"
CODE "main" INLINE
:memory process.instance/p, process.lock/l
:process.new "s" DEFAULT $"s" -> &p
:process.lock @&p -> &l
:process.write @&p 0 "it works"
[ ] -> l
:process.write @&p 1 "failure"
:symbol s
:com.message "s"
END
END
Lorsque cette application est lancée, une interruption intervient sur le second appel à l'instruction :process.write
: il a cependant fonctionné sur le premier appel, lorsque le processus distant était verrouillé par le processus courant !
Cela permet de garantir qu'une donnée au sein de n'importe quel processus ne peut être accédée que par une seule instruction à la fois, pour éviter les modifications concurrentes génératrices d'erreurs aléatoires.
Pour qu'un processus soit exécuté, il est nécessaire de l'attacher à un ordonnanceur.
Modifiez le code de l'extension :
PLUGIN process
DEFINE
TYPE process.instance
%{
SVM_Process _process;
operator std::string() const { return "process"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_process);
%}
print default: %{}
INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_PluginEntryPoint s = nullptr;
if(::svm_parameter_type_is_value(svm,argv[1]))
{
s = ::svm_parameter_value_get(svm,argv[1]);
}
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Process p = nullptr;
if(::svm_value_type_is_library(svm,v))
{
SVM_Code c = ::svm_value_library_get_code(svm,v);
p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
}
else
{
p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
}
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.current -> process.instance
%{
SVM_Process p = ::svm_process_get_current(svm);
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.owned process.instance -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
return NEW_VALUE(boolean,b);
%}
TYPE process.lock
%{
SVM_Process_Lock _lock;
operator std::string() const { return "lock"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_lock);
%}
print default: %{}
WAITING INSTRUCTION process.lock process.instance -> process.lock
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
VARIABLE_GLOBAL(l);
return NEW_PLUGIN(process,lock,new type_lock {l});
%}
INSTRUCTION process.write MUTABLE process.instance INT VALUE
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Address a = ARGV_VALUE(1,integer);
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,1);
SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
SVM_Value_Pointer pm = ::svm_memory_allocate_address(svm,k,z,a);
if(not pm)
{
ERROR_INTERNAL(MEMORY,"Invalid address");
}
::svm_memory_write_address(svm,k,a,v);
%}
WAITING INSTRUCTION process.attach process.instance PEP:scheduler INT:parameter -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
if(not ::svm_process_ownership_get_local(svm,p->_process))
{
ERROR_INTERNAL(FAILURE,"Impossible to attach process");
}
SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
SVM_Boolean b = ::svm_scheduler_process_attach(svm,s,p->_process,i);
return NEW_VALUE(boolean,b);
%}
WAITING INSTRUCTION process.detach process.instance PEP:scheduler INT:parameter -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
if(not ::svm_process_ownership_get_local(svm,p->_process))
[46m{
ERROR_INTERNAL(FAILURE,"Impossible to detach process");
[46m}
SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
SVM_Boolean b = ::svm_scheduler_process_detach(svm,s,p->_process,i);
return NEW_VALUE(boolean,b);
%}
Générez et compilez l'extension.
Notez la présence de la fonction svm_process_ownership_get_local
qui ici permet d'obtenir l'appartenance du processus (en le verrouillant si nécessaire) sur l'ensemble de l'instruction : l'appartenance est perdue dès que l'instruction se termine.
Cela permet d'attacher le processus sans avoir de verrou au préalable :
#!/usr/bin/env svm
LOG
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
LOCAL PLUGIN "svmpluginprocess/libsvmprocess.so"
PROCESS "process"
CODE "main" INLINE
:debug BREAK "process"
:memory process.instance/p, process.lock/l
:process.new "s" DEFAULT $"s" -> &p
:process.lock @&p -> &l
:process.write @&p 0 "it works"
[ ] -> l
:process.attach @&p run.parallel 0
:shutdown
:symbol s
:debug BREAK "s"
:com.message @&0
END
END
Pour bien suivre le déroulement de cette application, il est conseillé de l'exécuter dans le débugueur et de suivre pas à pas son exécution.
Il est possible de demander à un processus de suspendre son exécution, même lorsqu'il n'appartient pas au processus courant. La demande de suspension est totalement asynchrone, la suspension étant effective une fois l'instruction en cours d'exécution terminée.
Modifiez le code de l'extension :
PLUGIN process
DEFINE
TYPE process.instance
%{
SVM_Process _process;
operator std::string() const { return "process"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_process);
%}
print default: %{}
INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_PluginEntryPoint s = nullptr;
if(::svm_parameter_type_is_value(svm,argv[1]))
{
s = ::svm_parameter_value_get(svm,argv[1]);
}
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Process p = nullptr;
if(::svm_value_type_is_library(svm,v))
{
SVM_Code c = ::svm_value_library_get_code(svm,v);
p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
}
else
{
p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
}
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.current -> process.instance
%{
SVM_Process p = ::svm_process_get_current(svm);
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.owned process.instance -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
return NEW_VALUE(boolean,b);
%}
TYPE process.lock
%{
SVM_Process_Lock _lock;
operator std::string() const { return "lock"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_lock);
%}
print default: %{}
WAITING INSTRUCTION process.lock process.instance -> process.lock
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
VARIABLE_GLOBAL(l);
return NEW_PLUGIN(process,lock,new type_lock {l});
%}
INSTRUCTION process.write MUTABLE process.instance INT VALUE
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Address a = ARGV_VALUE(1,integer);
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,1);
SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
SVM_Value_Pointer pm = ::svm_memory_allocate_address(svm,k,z,a);
if(not pm)
{
ERROR_INTERNAL(MEMORY,"Invalid address");
}
::svm_memory_write_address(svm,k,a,v);
%}
WAITING INSTRUCTION process.attach process.instance PEP:scheduler INT:parameter -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
if(not ::svm_process_ownership_get_local(svm,p->_process))
{
ERROR_INTERNAL(FAILURE,"Impossible to attach process");
}
SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
SVM_Boolean b = ::svm_scheduler_process_attach(svm,s,p->_process,i);
return NEW_VALUE(boolean,b);
%}
WAITING INSTRUCTION process.detach process.instance PEP:scheduler INT:parameter -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
if(not ::svm_process_ownership_get_local(svm,p->_process))
{
ERROR_INTERNAL(FAILURE,"Impossible to detach process");
}
SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
SVM_Boolean b = ::svm_scheduler_process_detach(svm,s,p->_process,i);
return NEW_VALUE(boolean,b);
%}
INSTRUCTION process.suspend process.instance
%{
auto p = ARGV_PLUGIN(0,process,instance);
::svm_process_suspend(svm,p->_process);
%}
Générez et compilez l'extension.
Vous pouvez écrire une petite application utilisant cette nouvelle instruction, mais l'effet de cette instruction avec les ordonnanceurs disponibles est peu visible. En effet, une fois le processus suspendu, il est généralement à la charge de son ordonnanceur de relancer son exécution et les ordonnanceurs fournis de base avec la machine virtuelle vont relancer immédiatement les processus suspendus.
De même, il est possible de demander la terminaison d'un processus.
Modifiez le code de l'extension :
PLUGIN process
DEFINE
TYPE process.instance
%{
SVM_Process _process;
operator std::string() const { return "process"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_process);
%}
print default: %{}
INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_PluginEntryPoint s = nullptr;
if(::svm_parameter_type_is_value(svm,argv[1]))
{
s = ::svm_parameter_value_get(svm,argv[1]);
}
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Process p = nullptr;
if(::svm_value_type_is_library(svm,v))
{
SVM_Code c = ::svm_value_library_get_code(svm,v);
p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
}
else
{
p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
}
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.current -> process.instance
%{
SVM_Process p = ::svm_process_get_current(svm);
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.owned process.instance -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
return NEW_VALUE(boolean,b);
%}
TYPE process.lock
%{
SVM_Process_Lock _lock;
operator std::string() const { return "lock"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_lock);
%}
print default: %{}
WAITING INSTRUCTION process.lock process.instance -> process.lock
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
VARIABLE_GLOBAL(l);
return NEW_PLUGIN(process,lock,new type_lock {l});
%}
INSTRUCTION process.write MUTABLE process.instance INT VALUE
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Address a = ARGV_VALUE(1,integer);
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,1);
SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
SVM_Value_Pointer pm = ::svm_memory_allocate_address(svm,k,z,a);
if(not pm)
{
ERROR_INTERNAL(MEMORY,"Invalid address");
}
::svm_memory_write_address(svm,k,a,v);
%}
WAITING INSTRUCTION process.attach process.instance PEP:scheduler INT:parameter -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
if(not ::svm_process_ownership_get_local(svm,p->_process))
{
ERROR_INTERNAL(FAILURE,"Impossible to attach process");
}
SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
SVM_Boolean b = ::svm_scheduler_process_attach(svm,s,p->_process,i);
return NEW_VALUE(boolean,b);
%}
WAITING INSTRUCTION process.detach process.instance PEP:scheduler INT:parameter -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
if(not ::svm_process_ownership_get_local(svm,p->_process))
{
ERROR_INTERNAL(FAILURE,"Impossible to detach process");
}
SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
SVM_Boolean b = ::svm_scheduler_process_detach(svm,s,p->_process,i);
return NEW_VALUE(boolean,b);
%}
INSTRUCTION process.suspend process.instance
%{
auto p = ARGV_PLUGIN(0,process,instance);
::svm_process_suspend(svm,p->_process);
%}
INSTRUCTION process.terminate process.instance
%{
auto p = ARGV_PLUGIN(0,process,instance);
::svm_process_terminate(svm,p->_process);
%}
Générez et compilez l'extension.
Vous pouvez écrire une petite application utilisant cette nouvelle instruction. Cette fois, l'effet est plus visible, car le processus ciblé par cette nouvelle instruction va arrêter son exécution dès l'instruction courante terminée !
Il est possible de récupérer certaines informations sur un processus.
Modifiez le code de l'extension :
PLUGIN process
DEFINE
TYPE process.instance
%{
SVM_Process _process;
%}
delete default:
%{
VARIABLE_LOCAL(object->_process);
%}
print object:
%{
return ::svm_process_print(svm,object->_process);
%}
INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
%{
SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Value_PluginEntryPoint s = nullptr;
if(::svm_parameter_type_is_value(svm,argv[1]))
{
s = ::svm_parameter_value_get(svm,argv[1]);
}
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Process p = nullptr;
if(::svm_value_type_is_library(svm,v))
{
SVM_Code c = ::svm_value_library_get_code(svm,v);
p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
}
else
{
p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
}
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.current -> process.instance
%{
SVM_Process p = ::svm_process_get_current(svm);
VARIABLE_GLOBAL(p);
return NEW_PLUGIN(process,instance,new type_instance {p});
%}
INSTRUCTION process.owned process.instance -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
return NEW_VALUE(boolean,b);
%}
TYPE process.lock
%{
SVM_Process_Lock _lock;
operator std::string() const { return "lock"; }
%}
delete default:
%{
VARIABLE_LOCAL(object->_lock);
%}
print default: %{}
WAITING INSTRUCTION process.lock process.instance -> process.lock
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
VARIABLE_GLOBAL(l);
return NEW_PLUGIN(process,lock,new type_lock {l});
%}
INSTRUCTION process.write MUTABLE process.instance INT VALUE
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Address a = ARGV_VALUE(1,integer);
SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,1);
SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
SVM_Value_Pointer pm = ::svm_memory_allocate_address(svm,k,z,a);
if(not pm)
{
ERROR_INTERNAL(MEMORY,"Invalid address");
}
::svm_memory_write_address(svm,k,a,v);
%}
WAITING INSTRUCTION process.attach process.instance PEP:scheduler INT:parameter -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
if(not ::svm_process_ownership_get_local(svm,p->_process))
{
ERROR_INTERNAL(FAILURE,"Impossible to attach process");
}
SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
SVM_Boolean b = ::svm_scheduler_process_attach(svm,s,p->_process,i);
return NEW_VALUE(boolean,b);
%}
WAITING INSTRUCTION process.detach process.instance PEP:scheduler INT:parameter -> BLN
%{
auto p = ARGV_PLUGIN(0,process,instance);
if(not ::svm_process_ownership_get_local(svm,p->_process))
{
ERROR_INTERNAL(FAILURE,"Impossible to detach process");
}
SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
SVM_Boolean b = ::svm_scheduler_process_detach(svm,s,p->_process,i);
return NEW_VALUE(boolean,b);
%}
INSTRUCTION process.suspend process.instance
%{
auto p = ARGV_PLUGIN(0,process,instance);
::svm_process_suspend(svm,p->_process);
%}
INSTRUCTION process.terminate process.instance
%{
auto p = ARGV_PLUGIN(0,process,instance);
::svm_process_terminate(svm,p->_process);
%}
INSTRUCTION process.name process.instance -> STR
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_String n = ::svm_process_get_name(svm,p->_process);
return NEW_VALUE(string,n);
%}
INSTRUCTION process.state process.instance -> INT
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Process_State s = ::svm_process_get_state(svm,p->_process);
return NEW_VALUE(integer,s);
%}
INSTRUCTION process.irq process.instance -> IRQ ?
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Value_Interruption i = ::svm_process_get_interruption(svm,p->_process);
if(not i)
{
return NEW_NULL_VALUE(interruption);
}
return i;
%}
SYSTEM INSTRUCTION process.coredump process.instance -> STR
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_String n = ::svm_process_get_coredump(svm,p->_process);
return NEW_VALUE(string,n);
%}
INSTRUCTION process.kernel process.instance -> STR
%{
auto p = ARGV_PLUGIN(0,process,instance);
SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
SVM_String s = ::svm_kernel_print(svm,k);
return NEW_VALUE(string,s);
%}
Générez et compilez l'extension (notez que la fonction d'affichage du type process.instance
a également été modifiée), puis écrivez une petite application qui crée un processus, puis appelle ces fonctions sur le processus créé.
svm_scheduler_process_attach
. L'opération inverse se réalise avec la fonction svm_scheduler_process_detach
.svm_process_suspend
et svm_process_terminate
permettent de demander respectivement la suspension (une pause) et la terminaison (l'arrêt) du processus ciblé.svm_process_get_name
pour récupérer le nom du processus,svm_process_get_state
pour récupérer l'état d'exécution du processus,svm_process_get_interruption
pour récupérer l'interruption qui a arrêté le processus lorsque son état est égal à INTERRUPTED
,svm_process_get_coredump
pour récupérer une représentation textuelle de l'ensemble du contenu du processus (à utiliser de préférence dans une instruction système),svm_process_print
pour récupérer une représentation textuelle de l'état d'exécution du processus,svm_process_kernel_get_current
pour récupérer le noyau actuellement en cours d'exécution dans le processus.Vous venez de voir comment créer et manipuler les processus depuis une extension.
La maîtrise de la manipulation des processus est une compétence clef pour développer des applications de taille industrielle. En effet, toute architecture matérielle actuelle comporte plusieurs cœurs de calcul, et il serait vraiment dommage de ne pas en profiter lorsque cela peut être utile. Or, c'est au travers des processus que la machine hôte pourra être exploitée à ses pleines capacités.
Pour des applications plus modestes, il est utile de savoir comment créer un processus et l'attacher à un ordonnanceur. Le reste relève d'une utilisation avancée des processus, et peut être mis de côté au début, pour être repris plus tard lorsque la nécessité fait foi.