Autor Thema: Paging - Segen oder Fluch?  (Gelesen 21944 mal)

wissenshunger

  • Beiträge: 49
    • Profil anzeigen
Gespeichert
« am: 04. January 2014, 23:56 »
Hallo alle zusammen,

also mein größtes Problem liegt im Moment wieder am Thema Paging. Ich bin stark dem Tutorial nach gegang. Als das Thema Paging kam habe ich es immer wieder aufgeschoben, da ich es einfach nicht richtig verstehe. Ich habe also die ersten 4MB mit UserFlag gesetzt und es ruhen laßen.
Mittlerweile habe ich meinen KB-, RS232- Treiber fertig und meine SYSCALLS ausgebaut und arbeit mit Programme als Module. Es funktioniert mit einem laufendem Programm auch erstmal, mit mehren wird es problematisch.

Nun möchte ich meinen Floppy Treiber schreiben, dafür benötige ich DMA und ein funktionierendes Paging. Da ich aber mit den Ding gar net klar komme hier ein paar Verstädnisfrage sowie Fragen zum Code:

- Was genau ist ein Page Directory und was eine Page Table ?

- Vor lauter Poiter, was macht vmm_create_context () genau?

- Muss ich nach erstmaligen mappen den KernelBereich später wieder mappen?

- Was müsste vmm_allco () machen? Details?

- In die Struktur task füge ich context hin zu, führe ich nun auch nach interupt-wechsel context nach c3 (wie im Tut. Erklärt) bleibt der Kernel oder das Programm stehen. Es passiert einfach nichts mehr. Wieso?

- Hat jemand vll. ein Blockbild zum Paging um es sich besser vorstellen zu können?

- Braucht jeder Task sein eigenes Paging?

- Brauch ich wirklich Paping oder kann man die Speicherverwaltung auch einfach gestalten?

Ich hoffe ihr könnt mir in einigen Punkten weiterhelfen. Ich dreh sonst mit Paging durch, denn ich komm einfach nicht dazu jedem Task seinen eigenen Speicher zu geben.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 05. January 2014, 02:16 »
Deine Frage lautet kurz: Was ist Paging und wie funktioniert es, brauche ich es und überhaupt und so? :-D

Grundsätzlich brauchst du kein Paging, ein Betriebssystem funktioniert auch ohne. Wenn du dich auf x86 im Protected Mode beschränkst, kannst du Speicherschutz auch mit Segmentierung allein herstellen. Außerdem beherrscht auch nicht jede CPU Paging.

Eine pagingfähige MMU enthält nur eine Tabelle, die aus einer "CPU-Adresse" eine "RAM-Adresse" macht, im Sinne von "wenn die CPU 0x12340000 sagt, meint sie in Wirklichkeit 0x43210000". Diese Tabelle beschreibt Blöcke von jeweils 4 KB, enthält damit 1 Mio Einträge (4 GB Adressraum / 4 KB Pagegröße) und belegt damit 4 MB RAM. Das ist zu viel.

Also hat man die Tabelle in kleinere Teiltabellen zerteilt, die jeweils nur 1024 Einträge (4 MB Adressraum / 4 KB Pagegröße) haben und damit zufällig genau eine Page groß sind. Diese heißen Page Table - und die große Tabelle heißt Page Directory. Alle Page Tables zusammen belegen zwar auch 4 MB, aber sie müssen nicht existieren (dann kann man auf die dadurch beschriebenen Adressen halt nicht zugreifen) - das spart Speicher.

Jeder Task hat sein eigenes Page Directory, damit sich verschiedene Tasks nicht in die Quere kommen (Speicherschutz). Aber Teile der Page Directories können gleich sein (normalerweise ist der Kernel in allen Tasks gleich gemappt, und gemeinsam genutzter Speicher taucht auch in mehreren Tasks auf).

Ein vmm_create_context() erzeugt ein Page Directory und initialisiert es mit den Mapping, die sowieso überall sind. Ein vmm_alloc() sucht sich ein (oder mehrere) Speicherblöcke und mappt die als einzelnen, großen Block in ein Page Directory.

Wie du es im Detail implementierst, bleibt dir überlassen.
In Qemu kannst du mit "info mem" und "info tlb" anzeigen lassen, was gerade aktuell ist.

