Autor Thema: Dynamische Speicherverwaltung - Pages anfordern  (Gelesen 6520 mal)

Cheebi

  • Beiträge: 91
    • Profil anzeigen
    • Cheebis Webseite
Gespeichert
« am: 24. August 2007, 21:34 »
Hallo,

mal wieder das übliche thema, aber ganz klar is mir das ganze aus den anderen threads nicht geworden...
ich habe paging implementiert. nun soll jeder task sein eigenes PD bekommen. soweit ist mir noch alles klar nur sehe ich ein problem bei der dynamischen speicherverwaltung:

ich möchte, dass ein task (ring3) malloc() aufrufen kann, um speicher bereit gestellt zu bekommen. angenommen im PD des tasks ist im mom nur eine einzige page present (für den code), dann muss ja malloc() feststellen, dass es keinen heap hat und muss vom betriebssystem eine page anfordern... da liegt das problem. wie funktioniert das?
welchen teil des kernels ruft malloc() auf? und was macht der kernel dann genau? er muss ja irgendwo festlegen, dass eine bestimmte page von einem task genutzt wird. außerdem muss er im PD des tasks die page present setzen und dann noch die pageframeaddress anpassen... nach welchen kriterien wird geschieht das?

habe ich das klar genug formuliert? *verwirrt_bin*

Cheebi
0100 1001 0100 1100 0100 0001 0010 0000 0011 1010 0010 1101 0010 1010
http://www.cheebi.de

Korona

  • Beiträge: 94
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 24. August 2007, 21:57 »
Die meisten Os benutzen einen Systemcall, der mehr Pages in das aktuelle PD/in den aktuellen Prozess mappt. malloc() speichert in Listen, Trees oder anderen Strukturen welche Speicherbereiche frei sind, wenn keine mehr frei sind ruft malloc() diesen Systemcall auf, bei Unix kompatibeln System wäre das brk(), sbrk(), mmap() oder morecore(). Der Systemcall sucht sich dann eine freie physikalische Page, mappt sie irgentwo in das PD, setzt das Present-Bit und gibt einen Pointer auf sie zurück.

FreakyPenguin

  • Administrator
  • Beiträge: 301
    • Profil anzeigen
    • toni.famkaufmann.info
Gespeichert
« Antwort #2 am: 24. August 2007, 23:11 »
Also zuerst mal: malloc() ist im Normalfall eine Bibliotheksfunktion im Userspace und hat folglich mit dem Kernel nichts zu tun. Der Kernel stellt meist Syscalls zur verfuegung, die nur mit ganzen Pages arbeiten.
Was diese Syscalls genau machen, hängt von dem Konzept zur verwaltung von dynamischem Prozessspeicher ab. Dabei gibt es Grundsätzlich 2 Möglichkeiten. Korona hat die imo ein wenig durcheinander gebracht.
Bei der ersten Methode stellt der Kernel mal grundsätzlich 2 Syscalls zur Verfügung, die beispielsweise so aussehen: void* allocate_pages(size_t bytes); und void free_pages(void* base, size_t bytes); Der erste Syscall alloziert hierbei die nötigen physischen Seiten und mappt sie irgendwo (in einem bestimmten Berei) in den Adressraum des Prozesses. Der zweite Syscall gibt eine oder mehrere Seiten von einer Adresse an frei.
Die zweite Methode wäre die Methode mit Heap. Dabei steht dem Prozess ein Speicherbereich zur verfügung, den er per Sycalls (nahezu) beliebig dehnen und zusammenstauchen kann. Das wäre, die von Korona erwähnte Methode mit den *brk(); Was diese funktionen genau machen, kannst du bei Bedarf in den Manpages nachlesen. Wichtig ist vorallem, dass du das Prinzip begriffen hast, wie du das dann genau umsetzt, ist dir überlassen.

So, ich hoffe du verstehst in etwa was ich sagen will. ;-)
Falls noch Fragen dazu offen sind, stell sie ruhig.

Cheebi

  • Beiträge: 91
    • Profil anzeigen
    • Cheebis Webseite
Gespeichert
« Antwort #3 am: 25. August 2007, 01:13 »
danke ihr beiden,

