Reentranz
Reentranz
Hallo,
Ich hab vor mich mit der Programmierung von TSR Programmen zu beschäftigen und da kommt unweigerlich der Begriff Reentranz ins Spiel. Hab mir die größtenteils identischen Erläuterungen dazu durchgelesen, aber irgendwie blick ich da nicht ganz durch.
Folgendes ist mir bis hierher klar: DOS ist als Singletask System darauf ausgelegt daß DOS Funktionen, also jene die über den Interrupt 21h aufgerufen werden, schön der Reihe nach ausgeführt werden. Es kann ja schließlich auch nur ein Programm laufen welches diese Funktionen aufruft. Wenn man ein anderes starten will muß man das aktuell ausgeführte erst beenden.
Wenn nun ein TSR Programm durch seinen Hotkey aktiviert wird kann es sein daß es eine aktuell ausgeführte DOS Funktion unterbricht und das kann dann zu Problemen führen weil DOS genau auf so einen Fall nicht vorbereitet ist.
Versteh ich auch noch aber noch nicht worin das Problem genau besteht welches durch die Unterbrechung der DOS Funktion verursacht wird. Kann mir das jemand erläutern?
Lg, Markus
Ich hab vor mich mit der Programmierung von TSR Programmen zu beschäftigen und da kommt unweigerlich der Begriff Reentranz ins Spiel. Hab mir die größtenteils identischen Erläuterungen dazu durchgelesen, aber irgendwie blick ich da nicht ganz durch.
Folgendes ist mir bis hierher klar: DOS ist als Singletask System darauf ausgelegt daß DOS Funktionen, also jene die über den Interrupt 21h aufgerufen werden, schön der Reihe nach ausgeführt werden. Es kann ja schließlich auch nur ein Programm laufen welches diese Funktionen aufruft. Wenn man ein anderes starten will muß man das aktuell ausgeführte erst beenden.
Wenn nun ein TSR Programm durch seinen Hotkey aktiviert wird kann es sein daß es eine aktuell ausgeführte DOS Funktion unterbricht und das kann dann zu Problemen führen weil DOS genau auf so einen Fall nicht vorbereitet ist.
Versteh ich auch noch aber noch nicht worin das Problem genau besteht welches durch die Unterbrechung der DOS Funktion verursacht wird. Kann mir das jemand erläutern?
Lg, Markus
Re: Reentranz
Selbst getestet habe ich da nichts. So wie ich es bisher verstanden habe, benutzen die DOS-Routinen teilweise globale Variablen. Diese werden dann natürlich für die unterbrochene Funktion fehlerhaft, wenn die Unterbrecher-Funktion globale Werte verändert. Ich denke hier z.B. an die FAT oder anderes.
Genaues weiß ich aber auch nicht.
Genaues weiß ich aber auch nicht.
Re: Reentranz
Für mich ergibt sich da zusätzlich die Frage, wie Windoes (9x) das Problem umgeht. Das hat doch auch einen DOS Kernel. Der spätere NT Kernel wurde komplett neu geschrieben, da ist kein DOS mehr, aber wie machen das die Window 9x Versionen und auch windows 3x?
Re: Reentranz
Interessante Frage, go32!
Vielleicht ist Windows 95 gar nicht so mulittasking fähig, wie damals beworben. Nach dieser Seite hier
http://www-pc.uni-regensburg.de/systems ... winver.htm
gilt:
Vielleicht ist Windows 95 gar nicht so mulittasking fähig, wie damals beworben. Nach dieser Seite hier
http://www-pc.uni-regensburg.de/systems ... winver.htm
gilt:
Consumer Windows (Win95,Win98, WinME) erfordert also vom Systemprogramm die manuelle Ausrufung einer systemweiten Sperre, wenn es Systemdienste anfordert. Erst wenn der Systemdienst (der ja aus dem DOS-Kernel stammen kann) beendet ist, kann es die Sperre wieder aufheben lassen. Jedenfalls, wenn ich das richtig verstanden habe, da ich noch nie für irgendein WinXX was geschrieben habe.Windows 2000/NT/XP ist komplett wiedereintrittsfest (reentrant), große Teile von Consumer Windows dagegen nicht. Zu diesem nicht wiedereintrittsfesten Code gehört ein Großteil der Grafik- und Fensterverwaltungsfunktion (GDI und USER). Wenn eine 32-Bit-Anwendung unter Consumer Windows versucht, einen Systemdienst aufzurufen, der durch nicht wiedereintrittsfesten 16-Bit-Code implementiert ist, muss die Anwendung zuerst eine systemweite Sperre oder Mutex einrichten, um andere Threads daran zu hindern, auf die nicht wiedereintrittsfeste Codebasis zuzugreifen. Schlimmer noch, die 16-Bit-Anwendung hält diese Sperre während der Ausführung aufrecht. Infolgedessen werden Anwendungen oft nur mit einem Thread ausgeführt, auch wenn der Kern von Consumer Windows über einen preemptiven 32-Bit-Multithreaded-Planungsdienst verfügt, da ein Großteil des Systems noch durch nicht wiedereintrittsfesten Code implementiert ist.
Re: Reentranz
Mein Senf dazu:
Ich denke, wir sind uns einig, daß "normale" Programme, die von Interrupts unterbrochen werden, keine Probleme machen: Ein gescheit programmierter Interrupt rettet den Zustand (Flags, Register, also alles, was er ändern könnte) und stellt das vor Rückkehr wieder her.
Es gibt 3 Szenarien, die ich mir vorstellen kann, wo es eventuell ein Problem geben könnte:
1. Das Hauptprogramm benutzt extrem zeitkritische Dinge, ggf. sogar "Timerschleifen", d.h. durch Befehle und vorherige "Meß-Konstanten" ermittelte Sachen. Wenn das in sehr kleinen Zeitabständen ist und ein Interrupt grätscht dazwischen, ist das natürlich nix. Aber: So programmiert man ja eigentlich nicht auf PC.
2. Komische Programmiertricks, die den Prefetch-Queue (PFQ) ausnutzen - will sagen: Wenn man z.B. eine Schleife programmiert und davor einen Befehl hat, der einen anderen Befehl im Code verändert (also selbstmodifizierender Code) UND dieser Befehl liegt noch im Bereich des Prefetch-Queue, dann wird dieser veränderte Befehl nicht so ausgeführt, sondern der ursprüngliche Befehl und erst nach Überschreitung der PFQ-Länge ODER nach einem Sprung wird da neu gelesen. ABER: Ein Interrupt ist auch ein Sprung! Damit so ein Kram funktioniert, muß man a) wissen, wie lang der PFQ der Maschine ist und b) eben verhindern, daß INTs u.dergl. das aushebeln. Ja, besonders findige "Hacker" versuchen mit solchen Spielerchen, Code zu verschleiern - in normaler Programmierung kommt das nicht vor.
3. Unsere lieben PORTs! Es gibt in PCs Bauteile, die über Port-Befehle angesprochen werden müssen, manche haben sogar so eingebaute "Zähler" oder "Flips", so daß man z.B. einen 16-Bit-Wert übergibt, indem man direkt hintereinander zwei 8-Bit-Werte AUF DENSELBEN PORT ausgibt (man "resettet" den Flip vorher). (Klingt komisch? Schonmal die DMA-Chips angesteuert? Fast unerläßlich, wenn man die Sound-Blaster gescheit benutzen will...) Wenn man nun bei so einem Port-gesteuerten Bauteil den ersten Port-Befehl oder -wert gesendet hat (so daß das Bauteil auf einen Nachfolge-Befehl/wert wartet und genau dazwischen grätscht ein Interrupt rein, wäre das ja noch "halb so schlimm". Aber wenn innerhalb dieses Interrupts EBENFALLS Portzugriffe erfolgen sollte, sogar auf den gleiche Port-Bereich (der also dasselbe Bauteil oder einen anderen Bereich desselben Bauteils usw betrifft), zerstört man damit den im Hauptprogramm erst halb-gesendeten Portbefehl. (Die CPU macht *nicht* or all diesen alten Kram irgendwelche LOCKs und zwischen zwei Port-Befehlen stehen ja dann auch "normale" Befehle, z.B. die MOVs, um die neuen Werte zu laden usw.)
Vielleicht gibt's da auch noch mehr Sachen - aber das ist das, was mir so dazu einfällt.
Und:
Multitasking, was ja nicht nur Speicher verwaltet, sondern das - noch schwierigere - Verwalten sämtlicher Hardwarezugriffe übernimmt (damit allen teilnehmenden Tasks die gleichen Ressourcen zur Verfügung gestellt werden können), ist da quasi noch mal 'ne ganz andere Ecke. Und ja: Wenn man ein 16-Bit-DOS-Singletask-Programm, das auf seiner eigentlichen DOS-Maschine viel weniger "Rücksicht nehmen muß auf andere Prozesse" (weil die einzigen anderen da die paar TSRs sind) plötzlich in einer Multitask-Umgebung läuft, muß die Multitask-Umgebung mit "allem oder nichts" rechnen: Also auch mit komischen Tricks, eventuell etwas "schlampig" angelegten ISRs, selbstverständlich mit ungeschützten Zugriffen überallhin (Realmode-DOS hat eben keinen Speicherschutz) usw., um eben nicht das ganze Hauptsystem in Gefahr zu bringen.
Wollte das insgesamt nur mal erwähnen - vielleicht interessierts/hilfts ja.
Ich denke, wir sind uns einig, daß "normale" Programme, die von Interrupts unterbrochen werden, keine Probleme machen: Ein gescheit programmierter Interrupt rettet den Zustand (Flags, Register, also alles, was er ändern könnte) und stellt das vor Rückkehr wieder her.
Es gibt 3 Szenarien, die ich mir vorstellen kann, wo es eventuell ein Problem geben könnte:
1. Das Hauptprogramm benutzt extrem zeitkritische Dinge, ggf. sogar "Timerschleifen", d.h. durch Befehle und vorherige "Meß-Konstanten" ermittelte Sachen. Wenn das in sehr kleinen Zeitabständen ist und ein Interrupt grätscht dazwischen, ist das natürlich nix. Aber: So programmiert man ja eigentlich nicht auf PC.
2. Komische Programmiertricks, die den Prefetch-Queue (PFQ) ausnutzen - will sagen: Wenn man z.B. eine Schleife programmiert und davor einen Befehl hat, der einen anderen Befehl im Code verändert (also selbstmodifizierender Code) UND dieser Befehl liegt noch im Bereich des Prefetch-Queue, dann wird dieser veränderte Befehl nicht so ausgeführt, sondern der ursprüngliche Befehl und erst nach Überschreitung der PFQ-Länge ODER nach einem Sprung wird da neu gelesen. ABER: Ein Interrupt ist auch ein Sprung! Damit so ein Kram funktioniert, muß man a) wissen, wie lang der PFQ der Maschine ist und b) eben verhindern, daß INTs u.dergl. das aushebeln. Ja, besonders findige "Hacker" versuchen mit solchen Spielerchen, Code zu verschleiern - in normaler Programmierung kommt das nicht vor.
3. Unsere lieben PORTs! Es gibt in PCs Bauteile, die über Port-Befehle angesprochen werden müssen, manche haben sogar so eingebaute "Zähler" oder "Flips", so daß man z.B. einen 16-Bit-Wert übergibt, indem man direkt hintereinander zwei 8-Bit-Werte AUF DENSELBEN PORT ausgibt (man "resettet" den Flip vorher). (Klingt komisch? Schonmal die DMA-Chips angesteuert? Fast unerläßlich, wenn man die Sound-Blaster gescheit benutzen will...) Wenn man nun bei so einem Port-gesteuerten Bauteil den ersten Port-Befehl oder -wert gesendet hat (so daß das Bauteil auf einen Nachfolge-Befehl/wert wartet und genau dazwischen grätscht ein Interrupt rein, wäre das ja noch "halb so schlimm". Aber wenn innerhalb dieses Interrupts EBENFALLS Portzugriffe erfolgen sollte, sogar auf den gleiche Port-Bereich (der also dasselbe Bauteil oder einen anderen Bereich desselben Bauteils usw betrifft), zerstört man damit den im Hauptprogramm erst halb-gesendeten Portbefehl. (Die CPU macht *nicht* or all diesen alten Kram irgendwelche LOCKs und zwischen zwei Port-Befehlen stehen ja dann auch "normale" Befehle, z.B. die MOVs, um die neuen Werte zu laden usw.)
Vielleicht gibt's da auch noch mehr Sachen - aber das ist das, was mir so dazu einfällt.
Und:
Multitasking, was ja nicht nur Speicher verwaltet, sondern das - noch schwierigere - Verwalten sämtlicher Hardwarezugriffe übernimmt (damit allen teilnehmenden Tasks die gleichen Ressourcen zur Verfügung gestellt werden können), ist da quasi noch mal 'ne ganz andere Ecke. Und ja: Wenn man ein 16-Bit-DOS-Singletask-Programm, das auf seiner eigentlichen DOS-Maschine viel weniger "Rücksicht nehmen muß auf andere Prozesse" (weil die einzigen anderen da die paar TSRs sind) plötzlich in einer Multitask-Umgebung läuft, muß die Multitask-Umgebung mit "allem oder nichts" rechnen: Also auch mit komischen Tricks, eventuell etwas "schlampig" angelegten ISRs, selbstverständlich mit ungeschützten Zugriffen überallhin (Realmode-DOS hat eben keinen Speicherschutz) usw., um eben nicht das ganze Hauptsystem in Gefahr zu bringen.
Wollte das insgesamt nur mal erwähnen - vielleicht interessierts/hilfts ja.
Re: Reentranz
Hallo zusammen,
Zunächst mal danke für die recht umfangreichen Antworten
Es wurde bisher jedoch noch nicht darauf eingegangen worauf meine Frage abgezielt hat, nämlich was genau da „kaputt“ geht wenn es unter DOS zu so einer Situation kommt.
Soweit ich das verstanden hab ist die Unterbrechung einer DOS Funktion nicht das Problem an sich denn das passiert ja laufend durch diverse Hardware Interrupt-Routinen vermute ich jetzt mal oder?
Das eigentliche Problem kriegt man erst dann wenn ein TSR Programm bei der Aktivierung eine DOS Funktion unterbricht und dann seinerseits wiederum DOS Funktionen aufruft (was so ziemlich jedes Programm macht). So steht’s zumindest auf manchen Seiten beschrieben.
Genau hier häng ich, warum kracht es wenn das TSR Programm DOS Funktionen aufruft? Wird da irgendwas überschrieben was letztendlich dann zum Crash führt weil die unterbrochene Funktion nicht an dem Punkt weitermachen kann an dem sie unterbrochen wurde?
Ich hab mal wo gelesen daß das irgendwie mit verschiedenen Stacks zu tun hat welche die DOS Funktionen angeblich nutzen, aber was genau da abgeht wurde leider auch nicht beschrieben.
Mir geht’s nur um die Abläufe unter DOS, nicht im Kontext von Win95 etc. Aber das war trotzdem eine interessante Frage da hier im Kern ja auch DOS am werkeln ist.
Lg, Markus
Zunächst mal danke für die recht umfangreichen Antworten
Soweit ich das verstanden hab ist die Unterbrechung einer DOS Funktion nicht das Problem an sich denn das passiert ja laufend durch diverse Hardware Interrupt-Routinen vermute ich jetzt mal oder?
Das eigentliche Problem kriegt man erst dann wenn ein TSR Programm bei der Aktivierung eine DOS Funktion unterbricht und dann seinerseits wiederum DOS Funktionen aufruft (was so ziemlich jedes Programm macht). So steht’s zumindest auf manchen Seiten beschrieben.
Genau hier häng ich, warum kracht es wenn das TSR Programm DOS Funktionen aufruft? Wird da irgendwas überschrieben was letztendlich dann zum Crash führt weil die unterbrochene Funktion nicht an dem Punkt weitermachen kann an dem sie unterbrochen wurde?
Ich hab mal wo gelesen daß das irgendwie mit verschiedenen Stacks zu tun hat welche die DOS Funktionen angeblich nutzen, aber was genau da abgeht wurde leider auch nicht beschrieben.
Mir geht’s nur um die Abläufe unter DOS, nicht im Kontext von Win95 etc. Aber das war trotzdem eine interessante Frage da hier im Kern ja auch DOS am werkeln ist.
Lg, Markus
Re: Reentranz
Das weiß wohl niemand genau, weil es von MS nie eine genaue Stellungnahme dazu gab. Wie geschrieben nutzt DOS zum Teil globale Variablen in seinem Datensegment, die dann überschrieben werden können, wenn eine DOS-Funktion defacto eine andere unterbricht und diese dann mit den veränderten Daten weiterläuft. Es werden also schlicht Variablen überschrieben. Welche Daten welche DOS-Funktion genau benutzt ist m.W. aber nirgends dokumentiert.
Re: Reentranz
Habe jetzt noch mal das Thema in PC Intern (Buch) angelesen. Die fehlende Reentranz wird dort in etwa so erklärt: Jede Dos-Funktion benötigt einen umfangreichen Stack. Da dieser unter DOS grundsätzlich vom Aufruferprogramm gestellt wird, kann DOS keine bestimmte Größe voraussetzen. Es verwendet dabei einen eigenen Stack. Jede Dos-Funktion legt dann bei Aufruf SS:SP auf den eigenen Stack und sichert das gesamte System (Rücksprungadresse, Register etc.) dort. Bei Beendigung der Dos-Funktion wird der ganze Stack gelöscht. Jede Dos-Funktion darf daher davon ausgehen, dass der gesamte DOS-Stack ihr immer ganz alleine zur Verfügung steht.
Wird jetzt eine Dos-Funktion über ein TSR durch eine andere Dos-Funktion unterbrochen, wird die neue Funktion den DOS-Stack komplett neu überschreiben, insbesondere die Rücksprungadresse der unterbrochenen Funktion. Nach deren Beendigung findet die unterbochene DOS-Funktion dann nur noch fehlerhafte Daten auf dem DOS-Stack und es kommt zwangsläufig irgendwann zum Absturz.
Wird jetzt eine Dos-Funktion über ein TSR durch eine andere Dos-Funktion unterbrochen, wird die neue Funktion den DOS-Stack komplett neu überschreiben, insbesondere die Rücksprungadresse der unterbrochenen Funktion. Nach deren Beendigung findet die unterbochene DOS-Funktion dann nur noch fehlerhafte Daten auf dem DOS-Stack und es kommt zwangsläufig irgendwann zum Absturz.