home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / cebit_91 / tricks / execdemo.doc < prev    next >
Encoding:
Text File  |  1991-03-07  |  4.8 KB  |  91 lines

  1. Heap Heap Hurra!  (Trickkiste 3'91)
  2.  
  3. Es ist eine schöne Sache, wenn man aus einem laufenden Pro-
  4. gramm heraus ein anderes aufrufen kann - zum Beispiel, um
  5. mal eben ein paar Disketten zu formatieren. So verfügt denn
  6. auch fast jede professionelle Anwendung über eine "DOS-
  7. Shell"-Funktion.
  8. Turbo Pascal hat zu diesem Zweck in der Unit "Dos" die Stan-
  9. dardprozedur "Exec" implementiert, über die sich ein "Kind"-
  10. Programm laden und starten läßt. Der Haken ist allerdings:
  11. "Exec" funktioniert nur dann, wenn zuvor mit der Compiler-
  12. Direktive "{$M Stackgröße, HeapMin, HeapMax}" die maximale
  13. Heapgröße eingeschränkt wurde, damit das Kindprogramm in
  14. den freien Speicherbereich über dem Heap geladen werden
  15. kann.
  16. Bei kleineren Programmen, die vom Heap keinen Gebrauch machen,
  17. ist dies auch kein Problem. Muß man jedoch mit größeren
  18. Datenmengen operieren, kommt man ohne dynamische Variablen
  19. nicht aus. Dann macht es sich empfindlich bemerkbar, wenn
  20. permanent etliche kByte weniger Speicherplatz auf dem Heap
  21. verfügbar sind.
  22. Gerd Cebulla hat einen Weg gefunden, während der Laufzeit des
  23. Programms einen ungenutzten Teil des Heap freizugeben, so daß
  24. das Kindprogramm sozusagen auf den Heap geladen werden kann.
  25. Dem DOS-Profi wird hier vielleicht die Funktion $4A des
  26. DOS-Interrupts $21 einfallen, mit deren Hilfe sich der einem
  27. Programm zugeteilte Speicherplatz verkleinern oder vergrößern
  28. läßt. Diese Funktion erwartet im Register ES die Segmentadresse
  29. des Programmanfangs und in BX die gewünschte Speichergröße
  30. in Paragraphen, also in Einheiten à 16 Byte.
  31. Aber ganz so einfach ist die Sache nicht, und zwar wegen der Art
  32. und Weise, wie Turbo Pascal (und übrigens auch Microsofts Clone
  33. Quick Pascal) den Heap verwaltet.
  34. Als erstes kommt jeweils das Programmsegment-Präfix (PSP). Das
  35. PSP belegt immer genau 256 Byte und wird von DOS jedem ausführbaren
  36. Programm vorangestellt; seine Segmentadresse läßt sich über die
  37. Turbo-Systemvariable "PrefixSeg" ermitteln. Darauf folgt dann
  38. das eigentliche Programm inklusive aller statischen Variablen.
  39. uletzt kommt der Heap, der sich nochmals in drei Bereiche
  40. aufgliedert.
  41. Alle via "New" oder "GetMem" erzeugten Variablen legt Turbo-Pascal
  42. im unteren Teil des Heap ab. Hierauf folgt ein mehr oder minder großer
  43. ungenutzter Bereich, der freie Heap. Die Systemvariable "HeapPtr"
  44. enthält jeweils die Adresse des ersten freien Byte.
  45. Am oberen Ende des Speichers schließlich befindet sich die
  46. Fragmentliste, auf deren Anfang die Systemvariable "FreePtr" zeigt;
  47. hier werden alle durch "Dispose" und "FreeMem" erzeugten "Löcher"
  48. im Heap festgehalten. Und genau da liegt der Hase im Pfeffer:
  49. Wenn wir mit der oben erwähnten DOS-Funktion $4A den Speicher vom Beginn
  50. des freien Heap ab freigeben, wird das Kindprogramm mit hoher
  51. Wahrscheinlichkeit die Fragmentliste überschreiben -- jeder weitere
  52. Aufruf von "New" oder "Dispose" hätte dann unvorhersehbare bis
  53. katastrophale Folgen.
  54. Die Fragmentliste muß also unbedingt in Sicherheit gebracht werden.
  55. Diese Überlegung führt zu folgendem Algorithmus:
  56. 1. Fragmentliste an den Anfang des freien Heap kopieren, also an die
  57.    durch "HeapPtr" addressierte Speicherstelle.
  58. 2. Speicherbereich oberhalb der umkopierten Fragmentliste freigeben
  59.    (DOS-Funktion $4A);
  60. 3. Kindprogramm aufrufen (Exec);
  61. 4. freigegebenen Speicherbereich zurückfordern (DOS-Funktion $4A);
  62. 5. Fragmentliste an die durch "FreePtr" adressierte Speicherstelle
  63.    zurückkopieren.
  64.  
  65. Jetzt muß man nur noch wissen, wie sich die Länge der Fragmentliste
  66. ermitteln läßt. Der Segmentanteil von "FreePtr" bleibt während des
  67. gesamten Programmlaufs konstant und enthält die Adresse des ersten
  68. nicht mehr zum Programm gehörigen Segments minus $1000; bei einem
  69. 640-kByte-System steht hier in der Regel der Wert 9000h, da an der
  70. Segmentadresse A000h der EGA-Grafikspeicher beginnt. Der Offsetanteil
  71. von "FreePtr" ist 0, wenn noch keine Fragmentliste existiert, ansonsten
  72. zeigt er auf den Anfang der Liste. Die Fragmentliste wächst übrigens
  73. von unten nach oben und belegt für jedes Loch im Heap 8 Byte; bei einem
  74. Eintrag beträgt der Offset also FFF8h, bei zwei Einträgen FFF0h usw.
  75. Die Länge der Fragmentliste läßt sich demnach mit der Formel:
  76.  
  77. Fraglen := $10000 - Ofs(FreePtr^)
  78.  
  79. berechnen.
  80. Der eben entwickelte Algorithmus ist in der Prozedur "Execute"
  81. verwirklicht. Sie können diese Routine problemlos in eigene Programme
  82. übernehmen; vergessen Sie aber nicht die Anweisung "USES Dos" im
  83. Programmkopf. Beachten Sie, daß Sie keinesfalls ein Programm
  84. aufrufen dürfen, daß sich resident installiert (etwa einen Maustreiber
  85. oder das DOS-Dienstprogramm PRINT). Ein solches Programm würde
  86. nämlich den Heap blockieren und damit bei nächster Gelegenheit einen
  87. kapitalen Programmabsturz verursachen. Um das Schlimmste zu verhüten,
  88. bricht "Execute" in diesem Fall das Programm mit einer Fehlermeldung ab.
  89.  
  90.                                          (wr)
  91.