Was ist ein unterbrechungsfreier Prozess?

155

Manchmal, wenn ich ein Programm unter Linux schreibe und es aufgrund eines Fehlers abstürzt, wird es zu einem unterbrechungsfreien Prozess und läuft für immer weiter, bis ich meinen Computer neu starte (selbst wenn ich mich abmelde). Meine Fragen sind:

  • Was führt dazu, dass ein Prozess nicht mehr unterbrochen werden kann?
  • Wie kann ich das verhindern?
  • Dies ist wahrscheinlich eine dumme Frage, aber gibt es eine Möglichkeit, sie zu unterbrechen, ohne meinen Computer neu zu starten?
Jason Baker
quelle
Ist es möglich, dass ein Programm geschrieben werden kann, um einen Prozess zu initiieren, der in einen TASK_UNINTERUPTIBLEZustand versetzt wird, wenn sich das System nicht im Ruhezustand befindet, wodurch zwangsweise Daten gesammelt werden, die auf die Übertragung warten, sobald der Superuser beendet wird? Dies wäre eine Goldmine für Hacker, um Informationen abzurufen, in den Zombie-Status zurückzukehren und Informationen im Leerlauf über das Netzwerk zu übertragen. Einige können argumentieren, dass dies eine Möglichkeit ist, eine Blackdoorfür die Mächte zu schaffen, jedes System wie gewünscht zu betreten und zu verlassen. Ich bin der festen
Überzeugung,
2
Wäre bitte der Code zu teilen?
Wieder

Antworten:

197

Ein unterbrechungsfreier Prozess ist ein Prozess, der sich zufällig in einem Systemaufruf (Kernelfunktion) befindet und nicht durch ein Signal unterbrochen werden kann.

Um zu verstehen, was das bedeutet, müssen Sie das Konzept eines unterbrechbaren Systemaufrufs verstehen. Das klassische Beispiel ist read(). Dies ist ein Systemaufruf, der lange (Sekunden) dauern kann, da möglicherweise eine Festplatte hochgefahren oder Köpfe bewegt werden müssen. Während der meisten Zeit wird der Prozess in den Ruhezustand versetzt und blockiert die Hardware.

Während der Prozess im Systemaufruf schläft, kann er ein asynchrones Unix-Signal empfangen (z. B. SIGTERM). Dann geschieht Folgendes:

  • Der Systemaufruf wird vorzeitig beendet und ist so eingerichtet, dass -EINTR an den Benutzerbereich zurückgegeben wird.
  • Der Signalhandler wird ausgeführt.
  • Wenn der Prozess noch ausgeführt wird, erhält er den Rückgabewert vom Systemaufruf und kann denselben Aufruf erneut ausführen.

Wenn Sie frühzeitig vom Systemaufruf zurückkehren, kann der Benutzerbereichscode sein Verhalten als Reaktion auf das Signal sofort ändern. Zum Beispiel sauber als Reaktion auf SIGINT oder SIGTERM beenden.

Andererseits dürfen einige Systemaufrufe auf diese Weise nicht unterbrochen werden. Wenn das System aus irgendeinem Grund Blockierungen aufruft, kann der Prozess auf unbestimmte Zeit in diesem nicht tötbaren Zustand bleiben.

LWN hat im Juli einen schönen Artikel veröffentlicht , der dieses Thema berührt.

So beantworten Sie die ursprüngliche Frage:

  • So verhindern Sie dies: Finden Sie heraus, welcher Treiber Ihnen Probleme bereitet, und stellen Sie die Verwendung entweder ein oder werden Sie ein Kernel-Hacker, und beheben Sie das Problem.

  • So beenden Sie einen unterbrechungsfreien Prozess ohne Neustart: Lassen Sie den Systemaufruf irgendwie beenden. Häufig ist es am effektivsten, das Netzkabel zu ziehen, ohne den Netzschalter zu betätigen. Sie können auch ein Kernel-Hacker werden und den Treiber dazu bringen, TASK_KILLABLE zu verwenden, wie im LWN-Artikel erläutert.

