Ce didacticiel est destiné aux utilisateurs sachant écrire un peu de code de la Simple Virtual Machine.
Dans ce didacticiel, vous allez exécuter des noyaux en séquence.
Le temps de lecture de ce didacticiel est estimé à 20 minutes.
Pour commencer, créez le canevas de l'application dans le fichier exécutable sequenceur.svm en utilisant ce code :
#!/usr/bin/env svm
DESCRIPTION
Sequencer exploration
END
LOG
DEBUG "Sequencer"
PLUGIN "svmcom.so"
PROCESS "application"
CODE "main" INLINE
:memory com.device/stdin, STR/source, LIB/library, STR/data
:com.open com.terminal STDIN -> &stdin
:com.read @&stdin com.all -> &source
:library "library" @&source -> &library
:call $(@&library/) data
:goto no_value :unless &data INITIALISED
:com.message @&data
:shutdown
:label no_value
:com.message "No value"
END
END
Commencez par lancer l'application :
cat << EOM | ./sequenceur.svm
> :com.message "a"
> "b" -> &P
> :return
> EOM
a
b
cat << EOM | ./sequenceur.svm
> :com.message "a"
> :return
> EOM
a
No value
Dans ces deux exécutions, le code entré sur le terminal "joue le jeu" et l'exécution globale de l'application se passe comme prévue.
Cependant, il est ici aisé de corrompre l'application :
cat << EOM | ./sequenceur.svm
> :com.message "a"
> :shutdown 0
> :return
> EOM
a
cat << EOM | ./sequenceur.svm
> :com.message "a"
> EOM
a
cat << EOM | ./sequenceur.svm
> [ ] -> source
> [ ] -> library
> :return
> EOM
No value
Ici, nous pouvons faire deux remarques :
La situation n'est pourtant pas désespérée : la machine virtuelle peut contenir plusieurs noyaux !
Et cela change tout, car le code compilé peut être exécuté sur un processeur différent et avec une mémoire différente.
Pour créér un nouveau noyau, l'extension "run" contient une instruction dédiée à la création de noyau en mode protégé, conçue pour résoudre ces problèmes de sécurité.
Ajoutez au code l'extension "run" et modifiez le code :
#!/usr/bin/env svm
DESCRIPTION
Sequencer exploration
END
LOG
DEBUG "Sequencer"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
PROCESS "application"
CODE "main" INLINE
:memory com.device/stdin, STR/source, LIB/library, STR/data
:com.open com.terminal STDIN -> &stdin
:com.read @&stdin com.all -> &source
:library "library" @&source -> &library
:run.protected_call $(@&library/) data
:goto no_value :unless &data INITIALISED
:com.message @&data
:shutdown
:label no_value
:com.message "No value"
END
END
Et relancez l'application :
cat << EOM | ./sequenceur.svm
> :com.message "a"
> "b" -> &P
> :return
> EOM
### Simple Virtual Machine 1234 : PROCESS application | 2023-01-01 00:00:00 GMT ######################################################################
Kernel interrupted: @(main, line 5) Interruption FAILURE not handled: Unable to attach a protected kernel to the current process
Core dump:
Kernel:
State: I, transmit_interruption, interrupted @(main, line 5) Interruption FAILURE not handled: Unable to attach a protected kernel to the current process
Processor:
State:
Next instruction: <main:1/5> (Current one: <main:1/4>)
Current memory : &0*0
Allocated memory: &0*4
Aliases : data library source stdin
Flags :
Cascaded flags :
Local interruption handlers:
Cascaded local interruption handlers:
Saved states:
Global interruption handlers:
Code at <main:1/4>:
:memory com.device/stdin , STR/source , LIB/library , STR/data # <0> @(main, line 1)
:com.open com.terminal STDIN -> &stdin # <1> @(main, line 2) WAITING SYSTEM
:com.read @&stdin com.all -> &source # <2> @(main, line 3) WAITING
:library "library" @&source -> &library # <3> @(main, line 4)
====== HERE ======
:run.protected_call $(@&library/) data # <4> @(main, line 5)
==================
:goto no_value :unless &data INITIALISED # <5> @(main, line 6)
:com.message @&data # <6> @(main, line 7)
:shutdown # <7> @(main, line 8)
:label no_value
:com.message "No value" # <8> @(main, line 10)
Labels:
no_value -> <8>
Symbols:
Memory:
&0: com.device Terminal STDIN
&1: STR ":com.message \"a\"\n\"b\" -> &P\n:return\n"
&2: LIB <library>
&3: STR (sync)
Aliases:
data -> &3*1
library -> &2*1
source -> &1*1
stdin -> &0*1
Free space:
### Simple Virtual Machine 1234 : SYSTEM | 2023-01-01 00:00:00 GMT ###################################################################################
Process application interrupted: @(main, line 5) Interruption FAILURE not handled: Unable to attach a protected kernel to the current process
Et là, c'est le drame. Une erreur aussi incompréhensible qu'inattendue !
Comme deux noyaux ne peuvent (pour le moment) pas être exécutés simultanément, il faut impérativement les intégrer dans une séquence d'exécution, où chaque noyau est exécuté à son tour.
La séquence d'exécution des noyaux est entièrement gérée par un nouveau concept : le séquenceur.
Un séquenceur intervient principalement lorsque le noyau en cours d'exécution est suspendu ou terminé. C'est à ce moment précis qu'il décide quel noyau prendra le relais pour poursuivre l'exécution de l'application, en fonction de sa politique de séquencement.
Et pour qu'un séquenceur puisse prendre en charge un noyau, il faut attacher ce noyau au séquenceur. Or, dans notre cas, le séquenceur par défaut de la machine virtuelle n'accepte qu'un seul noyau et refuse celui créé par l'instruction :run.protected_call
.
Modifiez le code pour changer le séquenceur en celui fourni avec l'extension "run" (comme précisé dans l'aide de l'instruction :run.protected_call
) :
#!/usr/bin/env svm
DESCRIPTION
Sequencer exploration
END
LOG
DEBUG "Sequencer"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
PROCESS "application"
CODE "main" INLINE
:memory com.device/stdin, STR/source, LIB/library, STR/data
:com.open com.terminal STDIN -> &stdin
:com.read @&stdin com.all -> &source
:library "library" @&source -> &library
:run.protected_call $(@&library/) data
:goto no_value :unless &data INITIALISED
:com.message @&data
:shutdown
:label no_value
:com.message "No value"
END
SEQUENCER run.stack
END
Relancez l'application exactement de la même manière :
cat << EOM | ./sequenceur.svm
> :com.message "a"
> "b" -> &P
> :return
> EOM
a
b
Cela fonctionne à nouveau dans le cas nominal. Voyons maintenant dans les cas incorrects vu précédemment.
Relancez l'application avec l'instruction :shutdown 0
:
cat << EOM | ./sequenceur.svm
> :com.message "a"
> :shutdown 0
> :return
> EOM
### Simple Virtual Machine 1234 : PROCESS application | 2023-01-01 00:00:00 GMT ######################################################################
Kernel interrupted: @(library, line 2, from @(main, line 4)) Interruption SECURITY not handled: System instruction :shutdown 0 execution attempt in protected mode.
Core dump:
Kernel:
State: I, transmit_interruption, last_return_is_shutdown, protected_mode, interrupted @(library, line 2, from @(main, line 4)) Interruption SECURITY not handled: System instruction :shutdown 0 execution attempt in protected mode.
Processor:
State:
Next instruction: <library/2> (Current one: <library/1>)
Current memory : &0*1
Allocated memory: &0*1
Aliases :
Flags :
Cascaded flags :
Local interruption handlers:
Cascaded local interruption handlers:
Saved states:
Global interruption handlers:
Code at <library/1>:
:com.message "a" # <0> @(library, line 1, from @(main, line 4))
====== HERE ======
:shutdown 0 # <1> @(library, line 2, from @(main, line 4)) SYSTEM
==================
:return # <2> @(library, line 3, from @(main, line 4))
Labels:
Symbols:
Memory:
&0: STR (sync)
Aliases:
Free space:
### Simple Virtual Machine 1234 : PROCESS application | 2023-01-01 00:00:00 GMT ######################################################################
Kernel interrupted: @(library, line 2, from @(main, line 4)) Interruption SECURITY not handled: System instruction :shutdown 0 execution attempt in protected mode.
Core dump:
Kernel:
State: I, transmit_interruption, interrupted @(library, line 2, from @(main, line 4)) Interruption SECURITY not handled: System instruction :shutdown 0 execution attempt in protected mode.
Processor:
State:
Next instruction: <main:1/5> (Current one: <main:1/4>)
Current memory : &0*0
Allocated memory: &0*4
Aliases : data library source stdin
Flags :
Cascaded flags :
Local interruption handlers:
Cascaded local interruption handlers:
Saved states:
Global interruption handlers:
Code at <main:1/4>:
:memory com.device/stdin , STR/source , LIB/library , STR/data # <0> @(main, line 1)
:com.open com.terminal STDIN -> &stdin # <1> @(main, line 2) WAITING SYSTEM
:com.read @&stdin com.all -> &source # <2> @(main, line 3) WAITING
:library "library" @&source -> &library # <3> @(main, line 4)
====== HERE ======
:run.protected_call $(@&library/) data # <4> @(main, line 5)
==================
:goto no_value :unless &data INITIALISED # <5> @(main, line 6)
:com.message @&data # <6> @(main, line 7)
:shutdown # <7> @(main, line 8)
:label no_value
:com.message "No value" # <8> @(main, line 10)
Labels:
no_value -> <8>
Symbols:
Memory:
&0: com.device Terminal STDIN
&1: STR ":com.message \"a\"\n:shutdown 0\n:return\n"
&2: LIB <library>
&3: STR (sync)
Aliases:
data -> &3*1
library -> &2*1
source -> &1*1
stdin -> &0*1
Free space:
### Simple Virtual Machine 1234 : SYSTEM | 2023-01-01 00:00:00 GMT ###################################################################################
Process application interrupted: @(library, line 2, from @(main, line 4)) Interruption SECURITY not handled: System instruction :shutdown 0 execution attempt in protected mode.
Cette erreur est intéressante à plusieurs titres :
SECURITY
, signifiant que la machine a détecté l'emploi d'une instruction non autorisée en mode protégé !Ajoutez un tel gestionnaire d'interruption :
#!/usr/bin/env svm
DESCRIPTION
Sequencer exploration
END
LOG
DEBUG "Sequencer"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
PROCESS "application"
CODE "main" INLINE
:memory com.device/stdin, STR/source, LIB/library, STR/data
:com.open com.terminal STDIN -> &stdin
:com.read @&stdin com.all -> &source
:library "library" @&source -> &library
:interruption SECURITY error
:run.protected_call $(@&library/) data
:goto no_value :unless &data INITIALISED
:com.message @&data
:shutdown
:label no_value
:com.message "No value"
:shutdown
:label error
:com.message "Error found"
:shutdown
END
SEQUENCER run.stack
END
Puis relancez l'application à l'identique :
cat << EOM | ./sequenceur.svm
> :com.message "a"
> :shutdown 0
> :return
> EOM
a
### Simple Virtual Machine 1234 : PROCESS application | 2023-01-01 00:00:00 GMT ######################################################################
Kernel interrupted: @(library, line 2, from @(main, line 4)) Interruption SECURITY not handled: System instruction :shutdown 0 execution attempt in protected mode.
Core dump:
Kernel:
State: I, transmit_interruption, last_return_is_shutdown, protected_mode, interrupted @(library, line 2, from @(main, line 4)) Interruption SECURITY not handled: System instruction :shutdown 0 execution attempt in protected mode.
Processor:
State:
Next instruction: <library/2> (Current one: <library/1>)
Current memory : &0*1
Allocated memory: &0*1
Aliases :
Flags :
Cascaded flags :
Local interruption handlers:
Cascaded local interruption handlers:
Saved states:
Global interruption handlers:
Code at <library/1>:
:com.message "a" # <0> @(library, line 1, from @(main, line 4))
====== HERE ======
:shutdown 0 # <1> @(library, line 2, from @(main, line 4)) SYSTEM
==================
:return # <2> @(library, line 3, from @(main, line 4))
Labels:
Symbols:
Memory:
&0: STR (sync)
Aliases:
Free space:
Error found
Cette fois, l'exécution de l'application continue comme l'atteste la dernière ligne. Le premier noyau est toutefois toujours présent dans le résultat, car celui-ci se termine sur une interruption, ce qui est toujours soit une erreur importante dans l'application soit un événement notable comme ici.
Cette fois, lancez l'application avec le code sans instruction :return
:
cat << EOM | ./sequenceur.svm
> :com.message "a"
> EOM
a
No value
La présence du "No value" indique que malgré l'absence de retour de fonction, tout se passe bien. En effet, l'instruction d'arrêt du processeur implicitement jouée à la fin du code compilé arrête le noyau dans lequel le code compilé est exécuté. Celui qui exécute le code de l'application reprend l'exécution grâce au séquenceur run.stack.
Relancez une dernière fois l'application avec le code tentant d'accéder aux alias "source" et "library" :
cat << EOM | ./sequenceur.svm
> [ ] -> source
> [ ] -> library
> :return
> EOM
### Simple Virtual Machine 1234 : PROCESS application | 2023-01-01 00:00:00 GMT ######################################################################
Kernel interrupted: @(library, line 1, from @(main, line 4)) Interruption MEMORY not handled: Alias source is not linked to a pointer.
Core dump:
Kernel:
State: I, transmit_interruption, last_return_is_shutdown, protected_mode, interrupted @(library, line 1, from @(main, line 4)) Interruption MEMORY not handled: Alias source is not linked to a pointer.
Processor:
State:
Next instruction: <library/1> (Current one: <library/0>)
Current memory : &0*1
Allocated memory: &0*1
Aliases :
Flags :
Cascaded flags :
Local interruption handlers:
Cascaded local interruption handlers:
Saved states:
Global interruption handlers:
Code at <library/0>:
====== HERE ======
[ ] -> source # <0> @(library, line 1, from @(main, line 4))
==================
[ ] -> library # <1> @(library, line 2, from @(main, line 4))
:return # <2> @(library, line 3, from @(main, line 4))
Labels:
Symbols:
Memory:
&0: STR (sync)
Aliases:
Free space:
### Simple Virtual Machine 1234 : PROCESS application | 2023-01-01 00:00:00 GMT ######################################################################
Kernel interrupted: @(library, line 1, from @(main, line 4)) Interruption MEMORY not handled: Alias source is not linked to a pointer.
Core dump:
Kernel:
State: I, transmit_interruption, interrupted @(library, line 1, from @(main, line 4)) Interruption MEMORY not handled: Alias source is not linked to a pointer.
Processor:
State:
Next instruction: <main:1/6> (Current one: <main:1/5>)
Current memory : &0*0
Allocated memory: &0*4
Aliases : data library source stdin
Flags :
Cascaded flags :
Local interruption handlers:
SECURITY => <main:1/11>
Cascaded local interruption handlers:
Saved states:
Global interruption handlers:
Code at <main:1/5>:
:memory com.device/stdin , STR/source , LIB/library , STR/data # <0> @(main, line 1)
:com.open com.terminal STDIN -> &stdin # <1> @(main, line 2) WAITING SYSTEM
:com.read @&stdin com.all -> &source # <2> @(main, line 3) WAITING
:library "library" @&source -> &library # <3> @(main, line 4)
:interruption SECURITY error # <4> @(main, line 5)
====== HERE ======
:run.protected_call $(@&library/) data # <5> @(main, line 6)
==================
:goto no_value :unless &data INITIALISED # <6> @(main, line 7)
:com.message @&data # <7> @(main, line 8)
:shutdown # <8> @(main, line 9)
:label no_value
:com.message "No value" # <9> @(main, line 11)
:shutdown # <10> @(main, line 12)
:label error
:com.message "Error found" # <11> @(main, line 14)
:shutdown # <12> @(main, line 15)
Labels:
error -> <11>
no_value -> <9>
Symbols:
Memory:
&0: com.device Terminal STDIN
&1: STR "[ ] -> source\n[ ] -> library\n:return\n"
&2: LIB <library>
&3: STR (sync)
Aliases:
data -> &3*1
library -> &2*1
source -> &1*1
stdin -> &0*1
Free space:
### Simple Virtual Machine 1234 : SYSTEM | 2023-01-01 00:00:00 GMT ###################################################################################
Process application interrupted: @(library, line 1, from @(main, line 4)) Interruption MEMORY not handled: Alias source is not linked to a pointer.
Ici, vous pouvez constater que les alias de l'application ne sont pas définis, et seule une adresse est disponible : celle voulue par l'application, qui sert de valeur d'échange entre les deux noyaux.
Ce mécanisme d'échange de valeur, dénoté par la mention "(sync)" sur les adresses mémoires n'est pas lié à la création d'un noyau mais au comportement de l'instruction :run.protected_call
: cette instruction, pour simuler un appel de fontion, partage la zone mémoire des paramètres de la fonction entre les deux noyaux, ce qui active la synchronisation de l'adresse.
Il reste un point pratique à aborder en rapport avec le débugueur. Modifiez le code pour ajouter un point d'arrêt :
#!/usr/bin/env svm
DESCRIPTION
Sequencer exploration
END
LOG
DEBUG "Sequencer"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
PROCESS "application"
CODE "main" INLINE
:debug BREAK
:memory com.device/stdin, STR/source, LIB/library, STR/data
:com.open com.terminal STDIN -> &stdin
:com.read @&stdin com.all -> &source
:library "library" @&source -> &library
:interruption SECURITY error
:run.protected_call $(@&library/) data
:goto no_value :unless &data INITIALISED
:com.message @&data
:shutdown
:label no_value
:com.message "No value"
:shutdown
:label error
:com.message "Error found"
:shutdown
END
SEQUENCER run.stack
END
Et lancez l'application en mode débugueur pour l'exécuter pas à pas :
cat << EOM | ./sequenceur.svm -d 8080
> :com.message "a"
> "b" -> &P
> :return
> EOM
L'exécution pas à pas semble éviter le code compilé, qui s'exécute avec un autre processeur : le mode d'exécution dans le débugueur est lié au processeur courant.
Pour pouvoir aussi exécuter en mode pas à pas le code compilé, ajoutez aussi un point d'arrêt sur le code compilé :
cat << EOM | ./sequenceur.svm -d 8080
> :debug BREAK
> :com.message "a"
> "b" -> &P
> :return
> EOM
Dans le débugueur, on constate bien la présence des deux noyaux :
Dans la liste des noyaux, notez l'état des deux noyaux : celui de l'application est suspendu (en pause), et l'autre est en débogage.
Dans la fenêtre du noyau "library", les options du noyau indiquent clairement le mode protégé qui a empêché l'exécution de l'instruction système :shutdown 0
et l'option qui permet à une instruction :return
d'arrêter le processeur sans interruption.
Vous venez de voir que la machine virtuelle peut contenir plusieurs noyaux exécutés en séquence.
La machine fournit un séquenceur minimaliste qui n'accepte qu'un noyau, souvent suffisant pour la majorité des applications.
Les extensions peuvent cependant proposer d'autres séquenceurs ayant des caractéristiques différentes, notamment la capacité à accepter plusieurs noyaux avec une politique de séquencement adaptée.
Même si l'emploi d'un séquenceur d'extension est plutôt rare, il ne faut jamais négliger cette possibilité qui donne des résultats impressionnants lorsqu'elle est bien utilisée.
Enfin, les instructions qui créent des noyaux précisent souvent un séquenceur qui est adapté à leur fonctionnement.