GNU/Linux >> Linux Esercitazione >  >> Linux

Cosa succede quando si invia SIGKILL a un processo Zombie in Linux?

Per rispondere a questa domanda, devi capire come i segnali vengono inviati a un processo e come esiste un processo nel kernel.

Ogni processo è rappresentato come task_struct all'interno del kernel (la definizione è nel sched.h file di intestazione e inizia qui). Quella struttura contiene informazioni sul processo; per esempio il pid. L'informazione importante si trova nella riga 1566 dove è memorizzato il segnale associato. Questo è impostato solo se viene inviato un segnale al processo.

Un processo morto o un processo zombie ha ancora un task_struct . La struttura rimane, finché il processo genitore (naturale o per adozione) non ha chiamato wait() dopo aver ricevuto SIGCHLD raccogliere il suo processo figlio. Quando viene inviato un segnale, il signal_struct è impostato. Non importa se il segnale è intercettabile o meno, in questo caso.

I segnali vengono valutati ogni volta che il processo viene eseguito. O per essere esatti, prima il processo lo farebbe correre. Il processo è quindi nel TASK_RUNNING stato. Il kernel esegue il schedule() routine che determina il prossimo processo in esecuzione in base al suo algoritmo di pianificazione. Supponendo che questo processo sia il prossimo processo in esecuzione, il valore di signal_struct viene valutato, se c'è un segnale in attesa da gestire o meno. Se un gestore di segnale viene definito manualmente (tramite signal() o sigaction() ), viene eseguita la funzione registrata, se non l'azione predefinita del segnale viene eseguito. L'azione predefinita dipende dal segnale inviato.

Ad esempio, il SIGSTOP il gestore predefinito di signal cambierà lo stato del processo corrente in TASK_STOPPED e poi esegui schedule() per selezionare un nuovo processo da eseguire. Avviso, SIGSTOP non è catturabile (come SIGKILL ), pertanto non è possibile registrare un gestore di segnale manuale. In caso di segnale non rilevabile, verrà sempre eseguita l'azione predefinita.

Alla tua domanda:

Un processo defunto o morto non verrà mai determinato dallo scheduler come nel TASK_RUNNING stato di nuovo. Pertanto il kernel non eseguirà mai il gestore di segnale (predefinito o definito) per il segnale corrispondente, qualunque sia il segnale. Pertanto il exit_signal non sarà mai più fissato. Il segnale viene "consegnato" al processo impostando il signal_struct in task_struct del processo, ma non accadrà nient'altro, perché il processo non verrà mai più eseguito. Non c'è codice da eseguire, tutto ciò che rimane del processo è quella struttura del processo.

Tuttavia, se il processo genitore miete i suoi figli entro wait() , il codice di uscita che riceve è quello quando il processo "inizialmente" è morto. Non importa se c'è un segnale in attesa di essere gestito.


Un processo di zombi è praticamente già morto. L'unica cosa è che nessuno ha ancora riconosciuto la sua morte, quindi continua a occupare una voce nella tabella dei processi e un blocco di controllo (la struttura che il kernel di Linux mantiene per ogni thread in attività). Altre risorse come blocchi obbligatori sui file, segmenti di memoria condivisa, semafori, ecc. vengono recuperate.

Non puoi segnalarli perché nessuno può agire su questo segnale. Anche i segnali fatali come KILL sono inutili poiché il processo ha già terminato la sua esecuzione. Puoi provare tu stesso:

#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;
}

Qui inizio un processo che si biforca e dorme prima di aspettare il suo bambino. Il bambino non fa altro che dormire un po'. Puoi uccidere il bambino mentre dorme o subito dopo che è uscito per vedere la differenza:

$ 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. Come uccidere un processo zombie su Linux

  2. Come trovare e uccidere il processo Zombie in Linux

  3. Uccidere gli zombi, in stile Linux

  4. Linux:cosa fare quando un desktop Linux si blocca?

  5. UNIX / Linux:3 modi per inviare segnali ai processi

Come uccidere i processi Zombie in Linux

Linux – Quando non dovrei uccidere -9 Un processo?

Cosa fa un programma quando viene inviato il segnale Sigkill?

SIGTERM vs SIGKILL:qual è la differenza?

Cos'è un processo interrotto in Linux?

Qual è la definizione di una sessione in Linux?