10.3. Speicherverwaltung

Bei der Initialisierung des DiceRTE-Systems wird u.a. auch der für die Anwendungen benötigte Speicher eingerichtet. Wichtig hierzu sind die Variablen XMMAX, XMMIN und XMALIGN, die bei der Erstellung einer DiceRTE-Anwendung (durch BIND2EXE-Aufruf) festgelegt werden. Die Inhalte dieser drei Variablen werden beim Start auf 4K aufgerundet und es wird auf XMMIN<=XMMAX geprüft. Danach wird über die DPMI-Meminfo-Funktion festgestellt, wieviel Speicher tatsächlich verfügbar ist. Ist diese Anzahl größer als XMMAX, werden maximal XMMAX Bytes reserviert, ansonsten die verfügbare Menge, solange diese größer als XMMIN ist.

Der gesamte zur Verfügung gestellte Speicher wird über verkette DPMI-Blöcke verwaltet, in denen einzelne Nodes die belegten und freien Bereiche markieren. Ein DPMI-Block besteht aus einem Blockheader und mindestens einem Blocknode. Ein Blockheader sieht folgendermaßen aus :

Offset Typ Bedeutung
0 Dword Zeiger auf nächsten DPMI-Block (oder NULL:letzter Block)
4 Dword DPMI-Handle dieses Blocks
8 Dword Zeiger auf ersten Node im Block
12 Dword verfügbare Speichermenge im Block
16 Dword Anzahl freie Nodes im Block
20 Dword Anzahl belegte Nodes im Block
24 Dword[2] momentan unbenutzt

Die verfügbare Speichermenge (Offset 12) wird nur einmal bei der Einrichtung des Blocks gefüllt und danach nicht mehr verändert. Der Zweck dieses Feldes ist es, bei einer Speicheranforderung schnell entscheiden zu können, ob der Block überhaupt groß genug ist, um die Anforderung zu erfüllen. Erst wenn die Anzahl der freien Nodes > 0 und der Block groß genug ist, werden die Nodes im Block sequentiell nach einem ausreichenden Node durchsucht.

Aus dem Blockheader erhält man (bei Offset 8) die Adresse des ersten Blocknodes, der die Liste der Nodes dieses Blocks einleitet. Ein Node hat die folgende Struktur :

Offset Typ Bedeutung
0 Dword Zeiger auf nächsten Node (oder NULL:letzter Node)
4 Dword verfügbare Speichermenge im Node
8 Word Applikations-ID des Eigentümers (falls belegt)
10 Word Statuswort (Bit0=1:belegt, Bit1=1:gesperrt)
12 Dword Zeiger auf übergeordneten Blockheader

Bei der Initialisierung wird vom Startup-Code zunächst ein Block von mindestens XMMIN und maximal XMMAX Bytes reserviert. In diesem Block werden dann Nodes für das Modulverzeichnis, die Applikation und für alle eingeladenen DLLs reserviert. Übrig bleibt ein freier Node :

Alle bisher belegten Nodes bekommen die Applikations-ID <1> zugewiesen. Eine DiceRTE-Anwendung darf grundsätzlich nur die Nodes verändern (d.h. freigeben und Größe verändern), deren Appliations-ID größer oder gleich der eigenen ist. Jede durch AppExec neu gestartete Anwendung bekommt eine um eins erhöhte ID zugewiesen. Durch diesen Mechanismus können durch das Kernel noch belegte Nodes bereits beendeter Anwendungen erkannt und restlos freigegeben werden.

Um diese automatische Freigabe von Nodes zu unterdrücken, können diese explizit davor geschützt werden. Dazu dient die Funktion XmLock. Der so geschützte Node bleibt auch nach Beendigung der Anwendung belegt und wird erst bei der Terminierung des gesamten DiceRTE-Systems freigegeben. Die Sperrung eines Nodes kann nur von der Anwendung aufgehoben werden, die ihn gesperrt hat.

Der beim Systemstart eingerichtete DPMI-Block ist natürlich nicht unerschöpflich. Sollte die Restgröße des Blocks bei einer Speicheranforderung nicht ausreichen, wird zur Laufzeit ein weiterer DPMI-Block reserviert und an die Blockkette gehängt. Die Größe dieses neuen Blocks richtet sich nach der angeforderten Speichermenge ausgerichtet auf XMALIGN Bytes. Ist auch dieser Block einmal erschöpft, wird ein weiterer Block reserviert usw.

Bei einer Speicheranforderung werden alle Blöcke in der Kette sukzessiv nach einem passenden, freien Node durchsucht. Die einzelnen Blöcke werden dabei nach folgendem Schema durchlaufen :

  1. Ist mindestens ein freier Node im Block vorhanden (siehe Blockheader) ?
  2. Ist die verfügbare Speichermenge ausreichend (siehe Blockheader) ?
  3. existiert ein Node in angemessener Größe ?

Scheitert eine dieser drei Prüfungen, wird mit dem nächsten Block fortgefahren. Die Anforderung scheitert, wenn der letzte Block erreicht und auch dort kein passender Node gefunden wurde. In diesem Fall wird ein neuer Block reserviert und angehängt.

Bei der Freigabe eines Nodes werden benachbarte freie Nodes automatisch zu einem freien Node verschmolzen (siehe Funktionen MemFree und XmResize).