Typische Fehler bei der OO-Stubs Entwicklung
- Race Conditions
- Fehlender Koprozessor
- Zu große lokale Variablen (Stacks!)
- Unexpected Interrupt
- Watch Probleme
- Fehlerhafte Diskette
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.
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.