Ce didacticiel est destiné aux nouveaux utilisateurs de la Simple Virtual Machine.
Dans ce didacticiel, vous allez utiliser les entrées/sorties de base.
Le temps de lecture de ce didacticiel est estimé à 20 minutes si les extensions ont été abordées.
Pour commencer, créez le canevas de l'application dans le fichier exécutable peripheriques.svm en utilisant ce code :
#!/usr/bin/env svm
DESCRIPTION
Input/output example
END
LOG
DEBUG "Input/output" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
OPTION -f STR file
OPTION -p STR port
PROCESS "application"
CODE "main" INLINE
:debug BREAK
END
MEMORY ".*"
END
Modifiez le code pour obtenir :
#!/usr/bin/env svm
DESCRIPTION
Input/output example
END
LOG
DEBUG "Input/output" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
OPTION -f STR file
OPTION -p STR port
PROCESS "application"
CODE "main" INLINE
:debug BREAK
:memory com.device/stdin, com.device/stdout, com.device/stderr
:com.open com.terminal STDIN -> &stdin
:com.open com.terminal STDOUT -> &stdout
:com.open com.terminal STDERR -> &stderr
[ ] -> stdin
[ ] -> stdout
[ ] -> stderr
END
MEMORY ".*"
END
Lancez l'application en mode débugueur. Après la dernière instruction :com.open
, la mémoire ressemble à :
L'instruction :com.open
permet d'ouvrir un périphérique. Ici, les trois flux du terminal (respectivement entrée standard, sortie standard et sortie d'erreur) sont ouverts et pourront être utilisés pour les interactions avec le terminal.
Continuez l'exécution du code jusqu'à sa fin en mode pas à pas : les différents flux du terminal sont fermés en détruisant le périphérique correspondant. Notez que si les flux seront également fermés si une libération mémoire arrive sur le périphérique correspondant.
Modifiez le code pour ajouter une lecture depuis l'entrée standard :
#!/usr/bin/env svm
DESCRIPTION
Input/output example
END
LOG
DEBUG "Input/output" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
OPTION -f STR file
OPTION -p STR port
PROCESS "application"
CODE "main" INLINE
:debug BREAK
:memory com.device/stdin, com.device/stdout, com.device/stderr, STR/line
:com.open com.terminal STDIN -> &stdin
:com.open com.terminal STDOUT -> &stdout
:com.open com.terminal STDERR -> &stderr
:com.read @&stdin com.line -> &line
:com.message "<" @&line ">"
[ ] -> stdin
[ ] -> stdout
[ ] -> stderr
END
MEMORY ".*"
END
Puis exécutez le code :
./peripheriques.svm
text
<text
>
L'instruction :com.read
ici lit sur l'entrée standard jusqu'à ce qu'une ligne soit entrée (ceci est spécifié par le point d'entrée d'extension com.line
, puis le texte lu est reproduit sur le terminal avec l'instruction :com.message
.
Maintenant, modifiez le code pour utiliser une écriture directe sur le terminal :
#!/usr/bin/env svm
DESCRIPTION
Input/output example
END
LOG
DEBUG "Input/output" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
OPTION -f STR file
OPTION -p STR port
PROCESS "application"
CODE "main" INLINE
:debug BREAK
:memory com.device/stdin, com.device/stdout, com.device/stderr, STR/line
:com.open com.terminal STDIN -> &stdin
:com.open com.terminal STDOUT -> &stdout
:com.open com.terminal STDERR -> &stderr
:com.read @&stdin com.line -> &line
:com.write @&stderr "<" @&line ">\n"
[ ] -> stdin
[ ] -> stdout
[ ] -> stderr
END
MEMORY ".*"
END
Lancez l'application à nouveau. Le résultat sera identique, même si la méthode d'écriture est plus générique que :com.message
spécifique au terminal.
En plus du terminal, l'extension com permet d'utiliser les fichiers au travers de l'abstraction d'un périphérique.
Modifiez le code :
#!/usr/bin/env svm
DESCRIPTION
Input/output example
END
LOG
DEBUG "Input/output" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
OPTION -f STR file
OPTION -p STR port
PROCESS "application"
CODE "main" INLINE
:debug BREAK
:memory com.device/device, STR/text
:com.open com.file > @&file -> &device
:com.write @&device "text"
:com.open com.file < @&file -> &device
:com.read @&device com.all -> &text
:com.message @&text
END
MEMORY ".*"
END
Lancez l'application avec l'option -f :
./peripheriques.svm -f /tmp/file
text
cat /tmp/file
text
Le fichier (ici /tmp/file) a bien été écrit puis lu par le code grâce aux instructions :com.read
et :com.write
. L'instruction de lecture utilise le point d'entrée d'extension com.all
pour lire l'intégralité du fichier.
Le fichier est ici ouvert deux fois :
:com.open
indique que le fichier est ouvert en écriture seule,:com.open
indique que le fichier est ouvert en lecture seule.La seconde instruction :com.open
écrit son résultat à l'adresse où se trouvait le périphérique d'accès au fichier en écriture : cet accès est immédiatement fermé, et remplacé par celui en lecture seule. Relancez l'application en mode débugueur. Avant la seconde instruction :com.open
, vous devez obtenir :
Et après l'instruction :
Dans certaines circonstances, il peut être utile de lire ou écrire dans un fichier à une position différente. Modifiez le code pour obtenir :
#!/usr/bin/env svm
DESCRIPTION
Input/output example
END
LOG
DEBUG "Input/output" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
OPTION -f STR file
OPTION -p STR port
PROCESS "application"
CODE "main" INLINE
:debug BREAK
:memory com.device/device, STR/text
:com.open com.file > @&file -> &device
:com.write @&device "text text"
:com.command @&device SEEK BEGIN 4
:com.write @&device "TEXT"
:com.open com.file < @&file -> &device
:com.read @&device com.all -> &text
:com.message @&text
END
MEMORY ".*"
END
Puis lancez l'application :
./peripheriques.svm -f /tmp/file
textTEXTtext
L'instruction :com.command
permet de replacer le pointeur de lecture/écriture dans le fichier, ici placé sur le cinquième octet en partant du début du fichier.
Un autre type de périphérique fourni avec l'extension com permet d'utiliser les connexions réseaux TCP/IP.
Modifiez à nouveau le code :
#!/usr/bin/env svm
DESCRIPTION
Input/output example
END
LOG
DEBUG "Input/output" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
OPTION -f STR file
OPTION -p STR port
PROCESS "application"
CODE "main" INLINE
:debug BREAK
:memory com.device/device, STR/text
:com.open com.tcp > "localhost" @&port -> &device
:com.write @&device "query"
:com.read @&device com.line -> &text
:com.message @&text
END
MEMORY ".*"
END
Dans un premier terminal, lancez cette commande pour simuler un serveur réseau :
echo reply | netcat -l -p 31415
Puis lancez l'application dans un autre terminal :
./peripheriques.svm -p 31415
reply
Vous noterez la présence du mot "query" dans le terminal du serveur TCP/IP après l'exécution de l'application.
Ici, l'application a ouvert une connexion réseau en tant que client, a lancé une requête et obtenu une réponse.
Les seules différences avec un périphérique sur un fichier sont :
:com.open
mentionne le type com.tcp
, suivi de ">" pour indiquer une connexion en tant que client sur l'IP "localhost" et le port donné en argument de l'application,Modifiez le code pour obtenir :
#!/usr/bin/env svm
DESCRIPTION
Input/output example
END
LOG
DEBUG "Input/output" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmrun.so"
OPTION -f STR file
OPTION -p STR port
PROCESS "application"
CODE "main" INLINE
:debug BREAK
:memory com.device/server, com.device/device, STR/text
:com.open com.tcp < "localhost" @&port -> &server
:com.command @&server CLIENT -> &device
:com.read @&device com.line -> &text
:com.message @&text
:com.write @&device "reply"
END
MEMORY ".*"
END
Dans un premier terminal, lancez l'application :
./peripheriques.svm -p 31415
query
Dans un autre terminal, lancez cette commande pour simuler un client réseau :
echo query | netcat localhost 31415
reply
Cette fois-ci, l'application se comporte en serveur réseau : elle attend une connexion venant d'un client sur l'instruction :com.command
, puis attend une requête sur une ligne et répond.
Notez la présence de deux périphériques : le premier sert à attendre qu'un client se connecte, et le second sert à la communication avec le client. Lancée avec le débugueur, la mémoire contient après la lecture de la requête client :
Les deux périphériques sont ici visibles :
Ici, l'application n'accepte qu'une seule connexion. Il est possible de placer le code dans une boucle pour traiter plusieurs clients successivement. Pour les traiter en parallèle, il faut jouer avec l'architecture de la machine. Cela est possible mais dépasse largement le cadre de ce didacticiel.
Les fichiers et les connexions réseaux TCP/IP ne sont pas les seuls périphériques disponibles. D'autres extensions peuvent fournir des implémentations de périphérique.
Pour les utiliser, il suffit de préciser le type de périphérique avec ses arguments au moment de l'ouverture du périphérique.
Ensuite, les opérations de lecture et/ou d'écriture sont réalisées de la même manière que les fichiers. Les commandes en revanche peuvent varier pour d'autres périphériques.
:com.open
suivie du type de périphérique et de ses arguments d'ouverture,com.device
donnant accès au canal d'entrée/sortie,:com.read
, où la quantité d'octets à lire est indiqué par un point d'entrée d'extension (appellé protocole, car il indique comment découper le flux d'octets entrant en blocs atomiques et compréhensible par la suite du code) en argument,:com.write
,:com.command
, où la commande et ses arguments sont spécifiques au type de périphérique.Vous venez de voir comment manipuler les entrées/sorties dans une application.
Une application complète utilise fréquemment des entrées/sorties pour réaliser des tâches, et les maitriser permet de créer des applications ayant une intégration fluide avec leur environnement.
L'extension com étant une interface avec quelques implémentations, il est vraiment utile de regarder attentivement les documentations de cette extension (avec la commande man svm_plugin_com
) ainsi que la documentation des extensions proposant des périphériques et des protocoles.