Login
Newsletter
Werbung

Do, 25. November 2010, 15:00

Kernel-Crash-Analyse unter Linux

In einer idealen Welt stürzen weder Systeme noch Applikationen ab. Da die Welt nun mal weder ideal noch der Mensch in der Lage ist, ab einer gewissen Komplexität fehlerfreie Programme zu schreiben, hat wahrscheinlich jeder schon einmal einen Absturz gesehen. Dieser Artikel zeigt, wie Abstürze des ganzen Systems funktionieren, wie sie korrekt gehandhabt und wie sie untersucht werden können.

Die graue Theorie

Der Linux-Kernel ist zweifelsohne ein solides System. Er ist für seine Größe stabil, durchaus fehlertolerant und bietet wenig Möglichkeiten zum Angriff. Doch auch Linux ist von Fehlern nicht gefeit. Wohl jeder hat schon mal in seinem Leben einen Absturz oder ein sogenanntes Oops gesehen.

Untersuchung eines Kernelproblems mittels des grafischen gdb-Frontends ddd

Mirko Lindner

Untersuchung eines Kernelproblems mittels des grafischen gdb-Frontends ddd

Prinzipiell sind Oopses nichts Schlimmes, zeugen sie in der Regel nur von einem Fehlverhalten eines Subsystems, das so gravierend war, dass es dem Programmierer wichtig war, den Anwender davon in Kenntnis zu setzen. Viele solcher Fehler führen weder zum Stillstand noch zum Absturz des Kernels. Oftmals sind es eher informierende Ausgaben, die beispielsweise dem Anwender davon berichten, dass ein bestimmtes System nicht korrekt abgemeldet wurde, während ein Treiber entladen wurde. Ein Beispiel wären hier diverse /proc-Einträge, die bei der Abmeldung eines Treibers vergessen wurden.

Solche Fehler handhabt der Kernel in der Regel, ohne das System stoppen zu müssen. Sie können mitunter auch oft passieren, ohne dass ein Anwender etwas davon mitbekommt. Oftmals müssen diverse Debug-Einstellungen eingeschaltet werden, damit solche Ausgaben sichtbar sind.

Viel dramatischer sind Fehler, die zum Stillstand des Systems führen. Typischerweise sind es NULL-Pointer oder Zugriffe auf falsche Speicherbereiche im Kernel. Dabei spielt es keine Rolle, ob solche Zugriffe von Linux selbst oder von einem Treiber durchgeführt werden – alle Speicherfehler führen zum sofortigen Stillstand des Systems. Sofort?! Nein, nicht wirklich.

Auch wenn es den Anschein hat, dass der Kernel nach einem Fehler abgestürzt ist, ist dem nicht so. Am besten lässt es sich an der Meldung des Oopses selbst verdeutlichen. Der Oops selbst ist eine normale Kernel-Funktion, die in der Datei traps.c residiert. Die beiden für die Anzeige von Oops verantwortlichen Funktionen dump_stack() und show_trace() sind Funktionen, die regulär verlassen werden. Die eigentliche Behandlung eines Fehlers nach einem Oops erfolgt später direkt im Kernel oder im System, das einen Oops gemeldet hat.

Ein Beispiel gefällig? Die allseits bekannte Meldung »BUG: unable to handle kernel NULL pointer dereference« versteckt sich auf x86-Systemen in der Funktion vmalloc_fault() und der Datei fault.c. Doch erst der Aufruf von do_exit(SIGKILL) am Ende von vmalloc_fault() und eine abschließende Schleife am Ende von do_exit() oder der Aufruf von panic() versetzen den Kernel in eine Endlosschleife und machen dem System die Lichter aus. Bis dahin ist der Kernel durchaus in der Lage, noch zu interagieren und unter Umständen diverse Systeme herunterzufahren oder gar zu starten.

Arten von Kernel-Debugging

Ist ein Kernel erst einmal gestoppt, stehen dem Entwickler nur begrenzt Möglichkeiten zur Verfügung, den Fehler zu untersuchen. Im Gegensatz zu Applikationen, die im User-Space gestartet werden, wirken sich Fehler im Kernel sofort auf das komplette System aus. Da aber der Kernel nach einem Absturz immer noch funktional ist, ist es durchaus verständlich, dass sich diverse Entwickler Gedanken über eine Möglichkeit gemacht haben, diese Tatsache für weitere Untersuchungen zu nutzen.

Die wohl populärste Möglichkeit, Fehler im Kernel zu untersuchen, stellt dabei printk() dar. printk() ist nichts anderes als das Pendant für printf() im Kernel. Es bedarf allerdings wenig Vorstellungsvermögen, um zu erkennen, dass die Untersuchung von Fehlern mittels printk() nicht nur Erfahrung in der Programmierung des Kernels erfordert, sondern auch nur bedingt für eine Ferndiagnose eingesetzt werden kann. Vor allem komplizierte Probleme lassen sich mit printk() unter Umständen recht schwer lokalisieren.

Dies führte zu einer zweiten Möglichkeit – den Oops-Meldungen. Entgegen des ersten Eindrucks stellen diese Meldungen für einen Programmierer eine extreme Bereicherung dar. Sie beinhalten oftmals alle Informationen, die zur Lösung eines Problems benötigt werden. Der einzige Wermutstropfen ist lediglich, dass sie manchmal von Anwendern nicht korrekt interpretiert oder bei manchen Fehlerarten (Schleifen, Deadlocks usw.) nicht ausgegeben werden. Ferner können zum Beispiel Speicherbereiche so korrumpiert sein, dass auch die Oops-Meldungen keinen Mehrwert mehr bieten.

Hier greift nun die dritte Möglichkeit: Debugging. Zwar hat diese Art der Fehleruntersuchung erst recht spät in den Kernel Einzug gehalten, doch stellt sie oftmals die einzige Möglichkeit dar, komplizierte Fehler in einer vertretbaren Zeit zu finden. Das wohl bekannteste Werkzeug stellt dabei der ursprünglich als Patch implementierte und seit der Version 2.6.26 im Kernel vorhandene KGDB dar. Der eigentliche Debugging wird dabei mit GDB per Remote-Protokoll auf einem zweiten System ausgeführt.

Die wohl ergiebigste Art, Fehler zu untersuchen, stellen allerdings Crash-Dump-Tools dar. Im Gegensatz zu anderen Ansätzen bedarf es hier nicht der direkten Mitarbeit des Entwicklers. Ferner beinhalten Crash-Dumps, sofern sie vollständig sind, alle drei oben genannten Arten der Diagnose, so dass der Entwickler auf eine schier unerschöpfliche Fülle an Informationen zurückgreifen kann. Die zwei bekanntesten Tools sind dabei Kdump und LKCD (Linux Kernel Crash Dump), wobei das letztgenannte bereits eingestellt wurde.

Pro-Linux
Pro-Linux @Facebook
Neue Nachrichten
Werbung