wissenshunger

  • Beiträge: 49
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 05. January 2014, 15:22 »
Ein vmm_create_context() erzeugt ein Page Directory und initialisiert es mit den Mapping, die sowieso überall sind. Ein vmm_alloc() sucht sich ein (oder mehrere) Speicherblöcke und mappt die als einzelnen, großen Block in ein Page Directory.

Welches Mapping ist sowieso überall und wieso?

Wie kann man mit vmm_alloc () einen großen Speicherblock in dem Page Directory benutzen wenn jeder eintrag nur 4MB groß ist?

Was ich jetzt verstanden habe, dass jeder der 1024 Einträge des Page Directory jeweils auf eine Page Table zeigt die ebenfalls 1024 Einträge hat. Aber wieviel Speicher jeder Eintrag verwaltet und wie ich dann womit auf den tasälichen Speicher lesen und schreiben kann weiß ich leider noch nicht.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 05. January 2014, 17:51 »
Jeder Eintrag des Page Directories zeigt auf eine Page Table. Jeder Eintrag in der Page Table zeigt auf eine Page. Also verwaltet jeder Eintrag in der Page Table einen Speicher von 4 KB, und jeder Eintrag im Page Directory verwaltet 4 MB.

Üblicherweise mappt man den Kernel in jedem Task an die gleiche Stelle. Damit ist er überall immer gleich gemappt, damit kann das beim Anlegen von Page Directories gleich mit erledigt werden.

Ein Eintrag im Page Directory verwaltet 4 MB. Wenn du größere Speicherblöcke benutzen möchtest, dann brauchst du mehrere Einträge im Page Directory.

Was die Paging-Unit tut ist nichts anderes, als jedem 4K-Speicherblock im Adressraum einen 4K-Speicherblock im RAM zuzuweisen. Welchen genau, steht in den Tabellen. Auf den Speicher greifst du also ganz normal zu.

wissenshunger

  • Beiträge: 49
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 05. January 2014, 18:29 »
Ok, dass verstehe ich soweit.

Wie muss ich aber jetzt das Paging erweitern um Programme zusammen mit dem Kernel zu mappen?
Wenn ich im Moment einen Task starte hol dieser sich anscheit ja einen 4KB großen freien Block von pmm_alloc (). Das ist ja jetzt phsy. Speicher, spr. Hat die selbe Adresse wieder der wie als würde ich direkt Zugreifen.

wissenshunger

  • Beiträge: 49
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 05. January 2014, 18:31 »
Ich versteh halt leider noch nicht ganz welcher Befhel hier was an dem Page Directory und an der Pagd Table macht.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 05. January 2014, 23:30 »
Ich nehme mal an, dass du dir unsere Artikel zu Paging durchgelesen hast. Das Problem, dass deine vmm_alloc-Funktion (und ggf. andere Funktionen) lösen müssen, ist ein Page Directory so einzurichten, dass es im Kontext des Prozesses so aussieht, als wäre würde der Prozess ein paar Pages belegen und der Kernel ein paar Pages belegen. Die Pages, die der Prozess belegt sind einmal sein Code und Daten sowie der Stack. Die Platzierung von Code und Daten müssen so erfolgen, wie es das Programmformat (ELF) erfordert. Das heißt wenn du unser Tutorial befolgst, musst du in init_elf (oder besser wo immer dein Prozess erstellt wird) ein Page Directory anfordern (pmm_alloc) und dir was einfallen lassen, wie du an Page Tables kommst. Vor dem memset/memcpy in init_elf musst du dann vorher physischen Speicher anfordern und ihn im Page Directory des Prozesses mappen. Den Stack kannst du (theoretisch) beliebig platzieren, und dafür könntest du sowas wie vmm_alloc benutzen. Was diese Funktion können muss, ist in unserem Tutorial beschrieben.

Um den Kernel im Kontext des Prozesses zu mappen, könntest das Mapping, das du bereits hast (die ersten 4 MB) in neue Page Directory übertragen, indem du den ersten Eintrag aus deinem jetzigen Page Directory ins neue kopierst.

Ich hoffe das hilft dir etwas weiter.
Dieser Text wird unter jedem Beitrag angezeigt.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 06. January 2014, 00:46 »
Jede Page Table und jedes Page Directory sind einfach nur 4 KB große Speicherbereiche, wo du die Mapping-Informationen reinschreibst.

Dein pmm_alloc() gibt dir eine physische Adresse, die du in die Page Table einträgst. Die physische Adresse der Page Table trägst du dann in das Page Directory ein. Wenn du dann die physische Adresse des Page Directories in CR3 hineinschreibst, ist das Mapping aktiv.

