Antworten:
Während des Wartens auf read()
oder write()
von / zu einer Dateideskriptor-Rückgabe wird der Prozess in eine spezielle Art von Ruhezustand versetzt, die als "D" oder "Disk Sleep" bezeichnet wird. Dies ist etwas Besonderes, da der Prozess in einem solchen Zustand nicht abgebrochen oder unterbrochen werden kann. Ein Prozess, der auf eine Rückkehr von ioctl () wartet, wird ebenfalls auf diese Weise in den Ruhezustand versetzt.
Eine Ausnahme ist, wenn eine Datei (z. B. ein Terminal oder ein anderes Zeichengerät) im O_NONBLOCK
Modus geöffnet wird und übergeben wird, wenn angenommen wird, dass ein Gerät (z. B. ein Modem) Zeit zum Initialisieren benötigt. Sie haben jedoch in Ihrer Frage Blockgeräte angegeben. Außerdem habe ich noch nie einen versucht ioctl()
, der wahrscheinlich auf einem im nicht blockierenden Modus geöffneten fd blockiert (zumindest nicht wissentlich).
Wie ein anderer Prozess ausgewählt wird, hängt ganz vom verwendeten Scheduler ab sowie davon, was andere Prozesse möglicherweise getan haben, um ihre Gewichtung in diesem Scheduler zu ändern.
Es ist bekannt, dass einige User Space-Programme unter bestimmten Umständen für immer in diesem Zustand bleiben, bis sie neu gestartet werden. Diese werden normalerweise mit anderen "Zombies" gruppiert, aber der Begriff wäre nicht korrekt, da sie technisch nicht verstorben sind.
Wenn ein Prozess Daten von einer Festplatte abrufen muss, wird die Ausführung auf der CPU effektiv gestoppt, damit andere Prozesse ausgeführt werden können, da der Vorgang möglicherweise lange dauert. Mindestens 5 ms Suchzeit für eine Festplatte sind üblich, und 5 ms sind 10 Millionen CPU-Zyklen, eine Ewigkeit aus Sicht des Programms!
Aus Sicht des Programmierers (auch "im Benutzerbereich" genannt) wird dies als blockierender Systemaufruf bezeichnet . Wenn Sie aufrufen write(2)
(dies ist ein dünner libc-Wrapper um den gleichnamigen Systemaufruf), stoppt Ihr Prozess nicht genau an dieser Grenze. Im Kernel wird der Systemaufrufcode fortgesetzt. Meistens geht es bis zu einem bestimmten Festplattencontrollertreiber (Dateiname → Dateisystem / VFS → Blockgerät → Gerätetreiber), bei dem ein Befehl zum Abrufen eines Blocks auf der Festplatte an die richtige Hardware gesendet wird die meiste Zeit schneller Betrieb.
DANN wird der Prozess in den Ruhezustand versetzt (im Kernelraum wird das Blockieren als "Schlafen" bezeichnet - aus der Sicht des Kernels wird nie etwas "blockiert"). Es wird aktiviert, sobald die Hardware die richtigen Daten abgerufen hat. Der Prozess wird dann als ausführbar markiert und geplant. Schließlich führt der Scheduler den Prozess aus.
Schließlich wird im Benutzerbereich der blockierende Systemaufruf mit dem richtigen Status und den richtigen Daten zurückgegeben, und der Programmablauf wird fortgesetzt.
Es ist möglich, die meisten E / A-Systemaufrufe im nicht blockierenden Modus aufzurufen (siehe O_NONBLOCK
in open(2)
und fcntl(2)
). In diesem Fall kehren die Systemaufrufe sofort zurück und melden nur das Senden der Festplattenoperation. Der Programmierer muss zu einem späteren Zeitpunkt explizit prüfen, ob der Vorgang erfolgreich abgeschlossen wurde oder nicht, und sein Ergebnis abrufen (z select(2)
. B. mit ). Dies wird als asynchrone oder ereignisbasierte Programmierung bezeichnet.
Die meisten Antworten, die hier den D-Status erwähnen (der TASK_UNINTERRUPTIBLE
in den Linux-Statusnamen genannt wird), sind falsch. Der D- Zustand ist ein spezieller Schlafmodus, der nur in einem Kernel-Space-Code-Pfad ausgelöst wird, wenn dieser Code-Pfad nicht unterbrochen werden kann (weil er zu komplex zum Programmieren wäre), mit der Erwartung, dass er nur für einen sehr blocken würde kurze Zeit. Ich glaube, dass die meisten "D-Zustände" tatsächlich unsichtbar sind; Sie sind sehr kurzlebig und können mit Stichprobenwerkzeugen wie „top“ nicht beobachtet werden.
In einigen Situationen können im D-Zustand nicht abtötbare Prozesse auftreten. NFS ist dafür berühmt und ich habe es schon oft erlebt. Ich denke, es gibt einen semantischen Konflikt zwischen einigen VFS-Codepfaden, die davon ausgehen, dass immer lokale Festplatten erreicht werden und eine schnelle Fehlererkennung (bei SATA würde ein Fehlerzeitlimit bei einigen 100 ms liegen) und NFS, das tatsächlich Daten aus dem Netzwerk abruft ist widerstandsfähiger und hat eine langsame Wiederherstellung (ein TCP-Timeout von 300 Sekunden ist üblich). Lesen Sie diesen Artikel für die coole Lösung, die in Linux 2.6.25 mit dem TASK_KILLABLE
Status eingeführt wurde. Vor dieser Ära gab es einen Hack, bei dem Sie tatsächlich Signale an NFS-Prozessclients senden konnten, indem Sie ein SIGKILL an den Kernel-Thread senden rpciod
, aber vergessen Sie diesen hässlichen Trick.…
/proc/stat
?
Ein Prozess, der E / A ausführt, wird in den D-Zustand versetzt (unterbrechungsfreier Ruhezustand) , wodurch die CPU freigegeben wird, bis ein Hardware-Interrupt auftritt , der die CPU anweist, zur Ausführung des Programms zurückzukehren. Siehe man ps
für die anderen Prozesszustände.
Abhängig von Ihrem Kernel gibt es einen Prozessplaner , der eine Runqueue von Prozessen verfolgt, die zur Ausführung bereit sind. Zusammen mit einem Planungsalgorithmus teilt es dem Kernel mit, welcher Prozess welcher CPU zugewiesen werden soll. Es sind Kernelprozesse und Benutzerprozesse zu berücksichtigen. Jedem Prozess wird eine Zeitscheibe zugewiesen, die einen Teil der CPU-Zeit darstellt, die er verwenden darf. Sobald der Prozess seine gesamte Zeitscheibe verwendet hat, wird er als abgelaufen markiert und erhält im Planungsalgorithmus eine niedrigere Priorität.
Im 2.6-Kernel gibt es einen O (1) -Zeitkomplexitätsplaner. Unabhängig davon , wie viele Prozesse ausgeführt werden, werden CPUs in konstanter Zeit zugewiesen. Es ist jedoch komplizierter, da in Version 2.6 Preemption eingeführt wurde und der CPU-Lastausgleich kein einfacher Algorithmus ist. In jedem Fall ist es effizient und die CPUs bleiben nicht im Leerlauf, während Sie auf die E / A warten.
Wie bereits von anderen erklärt, sind Prozesse im "D" -Zustand (unterbrechungsfreier Schlaf) für das Aufhängen des ps-Prozesses verantwortlich. Mir ist das schon oft mit RedHat 6.x und automatisch bereitgestellten NFS-Home-Verzeichnissen passiert.
Um Prozesse im Status D aufzulisten, können Sie die folgenden Befehle verwenden:
cd /proc
for i in [0-9]*;do echo -n "$i :";cat $i/status |grep ^State;done|grep D
Um das aktuelle Verzeichnis des Prozesses und möglicherweise die gemountete NFS-Festplatte mit Problemen zu kennen, können Sie einen Befehl verwenden, der dem folgenden Beispiel ähnelt (ersetzen Sie 31134 durch die Nummer des Ruhezustands):
# ls -l /proc/31134/cwd
lrwxrwxrwx 1 pippo users 0 Aug 2 16:25 /proc/31134/cwd -> /auto/pippo
Ich fand heraus, dass das Geben des Befehls umount mit dem Schalter -f (force) an das zugehörige gemountete nfs-Dateisystem den Schlafprozess aufwecken konnte:
umount -f /auto/pippo
Das Dateisystem wurde nicht ausgehängt, da es ausgelastet war, aber der zugehörige Prozess wurde aktiviert und ich konnte das Problem ohne Neustart lösen.
Angenommen, Ihr Prozess ist ein einzelner Thread und Sie verwenden blockierende E / A., blockiert Ihr Prozess das Warten auf den Abschluss der E / A. Der Kernel wählt einen anderen Prozess aus, der in der Zwischenzeit ausgeführt werden soll, basierend auf der Genauigkeit, Priorität, der letzten Laufzeit usw. Wenn keine anderen ausführbaren Prozesse vorhanden sind, führt der Kernel keinen aus. Stattdessen wird der Hardware mitgeteilt, dass sich die Maschine im Leerlauf befindet (was zu einem geringeren Stromverbrauch führt).
Prozesse, die auf den Abschluss der E / A warten, werden normalerweise im Status D in z . B. ps
und angezeigt top
.
Ja, die Aufgabe wird im Systemaufruf read () blockiert. Eine andere Aufgabe, die bereit ist, wird ausgeführt, oder wenn keine anderen Aufgaben bereit sind, wird die inaktive Aufgabe (für diese CPU) ausgeführt.
Ein normaler, blockierender Disc-Lesevorgang bewirkt, dass die Task in den Status "D" wechselt (wie andere angemerkt haben). Solche Aufgaben tragen zum Lastdurchschnitt bei, obwohl sie nicht die CPU verbrauchen.
Einige andere Arten von E / A, insbesondere ttys und network, verhalten sich nicht ganz gleich - der Prozess endet im Status "S" und kann unterbrochen werden und zählt nicht zum Lastdurchschnitt.
Ja, Aufgaben, die auf E / A warten, werden blockiert und andere Aufgaben werden ausgeführt. Die Auswahl der nächsten Aufgabe erfolgt durch den Linux-Scheduler .
Im Allgemeinen wird der Prozess blockiert. Wenn sich der Lesevorgang in einem Dateiskriptor befindet, der als nicht blockierend markiert ist, oder wenn der Prozess asynchrone E / A verwendet, wird er nicht blockiert. Auch wenn der Prozess andere Threads hat, die nicht blockiert sind, können sie weiter ausgeführt werden.
Die Entscheidung, welcher Prozess als nächstes ausgeführt wird, liegt beim Scheduler im Kernel.