ddaa
quelle
30
Ich habe das Netzkabel an meinem Laptop gezogen und es funktioniert leider nicht. ;-)
Thecarpy
1
Ist es nicht EINTR statt EAGAIN? Auch read () gibt -1 zurück und errno wird auf den Fehler gesetzt.
Lethalman
2
@ Express: Sie verpassen in der Tat den Punkt. Lesen Sie den LWN-Artikel: lwn.net/Articles/288056 . Diese Probleme werden von faulen Programmierern von Gerätetreibern verursacht und müssen im Gerätetreibercode behoben werden.
Ddaa
4
@ddaa "Unix-Tradition (und damit fast alle Anwendungen) glauben, dass Dateispeicher-Schreibvorgänge nicht signalunterbrechbar sind. Es wäre nicht sicher oder praktisch, diese Garantie zu ändern." -> Dies ist genau der falscheste Teil dieser IMO. Unterbrechen Sie einfach die Lese- / Schreibanforderung des Treibers, und wenn das tatsächliche Gerät (Festplatte / Netzwerkkarte / usw.) die Daten liefert, ignorieren Sie sie. Ein Betriebssystemkernel sollte so erstellt werden, dass KEIN Entwickler ihn vermasseln kann.
Dexter
2
@ddaa Ich weiß, dass Linux kein Mikrokernel ist, obwohl ich nicht sicher bin, welcher Teil meines Kommentars sich darauf bezieht ... Und bedeutet Ihr Kommentar dann, dass ein Mikrokernel-Betriebssystem kein Problem mit diesen "unterbrechungsfreien" Prozessen hat? Denn wenn nicht, ist es vielleicht Zeit für mich, ein Mikrokernel-Fan zu werden ...: D
Dexter
49

Wenn sich ein Prozess im Benutzermodus befindet, kann er jederzeit unterbrochen werden (Umschalten in den Kernelmodus). Wenn der Kernel in den Benutzermodus zurückkehrt, prüft er, ob Signale anstehen (einschließlich derjenigen, die zum Beenden des Prozesses verwendet werden, wie z. B. SIGTERMund SIGKILL). Dies bedeutet, dass ein Prozess nur bei Rückkehr in den Benutzermodus abgebrochen werden kann.

Der Grund, warum ein Prozess im Kernelmodus nicht beendet werden kann, besteht darin, dass er möglicherweise die Kernelstrukturen beschädigen kann, die von allen anderen Prozessen auf demselben Computer verwendet werden (auf die gleiche Weise kann das Beenden eines Threads möglicherweise Datenstrukturen beschädigen, die von anderen Threads im selben Prozess verwendet werden). .

Wenn der Kernel etwas tun muss, das lange dauern kann (z. B. Warten auf eine Pipe, die von einem anderen Prozess geschrieben wurde, oder Warten, bis die Hardware etwas tut), schläft er, indem er sich als "Sleeping" markiert und den Scheduler aufruft, um zu einem anderen zu wechseln Prozess (wenn es keinen nicht schlafenden Prozess gibt, wechselt er zu einem "Dummy" -Prozess, der die CPU anweist, etwas langsamer zu werden, und sich in einer Schleife befindet - der Leerlaufschleife).

Wenn ein Signal an einen Ruhevorgang gesendet wird, muss es aufgeweckt werden, bevor es in den Benutzerbereich zurückkehrt und somit das anstehende Signal verarbeitet. Hier haben wir den Unterschied zwischen den beiden Hauptschlafarten:

  • TASK_INTERRUPTIBLE, der unterbrechbare Schlaf. Wenn eine Aufgabe mit diesem Flag markiert ist, schläft sie, kann aber durch Signale geweckt werden. Dies bedeutet, dass der Code, der die Aufgabe als schlafend markiert hat, ein mögliches Signal erwartet und nach dem Aufwachen danach sucht und vom Systemaufruf zurückkehrt. Nachdem das Signal verarbeitet wurde, kann der Systemaufruf möglicherweise automatisch neu gestartet werden (und ich werde nicht näher darauf eingehen, wie das funktioniert).
  • TASK_UNINTERRUPTIBLE, der ununterbrochene Schlaf. Wenn eine Aufgabe mit diesem Flag markiert ist, erwartet sie nicht, von etwas anderem als dem, worauf sie wartet, geweckt zu werden, entweder weil sie nicht einfach neu gestartet werden kann oder weil Programme erwarten, dass der Systemaufruf atomar ist. Dies kann auch für Schlafstörungen verwendet werden, von denen bekannt ist, dass sie sehr kurz sind.

