Skip to main content.

Navigation:

Typische Fehler bei der OO-Stubs Entwicklung


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.