Autor Thema: Anfängerfragen zu Paging  (Gelesen 12352 mal)

Developer30

  • Beiträge: 16
    • Profil anzeigen
Gespeichert
« am: 07. August 2013, 17:27 »
Hallo :-)
Ich möchte Paging in meinem Kernel implementieren. Die Grundidee hinter dem virtuellen Adressraum habe ich verstanden, allerdings ergeben sich trotzdem ein paar Fragen.
Auf der Wikiseite zu Paging heißt es bei der Page-Directory Tabelle:

Zitat von: Bit 7 des Page Directory Eintrags
Gibt die Größe der Page an. 0 steht für 4 kB, 1 für 4 MB (oder 2 MB bei Verwendung von PAE). Das S-Bit ist wirkungslos, wenn nicht im Steuerregister CR4 das PSE-Bit (4-MB-Pages) oder PAE-Bit (2-MB-Pages) gesetzt ist.
Ich dachte bisher, dass Pages immer 4kb seien, daher verstehe ich nicht, warum es hier eine Fallunterscheidung gibt :?

Außerdem frage ich mich, ob sich Bit 1 und Bit 2 nur auf den Page Directory Eintrag selbst beziehen oder auch auf die untergeordnete Pagetable. Wenn z.b. Bit 2 nicht gesetzt ist, können Ring 3 Tasks dann trotzdem auf untergeordnete Pagetable Einträge zugreifen, wenn bei diesen dann das 2. Bit gesetzt ist, oder "vererbt" sich das quasi von dem Page Directory auf die untergeordnete Page Table?

Im Tutorial zu Paging steht unter Dynamisches Mapping, dass es einen Trick gäbe, um die Paging-Strukturen zu mappen:
Zitat von: Paging Tutorial - Dynamisches Mapping
Für das Mappen eines Page Directory und allen zugehörigen Page Tables gibt es einen Trick: Wenn ein Eintrag eines Page Directory wieder auf ein Page Directory (z.B. sich selbst) zeigt, dann wird dieses Page Directory an dieser Stelle als Page Table verwendet. Dadurch werden alle enthaltenen Page Tables hintereinander in den virtuellen Speicher gemappt. Dieser Trick wird oft für das aktuelle Page Directory benutzt, d.h. ein bestimmter Eintrag in jedem PD zeigt wieder auf das PD selbst.
Das heißt, wenn ein Page Directory Eintrag auf sich selbst zeigt, dann wird es selbst zum ersten Page Table Eintrag und alle anderen Page Directory Einträge müssten dann ja zu weiteren Page Table Einträge werden, wodurch dann die gesamte Page Directory Tabelle gemappt wird, oder wie? Das heißt, es gäbe Sinn, den ersten Page Directory Eintrag immer auf sich selbst zeigen zu lassen - habe ich das richtig verstanden?

Zitat von: Paging Tutorial - Dynamisches Mapping
Beim Kernelstart müssen einige Bereiche gemappt werden:
  • Der Kernelcode selbst
  • Der Videospeicher von B8000 bis BFFFF
  • Evtl. Multiboot-Strukturen, die noch verwendet werden sollen
  • Zum Zeitpunkt der Paging-Initialisierung schon dynamisch angelegte Kerneldaten, z.B. die Bitmap der physischen Speicherverwaltung