wissenshunger

  • Beiträge: 49
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 06. January 2014, 16:31 »


Um den Kernel im Kontext des Prozesses zu mappen, könntest das Mapping, das du bereits hast (die ersten 4 MB) in neue Page Directory übertragen, indem du den ersten Eintrag aus deinem jetzigen Page Directory ins neue kopierst.

Das obere habe ich soweit verstanden. Danke dafür erstmal, aber kann ich den ein Mapping übertragen?
NACHTRAG: Wie mach ich einen für den Task neue Page Directory aktiv, denn ich dachte man kann nur einmal Mappen steht im Tut.?.
« Letzte Änderung: 06. January 2014, 16:55 von wissenshunger »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 06. January 2014, 17:46 »
Das obere habe ich soweit verstanden. Danke dafür erstmal, aber kann ich den ein Mapping übertragen?
Das Page Directory ist im Prinzip nur ein Array. Mit übertragen meine ich, dass du den Eintrag aus einem Page Directory ins andere kopierst:

new_pgdir[i] = old_pgdir[i];
Das ganze funktioniert auch mit Page Tables.

Zitat
NACHTRAG: Wie mach ich einen für den Task neue Page Directory aktiv, denn ich dachte man kann nur einmal Mappen steht im Tut.?.
Ein Page Directory aktivierst du, indem du die Adresse ins Register cr3 lädst. Das Page Directory kannst du danach immer noch verändern. (Wobei du den TLB flushen musst, wenn du einen existierenden Eintrag im aktuell aktiven Page Directory überschreibst.)
Dieser Text wird unter jedem Beitrag angezeigt.

wissenshunger

  • Beiträge: 49
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 07. January 2014, 01:28 »
Das heißt also zusammengefasst:
- Paging aktiveren spi. die ersten 4MB mappen.
- Wenn ich jetzt einen Task laden passiert folgendes:
- Neues Page Directory anlegen
- Erstes (altes (4MB)) ins neue kopieren
- Alle Page Table kopieren
- Wie lade ich das neue nach c3, also wie mach ich das neue Page Directory zum aktuellen?
- TLB flushen? (Muss ich dasin ASM machen?)

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 07. January 2014, 03:40 »
Zuerst:
- initiale Page Table erstellen und füllen (4 KB), initiales Page Directory (4 KB) erstellen und ersten Eintrag damit füllen
- Adresse des Page Directories nach CR3 tun (z.B. mit Inline-Assembler)
- Paging aktivieren
Wenn du Paging aktivierst, ohne ein gültiges Page Directory geladen zu haben, wirft dir die CPU höchstwahrscheinlich mehrere Exceptions vor die Füße und resettet.

Beim Task erstellen:
- Page Directory erstellen (4 KB)
- initiale Page Table dort eintragen (ist schon vorhanden)
- weitere Page Tables erstellen, entsprechend füllen und ins Page Directory eintragen (je 4 KB/Page Table)

Beim Task wechseln:
- Adresse des Page Directories des Task nach CR3 schreiben

Wenn du nach CR3 schreibst, wird der TLB automatisch geflusht. Selbst machen musst du es nur, wenn du im gerade aktiven Page Directory (oder den dort eingetragenen Page Tables) was veränderst. Entweder, du lädst CR3 danach mit dem gleichen Page Directory neu, was langsam ist, oder du nutzt Inline-Assembler für INVLPG, was erst ab dem 486er geht.

Jede Page Table beschreibt 4 MB Speicher, ist aber nur 4 KB groß. Das Page Directory selbst ist auch 4 KB groß. Lass dich nicht dadurch verwirren. :-)
« Letzte Änderung: 07. January 2014, 03:43 von Svenska »

wissenshunger

  • Beiträge: 49
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 12. January 2014, 17:10 »
Um das Thema mit dem Kernel, dass im Tutorial und Page angesprochen wird, dass der Kernel das 1. GB für sich reserviert, würde doch heißen das ich beim Init. Paging statt 4MB gleich 1GB mappen müssen!? Ist dies korrekt?

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 12. January 2014, 17:21 »
Nein, du musst nur so viel mappen, wie du brauchst. Die 1 GB für den Kernel bedeuten, dass in diesem Bereich nur Dinge für den Kernel gemappt werden und Programme darauf nicht zugreifen dürfen. Aber wenn der Kernel diesen Speicher nicht braucht, bleibt er ungemappt.
Dieser Text wird unter jedem Beitrag angezeigt.

