# Introduction

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 depuis une extension les noyaux de la machine virtuelle.

Le temps de lecture de ce didacticiel est estimé à 25 minutes.

# Mise en place

Pour commencer, créez le canevas de l'extension dans le fichier noyaux.svm_plugin en utilisant ce code :

    PLUGIN kernel

    DEFINE

# Création et récupération

## Création

Modifiez le code de l'extension :

    PLUGIN kernel

    DEFINE

    TYPE kernel.instance
    %{
    	SVM_Kernel _kernel;
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_kernel);
    %}

    INSTRUCTION kernel.new_code LIB BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    	SVM_Kernel k = ::svm_kernel_new_code(svm,ti,lris,pm,nullptr/* no accesscontrol */,c);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.new_symbol SYM BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Kernel k = ::svm_kernel_new_symbol(svm,ti,lris,pm,nullptr/* no accesscontrol */,s);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

Générez l'extension et compilez la. Vous pouvez créer une petite application appellant ces deux instructions pour tester, mais le résultat n'est pour le moment pas spectaculaire.

Les deux fonctions de l'interface à noter ici sont `svm_kernel_new_code` et `svm_kernel_new_symbol`. Ces deux fonctions créent un nouveau noyau en initialisant le processeur sur un code ou un symbole, avec une mémoire vide et des drapeaux altérant le comportement du noyau dans l'exécution du code.

## Noyau courant

