< Les appels systèmes | TutoOS | Gérer la mémoire - utiliser la pagination pour une tâche utilisateur >
Le package contenant les sources est téléchargeable ici : kernel_PagingEnable.tgz
Pour naviguer dans l'arborescence : PagingEnable
Nous avons déjà vu que dans le système de segmentation, une adresse physique est calculée à partir d'un sélecteur de segment et d'un offset. Avec la pagination activée, l'adresse obtenue par la segmentation est une adresse linéaire qui sera ensuite traduite en une adresse physique :
La pagination est un mécanisme d'adressage de la mémoire qui permet :
Quand la pagination est activée, le processeur découpe l'espace d'adressage linéaire (ou virtuel) et la mémoire physique en pages de taille fixe (généralement 4ko ou 4Mo) qui peuvent être associées librement. La traduction des adresses linéaires en adresses physiques est réalisée par le processeur grâce à plusieurs structures :
Le schéma ci-dessous illustre l'association entre des pages en mémoire linéaire (ou virtuelle) et en mémoire physique par l'unité de pagination de la MMU (Memory Management Unit) :
La traduction d'une adresse linéaire en une adresse physique se fait en plusieurs étapes :
0
et 1023
, qui pointe sur une entrée de ce répertoire de pages
0
et 4095
. Ce déplacement permet de pointer sur une adresse précise en mémoire.
Les entrées de ces deux tableaux se ressemblent beaucoup. Seuls les champs en grisé seront utilisés dans nos premières implémentations :
On peut noter que les adresses physiques du répertoire de pages ou de la table des pages sont... sur 20 bits ! En fait, ces adresses doivent être alignées sur 4ko, ce qui signifie que les 12 bits de poids faible doivent être à 0
. Le processeur forme cette adresse à partir des 20 bits de poids fort et la complète avec 12 bits à 0
. Nous avions déja vu ce type de mécanisme avec les sélecteurs de segment de la GDT.
Le TLB (Translation Lookaside Buffer) est un cache des répertoires et des tables des pages utilisés afin d'accélérer la traduction d'adresse. Quand un répertoire ou une table des pages est modifiée, les pages concernées doivent être immédiatement invalidées dans le TLB. Dans un premier temps, nous n'aurons pas besoin de nous préoccuper de ces détails.
Pour activer la pagination, il suffit de passer le bit 31 du registre CR0 à 1 :
Bien entendu, pour que celà fonctionne, il faut au préalable avoir initialisé un répertoire et au moins une table des pages !
Pour une première implémentation, nous n'allons pas créer de tâche utilisateur. La pagination s'appliquera donc uniquement au noyau.
Dans cette première implémentation, nous allons activer la pagination pour le noyau tel que les 4 premiers Mo de mémoire virtuelle coincident avec les 4 premiers Mo de mémoire physique :
Ce modèle est simple : la première page en mémoire virtuelle correspond à la première page en mémoire physique, la deuxième page en mémoire virtuelle correspond à la deuxième en mémoire physique et ainsi de suite...
Nous avons vu dans les chapitres précédent que notre noyau est tout petit et qu'il loge dans moins de 64 ko, nous allons adopter l'organisation suivante :
0x0
et 0x10000
0x10000
et 0x20000
0x20000
0x21000
Les entrées du répertoire et de la table seront initialisés avec les valeurs suivantes :
La fonction init_mm()
initialise le répertoire et les tables de pages du noyau et active la pagination :
Cette instruction fait pointer la variable pd0
sur le répertoire de pages à l'adresse physique 0x20000
. Notez que pd0
peut aussi être vu comme un tableau de 1024 entrées.
Ensuite, on initialise la première entrée du répertoire en pd0[0]
pour la faire pointer sur la première table de pages. Cette table est située juste après le répertoire, à l'adresse physique 0x21000
.
Pourquoi avoir choisi la première page de table et pas une des 1023 autre ? Tout simplement parceque l'un des points clef de notre schéma d'adressage est que l'adresse 0
virtuelle doit correspondre à l'adresse 0
en mémoire physique et ainsi que pour toutes les adresses dans la plage adressée. Le choix d'une autre table aurait été possible, mais celà aurait généré un décalage entre les adresses en mémoire virtuelle et celles mémoire physique.
Les deux premiers bits sont mis à 1 pour indiquer que les pages / tables pointées sont présentes en mémoire et qu'elles sont accessibles en lecture et en écriture.
Les autres entrées du répertoire de pages sont mises à zéro. On n'utilise donc qu'une seule table de page, ce qui suffit pour adresser 4 Mo.
La table de page est initialisée de façon à ce que chacune de ses entrées pointe sur une page mémoire successive. La première page adresse la mémoire à partir de l'adresse 0
et les autre pages se succèdent.
Lorsque le processeur tente d'accéder à une page qui n'est pas présente en mémoire physique, il déclenche une exception de type Page Fault. L'adresse qui a provoqué le défaut de page est stockée dans le registre CR2. A partir de cette adresse et selon le contexte d'exécution qui a provoqué l'exception, la routine de traitement devrait au choix :
Dans notre cas, le noyau va simplement afficher un message d'erreur et stopper.
L'affichage du message et des petits points montre que malgré le passage en mode paginé, le noyau fonctionne normalement. L'annexe sur le mode debug de Bochs indique quelques commandes utiles pour vérifier la bonne prise en compte du répertoire et des tables de pages.
< Les appels systèmes | TutoOS | Gérer la mémoire - utiliser la pagination pour une tâche utilisateur >