wissenshunger

  • Beiträge: 49
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 12. January 2014, 17:34 »
Das heißt, ich mappen zuviel ich für den Kernel brauche (im Moment 4MB) & mappe die Programm AB der "1GB Grenze" nach oben, bis der Speicher voll ist?

An dieser Stelle auch die Frage, wenn ich einen alten Rechner mit weniger als 4GB benutzer, angenohmen 2GB, wann merke ich dann wenn der Speicher voll ist, bzw. voll belegt? 1GB fällt ja für den Kernel weg! Wenn ich jetzt über Paging noch 3GB zur Verfügung habe, aber nur noch 1GB vorhanden ist, wie löse ich das Problem?

Und natürlich der gute alte 486'er mit 128MB Speicher, wie soll ich da 1GB Kernel mappen + Programme? Eigentlich gar nicht möglich oder?

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 12. January 2014, 17:50 »
Richtig, das ist nicht möglich. Deswegen gibts es ja den virtuellen Speicher, siehe auch http://de.wikipedia.org/wiki/Virtuelle_Speicherverwaltung. Du mappst immer nur das was du brauchst, und die größe des virtuellen Adressraums hat nichts damit zu tun wieviel physischer Speicher im System eingebaut ist.

wann merke ich dann wenn der Speicher voll ist, bzw. voll belegt?
Die Programme und der Kernel rufen Funktionen auf, um Speicher anzufordern. Die nutzen nicht ohne dein Wissen den Speicher, weil deine Speicherverwaltung, die du vermutlich noch schreiben musst, ganz genau weiß, wer wieviel Speicher (genauer sogar welche Bereiche) nutzt.

1GB fällt ja für den Kernel weg!
Wieder einmal musst du zwischen virtuellem und physischem Speicher unterscheiden. Der Kernel nutzt 1 GB des virtuellen Adressraums, aber der virtuelle Adressraum muss nicht vollständig auf den physischen Adressraum gemappt sein. Es werden dadrin Lücken sein, so lange der Kernel nicht so viel Speicher braucht.
Dieser Text wird unter jedem Beitrag angezeigt.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #16 am: 12. January 2014, 20:24 »
Hallo,

Und natürlich der gute alte 486'er mit 128MB Speicher, wie soll ich da 1GB Kernel mappen + Programme? Eigentlich gar nicht möglich oder?
Wow, wie hast du so viel RAM in die Maschine gekriegt? ;-)

Jeder Prozess hat 4 GB "Adressraum", belegt aber nur so viel RAM, wie du ihm auch gibst. Der Kernel z.B. ist in jeden Prozess gemappt, aber die Tabellen zeigen alle auf den gleichen RAM. Wenn du das mit Anwendungen machst, hast du Shared Memory.

Und dann kannst du natürlich noch mehr RAM mappen, als du hast ("overcommitment"). Wenn ein Programm da wirklich drauf zugreifen möchte, bekommst du einen Page-Fault und kannst dann entweder echten RAM zuweisen oder auf die Festplatte auslagern.

Dann musst du natürlich immer im Blick haben, wieviel RAM du noch frei hast - aber dafür hast du ja die physische Speicherverwaltung.

wissenshunger

  • Beiträge: 49
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 12. January 2014, 21:12 »
aber dafür hast du ja die physische Speicherverwaltung.

Ja die gibt mir im Moment ja Speicher mit pmm_alloc() zurück, aber woher weiß ich wenn der Speicher aufgebraucht ist, oder besser wo finde ich herraus wieviel physischen Speicher es gibt?

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 12. January 2014, 21:56 »
Hast du das pmm_alloc selbst implementiert?
Dieser Text wird unter jedem Beitrag angezeigt.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 12. January 2014, 22:50 »
Wieviel Speicher es gibt, erfährst du aus der Memory-Map, die dir dein multibootfähiger Bootloader beim Start mitgibt. Oder du fragst das BIOS, das kann dir da auch kompetent Rede und Antwort stehen. Übrigens sollte pmm_alloc() nur Adressen rausrücken, wo auch wirklich freier RAM zu finden ist.

"Gesamter Speicher - Rausgegebener Speicher = noch freier Speicher" ist dann sogar ganz einfach. :-)

 

Einloggen