Il est demandé de réaliser une machine virtuelle (U-235) et son mini-noyau d'exploitation (P-239), le tout se nommant MOX. Un ordonnanceur préemptif et une mémoire virtuelle seront placés au centre du noyau (P-239). La machine physique exécutera des programmes écrits dans son langage (U-235).
Les exécutables U-235 seront obtenus par compilation de programmes sources écrits en langage U.
Il est constitué de deux types d'instructions:
Dans le tableau suivant, on trouvera la liste des instructions, la liste de leurs arguments et leur correspondance (approximative) en langage C. Les arguments sont de trois types:
Mnémonique | Arguments | instruction C/Unix |
---|---|---|
ASSIGN |
réf, réf_ou_cste |
réf = réf_ou_cste; |
ASSIGN+ |
réf, réf_ou_cste |
réf += réf_ou_cste; |
ASSIGN- |
réf, réf_ou_cste |
réf -= réf_ou_cste; |
ASSIGN* |
réf, réf_ou_cste |
réf *= réf_ou_cste; |
IF |
réf_ou_cste, réf_ou_cste, étiquette |
if (réf_ou_cste==réf_ou_cste) goto étiquette; |
JUMP |
étiquette | goto étiquette; |
CALL |
étiquette | étiquette(); |
RET |
return; |
|
FORK |
réf | réf = fork(); |
EXEC |
réf_ou_cste | exec(réf_ou_cste); |
EXIT |
réf_ou_cste | exit(réf_ou_cste); |
WAIT |
réf, réf |
réf = wait(réf); |
SIGNAL |
réf_ou_sig, étiquette |
signal(réf_ou_cste,étiquette); |
KILL |
réf_ou_sig, réf |
kill(réf_ou_cste,réf); |
READ |
réf | read(STDIN_FILENO,réf,1); |
WRITE |
réf_ou_cste | write(STDOUT_FILENO,réf_ou_cste,1); |
GETPPID |
réf | réf = getppid(); |
Les arguments sont de quatre types:
*
(le
nombre est le numéro d'une case mémoire dont le contenu est le
numéro de la case mémoire à consulter;#
nombre;U_SIGILL
,
U_SIGMEM
, U_SIGUSER
ou
U_SIGKILL
;:
) et
permettre au programmeur d'utiliser des symboles pour ses sauts ou appels
de fonctions.La spécification du segment permet d'indiquer dans quel espace mémoire la référence doit se faire:
C
: segment de code qui ne peut être adressé en
assembleur. Il s'agit du segment concerné par les instructions comportant
un saut (étiquette);D
: segment de données statiques qui contient toujours 256
cases mémoires numérotées de 0 à 255. Au chargement d'un processus cet
espace est considéré comme constitué de 0;S
: segment de pile dont la taile est dynamique et évolue
en fonction des apples/retours de fonctions.On notera la possibilité de commenter du code U en commençant une ligne par
le caractère /
.
Un compilateur de U en U-235 vous est fournit (mox.tar.gz). Il ne permet d'écrire qu'une seule instruction par ligne de texte. Une instruction n'est jamais située en début de ligne, mais est toujours précédée d'au moins un espace ou une tabulation.
IMPORTANT: un programme U doît obligatoirement contenir une instrution
étiquettée par debut
et permettant d'indiquer le point d'entrée
du programme (équivalent du main()
du C).
Ce qui suit est un exemple de programme source U:
/ On commence par compter jusqu'a 10 debut: ASSIGN D13,#0 incr: ASSIGN+ D13,#1 IF D13,#10,fin_incr JUMP incr / On essaye d'appeler une fonction fin_incr: CALL remise_a_zero / On tente de capturer SIGUSER SIGNAL U_SIGUSER,sig_handler / Et un petit fork/exec/wait FORK D1 IF D1,#0,fils / Le pere attend WAIT D2,D3 / Il ecrit 'b' WRITE #65 EXIT #0 remise_a_zero: ASSIGN D13,#0 RET / Le code du fils! fils: / Il envoie un SIGUSER a son pere GETPPID D5 KILL U_SIGUSER,D5 / Il exec sur 'a' EXEC #64 EXIT #1 sig_handler: WRITE #64 RET
Il est constitué des mêmes instructions décrites précédemment mais codées
en binaire. Le format exact de chaque instruction pourra être retrouvé en
consultant le fichier assembler.h
. Les sources du compilateur
sont disponibles dans le fichier mox.tar.gz. Est
inclus dans cette distribution un désassembleur que l'on pourra utiliser pour
comprendre comment lire (puis exécuter) du code U-235.
Il est formellement déconseillé de modifier quoi que ce soit dans les
programmes fournis, vous y perdriez un temps considérable pour un gain null
ou presque. Le seul fichier à modifier (éventuellement) est le
Makefile
pour paramétrer la compilation sur votre système. Le
seul programme éventuellement intéressant à observer est
desassembleur.c
.
L'assembleur a pour syntaxe d'appel: assembler source.u
objet.u235
.
Le désassembleur a pour syntaxe d'appel: desassembler < objet.u235
.
Il est demandé d'écrire un programme permetant de simuler des exécutions de programmes U-235. Dans le noyau P-239, il s'agira de processus qui seront ordonnancés en partageant le temps par préemption.
Le système MOX devra utiliser un adressage virtuel pour les processus. Adressage qui devra permettre de partager le code lorsque différents processus exécuteront le même code.
La figure suivante illustre l'architecture globale du système:
On y trouve:
SIGHOR
(signal à choisir parmi les
existants POSIX) exécutera une instruction U-235 du processus courant ou
une fonctionnalité système (préemption, etc.);READ
, etc). Le protocole
conversationnel avec MOX est
simple: lorsque l'interface désire faire faire quelque chose à MOX, il
écrit sa requête dans le tube ou la file de message adéquate et prévient
MOX qu'une commande est à traiter en lui délivrant le signal
SIGIF
(à choisir parmi les signaux POSIX);On a déjà mentionné l'existence de différents segments mémoire (code, statique et pile). Chacun de ces espaces est constitué de pages de 64 octets de long lesquelles peuvent être selon les besoins déchargées en mémoire secondaire (swap).
La mémoire physique comme nous l'avons déjà indiqué est un simple segment de mémoire partagée permettant de contenir 16 pages de 64 octets.
Le swap est constitué de 128 pages de 64 octets.
L'instruction EXEC
qui permet de recouvrir le code du
processus courant par un nouveau code obtient le nouveau code à charger en
effectuant des lectures sur le fichier (du système Unix) dont le nom est
donnée par le paramètre. Ce dernier est toujours un entier (obtenu
éventuellement par référence) qui sera alors utilisé une fois converti en
chaîne de caractères comme nom du fichier à charger. Ainsi une instruction
comme EXEC #12
permettra de charger le programme contenu dans le
fichier 12.u235
.
L'instruction WAIT
prend deux arguments, le premier sera
l'adresse où sera rangée l'identité du fils terminé et la seconde celle
contenant le code de retour.
Les instructions READ
ou WRITE
ne permette de
lire ou d'écrire qu'un seul caractère à la fois et dont le code ASCII est
donnée en argument (écrire) ou renvoyé par le système (lire). Si l'écriture
est immédiate (par exemple affichage d'un message: le processus numéro
87 a écrit le caractère ASCII 64 'A'
), la lecture provoque le blocage
du processus en cours tant qu'aucune donnée ne lui a été fournie par
l'interface (on peut envisager un tampon de lecture).
Le quantum de temps alloué à un processus est de 10 tops d'horloge. Au-delà un processus est préempté par l'ordonnanceur.
Pour le reste...
Les soutenances auront lieu les 13 et 14 juin. Les inscriptions sont ouvertes auprès du secrétariat. Compte-tenu du temps imparti à la réalisation (2 mois), aucun problème d'inscription ne sera toléré.