GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Was passiert beim Senden von SIGKILL an einen Zombie-Prozess unter Linux?

Um diese Frage zu beantworten, müssen Sie verstehen, wie Signale an einen Prozess gesendet werden und wie ein Prozess im Kernel existiert.

Jeder Prozess wird als task_struct dargestellt innerhalb des Kernels (die Definition befindet sich in sched.h Header-Datei und beginnt hier). Diese Struktur enthält Informationen über den Prozess; zum Beispiel die pid. Die wichtigen Informationen befinden sich in Zeile 1566, wo das zugehörige Signal gespeichert ist. Dies wird nur gesetzt, wenn ein Signal an den Prozess gesendet wird.

Ein toter Prozess oder ein Zombie-Prozess hat immer noch einen task_struct . Die Struktur bleibt bestehen, bis der übergeordnete Prozess (natürlich oder durch Adoption) wait() aufgerufen hat nach Erhalt von SIGCHLD um seinen untergeordneten Prozess zu ernten. Wenn ein Signal gesendet wird, wird der signal_struct eingestellt ist. Dabei spielt es keine Rolle, ob das Signal fangbar ist oder nicht.

Signale werden jedes Mal ausgewertet, wenn der Prozess läuft. Genauer gesagt vorher der Prozess würde Lauf. Der Vorgang befindet sich dann im TASK_RUNNING Zustand. Der Kernel führt den schedule() aus Routine, die gemäß ihrem Scheduling-Algorithmus den nächsten laufenden Prozess ermittelt. Angenommen, dieser Prozess ist der nächste laufende Prozess, der Wert von signal_struct ausgewertet, ob ein zu behandelndes Wartesignal vorliegt oder nicht. Wenn ein Signalhandler manuell definiert wird (über signal() oder sigaction() ), wird die registrierte Funktion ausgeführt, wenn nicht die Standardaktion des Signals wird ausgeführt. Die Standardaktion hängt vom gesendeten Signal ab.

Zum Beispiel die SIGSTOP Der Standard-Handler von Signal ändert den Status des aktuellen Prozesses auf TASK_STOPPED und führen Sie dann schedule() aus um einen neuen auszuführenden Prozess auszuwählen. Achtung, SIGSTOP ist nicht abfangbar (wie SIGKILL ), daher gibt es keine Möglichkeit, einen manuellen Signalhandler zu registrieren. Im Falle eines unfangbaren Signals wird immer die Standardaktion ausgeführt.

Zu Ihrer Frage:

Ein nicht mehr funktionierender oder toter Prozess wird vom Scheduler niemals als in TASK_RUNNING bestimmt Zustand wieder. Daher wird der Kernel niemals den Signal-Handler (Standard oder definiert) für das entsprechende Signal ausführen, welches Signal auch immer es war. Daher die exit_signal wird nie wieder eingestellt. Durch Setzen des signal_struct wird das Signal an den Prozess "übergeben". in task_struct des Prozesses, aber es passiert nichts weiter, da der Prozess nie wieder ausgeführt wird. Es muss kein Code ausgeführt werden, alles, was vom Prozess übrig bleibt, ist diese Prozessstruktur.

Wenn jedoch der Elternprozess seine Kinder bis wait() erntet , der Exit-Code, den es erhält, ist derjenige, als der Prozess "ursprünglich" gestorben ist. Es spielt keine Rolle, ob ein Signal darauf wartet, bearbeitet zu werden.


Ein Zombie-Prozess ist im Grunde schon tot. Die einzige Sache ist, dass noch niemand seinen Tod bestätigt hat, also belegt er weiterhin einen Eintrag in der Prozesstabelle sowie einen Kontrollblock (die Struktur, die der Linux-Kernel für jeden aktiven Thread beibehält). Andere Ressourcen wie obligatorische Sperren für Dateien, Shared-Memory-Segmente, Semaphore usw. werden zurückgefordert.

Sie können ihnen kein Signal geben, weil niemand auf dieses Signal reagieren kann. Selbst fatale Signale wie KILL sind nutzlos, da der Prozess seine Ausführung bereits beendet hat. Sie können es selbst versuchen:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
    pid_t pid = fork();

    if (pid == -1)
        exit(-1);

    if (pid > 0) {
        //parent
        printf("[parent]: I'm the parent, the pid of my child is %i\n"
            "I'll start waiting for it in 10 seconds.\n", pid);
        sleep(10);
        int status;
        wait(&status);

        if (WIFSIGNALED(status)) {
            printf("[parent]: My child has died from a signal: %i\n", WTERMSIG(status));
        } else if (WIFEXITED(status)) {
            printf("[parent]: My child has died from natural death\n");
        } else {
            printf("[parent]: I don't know what happened to my child\n");
        }
    } else {
        //child
        printf("[child]: I'm dying soon, try to kill me.\n");
        sleep(5);
        printf("[child]: Dying now!\n");
    }

    return 0;
}

Hier starte ich einen Prozess, der sich verzweigt und schläft, bevor er auf sein Kind wartet. Das Kind tut nichts, außer ein wenig zu schlafen. Sie können das Kind töten, wenn es schläft oder kurz nachdem es den Raum verlassen hat, um den Unterschied zu sehen:

$ make zombie 
cc     zombie.c   -o zombie

$ ./zombie    
[parent]: I'm the parent, the pid of my child is 16693
I'll start waiting for it in 10 seconds.
[child]: I'm dying soon, try to kill me.
# Here, I did "kill -15 16693" in another console
[parent]: My child has died from a signal: 15

$ ./zombie
[parent]: I'm the parent, the pid of my child is 16717
I'll start waiting for it in 10 seconds.
[child]: I'm dying soon, try to kill me.
[child]: Dying now!
# Here, I did "kill -15 16717" in another console
[parent]: My child has died from natural death

Linux
  1. So finden und töten Sie den Zombie-Prozess in Linux

  2. Linux:Finde und töte Zombie-Prozesse

  3. Wann ist setsid() nützlich oder warum müssen wir Prozesse in Linux gruppieren?

  4. Was tun, wenn Strg + C einen Prozess nicht beenden kann?

  5. Was passiert mit einem Multithread-Linux-Prozess, wenn er ein Signal erhält?

So beenden Sie Zombie-Prozesse in Linux

SIGTERM vs. SIGKILL:Was ist der Unterschied?

Was ist ein gestoppter Prozess unter Linux?

Was ist die Definition einer Sitzung unter Linux?

Was ist ein Befehl, um die Priorität des Prozesses in Linux zu finden?

Was tun, wenn ein Linux-Desktop einfriert?