Dass die ganzen Bereiche, die vor der Paging Initialisierung erstellt wurden, gemappt werden müssen erscheint mir ja logisch. Nur bin ich mir nicht sicher, woher ich denn weiß, wo der Kernelcode selbst im Speicher zu finden ist. Also dem Linkscript kann ich entnehmen, dass mein Kernel bei 0x100000 beginnt und der Stack ist zu Beginn an der 2MB Grenze gesetzt. Heißt das denn auch, dass mein Kernelcode also vollständig zwischen 0x100000 und 0x200000 liegt und mit einer Größe von 1MB also 256 Pages beansprucht? (Ich vermute mal: nein :()
Zudem heißt es, dass z.b. Daten, wie die Bitmap der physischen Speicherverwaltung als dynamisch angelegte Kerneldaten gemappt werden müssten. Die Bitmap selbst habe ich aber garnicht dynamisch angelegt und das Tutorial zur physischen Speicherverwaltung legt sie ebenso statisch auf dem Stack an wie ich, was bedeutet, dass sie doch mit dem Kernelcode automatisch mitgemappt werden müsste - korrekt?

Ich hoffe ich habe nicht allzu wenig verstanden :|,

lg
Developer30

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 07. August 2013, 18:34 »
Zitat von: Bit 7 des Page Directory Eintrags
Gibt die Größe der Page an. 0 steht für 4 kB, 1 für 4 MB (oder 2 MB bei Verwendung von PAE). Das S-Bit ist wirkungslos, wenn nicht im Steuerregister CR4 das PSE-Bit (4-MB-Pages) oder PAE-Bit (2-MB-Pages) gesetzt ist.
Ich dachte bisher, dass Pages immer 4kb seien, daher verstehe ich nicht, warum es hier eine Fallunterscheidung gibt :?
Bis zum Pentium war das auch so. 4 KB Pages sind nur dann etwas unschön, wenn du 4 MB physischen Speicher am Stück hast und den komplett in den virtuellen Adressraum mappen willst. Dann musst du dafür extra eine Page Table anlegen, die 1024 Einträge hat, die aufsteigende Einträge X, X+4096, X+8192, usw... enthält, um jede der Pages einzeln zu mappen. Weil die Intel-Ingenieure dachten, dass man sich für diesen Fall die Page Table sparen könnte, haben die das Flag eingefügt, sodass schon der Eintrag im Page Directory den virtuellen Speicherbereich mappt.

Außerdem frage ich mich, ob sich Bit 1 und Bit 2 nur auf den Page Directory Eintrag selbst beziehen oder auch auf die untergeordnete Pagetable. Wenn z.b. Bit 2 nicht gesetzt ist, können Ring 3 Tasks dann trotzdem auf untergeordnete Pagetable Einträge zugreifen, wenn bei diesen dann das 2. Bit gesetzt ist, oder "vererbt" sich das quasi von dem Page Directory auf die untergeordnete Page Table?
Um die Terminologie mal etwas aufzuklären: Die Bits beziehen sich auf die Page im virtuellen Adressraum. Tasks greifen (in der Regel) nicht direkt auf die Page Table zu, sondern die CPU wertet die Paging-Datenstrukturen bei einem Speicherzugriff aus. Die "Vererbung" der Bits 1 und 2 funktioniert so: Wenn im Page Directory oder der Page Table Bit 1 (R/W) nicht gesetzt ist, ist die Page read-only. Nur wenn in beiden Datenstrukturen das Bit gesetzt ist, kann die Page beschrieben werden. Analog Bit 2 (U/S): Nur wenn es in beiden Datenstrukturen auf 1 gesetzt ist, können Ring 3 Tasks auf die Page zugreifen. Man kann auch sagen, dass die Bits jeweils mit einer AND-Operation verknüpft werden.

Das heißt, wenn ein Page Directory Eintrag auf sich selbst zeigt, dann wird es selbst zum ersten Page Table Eintrag und alle anderen Page Directory Einträge müssten dann ja zu weiteren Page Table Einträge werden, wodurch dann die gesamte Page Directory Tabelle gemappt wird, oder wie? Das heißt, es gäbe Sinn, den ersten Page Directory Eintrag immer auf sich selbst zeigen zu lassen - habe ich das richtig verstanden?
So ist es. Es muss übrigens nicht zwingend der erste Eintrag sein, sondern jeder Eintrag kann dafür genutzt werden.

Nur bin ich mir nicht sicher, woher ich denn weiß, wo der Kernelcode selbst im Speicher zu finden ist. Also dem Linkscript kann ich entnehmen, dass mein Kernel bei 0x100000 beginnt
Du kannst im Linkerskript sogar genaue Labels definieren, die dir sagen, wo der Kernel anfängt und aufhört:
...
SECTIONS
{
    . = 0x100000;

    kernel_start = .;

    // hier steht die Definition der Sektionen (.text, .data, ...)

    kernel_end = .;
}

Die Symbole kernel_start und kernel_end sind direkt vor dem Anfang bzw. direkt hinter dem Ende des Kernels platziert. Im C-Code kannst du auf die zum Beispiel so zugreifen:
extern char kernel_start[];
extern char kernel_end[];

// irgendwo im Code:
map_kernel_pages((uintptr_t)kernel_start, (uintptr_t)kernel_end);

In unserem Tutorial liegt der Stack in der .bss-Sektion also ebenfalls zwischen kernel_start und kernel_end und muss nicht separat gemappt werden. Wenn du deinen Stack explizit woanders hinlegst, solltest du ein paar Pages (1-2) dafür reservieren, aber 256 brauchst du sicherlich nicht.

Die Bitmap selbst habe ich aber garnicht dynamisch angelegt und das Tutorial zur physischen Speicherverwaltung legt sie ebenso statisch auf dem Stack an wie ich, was bedeutet, dass sie doch mit dem Kernelcode automatisch mitgemappt werden müsste - korrekt?
Die Bitmap ist zwar in der .data-Sektion und nicht auf dem Stack, aber es ist korrekt, dass die zusammen mit dem Kernel gemappt wird.
Dieser Text wird unter jedem Beitrag angezeigt.

Developer30

  • Beiträge: 16
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 07. August 2013, 18:57 »
Zunächst danke für deine Antwort :-).