TASK_KILLABLE (erwähnt in dem LWN-Artikel, auf den durch die Antwort von ddaa verwiesen wird) ist eine neue Variante.

Dies beantwortet Ihre erste Frage. Zu Ihrer zweiten Frage: Sie können einen unterbrechungsfreien Schlaf nicht vermeiden, er ist eine normale Sache (dies geschieht beispielsweise jedes Mal, wenn ein Prozess von / auf die Festplatte liest / schreibt). Sie sollten jedoch nur einen Bruchteil einer Sekunde dauern. Wenn sie viel länger dauern, handelt es sich normalerweise um ein Hardwareproblem (oder ein Gerätetreiberproblem, das für den Kernel gleich aussieht), bei dem der Gerätetreiber darauf wartet, dass die Hardware etwas tut, was niemals passieren wird. Dies kann auch bedeuten, dass Sie NFS verwenden und der NFS-Server nicht verfügbar ist (er wartet auf die Wiederherstellung des Servers; Sie können auch die Option "intr" verwenden, um das Problem zu vermeiden).

Der Grund, warum Sie nicht wiederherstellen können, ist derselbe, warum der Kernel wartet, bis er in den Benutzermodus zurückkehrt, um ein Signal zu liefern oder den Prozess abzubrechen: Dies würde möglicherweise die Datenstrukturen des Kernels beschädigen (Code, der auf einen unterbrechbaren Ruhezustand wartet, kann einen Fehler erhalten, der ihn anzeigt um in den Benutzerbereich zurückzukehren, in dem der Prozess abgebrochen werden kann; Code, der auf einen unterbrechungsfreien Ruhezustand wartet, erwartet keinen Fehler).