aber ganz klar, wie der kernel die pages in das PD des prozesses mappt ist mir immer noch nicht... Ich dachte, ich habe eine art kernel-heap, wo der kernel den code und die daten für seine prozesse unterbringt. diesen heap verwaltet er einfach ganz normal. wird nun ein prozess gestartet, so wird ein bestimmter bereich des heaps genutzt, um die prozessdaten unterzubringen. dann erstelle ich ein neues PD, in dem nur ein paar wenige pages present sind. diese pages enthalten den code und die daten und wurden sozusagen "aus" dem heap des kernels in das PD des prozesses gemappt. meine frage ist aber, wie kann ich einen syscall machen (bei dem logischerweise cr3 nicht geändert wird) und dabei auf den heap des kernels zugreifen, um notfalls neue bereiche in das prozessPD zu mappen?

Cheebi
0100 1001 0100 1100 0100 0001 0010 0000 0011 1010 0010 1101 0010 1010
http://www.cheebi.de

FreakyPenguin

  • Administrator
  • Beiträge: 301
    • Profil anzeigen
    • toni.famkaufmann.info
Gespeichert
« Antwort #4 am: 25. August 2007, 09:22 »
Ich glaube so langsam verstehe ich, was du meinst. ;-)

Im Normalfall haben Betriebssysteme ein fixes Speicherlayout. Darin ist ein Bereich festgelegt, in dem der Kernel liegt. Der Kernel muss in jeden Prozess gemappt werden, sonst sind ja garkeine Syscalls möglich weil die IDT und der Kernel-Code für den Syscall sonst nicht gemappt sind, sobald der Prozessor darauf zugreiffen will. Dieser Kernelbereich sollte in jedem Prozess identisch sein, und selbstverständlich auch an der selben Virtuellen Adresse liegen. Oft wird dafür das Letzte Gigabyte im virtuellen Adressraum benutzt.
Die Speicherverwaltung sollte natürlich auch kernel-intern wieder Funktionen wie kmm_allocate_mem und kmm_free_mem (oder so ähnlich) zur verfügung stellen, worauf  dann wiederum ein malloc aufgebaut werden kann (selbstverständlich ist hier auch eine Heap-Variante möglich).
Wird nun ein neuer Prozess erstellt, muss zuerst Speicher fuer die Prozessinformationsstruktur alloziert werden. Diese Struktur wird mit allen nötigen und unnötigen Infos gefüllt und dem Prozessbaum (oder wie die auch verwaltet werden) angehängt. Danach wird die Programmdatei geladen. Wenn man ein Format wie ELF benutzt findet man darin die Informationen, wo wieviel Speicher benötigt wird. Zuerst wird eine physikalische Seite alloziert und temporär gemappt um ein Pagedir für den neuen Prozess zu erstellen. Die selbe Prozedur auch mit den Pagetables. Diese beiden Dinge werden vorzugsweise mit Funktionen wie pagedir_create und map_page (das Seiten in ein PD mappt und falls nötig automatisch neue Pagetables anlegt) erledigt. Sobald die Seiten alloziert sind, werden die Daten aus der Programmdatei kopiert.
Der Syscall der Seiten mappt, muss (und darf) nur im aktuellen Adressraum agieren.

Cheebi

  • Beiträge: 91
    • Profil anzeigen
    • Cheebis Webseite
Gespeichert
« Antwort #5 am: 26. August 2007, 23:48 »
ohje.. warum funktioniert das denn nicht:

  typedef struct SMEMHEADER
  {                           
    SMEMHEADER  *prev;                 // vorheriges Element in der Liste  [**** error ****]
    SMEMHEADER  *next;                 // nächstes Element in der Liste
    ulong       size;                  // Größe des Speicherblocks
    ulong       id_val:31;             // ID-value - zum Erkennen des Headers
    bool        used:1;                // used : frei ? genutzt
  } SMEMHEADER;

Ausgabe von gcc:

parse error before 'SMEMHEADER'             [in Zeile ****error****]

das liegt ja wahrscheinlich daran, dass der typ von next und prev in dem moment erst deklariert wird, oder? wie löst ihr das problem?

Cheebi
« Letzte Änderung: 03. September 2007, 14:32 von Cheebi »
0100 1001 0100 1100 0100 0001 0010 0000 0011 1010 0010 1101 0010 1010
http://www.cheebi.de

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 27. August 2007, 00:18 »
typedef struct SMEMHEADER  SMEMHEADER;

