|
|
 |
 |
Typische Fehler bei der OO-Stubs Entwicklung
- Zu Hause läuft mein Programm doch!
- Warum stürzt der Rechner immer ab?
Zu Hause läuft mein Programm doch!
Race Conditions
Symptome
Auf einem schnellen Pentium läuft euer System tadellos,
selbst im Langzeittest, aber auf ollen Kisten stürzt es
früher oder später ab.
Ursache
Ihr habt nicht alle kritischen Abschnitte geschützt. Es gibt also
mindestens eine Stelle in eurem Programm, an der eine Unterbrechung
für Fehler in den Datenstrukturen sorgt. Nun ist es nur eine
Frage der Wahrscheinlichkeit, ob und wann die Unterbrechung genau dort
auftritt. Diese Wahrscheinlichkeit ist umso größer, je
weniger Anweisungen zwischen zwei Unterbrechungen ausgeführt
werden können. Bei kurzen Timer-Intervallen und bei langsamen
Prozessoren ist die Fehlerwahrscheinlichkeit also größer
als bei langen Timer-Intervallen und schnellen Prozessoren.
Lösung
Überlegt noch mal ganz genau, zwischen welchen Anweisungen eine
Unterbrechung für die Datenstrukturen eures Systems
gefährlich werden könnte und schützt diese.
Fehlender Koprozessor beim i386
Symptome
Auf einem i486 oder Pentium läuft eure Anwendung problemlos, auf
einem alten i386er stürzt es immer an der selben Stelle
mit einem "unexpected interrupt" ab.
Ursache
Systeme mit i386 Prozessor besitzen nicht grundsätzlich einen
mathematischen Koprozessor und
können daher nicht mit Gleitkommazahlen (float, double) rechnen.
Alle Maschinenbefehle, die derartige Berechnungen ausführen,
lösen auf diesen Systemen also eine Exception (Nr. 7) aus.
Lösung
Wenn ihr nicht gerade Lust habt, eine Koprozessor-Emulation zu
schreiben oder zu portieren und als Exception-Handler bei OO-Stubs
anzubinden, solltet ihr auf hohe Mathematik verzichten. Für die
Lösung der Aufgaben 1 bis 6 braucht ihr solche Anweisungen
garantiert nicht. Bei Aufgabe 7 müsst ihr euch
schlimmstenfalls eben einschränken. Mit Ganzzahl-Arithmetik
lassen sich auch feine Programme schreiben und viele Probleme
lösen.
Warum stürzt der Rechner immer ab?
Zu große lokale Variablen (Stacks!)
Symptome
Euer Programm stürzt irgendwann ab, liefert einen "unexpected
interrupt", bootet den Rechner oder erzeugt andere unerwünschte
Ergebnisse. Möglicherweise tritt der Fehler nicht auf, wenn ihr
irgendwo scheinbar belanglose Anweisungen einfügt, einen
Anwendungsprozess mehr oder weniger erzeugt usw.
Ursache
Ihr legt zu große lokale Variablen in main oder in
einem eurer Anwendungsprozesse an.
Erklärung
Das Hauptprogramm main von OO-Stubs bekommt von uns bei
der Systeminitialisierung einen Stack der Größe 4KByte
zugewiesen. Zum Anlegen kleiner lokaler Variablen ist das vollkommen
ausreichend, nicht aber, um die Anwendungsprozesse mit eigenen Stacks
zu versorgen.
Das folgende Stück Code ist also falsch!
int main ()
{ char stack[4096];
Application appl (stack+4096);
...
}
Hier werden auf dem initialen Stack ein 4KB großes Feld und ein
Application Objekt angelegt, was zusammengenommen bereits
größer als die verfügbaren 4KB ist. Da aber OO-Stubs
keine Überprüfung der Stackgrenzen vornimmt und auch sonst
keine Schutzkonzepte implementiert, wird mit den lokalen Variablen
lustig Speicher überschrieben, der dem main überhaupt nicht
zur Verfügung steht. Das Ergebnis ist, dass globale
Variablen wie z.B. die Interrupt-Vektortabelle überschrieben
werden. Das kann unbemerkt bleiben, z.B. wenn nur Interruptvektoren
zerstört werden, die nie benötigt werden, es kann aber auch
zu Abstürzen oder anderen Fehlern führen. Das passiert
insbesondere dann, wenn ihr entweder euren eigenen Code
überschreibt oder wenn durch den Fehler unsinnige Werte als
Adressen von Funktionen interpretiert werden (siehe auch Unexpected Interrupt).
Lösung
Legt große Variablen wie die Stacks der Anwendungsprozesse immer
global an. Dann sorgt nämlich der Compiler dafür, dass
der Speicherplatz für sie zur Verfügung steht. Wer will,
kann das Schlüsselwort static verwenden um anzuzeigen, dass
die entsprechende Variable nur in der Datei referenziert werden soll, in der sie deklariert wurde:
static char stack[4096];
int main ()
{ Application appl (stack+4096];
...
}
Unexpected Interrupt
Symptome
Euer Programm stürzt mit der Meldung "unexpected interrupt -
processor halted" ab, obwohl ihr Timer- und Tastaturinterrupts korrekt
behandelt und sonst keine Interrupts erlaubt sind.
Ursache
Wahrscheinlich schreibt ihr in eurem Programm irgendwo an eine Stelle
im Speicher, die euch nicht gehört, oder ihr ruft eine Methode
auf einem Objekt auf, das nicht existiert. Der erste Fall tritt zum
Beispiel auf, wenn ihr mehr lokale Variablen anlegt, als der Stack
verkraften kann (siehe zu große lokale
Variablen). Der zweite Fall tritt beispielsweise auf, wenn ihr
Queue::dequeue() verwendet und nicht prüft, ob das
Ergebnis überhaupt ein gültiges Objekt ist, z.B. so:
Entrant* next;
next = (Entrant*) readylist.dequeue ();
dispatch (*next)
Wenn die Queue nämlich bereits vorher leer war, wird ein
Null-Zeiger zurückgeliefert. In dem Beispiel oben wird die Null
nun als Anfangsadresse eines Entrant Objekts
interpretiert. Wenn dispatch () nun in
toc_switch die Register mit den in der Struktur
toc gespeicherten Registerwerten dieses Objekts
belegen möchte, werden statt gültiger Werte die Inhalte der
Speicherstellen 0-3 für ebx, 4-7 für esi, ... und 16-19
für esp genommen. Was als nächstes passiert, hängt im
wesentlichen davon ab, welche Werte diese 20 Speicherstellen
zufällig hatten. Bei OO-Stubs soll der Prozesswechsel ja
durch eine ret Anweisung erfolgen, der Inhalt der
Speicherstelle, auf die der soeben initialisierte Stackpointer zeigt,
wird also als Rücksprungadresse interpretiert. Dies kann
natürlich wirklich eine Adresse im Codebereich sein, so dass
nun einfach irgendein Stück des Programms ausgeführt wird,
nur eben sicher nicht das richtige und sicher mit falschen
Registerinhalten. Wahrscheinlicher ist aber, dass es sich gar
nicht um eine Codeadresse handelt. Das erkennt der Prozessor aber
nicht, er wird also versuchen, das Bitmuster, dass er an dieser
Stelle vorfindet, als Anweisung zu interpretieren und
auszuführen. Das kann eine Weile gut gehen, obwohl natürlich
vollkommener Unnsinn dabei herauskommt. Früher oder später
wird der Prozessor dann aber doch auf ein Bitmuster stoßen,
dessen Ausführung eine Exception auslöst (z.B. durch den
int Befehl, eine Division durch Null, einen der
verschiedenen Debug-Befehle oder ein Bitmuster, das sich nun wirklich
nicht mehr als Anweisung interpretieren lässt).
Wenn der Code von OO-Stubs, der die Behandlung
der Interrupts und Exceptions vornimmt, durch den Fehler noch nicht
zerstört wurde, wird nun Panic::prologue()
ausgeführt, also "unexpected interrupt - processor halted"
ausgegeben und der Prozessor gestoppt.
Dasselbe Problem tritt übrigens auch auf, wenn einer eurer
Anwendungsprozesse terminiert ohne exit() oder
kill zu verwenden. Wenn er nämlich aus
Thread::action() zurück nach kickoff
kehrt und ihr dort keinen Notstopp eingebaut habt, wird die unsinnige
Rücksprungadresse verwendet, die ihr ganz am Anfang bei
toc_settle dort eingetragen habt.
Lösung
Guckt eurer Programm noch einmal ganz kritisch danach durch, ob eure
Stacks für die lokalen Variablen ausreichen, ob ihr
irgendwo Objektzeiger verwendet, die möglicherweise Null-Zeiger
sein könnten oder ob einer eurer Prozesse terminiert ohne
Scheduler::exit() aufzurufen.
Watch Probleme
Symptome
Eurer Programm zeigt ein merkwürdiges Verhalten bei kurzen (< 200
Mikrosekunden) Intervallen für die Zeitgeberunterbrechung:
z.B. extrem seltene Unterbrechungen, unexpected interrupts, u.a.
Ursache
Es sieht fast aus, als handele es sich um einen
Hardware-Fehler. Sicher sind wir uns aber nicht.
Lösung
Vermeidet zu kleine Zeitgeberunterbrechungen. Sinnvolle Werte für
präemptives Scheduling bei OO-Stubs liegen im Bereich von ca. 10
oder 20 Millisekunden, also Watch watch(20000) .
Fehlerhafte Diskette
Symptome
Unerklärliche Abstürze.
Ursache
Erstaunlich oft waren in diesem Fall einfach nur die Bootdisketten
kaputt. Anscheinend ist der Verschleiß größer als
vermutet.
Lösung
Versucht es mal mit einer frischen Diskette. Vielleicht hilft es
ja. Ansonsten solltet ihr euch noch mal die zuvor beschriebenen
Fehlerquellen durch den Kopf gehen lassen.
|
 |
 |
|