Modifiez le code de l'extension :

    PLUGIN kernel

    DEFINE

    TYPE kernel.instance
    %{
    	SVM_Kernel _kernel;
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_kernel);
    %}

    INSTRUCTION kernel.new_code LIB BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    	SVM_Kernel k = ::svm_kernel_new_code(svm,ti,lris,pm,nullptr/* no accesscontrol */,c);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.new_symbol SYM BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Kernel k = ::svm_kernel_new_symbol(svm,ti,lris,pm,nullptr/* no accesscontrol */,s);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.get_current -> kernel.instance
    %{
    	SVM_Kernel k = ::svm_kernel_get_current(svm);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

Générez l'extension et compilez la. Vous pouvez créer une petite application appellant cette nouvelle instruction.

Ici, le noyau renvoyé par la fonction `svm_kernel_get_current` est celui dans lequel l'instruction s'exécute.

Notez que le générateur d'extension définit une macro C/C++ pouvant être utilisée ainsi `CURRENT(kernel)` pour obtenir le noyau courant.

- L'interface programmatique permet de créer un noyau via les fonctions `svm_kernel_new_code` et `svm_kernel_new_symbol`.
- Elle permet également de récupérer le noyau courant via la fonction `svm_kernel_get_current` ou l'appel de macro `CURRENT(kernel)`.

# Opérations

Modifiez le code de l'extension :

    PLUGIN kernel

    DEFINE

    TYPE kernel.instance
    %{
    	SVM_Kernel _kernel;
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_kernel);
    %}

    INSTRUCTION kernel.new_code LIB BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    	SVM_Kernel k = ::svm_kernel_new_code(svm,ti,lris,pm,nullptr/* no accesscontrol */,c);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.new_symbol SYM BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Kernel k = ::svm_kernel_new_symbol(svm,ti,lris,pm,nullptr/* no accesscontrol */,s);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.get_current -> kernel.instance
    %{
    	SVM_Kernel k = ::svm_kernel_get_current(svm);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.suspend kernel.instance
    %{
    	auto tk = ARGV_PLUGIN(0,kernel,instance);
    	::svm_kernel_suspend(svm,tk->_kernel);
    %}

    INSTRUCTION kernel.terminate kernel.instance
    %{
    	auto tk = ARGV_PLUGIN(0,kernel,instance);
    	::svm_kernel_terminate(svm,tk->_kernel);
    %}

Générez l'extension puis compilez la. Ecrivez une petite application de test appellant ces instructions sur le noyau courant.

Vous pourrez constater que :

- La fonction `svm_kernel_suspend` en l'état semble avoir peu d'effet sur l'exécution du noyau courant. Pourtant, elle met littéralement en pause le noyau : en fonction du séquenceur, un autre noyau peut prendre le relais lorsque l'instruction contenant cette fonction rend la main à la machine virtuelle.
- La fonction `svm_kernel_terminate` conclut brutalement l'exécution du code. Appliqué à un autre noyau, il requiert l'arrêt de l'exécution du code entre deux instructions au sein d'un noyau.

- La fonction `svm_kernel_suspend` de l'interface programmatique met en pause l'exécution du code dans un noyau.
- La fonction `svm_kernel_terminate` de l'interface programmatique met fin à l'exécution du code dans un noyau.

# Introspection

## Affichage

Modifiez le code de l'extension :

    PLUGIN kernel

    DEFINE

    TYPE kernel.instance
    %{
    	SVM_Kernel _kernel;
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_kernel);
    %}

    INSTRUCTION kernel.new_code LIB BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    	SVM_Kernel k = ::svm_kernel_new_code(svm,ti,lris,pm,nullptr/* no accesscontrol */,c);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.new_symbol SYM BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Kernel k = ::svm_kernel_new_symbol(svm,ti,lris,pm,nullptr/* no accesscontrol */,s);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.get_current -> kernel.instance
    %{
    	SVM_Kernel k = ::svm_kernel_get_current(svm);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.print kernel.instance -> STR
    %{
    	auto tk = ARGV_PLUGIN(0,kernel,instance);
    	SVM_String s = ::svm_kernel_print(svm,tk->_kernel);
    	return NEW_VALUE(string,s);
    %}

Générez l'extension puis compilez la. Ecrivez une petite application de test appellant cette dernière instruction sur le noyau courant ainsi que sur des noyaux créés par les deux premières instructions.

La chaine générée par la fonction `svm_kernel_print` contient l'état d'exécution du noyau, ainsi que la liste des drapeaux activés sur ce noyau.

D'ailleurs, il peut être judicieux ici d'utiliser cette fonction directement sur le type :

    PLUGIN kernel

    DEFINE

    TYPE kernel.instance
    %{
    	SVM_Kernel _kernel;
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_kernel);
    %}
    print object:
    %{
    	return ::svm_kernel_print(svm,object->_kernel);
    %}

    INSTRUCTION kernel.new_code LIB BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    	SVM_Kernel k = ::svm_kernel_new_code(svm,ti,lris,pm,nullptr/* no accesscontrol */,c);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.new_symbol SYM BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Kernel k = ::svm_kernel_new_symbol(svm,ti,lris,pm,nullptr/* no accesscontrol */,s);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.get_current -> kernel.instance
    %{
    	SVM_Kernel k = ::svm_kernel_get_current(svm);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.print kernel.instance -> STR
    %{
    	auto tk = ARGV_PLUGIN(0,kernel,instance);
    	SVM_String s = ::svm_kernel_print(svm,tk->_kernel);
    	return NEW_VALUE(string,s);
    %}

## Cadavre

Modifiez le code de l'extension :

    PLUGIN kernel

    DEFINE

    TYPE kernel.instance
    %{
    	SVM_Kernel _kernel;
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_kernel);
    %}

    INSTRUCTION kernel.new_code LIB BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    	SVM_Kernel k = ::svm_kernel_new_code(svm,ti,lris,pm,nullptr/* no accesscontrol */,c);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.new_symbol SYM BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Kernel k = ::svm_kernel_new_symbol(svm,ti,lris,pm,nullptr/* no accesscontrol */,s);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.get_current -> kernel.instance
    %{
    	SVM_Kernel k = ::svm_kernel_get_current(svm);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.coredump kernel.instance -> STR
    %{
    	auto tk = ARGV_PLUGIN(0,kernel,instance);
    	SVM_String s = ::svm_kernel_get_coredump(svm,tk->_kernel);
    	return NEW_VALUE(string,s);
    %}

Générez l'extension puis compilez la. Ecrivez une petite application de test appellant cette dernière instruction sur le noyau courant ainsi que sur des noyaux créés par les deux premières instructions.

Cette fois, la fonction `svm_kernel_get_coredump` génère une chaine contenant la totalité des informations du noyau, comme en cas d'erreur !

## Etat

Modifiez le code de l'extension :

    PLUGIN kernel

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    TYPE kernel.instance
    %{
    	SVM_Kernel _kernel;
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_kernel);
    %}

    INSTRUCTION kernel.new_code LIB BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    	SVM_Kernel k = ::svm_kernel_new_code(svm,ti,lris,pm,nullptr/* no accesscontrol */,c);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.new_symbol SYM BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Kernel k = ::svm_kernel_new_symbol(svm,ti,lris,pm,nullptr/* no accesscontrol */,s);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.get_current -> kernel.instance
    %{
    	SVM_Kernel k = ::svm_kernel_get_current(svm);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.state kernel.instance
    %{
    	auto tk = ARGV_PLUGIN(0,kernel,instance);
    	std::cout << "State (raw): " << ::svm_kernel_get_state(svm,tk->_kernel) << std::endl
    		<< "Interruption: ";
    	SVM_Value_Interruption i = ::svm_kernel_get_interruption(svm,tk->_kernel);
    	if(i==nullptr)
    	{
    		std::cout << "(none)";
    	}
    	else
    	{
    		SVM_String is = ::svm_value_print(svm,i);
    		std::cout << RAW_STRING(is);
    	}
    	std::cout << std::endl
    		<< "Transmit interruptions: " << ::svm_kernel_has_transmitinterruption(svm,tk->_kernel) << std::endl
    		<< "Last return is shutdown: " << ::svm_kernel_has_lastreturnisshutdown(svm,tk->_kernel) << std::endl
    		<< "Protected mode: " << ::svm_kernel_has_protectedmode(svm,tk->_kernel) << std::endl
    		<< "Runnable: " << ::svm_kernel_is_runnable(svm,tk->_kernel) << std::endl
    		<< "Terminated: " << ::svm_kernel_is_terminated(svm,tk->_kernel) << std::endl;
    %}

Générez l'extension puis compilez la. Ecrivez une petite application de test appellant cette dernière instruction sur le noyau courant ainsi que sur des noyaux créés par les deux premières instructions.

Cette fois, une vue détaillée de l'état du noyau peut être extraite pour un diagnostic précis d'un noyau.

- La fonction `svm_kernel_print` de l'interface programmatique permet d'obtenir une chaine courte représentant l'état du noyau.
- La fonction `svm_kernel_get_coredump` de l'interface programmatique permet quant à elle d'obtenir une chaine longue détaillant l'ensemble du noyau.
- Les fonctions de l'interface programmatique :
  - `svm_kernel_get_state` permet de récupérer l'état général d'exécution d'un noyau,
  - `svm_kernel_get_interruption` permet de récupérer l'éventuelle interruption d'un noyau,
  - `svm_kernel_has_transmitinterruption`, `svm_kernel_has_lastreturnisshutdown`, `svm_kernel_has_protectedmode` permettent de récupérer l'état des drapeaux d'un noyau,
  - `svm_kernel_is_runnable` permet de savoir si un noyau peut être exécuté,
  - `svm_kernel_is_terminated` permet de savoir si un noyau a terminé son exécution.

# Utilisation

Modifiez le code de l'extension :

    PLUGIN kernel

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    TYPE kernel.instance
    %{
    	SVM_Kernel _kernel;
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_kernel);
    %}

    INSTRUCTION kernel.new_code LIB BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    	SVM_Kernel k = ::svm_kernel_new_code(svm,ti,lris,pm,nullptr/* no accesscontrol */,c);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.new_symbol SYM BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Kernel k = ::svm_kernel_new_symbol(svm,ti,lris,pm,nullptr/* no accesscontrol */,s);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.get_current -> kernel.instance
    %{
    	SVM_Kernel k = ::svm_kernel_get_current(svm);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.attach kernel.instance -> BLN
    %{
    	auto tk = ARGV_PLUGIN(0,kernel,instance);
    	SVM_Boolean b = ::svm_process_kernel_attach(svm,CURRENT(process),tk->_kernel,0,nullptr);
    	return NEW_VALUE(boolean,b);
    %}

    INSTRUCTION kernel.detach kernel.instance -> BLN
    %{
    	auto tk = ARGV_PLUGIN(0,kernel,instance);
    	SVM_Boolean b = ::svm_process_kernel_detach(svm,CURRENT(process),tk->_kernel,0,nullptr);
    	return NEW_VALUE(boolean,b);
    %}

Générez l'extension puis compilez la. Créez un fichier exécutable pour tester l'instruction `:kernel.attach` avec ce code :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmpluginkernel/libsvmkernel.so"
    PLUGIN "svmcom.so"
    PLUGIN "svmrun.so"
    PROCESS "kernel"
    	CODE "main" INLINE
    		:debug BREAK
    		:memory LIB/l, kernel.instance/k
    		:library "lib" """
    		:debug BREAK "lib"
    		:com.message 2
    		""" -> &l
    		:kernel.new_code @&l TRUE FALSE TRUE -> &k
    		:com.message 1
    		:kernel.attach @&k
    		:com.message 3
    	END
    	SEQUENCER run.stack
    END

Notez que :

1. Ces deux instructions ici attachent et détachent le noyau au processus courant.
2. La présence de la directive `SEQUENCER run.stack` dans le processus de l'application, indispensable pour que le noyau soit attaché au processus courant.
3. Attacher un noyau à un processus est obligatoire pour exécuter le code à l'intérieur de ce noyau. Le moment où son exécution est effective dépend du séquenceur associé au processus.
4. Dans cette application, le noyau courant se termine avant que le noyau attaché soit lancé par le séquenceur : pour que l'exécution passe immédiatement au noyau attaché, il faut suspendre le noyau courant :

    PLUGIN kernel

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    TYPE kernel.instance
    %{
    	SVM_Kernel _kernel;
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_kernel);
    %}

    INSTRUCTION kernel.new_code LIB BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    	SVM_Kernel k = ::svm_kernel_new_code(svm,ti,lris,pm,nullptr/* no accesscontrol */,c);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.new_symbol SYM BLN : transmit_interruptions BLN : last_return_is_shutdown BLN : protected_mode -> kernel.instance
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Boolean ti = ARGV_VALUE(1,boolean);
    	SVM_Boolean lris = ARGV_VALUE(2,boolean);
    	SVM_Boolean pm = ARGV_VALUE(3,boolean);
    	SVM_Kernel k = ::svm_kernel_new_symbol(svm,ti,lris,pm,nullptr/* no accesscontrol */,s);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.get_current -> kernel.instance
    %{
    	SVM_Kernel k = ::svm_kernel_get_current(svm);
    	VARIABLE_GLOBAL(k);
    	auto tk = new type_instance;
    	tk->_kernel = k;
    	return NEW_PLUGIN(kernel,instance,tk);
    %}

    INSTRUCTION kernel.attach kernel.instance -> BLN
    %{
    	auto tk = ARGV_PLUGIN(0,kernel,instance);
    	SVM_Boolean b = ::svm_process_kernel_attach(svm,CURRENT(process),tk->_kernel,0,nullptr);
    	return NEW_VALUE(boolean,b);
    %}

    INSTRUCTION kernel.detach kernel.instance -> BLN
    %{
    	auto tk = ARGV_PLUGIN(0,kernel,instance);
    	SVM_Boolean b = ::svm_process_kernel_detach(svm,CURRENT(process),tk->_kernel,0,nullptr);
    	return NEW_VALUE(boolean,b);
    %}

    INSTRUCTION kernel.suspend kernel.instance
    %{
    	auto tk = ARGV_PLUGIN(0,kernel,instance);
    	::svm_kernel_suspend(svm,tk->_kernel);
    %}