CesarB
quelle
1
Ein Fehler beim Sperren des Dateisystems ist ebenfalls eine wahrscheinliche Ursache, IME.
Tobu
3
Ich verstehe das alles nicht. "Sie können einen unterbrechungsfreien Schlaf nicht vermeiden" - kann das Betriebssystem nicht so hergestellt werden, dass ein unterbrechungsfreier Schlaf einfach nicht als Zustand existiert? Dann der Teil über Korruption - kann der Kernel-Modus-Teil des Prozesses selbst (oder was auch immer die Korruption verursachen KÖNNTE) nicht beendet oder nur sein Code direkt im Speicher geändert werden, um einfach zurückzukehren? Bitte erklären Sie, warum dies so schwer / unmöglich ist, dass selbst Linux es nicht getan hat. (Ich dachte, dieses Problem besteht nur unter Windows)
Dexter
Der einzige Fall, den ich mir vorstellen kann, würde das (sichere) Beenden dieser Prozesse wirklich unmöglich machen (und nicht nur, sagen wir, außergewöhnlich schwer), wenn die Hardware selbst die Beschädigung verursachen könnte. Hardware kann nicht gesteuert werden; Kernel kann . Aber es ist der Kernel, der Daten von der Hardware abruft und den Speicher ändert (deshalb darf er nicht freigegeben werden, bevor der Prozess in den Benutzermodus zurückkehrt, und warum kann die Beschädigung auftreten) ... den Kernel-Code im Speicher ändern und keine Probleme mehr.
Dexter
@Dexter stellt sich den Kernel so vor, als wäre es ein einzelner Multithread-Prozess, bei dem der Kernel-Modus-Teil jedes Prozesses ein Thread innerhalb des Kernels ist. Ihr Vorschlag wäre so schlecht wie das Beenden eines einzelnen Threads in einem Multithread-Programm: Es könnten baumelnde Sperren, Datenstrukturen, die vorübergehend geändert werden oder gerade geändert werden, usw. zurückbleiben.
CesarB
@CesarB Nun, Sie haben Recht damit, einen Thread zu beenden ... Aber kann der "Haupt" -Thread (das wäre der Betriebssystemkern und andere Threads wären beispielsweise Treiber) irgendwie damit umgehen? Obwohl diese Strukturen "mitten in der Modifikation" ein wirklich schwieriges Problem zu sein scheinen ... Vielleicht werden wir wirklich nie ein Betriebssystem sehen, in dem unterbrechungsfreie Prozesse unmöglich wären :(
Dexter
23

Unterbrechungsfreie Prozesse warten normalerweise auf E / A nach einem Seitenfehler.

Bedenken Sie:

  • Der Thread versucht, auf eine Seite zuzugreifen, die sich nicht im Kern befindet (entweder eine ausführbare Datei, die nach Bedarf geladen wird, eine Seite mit anonymem Speicher, die ausgelagert wurde, oder eine mmap () -D-Datei, die nach Bedarf geladen wird gleiche Sache)
  • Der Kernel lädt es jetzt (versucht es)
  • Der Vorgang kann erst fortgesetzt werden, wenn die Seite verfügbar ist.

Der Prozess / die Aufgabe kann in diesem Zustand nicht unterbrochen werden, da keine Signale verarbeitet werden können. Wenn dies der Fall wäre, würde ein weiterer Seitenfehler auftreten und es würde wieder dort sein, wo es war.

Wenn ich "Prozess" sage, meine ich wirklich "Aufgabe", was unter Linux (2.6) grob in "Thread" übersetzt wird, der möglicherweise einen einzelnen "Threadgruppen" -Eintrag in / proc hat oder nicht

In einigen Fällen kann es lange dauern. Ein typisches Beispiel hierfür ist, wenn sich die ausführbare Datei oder die mmap-Datei in einem Netzwerkdateisystem befindet, in dem der Server ausgefallen ist. Wenn die E / A schließlich erfolgreich ist, wird die Aufgabe fortgesetzt. Wenn es irgendwann fehlschlägt, bekommt die Aufgabe im Allgemeinen einen SIGBUS oder so.

MarkR
quelle
1
Wenn es irgendwann fehlschlägt, bekommt die Aufgabe im Allgemeinen einen SIGBUS oder so. Warten Sie, kann der Kernel nicht so erstellt werden, dass er beim Beenden dieser "unterbrechungsfreien" Prozesse einfach anzeigt, dass die E / A-Operation fehlgeschlagen ist? Dann würde der Prozess in den Benutzermodus zurückkehren und weg sein? Es muss eine Möglichkeit geben, diese D-State-Prozesse sicher zu beenden. Ich denke, es ist einfach nicht einfach und deshalb haben noch weder Windows noch Linux diese Möglichkeit. Auf der anderen Seite möchte ich diese Prozesse zumindest unsicher beenden können. Ich kümmere mich nicht um einen möglichen Systemabsturz oder was auch immer ...
Dexter
@ Express hmm, ich habe dieses Problem mit Windows noch nie erlebt. Wie kann man es dort reproduzieren? Zumindest laut diesem Beitrag können alle E / A-Anforderungen in Windows unterbrochen werden.
Ruslan
1

Zu Ihrer dritten Frage: Ich denke, Sie können die unterbrechungsfreien Prozesse durch Ausführen beenden sudo kill -HUP 1. Es wird init neu starten, ohne die laufenden Prozesse zu beenden, und nachdem es ausgeführt wurde, waren meine unterbrechungsfreien Prozesse verschwunden.

Ron Granger
quelle
-3

Wenn Sie von einem "Zombie" -Prozess sprechen (der in der ps-Ausgabe als "Zombie" bezeichnet wird), ist dies ein harmloser Datensatz in der Prozessliste, der darauf wartet, dass jemand seinen Rückkehrcode sammelt, und er kann ignoriert werden.

Könnten Sie bitte beschreiben, was und "unterbrechungsfreier Prozess" für Sie ist? Überlebt es den "Kill -9" und tuckert glücklich mit? Wenn dies der Fall ist, bleibt es bei einem Systemaufruf hängen, der in einem Treiber steckt, und Sie bleiben bei diesem Prozess bis zum Neustart (und manchmal ist es besser, bald neu zu starten) oder dem Entladen des entsprechenden Treibers (was unwahrscheinlich ist) stecken. . Sie könnten versuchen, mit "strace" herauszufinden, wo Ihr Prozess steckt, und ihn in Zukunft vermeiden.

Geschickt
quelle
Können Treiber nicht auf die gleiche Weise gewaltsam entladen werden, wie ein Prozess beendet werden könnte? Ich weiß, dass der Kernel-Modus einen privilegierteren Zugriff hat als der Benutzermodus, aber er kann niemals privilegierter sein als das Betriebssystem selbst. Alles, was im Kernelmodus ausgeführt wird, kann immer etwas anderes manipulieren, das im Kernelmodus ausgeführt wird - es gibt einfach keine Kontrolle.
Dexter