Introduction

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.

Mise en place

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

Terminal

Ouverture et fermeture

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 à :

Input/output
Memory - K main - P application
AddressTypeValue
&0STR
&1STR
&2com.deviceTerminal STDIN
&3com.deviceTerminal STDOUT
&4com.deviceTerminal STDERR
AliasPointer
file&0*1
port&1*1
stderr&4*1
stdin&2*1
stdout&3*1
Address:
Display
Aliases
P
Focus
Back
Clear

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.

Lecture

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.

Ecriture

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.

Fichiers

En plus du terminal, l'extension com permet d'utiliser les fichiers au travers de l'abstraction d'un périphérique.

Opérations de base

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 :

  1. Le marqueur > de la première instruction :com.open indique que le fichier est ouvert en écriture seule,
  2. Le marqueur < de la première instruction :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 :

Input/output
Memory - K main - P application
AddressTypeValue
&0STR"/tmp/file"
&1STR
&2com.deviceFile > /tmp/file (11)
&3STR
AliasPointer
file&0*1
port&1*1
device&2*1
text&3*1
Address:
Display
Aliases
P
Focus
Back
Clear

Et après l'instruction :

Input/output
Memory - K main - P application
AddressTypeValue
&0STR"/tmp/file"
&1STR
&2com.deviceFile < /tmp/file (6)
&3STR
AliasPointer
file&0*1
port&1*1
device&2*1
text&3*1
Address:
Display
Aliases
P
Focus
Back
Clear

Commandes

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.

Connexions TCP/IP

Un autre type de périphérique fourni avec l'extension com permet d'utiliser les connexions réseaux TCP/IP.

Client

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 :

Serveur

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 :

Input/output
Memory - K main - P application
AddressTypeValue
&0STR
&1STR"31415"
&2com.deviceTCP socket < localhost:31415 (13)
&3com.deviceTCP socket > < :::35150 (14)
&4STR"query\n"
AliasPointer
file&0*1
port&1*1
device&3*1
server&2*1
text&4*1
Address:
Display
Aliases
P
Focus
Back
Clear

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.

Périphériques

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.

Conclusion

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.