Mardi 22 mars 2005 |
Load (addr, r)
qui copie la valeur présente à l'adresse mémoire
addr
dans le registre r
et l'instruction Store (r, addr)
qui
copie la valeur contenue dans le registre r
à l'adresse addr
.get
et set
de ce
module sont appelées par les instructions Load
et Store
de la
machine pour accéder à la mémoire. Dans cette version,
Memory.get
et Memory.set
accèdent donc directement aux
cases du tableau Memory.memory
.Memory.memory
) en
utilisant une table de pages.preg.(pt)
. Cette table a la taille d'une page (Memory.page_size
).
Chaque entrée de la table des pages est
constituée de deux entiers. Le premier représente le mode d'accès à la
page du processus : UN
non chargée en mémoire; RW
chargée avec
accès en lecture/écriture et COW
chargée, mais doit être copiée
avant la première écriture. Le second entier de l'entrée correspond au
numéro de la page en mémoire physique (si cela a un sens).ocamlc -o main instr.ml memory.ml machine.ml system.ml main.ml |
main 0
, celui-ci doit afficher 12
et 13
.Memory.free_pages
qui
permet de connaître, pour chaque page, le nombre de processus qui
référencent cette page. Écrire également une fonction
Memory.new_page : unit -> int
qui recherche une page vide dans la mémoire
(suite à des désallocations la mémoire peut se retrouver fragmentée), la
réserve, la remplit de 0
, puis retourne le numéro de cette page.
Si la mémoire est pleine, lever une exception Out_of_memory
.Memory.free_pages
réécrire une
fonction Memory.used_page : unit -> bool valide qui retourne
false
si toutes les pages sont libres. Cette fonction est utilisée
dans main.ml
pour vérifier à la fin que vous avez bien libéré la mémoire de
chaque processus. Vérifiez que le programme de test main 0
n'affiche
pas qu'il y a des pages non libérées.
Memory.memory
est découpée en
Memory.page_number
pages physiques. Une page physique k
(donc avec 0 < k < Memory.page_number
) correspond donc aux adresses physiques entre
k × page_size
incluse et (k+1) × page_size
exclue.preg.(pt)
. Pour l'instant on se limitera à un seul
processus (le processus initial).
Une adresse physique est donc déterminée univoquement par le numéro physique
de la table des pages et une adresse logique. entry_address ptable page_nb: int -> int -> int
qui retourne l'adresse
physique de l'entrée (mode et adresse physique)
correspondant à la page logique page_nb
, connaissant la page physique
ptable
de la table des pages.
Memory.get: int -> int -> int
et
Memory.set: int -> int -> int -> unit
afin de lire ou écrire le
contenu d'une adresse logique, connaissant la page physique ptable
de la table des pages.
Lorsque la page n'est pas réservée (UN
) les
fonctions lèvent l'exception Page_fault
avec le numéro de la page logique en
argument. On supposera pour l'instant que les pages sont soit non
réservées, soit réservées en écriture.
Memory.allocate_page ptable page_nb: int -> int -> unit
qui réserve une
nouvelle page physique pour la page logique page_nb
connaissant la page physique ptable
de la table des pages.
Memory.new_ptable: unit -> int
qui retourne une nouvelle
page physique après en avoir fait une table de pages vide.
System.init_state
afin de réserver la table des
pages du processus initial.
System.page_fault
afin de réserver une page en cas
de faute de page. Tester votre programme en vérifiant qu'après
utilisation de la table des pages le programme main 0
affiche le même résultat
que sans table de pages.
Memory.release_ptable ptable size
qui
libère toutes les pages associées à la table des pages dont la page
physique est ptable
et dont le nombre de pages est size
.sys_Exit
afin de libérer les
pages mémoires réservées à la fin des processus. On vérifiera que
toutes les pages sont bien désallouées en appelant main 0
.fork
.Memory.clone_ptable ptable size
qui clone
la table des pages dont la page physique est passée en argument et dont le
nombre de pages est donné par size
.
sys_Fork
pour prendre en compte
l'utilisation de la mémoire virtuelle. Tester cette modification en
appelant main 1
qui crée trois processus qui effectuent des accès
mémoire et qui doivent normalement afficher 12
, 12
, 13
, 12
, 14
, 12
, 13
, 12
et 14
.fork
les pages partagées sont
marquées COW
(Copy On Write) et elles sont recopiées lors
d'un accès en écriture set
.Memory.clone_ptable
pour supporter la copie
paresseuse. Memory.set
pour tenir compte de ce
changement. Tester vos modifications en appelant à nouveau main 1
.
brk
en modifiant la valeur du champs ptable_size
du
processus.brk
peut diminuer la taille de la table
des pages et il faut alors désallouer les pages qui ne sont plus
accessibles.sys_Brk
.
System.page_fault
pour prendre en compte cette
information lors d'une faute de page pour lever une exception
Segmentation_fault
lors de l'accès à une page hors limite. Testez
vos modifications en appelant le programme de test main 2
qui doit afficher 12
, 13
, 12
, 14
, Segmentation fault
, 12
, 13
, 12
, 14
, Segmentation fault
.Ce document a été traduit de LATEX par HEVEA