Ainsi que le code de l'application :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmpluginkernel/libsvmkernel.so"
    PLUGIN "svmcom.so"
    PLUGIN "svmrun.so"
    PROCESS "kernel"
    	CODE "main" INLINE
    		:debug BREAK
    		:memory LIB/l, kernel.instance/k, kernel.instance/c
    		:library "lib" """
    		:debug BREAK "lib"
    		:com.message 2
    		""" -> &l
    		:kernel.new_code @&l TRUE FALSE TRUE -> &k
    		:com.message 1
    		:kernel.attach @&k
    		:com.message 3
    		:kernel.get_current -> &c
    		:kernel.suspend @&c
    		:com.message 4
    	END
    	SEQUENCER run.stack
    END

Cette fois, le noyau attaché est exécuté lorsque le noyau courant est suspendu. Lorsque le noyau attaché se termine, l'exécution du noyau courant reprend là où il en était grâce au séquenceur `run.stack`.

- La fonction `svm_process_kernel_attach` de l'interface programmatique permet d'attacher un noyau à un processus.
- La fonction `svm_process_kernel_detach` de l'interface programmatique permet de' détacher un noyau d'un processus.
- Attacher un noyau à un processus est une étape obligatoire pour exécuter le code de ce noyau.
- L'exécution du noyau est lancée lorsque la politique du séquenceur associé au processus auquel est attaché le noyau le décide.
- Suspendre (ou terminer) un noyau fait appel au séquenceur du processus dans lequel le noyau est attaché pour lancer le prochain noyau à exécuter.

# Conclusion

Vous venez de voir comment manipuler les noyaux depuis une extension.

La manipulation des noyaux eux-mêmes est rare dans le code d'instructions, et il est plus fréquent de modifier le contenu de ces noyaux.

Cependant, le code des séquenceurs que nous verrons dans le prochain didacticiel fait un appel important à cette partie de l'interface pour incarner une politique de séquencement au sein d'un processus.