Bis zum Pentium war das auch so. 4 KB Pages sind nur dann etwas unschön, wenn du 4 MB physischen Speicher am Stück hast und den komplett in den virtuellen Adressraum mappen willst. Dann musst du dafür extra eine Page Table anlegen, die 1024 Einträge hat, die aufsteigende Einträge X, X+4096, X+8192, usw... enthält, um jede der Pages einzeln zu mappen. Weil die Intel-Ingenieure dachten, dass man sich für diesen Fall die Page Table sparen könnte, haben die das Flag eingefügt, sodass schon der Eintrag im Page Directory den virtuellen Speicherbereich mappt.
Ok, das gibt Sinn :-D.

Man kann auch sagen, dass die Bits jeweils mit einer AND-Operation verknüpft werden.
Ah ok. Ich verstehe..

Es muss übrigens nicht zwingend der erste Eintrag sein, sondern jeder Eintrag kann dafür genutzt werden.
Wenn ich nicht den ersten Eintrag nehme, dann muss der Eintrag, den ich nehme, aber auf den ersten Eintrag zeigen und nicht auf sich selbst oder? Sonst würden die vorherigen Einträge doch verloren gehen, oder habe ich einen Denkfehler?

Zitat von: Jidder link=topic=3269.msg37950#msg37950 date=1375893270
Du kannst im Linkerskript sogar genaue Labels definieren, die dir sagen, wo der Kernel anfängt und aufhört
Oh das wusste ich nicht. Kennt jemand einen anschaulischen Artikel zu Linkskripts auf lowlevel.eu (oder sonst wo)? weil ich glaube da habe ich noch etwas Nachholbedarf.

