Wie "tötet" Linux einen Prozess?


91

Es verwirrt mich oft, dass ich, obwohl ich seit mehreren Jahrzehnten professionell mit Computern und Linux arbeite, den größten Teil der Betriebssystemfunktionen wie eine Black Box behandle, nicht anders als Magie.

Heute habe ich über den killBefehl nachgedacht , und während ich ihn mehrmals am Tag verwende (sowohl in seiner "normalen" als auch in seiner typischen -9Form), muss ich zugeben, dass ich absolut keine Ahnung habe, wie er hinter den Kulissen funktioniert.

Wenn ein laufender Prozess aus meiner Sicht "hängt", rufe ich killseine 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 kill -9an einen Prozess erfordert nicht die Kooperation des Prozesses (wie das Behandeln eines Signals), sondern beendet ihn nur.

  • Wie verhindert Linux, dass der Prozess weiterhin CPU-Zeit in Anspruch nimmt?
  • 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 beanspruchten Ressourcen speichert, und wenn ich einen Prozess "abbringe", geht Linux diese Tabelle einfach durch und gibt die Ressourcen nacheinander frei?

Ich würde das wirklich gerne alles wissen!



Meine Antwort auf eine Frage zu SIGKILL könnte auch hier relevant sein.
telcoM

Antworten:


72

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

Sie nehmen an, dass einige Signale, da sie abgefangen und ignoriert werden können, alle eine Kooperation beinhalten. Aber wie pro man 2 signal„ die Signale SIGKILL und SIGSTOP nicht gefangen oder ignoriert werden“. SIGTERM kann abgefangen werden, weshalb Plain killnicht 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), führt der Kernel eine Standardaktion aus. Im Falle von SIGTERM und SIGKILL, das den Prozess zu beenden ist (es sei denn , seine PID 1 ist, wird der Kernel nicht beendet init) 2 werden ihre Datei - Handles Bedeutung geschlossen, dessen Speicher an das System Pool zurückgegeben, seine Eltern erhält SIGCHILD, seine Waise Kinder werden von init usw. geerbt, so als ob es aufgerufen hätte exit(siehe man 2 exit). Der Prozess existiert nicht mehr - es sei denn, er endet als Zombie. In diesem Fall wird er in der Prozesstabelle des Kernels mit einigen Informationen aufgeführt. Das passiert, wenn sein Elternteil es nicht tutwaitund mit diesen Informationen richtig umgehen. Zombie-Prozessen ist jedoch kein Speicher mehr zugeordnet und sie können 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 speichert, und wenn ich einen Prozess "abbringe", geht Linux diese Tabelle einfach durch und gibt die Ressourcen nacheinander frei?

Ich denke das ist genau genug. Der physische Speicher wird seitenweise nachverfolgt (eine Seite entspricht normalerweise einem 4-KB-Block), und diese Seiten werden aus einem globalen Pool entnommen und an diesen zurückgegeben. Etwas komplizierter ist es, dass 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.

Klar, alle Signale sind eine Abstraktion. Sie sind konzeptionell, genau wie "Prozesse". Ich spiele ein bisschen Semantik, aber wenn Sie meinen, SIGKILL ist qualitativ anders als SIGTERM, dann ja und nein. Ja in dem Sinne, dass es nicht aufgefangen werden kann, aber nein in dem Sinne, dass beide Signale sind. Analog dazu ist ein Apfel keine Orange, aber Äpfel und Orangen sind nach einer vorgefassten Definition beide Früchte. SIGKILL scheint mehr abstrakt , da Sie es nicht fangen können, 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 SIGTERM senden kill. Es spuckt Sachen aus wie:

Received 15 from pid 25331, uid 1066.

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

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


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 im unterbrechungsfreien Schlaf . Diese können erst aufgeweckt 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 kill -9in der Praxis besser ist. Mein Beispiel-Handler ist in dem Sinne schlecht, dass er nicht dazu führt exit(). Der eigentliche Zweck eines SIGTERM-Handlers besteht darin, dem Prozess die Möglichkeit zu geben, z. B. temporäre Dateien zu bereinigen und diese dann freiwillig zu beenden. Wenn Sie verwenden kill -9, hat dies keine Chance. Tun Sie dies nur, wenn der Teil "freiwillig beenden" fehlgeschlagen zu sein scheint.


Ok, aber -9womit töten Sie den Prozess, denn das ist das eigentliche Problem, wer will, dass dieser stirbt? ;)
Kiwy

@ Kiwy: Der Kernel. IPC einschließlich der durchgehenden Signale; Der Kernel implementiert die Standardaktionen.
Goldlöckchen

12
Erwähnenswert ist, dass der Festplattenschlaf (D) alle Signale blockiert, während sich der Prozess in diesem Zustand befindet. Daher kill -9funktioniert der Versuch, bestimmte I / O-gebundene Prozesse zu erreichen, nicht, zumindest nicht sofort.
Tim Post

7
Da ein kill -9nicht abgefangen werden kann, kann ein Prozess, der es empfängt, keine Bereinigung durchführen (z. B. temporäre Dateien entfernen, gemeinsam genutzten Speicher freigeben usw.), bevor er beendet wird. Verwenden Sie daher kill -9(aka kill -kill) nur als letzten Ausweg. Beginnen Sie mit einem kill -hupund / oder kill -termzuerst und verwenden Sie dann kill -killals letzten Schlag.
JRFerguson

"Der Prozess existiert nicht mehr - es sei denn, er endet als Zombie. In diesem Fall wird er in der Prozesstabelle des Kernels mit einigen Informationen aufgeführt." Tatsächlich werden alle Prozesse in den Zombie-Status versetzt, wenn sie sterben, und der Zombie verschwindet, wenn sie sterben Die Eltern warten auf das Kind, normalerweise geschieht das zu schnell, als dass Sie es sehen könnten
clever

3

Jeder Prozess wird für eine geplante Zeit ausgeführt und dann vom Hardware-Timer unterbrochen, um den CPU-Kern für andere Aufgaben bereitzustellen. Dies ist der Grund, warum es möglich ist, 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 Kernel-Code 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.


0

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, welchen Speicher er zuordnet, welche Threads er hat und wann sie geplant sind, welche Dateien er geöffnet hat usw. Wenn der Kernel einen Prozess abbricht, macht er eine Notiz in Die Datenstruktur des Prozesses (und möglicherweise in der Datenstruktur jedes Threads), die der Prozess beenden soll.

Wenn einer der Prozessthreads derzeit auf einer anderen CPU geplant ist, löst der Kernel möglicherweise einen Interrupt auf dieser anderen CPU aus, damit dieser Thread nicht mehr schneller 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 Prozessthreads mehr geplant ist, gibt der Kernel die Ressourcen des Prozesses frei (Speicher, Dateideskriptoren usw.). Jedes Mal, wenn der Kernel eine Ressource freigibt, prüft er, ob sein Besitzer 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 eine DMA ausgeführt wird , wird der Speicher verwendet, auf den zugegriffen wird, und das Abbrechen der DMA erfordert Kontakt mit dem Peripheriegerät). Der Fahrer für eine solche Ressource wird benachrichtigt und kann versuchen, die Stornierung zu beschleunigen. Sobald der Vorgang nicht mehr ausgeführt wird, schließt der Treiber die Freigabe dieser Ressource ab.

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.