Selbst wenn Sie alle Beobachtungs- und Debugging-Techniken verwendet haben, bleiben manchmal noch Fehler im Treiber, die zu einem Systemfehler führen, wenn der Treiber ausgeführt wird. Wenn so etwas passiert, ist es wichtig, so viele Informationen wie möglich zu sammeln, um das Problem zu lösen.
Beachten Sie, daß “Fehler” hier nicht gleich “Panik” bedeutet. Der Linux-Code ist robust genug, um die meisten Fehler sanft abfedern zu können: Ein Fehler führt meistens nur zur Beendigung des aktuellen Prozesses, aber das System läuft weiter. Das System kann jedoch in Panik geraten, wenn ein Fehler außerhalb eines Prozeßkontextes auftritt oder ein lebenswichtiger Teil des Systems betroffen ist. Aber wenn das Problem nur ein Treiberfehler ist, dann wird normalerweise lediglich der Prozeß abgeschossen, der den Treiber unseligerweise benutzt hat. Der einzige nicht wiedergutzumachende Schaden, der auftritt, wenn ein Prozeß zerstört wird, ist der mögliche Verlust des vom Prozeß allozierten Speichers. Beispielsweise könnten via kmalloc allozierte dynamische Listen verloren sein. Da aber der Kernel für jedes offene Gerät close aufruft, kann der Treiber alles freigeben, was in open alloziert wurde.
Wir haben bereits erwähnt, daß eine informative Meldung auf der Konsole ausgegeben wird, falls sich Kernel-Code nicht richtig verhält. Im nächsten Abschnitt wird erklärt, wie man diese Meldungen entschlüsselt und benutzt. Obwohl diese Meldungen für den Anfänger ziemlich obskur aussehen, sind diese Prozessor-Dumps voller interessanter Informationen und oft schon ausreichend, um einen Programmfehler zu finden, ohne daß noch zusätzlich getestet werden muß.
Die meisten Fehler treten auf, wenn ein NULL-Zeiger dereferenziert oder ein anderer inkorrekter Zeiger-Wert verwendet wird. Das führt normalerweise zu einer Oops-Meldung.
Jede Adresse, die der Prozessor verwendet, ist eine virtuelle Adresse, die über eine komplexe Struktur aus sogenannten Seitentabellen (page tables, siehe in Kapitel 13) auf physikalische Adressen abgebildet wird. Wenn ein ungültiger Zeiger dereferenziert wird, kann der Seitenaustausch-Algorithmus den Zeiger nicht auf eine physikalische Adresse abbilden, und der Prozessor meldet dem Betriebssystem einen Seitenfehler (page fault). Wenn die Adresse nicht gültig ist, dann kann der Kernel die fehlende Seite nicht laden. Passiert dies, während der Prozessor im Supervisor-Modus ist, erzeugt der Kernel eine Oops-Meldung.
Interessanterweise war die erste nach der Version 2.0 eingeführte Verbesserung die automatische Fehlerbehandlung ungültiger Adressen beim Transportieren von Daten in und aus dem User-Space. Linus entschloß sich dazu, die Hardware fehlerhafte Speicherreferenzen abfangen zu lassen, so daß der Normalfall (die Adressen sind korrekt) effizienter abgehandelt wird.
Eine Oops-Meldung zeigt den Prozessor-Status zum Zeitpunkt des Fehlers an. Dazu gehören der Inhalt der CPU-Register, die Lage der Seitendeskriptoren-Tabellen und andere, auf den ersten Blick unverständliche Informationen. Die Meldung wird durch printk-Anweisungen in der Fehlerbehandlungsroutine (arch/*/kernel/traps.c) ausgegeben und, wie weiter vorn in Abschnitt “Printk” beschrieben, weitergeleitet.
Lassen Sie uns eine solche Meldung ansehen. Hier sehen Sie, was geschieht, wenn ein NULL-Zeiger auf einem PC refenziert wird, der die Version 2.4 des Kernels verwendet. Die wichtigste Information ist hier der Anweisungszeiger (EIP), also die Adresse der fehlerhaften Anweisung.
UnabletohandlekernelNULLpointerdereferenceatvirtualaddress\ 00000000 printingeip: c48370c3 *pde=00000000 Oops:0002 CPU:0 EIP:0010:[<c48370c3>] EFLAGS:00010286 eax:ffffffeaebx:c2281a20ecx:c48370c0edx:c2281a40 esi:4000c000edi:4000c000ebp:c38adf8cesp:c38adf8c ds:0018es:0018ss:0018 Processls(pid:23171,stackpage=c38ad000) Stack:0000010ec01356e6c2281a204000c0000000010ec2281a40c38ac000\ 0000010e 4000c000bffffc1c0000000000000000c38adfc4c010b86000000001\ 4000c000 0000010e0000010e4000c000bffffc1c000000040000002b0000002b\ 00000004 CallTrace:[<c01356e6>][<c010b860>] Code:c705000000000000000031c089ec5dc38db60000 |
Die obige Meldung wurde das durch Schreiben auf ein Gerät provoziert, das dem faulty-Modul gehört, einem Modul, in das wir absichtlich Fehler eingebaut haben. Die Implementation der write-Methode von faulty.c ist trivial:
ssize_tfaulty_write(structfile*filp,constchar*buf,size_tcount, loff_t*pos) { /*DurchDereferenziereneinesNULL-ZeigersganzeinfacheinenFehlerhervorrufen*/ *(int*)0=0; return0; } |
Wie Sie sehen, dereferenzieren wir hier einen NULL-Zeiger. Weil 0 nie ein zulässiger Zeigerwert ist, tritt ein Fehler auf, den der Kernel in die oben gezeigte Oops-Meldung umwandelt. Der aufrufende Prozeß wird dann beendet.
Das Modul faulty enthält noch weitere interessante Fehlerfälle in seiner read-Implementation:
charfaulty_buf[1024]; ssize_tfaulty_read(structfile*filp,char*buf,size_tcount, loff_t*pos) { intret,ret2; charstack_buf[4]; printk(KERN_DEBUG"read:buf%p,count%li\n",buf,(long)count); /*DienaechsteZeilefuehrtbei2.0zueinemOops,ab2.2nichtmehr*/ ret=copy_to_user(buf,faulty_buf,count); if(!ret)returncount;/*wesurvived*/ printk(KERN_DEBUG"didn'tfail:retry\n"); /*EinPuffer-Ueberlaufsollteauchbei2.2und2.4wirken*/ sprintf(stack_buf,"1234567\n"); if(count>8)count=8;/*copy8bytestotheuser*/ ret2=copy_to_user(buf,stack_buf,count); if(!ret2)returncount; returnret2; } |
Diese Funktion liest zuerst aus einem globalen Puffer, ohne die Größe der Daten zu überprüfen, und erzeugt dann durch Schreiben in einen lokalen Puffer einen Buffer Overrun. Die erste Situation führt nur in der Version 2.0 des Kernels zu einem Oops, weil spätere Versionen sich automatisch um die Kopierfunktionen zum oder vom User-Space kümmern. Der Puffer-Überlauf führt in allen Kernel-Versionen zu einem Oops; weil aber die return-Anweisung den Anweisungszeiger ins Niemandsland bringt, ist diese Art von Fehlern sehr viel schwieriger aufzuspüren, und Sie können etwa folgendes bekommen:
EIP:0010:[<00000000>] [...] CallTrace:[<c010b860>] Code:BadEIPvalue. |
Wenn Benutzer mit Oops-Meldungen konfrontiert werden, besteht das Hauptproblem darin, daß die hexadezimalen Werte nur wenig eigentliche Bedeutung enthalten; damit sie für den Programmierer eine Bedeutung bekommen, müssen sie in Symbole aufgelöst werden. Dafür gibt es Hilfsprogramme: klogd und ksymoops. Ersteres erledigt die Decodierung der Symbole ganz automatisch, wenn es gerade läuft, letzteres muß vom Benutzer explizit aufgerufen werden. In der folgenden Darstellung verwenden wir die Daten aus unserem ersten Oops-Beispiel, in dem wir einen NULL-Zeiger dereferenziert haben.
Der klogd-Daemon kann Oops-Meldungen decodieren, bevor sie überhaupt in den Protokolldateien auftauchen. klogd kann alle Informationen liefern, die Entwickler brauchen, um ein Problem einzukreisen, auch wenn die Entwickler dafür manchmal ein bißchen nachhelfen müssen.
Ein Auszug der Oops-Meldung für faulty sieht im Systemprotokoll folgendermaßen aus (beachten Sie die decodierten Symbole in der EIP-Zeile und im Stack Trace):
UnabletohandlekernelNULLpointerdereferenceatvirtualaddress\ 00000000 printingeip: c48370c3 *pde=00000000 Oops:0002 CPU:0 EIP:0010:[faulty:faulty_write+3/576] EFLAGS:00010286 eax:ffffffeaebx:c2c55ae0ecx:c48370c0edx:c2c55b00 esi:0804d038edi:0804d038ebp:c2337f8cesp:c2337f8c ds:0018es:0018ss:0018 Processcat(pid:23413,stackpage=c2337000) Stack:00000001c01356e6c2c55ae00804d03800000001c2c55b00c2336000\ 00000001 0804d038bffffbd40000000000000000bffffbd4c010b86000000001\ 0804d038 00000001000000010804d038bffffbd4000000040000002b0000002b\ 00000004 CallTrace:[sys_write+214/256][system_call+52/56] Code:c705000000000000000031c089ec5dc38db60000 |
klogd liefert den größten Teil der notwendigen Information, um das Problem einzukreisen. In diesem Fall sehen wir, daß sich der Anweisungszeiger (EIP) in der Funktion faulty_write befand, wissen also, wo wir mit der Suche anfangen müssen. Der String 3/576 teilt uns mit, daß sich der Prozessor an Byte 3 einer Funktion befand, die 576 Bytes lang zu sein scheint. Beachten Sie, daß die Werte dezimal, nicht hexadezimal sind.
Man muß allerdings als Entwickler etwas vorsichtig sein, um nützliche Informationen über Fehler zu bekommen, die in ladbaren Modulen auftreten. klogd lädt alle verfügbaren Symbolinformationen beim Start und verwendet danach diese Symbole. Wenn Sie ein Modul laden, nachdem klogd initialisiert worden ist (was normalerweise beim Systemstart geschieht), dann stehen klogd keine Informationen über die Symbole Ihres Moduls zur Verfügung. Um klogd zu zwingen, sich diese Informationen zu holen, müssen Sie dem klogd-Prozeß nach dem Laden (oder Neuladen) Ihres Moduls ein SIGUSR1-Signal schicken, bevor Sie etwas unternehmen, was zu einem Oops führen könnte.
Es ist auch möglich, klogd mit der Option –p ("paranoid") zu starten, die zum Neuladen der Symbolinformationen führt, wann immer klogd auf eine Oops-Meldung stößt. Die Manual-Page von klogd rät aber davon ab, weil klogd den Kernel nach Informationen fragen muß, nachdem das Problem aufgetreten ist. Eine so erlangte Information kann schlicht und einfach falsch sein.
Damit klogd korrekt funktionieren kann, muß eine aktuelle Version der Symboltabellendatei System.map vorliegen. Normalerweise steht diese Datei in /boot; wenn Sie einen Kernel aus einem vom Standard abweichenden Verzeichnis gebaut und installiert haben, müssen Sie eventuell System.map nach /boot kopieren oder klogd mitteilen, woanders zu suchen. klogd verweigert das Decodieren der Symbole, wenn die Symboltabelle nicht zum aktuellen Kernel paßt. Wenn ein Symbol im Systemprotokoll decodiert worden ist, dann können Sie ziemlich sicher sein, daß es korrekt ist.
Manchmal reicht klogd nicht aus, um ein Problem einzukreisen. Gewöhnlich brauchen Sie sowohl die hexadezimale Adresse als auch das dazugehörige Symbol, und oft brauchen Sie auch die Offsets als hexadezimale Zahlen sowie mehr Informationen als das reine Decodieren der Adreßinformation. Es ist auch nicht ungewöhnlich, daß der klogd-Prozeß während des Fehlers abgeschossen wird. In solchen Situationen braucht man ein kraftvolleres Oops-Analysewerkzeug. ksymoops ist so ein Werkzeug.
Vor der 2.3-Entwicklungsserie wurde ksymoops mit den Kernel-Quellen im Verzeichnis scripts verteilt. Heutzutage befindet es sich auf einem eigenen FTP-Server und wird unabhängig vom Kernel gepflegt. Selbst wenn Sie mit einem älteren Kernel arbeiten, sollten Sie sich am besten von ftp://ftp.ocs.com.au/pub/ksymoops die neueste Version dieses Werkzeugs holen.
Um sein Potential voll ausschöpfen zu können, braucht ksymoops neben der Fehlermeldung eine Reihe zusätzlicher Informationen; mit Kommandozeilenoptionen können Sie mitteilen, wo sich diese befinden. Das Programm braucht:
Diese Tabelle muß zu dem Kernel passen, der lief, als der Oops auftrat. Der Default ist /usr/src/linux/System.map.
ksymoops muß wissen, welche Module gerade geladen waren, als der Oops auftrat, um die symbolischen Informationen daraus extrahieren zu können. Wenn Sie diese Liste nicht angeben, holt ksymoops sie sich aus /proc/modules.
Defaultmäßig wird diese Liste aus /proc/ksyms geholt.
Beachten Sie, daß ksymoops ein normales Kernel-Image benötigt, nicht eine der komprimierten Versionen vmlinz, zImage oder bzImage, die die meisten Systeme zum Booten verwenden. Defaultmäßig wird kein Kernel-Image verwendet, weil die meisten Leute keines haben. Wenn Sie das passende Image parat haben, dann sollten Sie dem Programm mit der Option -v mitteilen, wo sich dieses befindet.
ksymoops sucht in den Standardverzeichnissen für Module, aber während der Entwicklung müssen Sie fast immer sagen, wo Ihre Module gerade liegen; dies geschieht mit der Option -o.
Obwohl ksymoops die Dateien aus /proc verwendet, um einige Informationen zu suchen, die es benötigt, können diese Ergebnisse unzuverlässig sein. Wahrscheinlich ist das System seit dem Oops neugestartet worden, und die Informationen aus /proc stimmen möglicherweise nicht mit denen überein, die gültig waren, als der Fehler auftrat. Wenn es möglich ist, ist es besser, /proc/modules und /proc/ksyms zu sichern, bevor der Oops auftritt.
Wir empfehlen allen Treiberentwicklern, die Manual-Page von ksymoops zu lesen; dies ist ein sehr informatives Dokument.
Das letzte Argument auf der Kommandozeile von ksymoops ist die Lage der Oops-Meldung; wenn diese nicht angegeben wird, liest das Programm in bester Unix-Tradition von stdin. Die Meldung kann mit Glück aus dem Systemprotokoll geholt werden; bei einem ganz üblen Crash müssen Sie sie möglicherweise vom Bildschirm abschreiben und später wieder eintippen (sofern Sie keine serielle Konsole verwenden, die bei der Kernel-Entwicklung sehr praktisch ist).
Beachten Sie, daß sich ksymoops von einer Oops-Meldung, die bereits von klogd verarbeitet worden ist, irritieren läßt. Wenn Sie klogd verwenden und Ihr System auch nach dem Oops noch läuft, dann können Sie oft mit dem Befehl dmesg eine saubere Oops-Meldung bekommen.
Wenn Sie nicht die gesamten hier genannten Informationen explizit angeben, wird ksymoops Warnungen ausgeben. Dies geschieht auch, wenn Module geladen werden, die keine Symbole definieren, und bei ähnlichen Problemen. Es ist selten, daß man ksymoops ohne Warnung ausführen kann.
Die Ausgabe von ksymoops sieht oft etwa so aus:
>>EIP;c48370c3<[faulty]faulty_write+3/20><===== Trace;c01356e6<sys_write+d6/100> Trace;c010b860<system_call+34/38> Code;c48370c3<[faulty]faulty_write+3/20> 00000000<_EIP>: Code;c48370c3<[faulty]faulty_write+3/20><===== 0:c705000000movl$0x0,0x0<===== Code;c48370c8<[faulty]faulty_write+8/20> 5:0000000000 Code;c48370cd<[faulty]faulty_write+d/20> a:31c0xorl%eax,%eax Code;c48370cf<[faulty]faulty_write+f/20> c:89ecmovl%ebp,%esp Code;c48370d1<[faulty]faulty_write+11/20> e:5dpopl%ebp Code;c48370d2<[faulty]faulty_write+12/20> f:c3ret Code;c48370d3<[faulty]faulty_write+13/20> 10:8db6000000leal0x0(%esi),%esi Code;c48370d8<[faulty]faulty_write+18/20> 15:00 |
Wie Sie sehen, liefert ksymoops Informationen über EIP und den Kernel-Stack fast genauso wie klogd, allerdings genauer und in hexadezimaler Schreibweise. Beachten Sie, daß die Funktion faulty_write korrekterweise mit einer Länge von 0x20 Bytes gemeldet wird. Dies ist möglich, weil ksymoops die Objektdatei Ihres Moduls liest und alle notwendigen Informationen extrahiert.
In diesem Fall bekommen Sie darüber hinaus auch noch einen Assembler-Dump des Codes, bei dem der Fehler auftrat. Diese Information kann oft verwendet werden, um herauszufinden, was eigentlich passiert ist; hier handelt es sich offensichtlich um eine Anweisung, die eine 0 an die Adresse 0 schreibt.
Es ist ein interessantes Feature von ksymoops, daß dieses Programm auf fast alle Plattformen portiert worden ist, auf denen Linux läuft, und die bfd-Bibliothek (Binary Format Description) verwendet, um mehrere Computer-Architekturen gleichzeitig zu unterstützen. Um aus der PC-Welt herauszukommen, schauen wir uns jetzt die gleiche Oops-Meldung auf der SPARC64-Plattform an (mehrere Zeilen sind aus Layout-Gründen umbrochen worden):
UnabletohandlekernelNULLpointerdereference tsk->mm->context=0000000000000734 tsk->mm->pgd=fffff80003499000 \|/____\|/ "@'/..\`@" /_|\_ _/|_\ \_ _U_/ ls(16740):Oops TSTATE:0000004400009601TPC:0000000001000128TNPC:0000000000457fbc\ Y:00800000 g0:000000007002ea88g1:0000000000000004g2:0000000070029fb0\ g3:0000000000000018 g4:fffff80000000000g5:0000000000000001g6:fffff8000119c000\ g7:0000000000000001 o0:0000000000000000o1:000000007001a000o2:0000000000000178\ o3:fffff8001224f168 o4:0000000001000120o5:0000000000000000sp:fffff8000119f621\ ret_pc:0000000000457fb4 l0:fffff800122376c0l1:ffffffffffffffeal2:000000000002c400\ l3:000000000002c400 l4:0000000000000000l5:0000000000000000l6:0000000000019c00\ l7:0000000070028cbc i0:fffff8001224f140i1:000000007001a000i2:0000000000000178\ i3:000000000002c400 i4:000000000002c400i5:000000000002c000i6:fffff8000119f6e1\ i7:0000000000410114 Caller[0000000000410114] Caller[000000007007cba4] InstructionDUMP:010000009010200081c3e008<c0202000>\ 3068000501000000010000000100000001000000 |
Beachten Sie, daß der Anweisungsauszug nicht bei der Anweisung anfängt, die den Fehler verursachte, sondern drei Anweisungen davor. Dies liegt daran, daß RISC-Plattformen mehrere Anweisungen parallel ausführen und Ausnahmen eventuell verzögern müssen; deswegen muß man in der Lage sein, die letzten paar Anweisungen zu sehen.
Hier sehen Sie die Ausgabe von ksymoops, wenn man die Eingabedatei ab der TSTATE-Zeile übergibt:
>>TPC;0000000001000128<[faulty].text.start+88/a0><===== >>O7;0000000000457fb4<sys_write+114/160> >>I7;0000000000410114<linux_sparc_syscall+34/40> Trace;0000000000410114<linux_sparc_syscall+34/40> Trace;000000007007cba4<END_OF_CODE+6f07c40d/????> Code;000000000100011c<[faulty].text.start+7c/a0> 0000000000000000<_TPC>: Code;000000000100011c<[faulty].text.start+7c/a0> 0:01000000nop Code;0000000001000120<[faulty].text.start+80/a0> 4:90102000clr%o0!0<_TPC> Code;0000000001000124<[faulty].text.start+84/a0> 8:81c3e008retl Code;0000000001000128<[faulty].text.start+88/a0><===== c:c0202000clr[%g0]<===== Code;000000000100012c<[faulty].text.start+8c/a0> 10:30680005b,a%xcc,24<_TPC+0x24>\ 0000000001000140<[faulty]faulty_write+0/20> Code;0000000001000130<[faulty].text.start+90/a0> 14:01000000nop Code;0000000001000134<[faulty].text.start+94/a0> 18:01000000nop Code;0000000001000138<[faulty].text.start+98/a0> 1c:01000000nop Code;000000000100013c<[faulty].text.start+9c/a0> 20:01000000nop |
Um diesen disassemblierten Code auszugeben, mußten wir ksymoops zunächst das Ziel-Dateiformat und die Ziel-Architektur angeben (weil die native Architektur des SPARC64-User-Space 32 Bit breit ist). In diesem Fall brauchten wir dafür die Optionen -t elf64-sparc -a sparc:v9.
Vielleicht beschweren Sie sich jetzt, daß dieser Aufruf-Trace keine interessanten Informationen enthält; die SPARC-Prozessoren speichern aber nicht den gesamten Aufruf-Trace auf dem Stack; die Register O7 und I7 enthalten die Anweisungszeiger der letzten beiden aufrufenden Funktionen, weswegen diese hier in der Nähe des Aufruf-Traces gezeigt werden. In diesem Falle befand sich die fehlerhafte Anweisung in einer Funktion, die von sys_write aufgerufen wurde.
Beachten Sie, daß unabhängig von der Plattform und der Architektur das verwendete Format im disassemblierten Code immer das gleiche wie das von objdump verwendete ist. objdump ist ein hilfreiches Werkzeug; wenn Sie sich die gesamte fehlerhafte Funktion anschauen wollen, können Sie den Befehl objdump –d faulty.o verwenden (auch hier brauchen Sie für SPARC64 spezielle Optionen: —target elf64-sparc—architecture sparc:v9). Weitere Informationen zu objdump und den Kommandozeilenoptionen dieses Programms finden Sie in dessen Man-Page.
Man braucht ein wenig Übung sowie gewisse Kenntnisse über den verwendeten Prozessor und die Konventionen des verwendeten Assemblers, um Oops-Meldungen decodieren zu können, aber es lohnt sich. Die Zeit, diese Technik zu erlernen, macht sich schnell bezahlt. Selbst, wenn Sie sich vorher schon mit PC-Assembler auf Nicht-Unix-Betriebssystemen beschäftigt haben, müssen Sie einiges neu lernen, weil sich die Unix-Syntax von der Intel-Syntax unterscheidet. (Eine gute Beschreibung der Unterschiede finden Sie in der Info-Datei von as im Kapitel “I386-specific”.)
Obwohl die meisten Fehler im Kernel-Code als Oops-Meldungen enden, können sie manchmal auch zu einem völligen Stillstand des Systems führen. Wenn das System stehenbleibt, wird auch keine Meldung ausgegeben. Wenn der Code beispielsweise in eine Endlosschleife gerät, hört der Kernel mit der Zuweisung des Prozessors an die Prozesse auf, und das System antwortet auf keinerlei Eingaben mehr, auch nicht auf die magische Kombination Strg-Alt-Entf. Sie haben zwei Möglichkeiten, mit stehengebliebenen Systemen umzugehen: Entweder Sie verhindern das Stehenbleiben von vornherein, oder Sie können den Fehler hinterher finden.
Sie können Endlosschleifen verhindern, indem Sie schedule-Aufrufe an strategischen Punkten einfügen. Wie Sie sich denken können, ruft schedule den Scheduler auf und ermöglicht es so anderen Prozessen, ebenfalls CPU-Zeit abzubekommen. Wenn ein Prozeß aufgrund eines Fehlers in Ihrem Treiber im Kernel-Space in einer Endlosschleife läuft, dann können Sie diesen Prozeß wegen der schedule-Aufrufe noch mit kill beenden — natürlich, nachdem Sie herausgefunden haben, was passiert ist.
Sie dürfen natürlich nicht vergessen, daß jeder schedule-Aufruf eine potentielle zusätzliche Quelle von reentranten Aufrufen Ihres Treibers ist, weil andere Prozesse ausgeführt werden können. Diese Reentranz sollte normalerweise kein Problem sein, wenn Sie die passenden Sperren in Ihrem Treiber verwenden. Stellen Sie aber sicher, daß Ihr Treiber nie schedule aufruft, wenn er über ein Spinlock verfügt.
Wenn Ihr Treiber wirklich das System zum Stillstand gebracht hat und Sie nicht wissen, wo Sie die schedule-Aufrufe einfügen sollen, dann ist es am besten, einige Ausgaben auf die Konsole einzufügen (indem Sie den console_loglevel-Wert verändern).
Manchmal scheint das System zu hängen, obwohl dies gar nicht der Fall ist. Das kann beispielsweise passieren, wenn die Tastatur auf irgendeine merkwürdige Art und Weise gesperrt bleibt. Dieses vermeintliche Hängenbleiben können Sie aufspüren, indem Sie die Ausgabe eines Programms beobachten, das Sie nur zu diesem Zweck betrachten. Eine Uhr oder eine Systemlastanzeige sind eine gute Statusanzeige; solange diese aktualisiert, arbeitet auch der Scheduler noch. Wenn Sie keinen grafischen Bildschirm verwenden, können Sie den Scheduler durch ein Programm kontrollieren, das die Tastatur-LEDs blinken, den Motor des Diskettenlaufwerks periodisch anlaufen oder den Lautsprecher knacken läßt — normale Beeps sind ziemlich lästig und sollten vermieden werden; benutzen Sie statt dessen den ioctl-Befehl KDMKTONE. Ein Beispielprogramm, das die Tastatur-LED wie einen Herzschlag aufblinken läßt, ist in misc-progs/heartbeat.c in den Quellen auf dem FTP-Server von O'Reilly zu finden.
Wenn Ihre Tastatur keine Eingaben entgegennimmt, melden Sie sich am besten über Ihr Netzwerk beim System an und beenden alle kritischen Prozesse oder setzen die Tastatur (mit kbd_mode –a) zurück. Festzustellen, daß es sich lediglich um eine aufgehängte Tastatur handelt, ist aber wenig nützlich, wenn Sie kein Netzwerk zur Verfügung haben. In diesem Fall könnten Sie ein alternatives Eingabegerät einrichten, um wenigstens das System sauber neu zu starten. Ein Herunterfahren und Neustarten ist eine geringere Belastung für Ihren Computer als der sogenannte große rote Knopf und erspart Ihnen das zeitraubende fsck-Überprüfen Ihrer Festplatten.
Ein solches alternatives Eingabegerät kann beispielsweise die Maus sein. Version 1.10 und neuere Versionen des gpm-Maus-Servers enthalten eine Kommandozeilenoption, die eine ähnliche Funktionalität einschaltet, aber nur im Textmodus funktioniert. Wenn Sie keine Netzwerkverbindung haben und im Grafikmodus arbeiten, empfehlen wir Ihnen, eine eigene Lösung zu verwenden; etwa einen Schalter, der mit dem DCD-Pin der seriellen Schnittstelle verbunden ist und ein Skript, das diesen Status abfragt.
Ein unersetzliches Werkzeug in diesen Situationen ist die “magische SysRq-Taste”, die in 2.2 und neueren Kerneln auf mehr Architekturen als früher zur Verfügung steht. Die magische SysRq-Taste wird durch eine Kombination der Tasten ALT und SysRq auf der PC-Tastatur oder durch die Tasten ALT und Stop auf SPARC-Tastaturen ausgelöst. Eine dritte Taste, die gemeinsam mit diesen beiden gedrückt wird, führt eine aus einer Reihe nützlicher Aktionen aus, die in der folgenden Liste angegeben sind:
Schaltet den rohen Tastaturmodus in Situationen aus, in denen Sie kbd_mode nicht verwenden können.
Ruft die “Secure Attention”-(SAK)-Funktion auf. SAK beendet alle Prozesse, die in der aktuellen Konsole laufen, und hinterläßt Ihnen so ein sauberes Terminal.
Führt eine Notsynchronisation aller Festplatten durch.
Versucht, alle Festplatten nur lesbar neu zu mounten. Diese Operation, die normalerweise direkt nach einem s verwendet wird, kann eine Menge Zeit zum Überprüfen der Dateisysteme sparen, wenn das System in ernsthaften Schwierigkeiten ist.
Startet das System unmittelbar neu. Achten Sie darauf, vorher die Festplatten zu synchronisieren und neu zu mounten.
Gibt die aktuellen Registerinformationen aus.
Gibt die aktuelle Task-Liste aus.
Gibt Speicherinformationen aus.
Es gibt noch weitere magische SysRq-Funktionen; die vollständige Liste finden Sie in der Datei sysrq.txt im Verzeichnis Documentation in den Kernel-Quellen. Beachten Sie, daß SysRq explizit in der Kernel-Konfiguration eingeschaltet sein muß und daß dies in den meisten Distributionen aus offensichtlichen Sicherheitsgründen nicht der Fall ist. Auf einem System, auf dem Treiber entwickelt werden, lohnt es sich aber, sich die Mühe zu machen, einen neuen Kernel zu kompilieren, um SysRq zu ermöglichen. SysRq muß dann zur Laufzeit mit einem Befehl wie dem folgenden eingeschaltet werden:
echo1>/proc/sys/kernel/sysrq |
Eine weitere Vorsichtsmaßnahme beim Reproduzieren von Systemaufhängern ist das nur lesbare Mounten (oder Entmounten) aller Festplatten. Wenn die Festplatten nur zum Lesen oder gar nicht gemountet sind, besteht nicht die Gefahr, daß die Dateisysteme beschädigt werden oder in einen inkonsistenten Zustand geraten. Eine andere Möglichkeit ist es, einen Computer zu verwenden, der alle seine Dateisysteme über NFS, das Network File System, mountet. Dazu muß "NFS-Root" im Kernel eingeschaltet sein und es müssen spezielle Parameter beim Booten übergeben werden. In diesem Fall können Sie selbst ohne SysRq jede Beschädigung von Dateisystemen vermeiden, weil die Kohärenz des Dateisystems von dem NFS-Server sichergestellt wird, den Ihr Gerätetreiber nicht mit in den Abgrund reißt.