struct SMEMHEADER
  {
    SMEMHEADER  *prev;                                                          // vorheriges Element in der Liste  [**** error ****]
    SMEMHEADER  *next;                                                          // nächstes Element in der Liste
    ulong       size;                                                           // Größe des Speicherblocks
    ulong       id_val:31;                                                      // ID-value - zum Erkennen des Headers
    bool        used:1;                                                         // used : frei ? genutzt
  };

oder

typedef struct SMEMHEADER
  {
    struct SMEMHEADER  *prev;                                                          // vorheriges Element in der Liste  [**** error ****]
    struct SMEMHEADER  *next;                                                          // nächstes Element in der Liste
    ulong       size;                                                           // Größe des Speicherblocks
    ulong       id_val:31;                                                      // ID-value - zum Erkennen des Headers
    bool        used:1;                                                         // used : frei ? genutzt
  } SMEMHEADER;

Was dir lieber ist.
« Letzte Änderung: 27. August 2007, 00:22 von PorkChicken »
Dieser Text wird unter jedem Beitrag angezeigt.

Cheebi

  • Beiträge: 91
    • Profil anzeigen
    • Cheebis Webseite
Gespeichert
« Antwort #7 am: 27. August 2007, 22:32 »
danke
0100 1001 0100 1100 0100 0001 0010 0000 0011 1010 0010 1101 0010 1010
http://www.cheebi.de

Cheebi

  • Beiträge: 91
    • Profil anzeigen
    • Cheebis Webseite
Gespeichert
« Antwort #8 am: 30. August 2007, 22:22 »
nochmal danke soweit, jetzt ist mir einiges klarer...

ich habe aber mmer noch ein großes Problem:

angenommen ich will einen neuen prozess starten, dann reserviere ich auf dem kernelheap genug platz für code- und datensegment des prozesses. wohin aber lade ich das pagedirectory [und die pagetables] für den prozess? auf den heap des kernels darf es nicht, weil der nicht garantiert 4kB-aligned ist... wie löst ihr das problem?

Cheebi
0100 1001 0100 1100 0100 0001 0010 0000 0011 1010 0010 1101 0010 1010
http://www.cheebi.de

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #9 am: 31. August 2007, 10:22 »
Gibt 3 Möglichkeiten, allen gemeinsam ist, dass du den page frame durch deinen "page-allocator" allozierst. Um dann darauf zuzugreifen muss dieser natürlich irgendwo in den virtuellen Adressraum gemappt werden. Da hast du dann folgende Möglichkeiten:
* Den ges. physikalischen Adressraum im Kerneladressraum einblenden
* Eine phys. Page auf die zugegriffen wird bei Bedarf in den Kerneladressraum einblenden
* Speziell für die PageDirs/-Tables gibts den Trick, dass du das PageDir des Prozesses als PageTable im Kernel angibst. Damit werden die kompletten 4MB PageTables in den Adressraum eingeblendet.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

Cheebi

  • Beiträge: 91
    • Profil anzeigen
    • Cheebis Webseite
Gespeichert
« Antwort #10 am: 02. September 2007, 03:25 »
Gibt 3 Möglichkeiten [...]
* Den ges. physikalischen Adressraum im Kerneladressraum einblenden
* Eine phys. Page auf die zugegriffen wird bei Bedarf in den Kerneladressraum einblenden
* Speziell für die PageDirs/-Tables gibts den Trick, dass du das PageDir des Prozesses als PageTable im Kernel angibst. Damit werden die kompletten 4MB PageTables in den Adressraum eingeblendet.
Ok... Möglichkeit drei gefällt mir, aber kann man das nicht mit Nummer zwei verbinden? Also, ich stell mir das folgendermaßen vor:

Es gibt eine Funktion, der sage ich, auf wessen PD ich zugreifen möchte. Diese lädt mir dann das PD eines Prozesses in einen ganz bestimmten Eintrag des Kernel-PDs. Dann kann ich über ein und den selben 4MB-Frame eines Kernel-PD-Eintrages auf DP + PTs eines Prozesses zugreifen...

So brauche ich nur ein einziges mal virtuelle 4MB, denn sonst würde ja mit Möglichkeit drei, für jeden prozess 4MB virtuelle Adressen benötigt...
Hab ich das richtig verstanden und ist meine Idee möglich?

