it-swarm.com.de

Wie "beendet" Linux einen Prozess?

Es verwirrt mich oft, dass ich, obwohl ich seit mehreren Jahrzehnten professionell mit Computern und Linux seit einem Jahrzehnt arbeite, den größten Teil der Funktionalität des Betriebssystems als Black Box betrachte, ähnlich wie Magie.

Heute habe ich über den Befehl kill nachgedacht, und während ich ihn mehrmals täglich verwende (sowohl im "normalen" als auch im -9 - Geschmack), muss ich zugeben, dass ich absolut keine Ahnung habe, wie er funktioniert hinter den Kulissen.

Wenn aus meiner Sicht ein laufender Prozess "hängen bleibt", rufe ich kill auf seiner PID auf und dann läuft er plötzlich nicht mehr. Magie!

Was passiert dort wirklich? Manpages sprechen von "Signalen", aber das ist sicherlich nur eine Abstraktion. Das Senden von kill -9 An einen Prozess erfordert nicht die Zusammenarbeit des Prozesses (wie das Behandeln eines Signals), sondern beendet es einfach.

  • Wie verhindert Linux, dass der Prozess weiterhin CPU-Zeit beansprucht?
  • Wird es aus der Planung entfernt?
  • Trennt es den Prozess von seinen geöffneten Dateihandles?
  • Wie wird der virtuelle Speicher des Prozesses freigegeben?
  • Gibt es so etwas wie eine globale Tabelle im Speicher, in der Linux Verweise auf alle von einem Prozess belegten Ressourcen enthält, und wenn ich einen Prozess "töte", geht Linux einfach diese Tabelle durch und gibt die Ressourcen einzeln frei?

Ich würde das alles wirklich gerne wissen!

91
seratoninant

Das Senden von kill -9 an einen Prozess erfordert nicht die Zusammenarbeit des Prozesses (wie das Behandeln eines Signals), sondern beendet es einfach.

Sie gehen davon aus, dass einige Signale, die abgefangen und ignoriert werden können, alle eine Zusammenarbeit beinhalten. Aber gemäß man 2 signal Können "die Signale SIGKILL und SIGSTOP nicht abgefangen oder ignoriert werden". SIGTERM kann abgefangen werden, weshalb einfaches kill nicht immer effektiv ist - im Allgemeinen bedeutet dies, dass etwas im Handler des Prozesses schief gelaufen ist.1

Wenn ein Prozess keinen Handler für ein bestimmtes Signal definiert (oder nicht definieren kann), der Kernel führt eine Standardaktion aus. Im Fall von SIGTERM und SIGKILL wird der Prozess beendet (es sei denn seine PID ist 1; der Kernel wird nicht beendet init)2 Dies bedeutet, dass die Dateihandles geschlossen sind, der Speicher in den Systempool zurückgegeben wird, das übergeordnete Element SIGCHILD empfängt, die verwaisten untergeordneten Elemente von init usw. geerbt werden, als hätte es exit aufgerufen (siehe man 2 exit). Der Prozess existiert nicht mehr - es sei denn, er endet als Zombie. In diesem Fall wird er weiterhin mit einigen Informationen in der Prozesstabelle des Kernels aufgeführt. Dies geschieht, wenn das übergeordnete Element nicht wait verwendet und diese Informationen ordnungsgemäß verarbeitet. Zombie-Prozessen ist jedoch kein Speicher mehr zugewiesen und kann daher nicht weiter ausgeführt werden.

Gibt es so etwas wie eine globale Tabelle im Speicher, in der Linux Verweise auf alle von einem Prozess belegten Ressourcen enthält und wenn ich einen Prozess "töte", geht Linux einfach diese Tabelle durch und gibt die Ressourcen einzeln frei?

Ich denke das ist genau genug. Der physische Speicher wird nach Seiten verfolgt (eine Seite entspricht normalerweise einem 4-KB-Block), und diese Seiten werden aus einem globalen Pool entnommen und an diesen zurückgegeben. Es ist etwas komplizierter, da einige freigegebene Seiten zwischengespeichert werden, falls die darin enthaltenen Daten erneut benötigt werden (dh Daten, die aus einer noch vorhandenen Datei gelesen wurden).

Manpages sprechen von "Signalen", aber das ist sicherlich nur eine Abstraktion.

Sicher, alle Signale sind eine Abstraktion. Sie sind konzeptionell, genau wie "Prozesse". Ich spiele ein bisschen Semantik, aber wenn Sie meinen, SIGKILL unterscheidet sich qualitativ von SIGTERM, dann ja und nein. Ja in dem Sinne, dass es nicht gefangen werden kann, aber nein in dem Sinne, dass beide Signale sind. In Analogie dazu ist ein Apple ist keine Orange, aber Äpfel und Orangen sind nach einer vorgefassten Definition beide Früchte. SIGKILL scheint mehr abstrakt, da Sie können ' Ich fange es nicht, aber es ist immer noch ein Signal. Hier ist ein Beispiel für die Handhabung von SIGTERM. Ich bin sicher, dass Sie diese bereits gesehen haben:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sighandler (int signum, siginfo_t *info, void *context) {
    fprintf (
        stderr,
        "Received %d from pid %u, uid %u.\n",
        info->si_signo,
        info->si_pid,
        info->si_uid
    );
}