Zitat von: Jidder link=topic=3269.msg37950#msg37950 date=1375893270
Die Bitmap ist zwar in der .data-Sektion und nicht auf dem Stack, aber es ist korrekt, dass die zusammen mit dem Kernel gemappt wird.
Achso, weil das eine globale/statische Variable ist, oder wieso? Weil ich glaube, dass Variablen, deren Gültigkeit z.B. nur in einer Funktionen besteht, auf dem Stack erstellt werden.
« Letzte Änderung: 07. August 2013, 18:59 von Developer30 »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 07. August 2013, 19:20 »
Wenn ich nicht den ersten Eintrag nehme, dann muss der Eintrag, den ich nehme, aber auf den ersten Eintrag zeigen und nicht auf sich selbst oder? Sonst würden die vorherigen Einträge doch verloren gehen, oder habe ich einen Denkfehler?
Ja, der muss auf den ersten Eintrag zeigen. Die Adresse des ersten Eintrags ist ja die Adresse des Page Directorys. Und da die Adresse sowieso alle auf 4 KB ausgerichtet sein müssen, geht es auch gar nicht anders.

Oh das wusste ich nicht. Kennt jemand einen anschaulischen Artikel zu Linkskripts auf lowlevel.eu (oder sonst wo)? weil ich glaube da habe ich noch etwas Nachholbedarf.
Ich glaube wir haben da nichts zu. Ich kann da nur auf das Handbuch von ld verweisen

Achso, weil das eine globale/statische Variable ist, oder wieso? Weil ich glaube, dass Variablen, deren Gültigkeit z.B. nur in einer Funktionen besteht, auf dem Stack erstellt werden.
Genau.
Dieser Text wird unter jedem Beitrag angezeigt.

streetrunner

  • Beiträge: 67
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 07. August 2013, 19:21 »
Zitat
Achso, weil das eine globale/statische Variable ist, oder wieso?
Leider ist sie mit "statisch" auch wirklich statisch :-(, was bedeutet das sie entweder zu groß oder zu klein dimensioniert ist, aber wohl in den wenigsten Fällen genau die passende Größe hat.

Developer30

  • Beiträge: 16
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 07. August 2013, 19:29 »
Leider ist sie mit "statisch" auch wirklich statisch :-(, was bedeutet das sie entweder zu groß oder zu klein dimensioniert ist, aber wohl in den wenigsten Fällen genau die passende Größe hat.
Wieso, im Datensegment wird doch genau so viel Speicher für die Bitmap reserviert, wie ich angegeben habe. Das heißt sie wird genau die Größe haben, um 1048576 Blöcke à 4kb darzustellen.

streetrunner

  • Beiträge: 67
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 07. August 2013, 19:43 »
Nun, vllt. habe ich mich etwas unklar ausgedrückt :-D: In deiner Variable hällst du fest welche Speicherseiten belegt sind und welche nicht. Du musst aber bevor du deinen Kernel kompilierst festlegen wie viel Speicher Du maximal verwalten kannst. Wenn du nun 1048576 Blöcke hast, aber dein System nur 256MB Speicher, dann liegt eine ganze Menge deiner Blöcke brach. Heutzutage nicht schlimm, blöd ist es nur wenn du zu wenig Blöcke hast. Dann kannst du nicht den ganzen Speicher verwenden, außer du "bastelst" was hinten dran (und sowas wird [zumindest bei mir immer] echt unschön).

Developer30

  • Beiträge: 16
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 07. August 2013, 20:12 »
Wenn du nun 1048576 Blöcke hast, aber dein System nur 256MB Speicher, dann liegt eine ganze Menge deiner Blöcke brach.
Das ist wohl wahr  :-(

blöd ist es nur wenn du zu wenig Blöcke hast. Dann kannst du nicht den ganzen Speicher verwenden, außer du "bastelst" was hinten dran (und sowas wird [zumindest bei mir immer] echt unschön).
In einem 32 bit System kann ich ja eh nur maximal 4GB adressieren und mit 1048576 Blöcken decke ich diese Größe ja ab.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 08. August 2013, 16:49 »
Du musst aber bevor du deinen Kernel kompilierst festlegen wie viel Speicher Du maximal verwalten kannst.
Richtig. Aber du musst ja nicht den gesamten Speicher in einer großen Bitmap verwalten. :-)

 

Einloggen