Gruß,
 Cheebi
0100 1001 0100 1100 0100 0001 0010 0000 0011 1010 0010 1101 0010 1010
http://www.cheebi.de

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #11 am: 02. September 2007, 12:10 »
Ich glaub ich muss was richtigstellen beim Möglichkeit 3: Du kannst auch in jedem Prozess PageDir als einen Eintrag selbiges Process PageDir angeben. Damit könntest du schonmal im Kernel auf die PageTables des momentan laufenden Prozesses zugreifen.
Für IPC oder ähnliches brauchst du dann nochmal evtl. virtuell 4MB, um an die PageTables eines anderen Prozesses zu kommen.
Wichtig ist auch, dass du damit nur die PageTables, nicht aber das PageDir verändern kannst. Dazu müsstest du das PageDir gesondert mappen.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

Cheebi

  • Beiträge: 91
    • Profil anzeigen
    • Cheebis Webseite
Gespeichert
« Antwort #12 am: 09. September 2007, 15:03 »
Irgendwie bin ich immer noch schwer mit dem Memorymanagement beschäftigt...
Im Moment versuche ich, nur die tatsächlich vorhanden Pages zu mappen (bei der Initialisierung, um dann das Paging zu aktivieren). Auf meinem Test-PC sind 128MB RAM installiert. Wenn ich diese 1:1 mape, dann gibts nen Neustart (schätzt erst Page-Fault und dann irgendwann Triple-Fault). Das liegt doch bestimmt am Video-Bereich... ich nutze VESA.. deshalb müsste ich doch eig. auch den Video-RAM mappen, oder? Nur stelle ich das wohl irgendwie falsch an... Ich mape von (LinearFrameBuffer-Begin) bis (LinearFrameBuffer-Beginn + TotalVideoMemory). TotalVideoMemory bekommt man aus dem VesaInfoBlock.
Was soll ich tun? Habe ich einen Denk-Fehler und vegess irgendeinen RAM-Bereich zu mappen, oder sollte ich einfach alles (auch die nicht vorhandenen physikalischen Pages) 1:1 mappen?

Gruß,

Cheebi
0100 1001 0100 1100 0100 0001 0010 0000 0011 1010 0010 1101 0010 1010
http://www.cheebi.de

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #13 am: 09. September 2007, 15:18 »
Was sagt bochs zu deinem Code? Welche Exceptions treten da auf (CPU0 logging überall auf 'report')? Wo ist der PageFault (EIP & CR2 sind da gemeint, sollte im Log stehen)?
zu VBE: Eigentlich sollte der Beginn des linear framebuffer direkt zurückgegeben werden und nicht erst ausgerechnet werden müssen.
Ich würd zum Testen übrigens im Textmode bleiben, da ist das Debuggen um einiges einfacher.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

Cheebi

  • Beiträge: 91
    • Profil anzeigen
    • Cheebis Webseite
Gespeichert
« Antwort #14 am: 12. September 2007, 19:21 »
hm.. also das Problem is gelöst... Ich habe nur vergessen, dass ich zuerst den RAM durchzählen muss, bevor ich die Pages NOT_PRESENT setze, die physikalisch nicht vorhanden sind...
Eine letzte Frage habe ich noch, ich will ja nicht ewig nerven *g*:
Wie hat eine Funktion create_PageDir(ushort PID) auszusehen? Sie muss ja irgendwie ein PageDir für einen Prozess erstellen. Könnt ihr mir ein gutes (und einfaches) Hobby-OS  nennen, wo ich mir das ma anschauen kann?

Vieln Dank,

 Cheebi
0100 1001 0100 1100 0100 0001 0010 0000 0011 1010 0010 1101 0010 1010
http://www.cheebi.de

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #15 am: 12. September 2007, 19:55 »
Du musst eigentlich das einen Teil des Kernel-PageDirectories kopieren, damit der kernel in jeden Prozessadressraum eingeblendet wird. Danach musst du eigentlich nur noch das Dateiformat für deine Programme durchgehen, die ganzen Segmente kopieren und dann mappen. Das schwierige ist eigentlich nur das Dateiformat zu parsen.
Ich geb jetzt keinen sourcecode für das erstellen des PageDirs, da das wirklich trivial mit einem memset und einem memcpy zu machen ist.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

 

Einloggen