int main (void) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = sighandler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGTERM, &sa, NULL);
    while (1) sleep(10);
    return 0;
}

Dieser Prozess wird nur für immer schlafen. Sie können es in einem Terminal ausführen und mit kill SIGTERM senden. Es spuckt Sachen aus wie:

Received 15 from pid 25331, uid 1066.

1066 ist meine UID. Die PID ist die der Shell, von der kill ausgeführt wird, oder die PID von kill, wenn Sie sie teilen (kill 25309 & echo $?).

Auch hier macht es keinen Sinn, einen Handler für SIGKILL festzulegen, da dies nicht funktioniert.3 Wenn ich kill -9 25309, Wird der Prozess beendet. Aber das ist immer noch ein Signal; Der Kernel hat die Information darüber, wer das Signal gesendet hat, welche Art von Signal es ist usw.


1. Wenn Sie sich die Liste der möglichen Signale nicht angesehen haben, lesen Sie kill -l.

2. Eine weitere Ausnahme, wie Tim Post weiter unten erwähnt, gilt für Prozesse in unterbrechungsfreier Schlaf. Diese können erst aktiviert werden, wenn das zugrunde liegende Problem behoben ist. Daher werden ALLE Signale (einschließlich SIGKILL) für die Dauer zurückgestellt. Ein Prozess kann diese Situation jedoch nicht absichtlich erzeugen.

3. Dies bedeutet nicht, dass die Verwendung von kill -9 In der Praxis besser ist. Mein Beispielhandler ist in dem Sinne schlecht, dass er nicht zu exit() führt. Der eigentliche Zweck eines SIGTERM-Handlers besteht darin, dem Prozess die Möglichkeit zu geben, temporäre Dateien zu bereinigen und dann freiwillig zu beenden. Wenn Sie kill -9 Verwenden, erhalten Sie diese Chance nicht. Tun Sie dies also nur, wenn der Teil "Freiwillig beenden" fehlgeschlagen zu sein scheint.

72
goldilocks

Jeder Prozess wird zur geplanten Zeit ausgeführt und dann vom Hardware-Timer unterbrochen, um seinen CPU-Kern für andere Aufgaben bereitzustellen. Aus diesem Grund ist es möglich, viel mehr Prozesse als CPU-Kerne zu haben oder sogar alle Betriebssysteme mit vielen Prozessen auf einer einzigen Kern-CPU auszuführen.

Nachdem der Prozess unterbrochen wurde, kehrt die Steuerung zum Kernelcode zurück. Dieser Code kann dann entscheiden, die Ausführung des unterbrochenen Prozesses nicht fortzusetzen, ohne dass die Prozessseite mitarbeitet. kill -9 kann in jeder Zeile Ihres Programms ausgeführt werden.

3
h22

Hier ist eine idealisierte Beschreibung, wie das Beenden eines Prozesses funktioniert. In der Praxis wird jede Unix-Variante viele zusätzliche Komplikationen und Optimierungen aufweisen.

Der Kernel verfügt über eine Datenstruktur für jeden Prozess, in der Informationen darüber gespeichert sind, welcher Speicher zugeordnet ist, welche Threads vorhanden sind und wann sie geplant sind, welche Dateien geöffnet sind usw. Wenn der Kernel beschließt, einen Prozess abzubrechen, macht er eine Notiz in die Datenstruktur des Prozesses (und möglicherweise in der Datenstruktur jedes der Threads), die der Prozess beenden soll.

Wenn einer der Threads des Prozesses derzeit auf einer anderen CPU geplant ist, löst der Kernel möglicherweise einen Interrupt auf dieser anderen CPU aus, damit dieser Thread schneller nicht mehr ausgeführt wird.

Wenn der Scheduler feststellt, dass sich ein Thread in einem Prozess befindet, der beendet werden muss, wird er nicht mehr geplant.

Wenn keiner der Threads des Prozesses mehr geplant ist, gibt der Kernel die Ressourcen des Prozesses frei (Speicher, Dateideskriptoren,…). Jedes Mal, wenn der Kernel eine Ressource freigibt, prüft er, ob sein Eigentümer noch über aktive Ressourcen verfügt. Sobald der Prozess keine Live-Ressource mehr hat (Speicherzuordnung, offener Dateideskriptor,…), kann die Datenstruktur für den Prozess selbst freigegeben und der entsprechende Eintrag aus der Prozesstabelle entfernt werden.

Einige Ressourcen können sofort freigegeben werden (z. B. Freigabe von Speicher, der nicht von einer E/A-Operation verwendet wird). Andere Ressourcen müssen warten, z. B. können Daten, die eine E/A-Operation beschreiben, nicht freigegeben werden, während die E/A-Operation ausgeführt wird (während ein DMA In Bearbeitung wird der Speicher verwendet, auf den zugegriffen wird, und das Abbrechen von DMA erfordert die Kontaktaufnahme mit dem Peripheriegerät). Der Treiber für eine solche Ressource wird benachrichtigt und versucht möglicherweise, die Stornierung einmal zu beschleunigen Der Vorgang wird nicht mehr ausgeführt. Der Treiber schließt die Freigabe dieser Ressource ab.

(Der Eintrag in der Prozesstabelle ist tatsächlich eine Ressource, die zum übergeordneten Prozess gehört und die freigegeben wird, wenn der Prozess stirbt nd der übergeordnete Prozess bestätigt das Ereignis .)