Im Idealfall sollte ein Betriebssystem nach einem Programmabsturz dafür sorgen, daß es nicht zu Folgefehlern kommt, die einen kompletten Crash nach sich ziehen. Daß dies TOS nur selten gelingt, zeigt die Erfahrung. Durch einen kleinen Eingriff läßt sich die Stabilität des Systems nach einem Absturz erhöhen.
Doch zunächst sollte man sich Gedanken darüber machen, woran es denn liegen kann, daß nach einem abrupten Programmende häufig gar nichts mehr geht. Durch die Komplexität des GEM bedingt, ist die Wiederaufnahme des normalen Betriebs nach einem Absturz prinzipiell schwierig. Gerade in den TOS-Versionen ab TOS 1.04 wurde jedoch durch die Einführung des AES-Aufrufs wind_get eine Vorkehrung getroffen, die es dem GEM eher erlaubt, sich von den Folgen eines Absturzes zu erholen. Aber auch bei fehlerhaften TOS-Programmen beobachtet man, daß der Rechner nach einem Absturz oder spätestens nach dem Start des nächsten Programms eine Ruhepause einlegt, aus der man ihn nur durch Betätigung des Reset-Knopfes wieder erwecken kann. Schuld an diesem Verhalten sind häufig Systemvektoren, die von einem nicht residenten Programm verändert wurden und nach dessen Absturz ins Leere zeigen. Solange kein weiteres Programm gestartet wird, scheint alles im Lot zu sein, denn noch befindet sich der Code des ursprüngliches Programmes im Speicher. Wird nun aber ein neues Programm geladen, landet der Prozessor beim Sprung durch die Vektoren in einem Undefinierten Datenbereich, und der endgültige Absturz ist perfekt.
Somit wären also veränderte Vektoren als die eigentliche Ursache für viele System-Crashs ausgemacht. Das Verbiegen von Vektoren ist für die Funktion vieler Programme unvermeidlich, so daß es keinen Sinn macht, das Ändern von Systemvektoren prinzipiell zu unterlassen.
Aber dennoch: Diese Vektoren dürfen ausschließlich von residenten Programmen manipuliert werden, wobei das XBRA-Verfahren zu verwenden ist [1], Residente Programme verbleiben nach ihrer Beendigung im Speicher, so daß weiterhin definierter Code an der Einsprungadresse des Vektors steht.
Die eigentlichen Übeltäter sind somit die nicht residenten Programme, falls diese Vektoren verbiegen. Häufig geschieht dies lediglich dazu, beim Auftreten eines Bus- oder Adreßfehlers einen Absturz in Form von Bomben zu vermeiden und stattdessen eine entsprechende Meldung auszugeben, die den Anwender zwar beruhigen mag, aber das eigentliche Problem, nämlich den Programmfehler, nicht aus der Welt schafft.
Neben ungültigen Werten in den Exception-Vektoren, die ja nicht regelmäßig angesprungen werden, droht eine viel größere Gefahr dann, wenn ein Programm einen VBL-Slot belegt und abstürzt, ohne diesen wieder freigegeben zu haben. Da der Vertical Blank Interrupt regelmäßig auftritt, ist in solchen Fällen ein baldiger Absturz unvermeidlich. (Die Rechenkünstler unter den Lesern haben sicherlich schon ausgerechnet, daß es bis zum endgültigen Zusammenbruch bei einem Farbmonitor etwa 2.6x10'3 s länger dauert als bei Monochrom-Monitoren.)
Eigentlich liegt es auf der Hand, wie man dem geschilderten Problem zu Leibe rücken kann. Jedesmal dann, wenn ein Programm (auf welche Weise auch immer) beendet wird, müssen alle Systemvektoren und die Vektoren in der VBL-Queue überprüft werden. Zeigt ein Vektor in einen nicht mehr allozierten Bereich, liegt ein Fehler vor. Im Falle eines Eintrags in der VBL-Queue genügt es, diesen durch einen Null-Pointer zu löschen. Bei den restlichen Vektoren ist die Sachlage komplizierter, da diese ja von mehreren Programmen gleichzeitig benutzt werden können und man überdies nichts über deren ursprünglichen Wert aussagen kann. Hier ist man darauf angewiesen, daß alle vektorverbiegenden Programme das XBRA-Verfahren verwendet haben. Nur dann ist es möglich, den Vektor des unsanft beendeten Programms aus der Vektorkette auszuklinken, ohne daß andere Routinen davon beeinträchtig werden. Wurden Vektoren ohne Nutzung des XBRA-Mechanismus verbogen, bleibt lediglich die Möglichkeit, den ins Nirwana zeigenden Vektor vorsichtshalber auf einen RTE-Befehl umzulenken.
Natürlich muß man sich vor der Überprüfung der Systemvektoren vergewissern, daß ein Programm sich terminiert, ohne resident im Speicher zu verbleiben. Andernfalls wäre es natürlich falsch, verbogenene Vektoren, die in dieses Programm zeigen, zurückzusetzen. Nur bei nicht residenten Programmen ist ein Eingriff erlaubt.
Die gestellten Forderungen erfüllt das Programm CHECKIT. Dabei ist die Systemvariable etv_term für unsere Anwendung sehr hilfreich. Dieser Vektor zeigt auf eine Routine, die nach der Beendigung jedes Programms angesprungen wird. Dies ist auch dann der Fall, wenn es sich um einen unvorhergesehenen Abbruch, beispielsweise durch einen Absturz, handelt.
Über den aktuellen Prozeß-Deskriptor läßt sich leicht feststellen, in welchem Speicherbereich TEXT, DATA und BSS-Segment dieses Programms lagen. Diese Informationen werden für die Entscheidung benötigt, ob ein Systemvektor in den Code- oder Datenbereich des Programms zeigt. Um welchen Programmtyp, also resident oder nicht resident, es sich handelt, erkennt CHECKIT anhand des letzten von diesem Programm abgesetzten GEMDOS-Aufrufs. Handelte es sich hierbei um PTERMRES, so bleibt der Programmcode resident im Speicher, und es ist nicht notwendig, irgendwelche Vektoren zu überprüfen. In allen anderen Fällen muß CHECKIT in Aktion treten.
Das hier vorgestellte Verfahren greift natürlich nur solange, wie ein Programm keine Vektoren in einen per MALLOC angeforderten Speicherblock legt. Prinzipiell wäre es zwar denkbar, auch bei MFREE-Aufrufen zu überprüfen, ob ein Vektor in den freigegebenen Speicherblock zeigt, aber leider benutzt das Desktop beim MFREE nach der Beendigung eines Programms nicht den GEMDOS-Vektor. Stattdessen wir direkt ins GEMDOS eingesprungen, so daß man hier nicht eingreifen kann Erst im MultiTOS wird sich dies ändern, da das Desktop dann lediglich noch eine GEM-Anwendung unter vielen darstellt, die für Systemaufrufe die TRAP-Vektoren benutzen muß.
Die genaue Funktionsweise von CHECKIT kann dem kommentierten Programm-Listing entnommen werden. Der Prozeß-Deskriptor läßt sich erst ab TOS 1.02 aus der _sysbase-Struktur entnehmen. Für TOS 1.00 muß man auf die inzwischen dokumentierte Adresse zurückgreifen, die sich im spanischen TOS von den restlichen länderspezifischen TOS-Versionen unterscheidet.
Daß CHECKIT die Installation der neuen Systemvektoren über die BIOS-Routine SETEXC erledigt und dazu nicht selber direkt auf die entsprechenden Adressen zugreift, hat übrigens einen guten Grund. Auf dem ST war es noch relativ sicher, TRAP-Vektoren durch direkten Zugriff auf die entsprechende Vektoradresse zu manipulieren, da diese Adresse konstant war. Bei anderen Prozessoren als dem 68000 sieht das jedoch anders aus. Hier kann der Beginn der Tabelle der Exception-Vektoren durch Manipulation des Vector-Base-Registers (VBR) an eine andere Stelle verlegt werden. Um sicherzugehen, daß man wirklich den gewünschten Vektor ändert, bleibt somit nur der Weg über das BIOS, das auch in zukünftigen TOS-Versionen, die das VBR nutzen könnten, den Vektor garantiert an die richtige Adresse schreibt.
Literatur:
[1] Jankowski, Rabich, Reschke, „Atari Profibuch ST-STE-TT", SYBEX-Verlag
*****************************
* *
* CHECKIT V1.0 *
* Vektorcheek für ST und TT *
* *
* April 1992 by Uwe Seimet *
* (c) 1992 MAXON Computer *
* *
*****************************
GEMDOS = 1
CCONWS = 9
PTERMRES = 49
MSHRINK = 74
BIOS = 13
SETEXC = 5
XBIOS = 14
KBDVBASE = 34
SUPEXEC = 38
etv_timer = $400
resvalid = $426
resvector = $42a
_memtop = $436
nvbls = $454
_vblqueue = $456
_sysbase = $4f2
_p_cookies = $5a0
_ramtop = $5a4
_ramvalid = $5a8
ttmagic = $1357bd13 ;Magic für TT-PAM
text
move.l 4(sp),a0
move.l 12(a0),a6 ;Länge des ;TEXT-Segments
add.l 20(a0),a6 ;Länge DATA
add.l 28(a0),a6 ;Länge BSS
lea $100(a6),a6 ;für Basepage
pea (a6)
pea (a0)
clr -(sp)
move #MSHRINK,-(sp) ;überzähligen
trap #GEMDOS ;Speicher
lea 12(sp),sp ;freigeben
pea tttest(pc)
move #SUPEXEC,-(sp) ;auf TT
trap #XBIOS ;testen
addq.l #6,sp
pea gemdos(pc)
move #33,-(sp)
move #SETEXC,-(sp) ; neuer
trap #BIOS ;GEMDOS-Vektor
addq.l #8,sp
move.l d0,o_gemdos
pea term(pc)
move #258,-(sp)
move #SETEXC,-(sp) ;neuer Vektor
trap #BIOS ;für etv_term
addq.l #8,sp
move.l d0,o_term
pea message
move #CCONWS,-(sp)
trap #GEMDOS
addq.l #6,sp
clr -(sp)
pea (a6)
move #PTERMRES,-(sp) ;Programm
trap #GEMDOS ;resident halten
dc.l "XBRA"
dc.l "USCK"
o_term: dc.l 0
term: tst.b resflg ;nichts tun
bne cont ;bei residentem Programm
move.l _sysbase,a0
move.l a0,a1
move.l $28(a1),a0
move.l (a0),a0 ;Prozeß-Deskriptor
cmp #$0102,$02(a1) ;neuer als
bcc.s newtos ;TOS 1.00-
move.l $873c,a0 ;Default für spanisches TOS 1.00
move $1c(a1),d0 ;Ländercode
lsr #1,d0 ;isolieren
emp #4,d0 ;spanisch?
beq.s newtos ;ja-
move.l $602c,a0 ;für TOS 1.00
newtos: move.l 8(a0),a1 ;Beginn des
move.l a1,a2 ;TEXT-Segments
add.l 12(a0),a2 ;+ Länge TEXT
add.l 20(a0),a2 ;+ Länge DATA
add.l 28(a0),a2 ;+ Länge BSS
;ergibt Endadresse
move nvbls,d0
move.l _vblqueue,a0
bra.s checkvbl
checkv: move.l (a0)+,d1
cmp.l d1,a1 ;zeigt Vektor
bcc.s checkvbl ;in Programm-
cmp.l d1,a2 ;bereich?
bcs.s checkvbl
clr.l -4(a0) ;Vektor
checkvbl:dbra d0,checkv ;löschen
lea $08,a0
moveq #8,d0
bsr.s check
lea $80,a0 ;TRAP-Vektoren
moveq #16,d0
bsr.s check
lea $64,a0 ;Autovektor
moveq #6,d0 ;Interrupts
bsr.s check
lea $0100,a0 ;MFP1-
moveq #15,d0 ;Interrupts
bsr.s check
tst.b ttflg
beq.s nott
lea 320,a0 ;MFP2-, SCC-
moveq #31,d0 ;Interrupts
bsr.s check ;nur bei TT
nott: lea $0114,a0 ;Systemtimer
moveq #1,d0
bsr.s check ;und IKBD/MIDI
lea etv_timer,a0
moveq #2,d0
bsr.s check ;etv-Vektoren
movem.l a1-a2,-(sp)
move #KBDVBASE,-(sp)
trap #XBIOS
addq.l #2,sp
movem.l (sp)+,a1-a2
move.l d0,a0
moveq #8,d0
bsr.s check
movem.l a1-a2,-(sp)
dc.w $a000
move.l a2,a0
movem.l (sp)+,a1-a2
moveq #15,d0 ;LINEA-
bsr.s check ;Vektoren
cmp.l #$31415926,resvalid
bne.s cont
move.l resvector,a0
moveq #0,d0
bsr.s check
cont: move.l o_term(pc),a0 ;weiter
jmp (a0) ;im TOS
*Testen, ob Vektor in ehemaligen Programrabereich
*zeigt. Wenn ja, aus Vektorkette enfernen oder
*auf RTE setzen.
check:
move.l #iret,d2 ;Zeiger
move.l a0,a3 ;auf RTE
trp: move.l (a3),d1
cmp.l #ttmagic,_ramvalid
bne.s nottram ;kein TT-RAM-
cmp.l _ramtop,d1 ;zeigt Vektor
bcc.s invalid ;in TT-RAM?
cmp.l #$01000000,d1
bcc.s test
nottram: cmp.l _memtop,d1 ;zeigt Vektor
bcc.s invalid ;in ST-RAM?
cmp.l #$0400,d1
bcs.s invalid
*Vektor ist gültig und zeigt in ST- oder TT-RAM
test: cmp.l d1,a1 ;zeigt Vektor
bcc.s valid ;in Programm-
cmp.l d1,a2 ;bereich?
bcs.s valid ;nein-
move.l d1,a4
cmp.l #"XBRA",-12(a4) ;XBRA benutzt?
bne noxbra
move.l -4(a4),d2 ;neu verketten
noxbra: move.l d2,(a3)
valid: move.l d1,a3
cmp.l #"XBRA",-12(a3)
beq trp
invalid: addq.l #4,a0 ;nächster
dbra d0,check ;Vektor
rts
*Auf TT testen
tttest:
move.l _p_cookies,d0
beq.s nojar
move.l d0, a0
loop: movem.l (a0)+,d0-d1
tst.l d0
beq.s nojar
cmp.l #"_ MCH",d0
bne loop
cmp.l #$00020000,d1 ;TT?
scc ttflg
nojar: rts
dc.l "XBRA"
dc.l "USCK"
o_gemdos:dc.l 0
gemdos:
lea 8(sp),a0 ;Supervisor-
btst #5,(sp) ;Modus?
bne super ;ja-
move.l usp,a0
super: cmp #PTERMRES,(a0) ;-residentes
seq resflg ;Programm?
move.l o_gemdos(pc),a0
jmp (a0) ;weiter im TOS
iret: rte
data
message: dc.b $0d,$0a,"CHECKIT V1.0 installiert"
dc.b $0d,$0a
dc.b "1992 by Uwe Seimet",$0d,$0a,0
bss
ttflg: ds.b 1 ;Flag für TT