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


10

In Linux, quando un processo figlio termina e il suo genitore non lo ha ancora atteso, diventa un processo zombi. Il codice di uscita del bambino è memorizzato nel descrittore pid.

Se un SIGKILLviene inviato al bambino, non dovrebbe esserci alcun effetto.

Questo significa che il codice di uscita non verrà modificato dal SIGKILLo il codice di uscita verrà modificato per indicare che il figlio è uscito perché ha ricevuto un SIGKILL?

Risposte:


14

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_structall'interno del kernel (la definizione è nel sched.hfile di intestazione e inizia qui ). Quella struttura contiene informazioni sul processo; per esempio il pid. Le informazioni importanti sono nella riga 1566 in cui è memorizzato il segnale associato. Questo è impostato solo se un segnale viene inviato al processo.

Un processo morto o un processo zombi ha ancora un task_struct. La struttura rimane, fino a quando il processo genitore (naturale o per adozione) ha chiamato wait()dopo aver ricevuto SIGCHLDper raccogliere il suo processo figlio. Quando viene inviato un segnale, signal_structviene impostato. Non importa se il segnale è catchable o no, in questo caso.

I segnali vengono valutati ogni volta che viene eseguito il processo. O per essere precisi, prima che il processo si eseguisse. Il processo è quindi nello TASK_RUNNINGstato. Il kernel esegue la schedule()routine che determina il prossimo processo in esecuzione secondo il suo algoritmo di pianificazione. Supponendo che questo processo sia il prossimo processo in esecuzione, signal_structviene valutato il valore di , indipendentemente dal fatto che vi sia un segnale di attesa da gestire o meno. Se un gestore di segnale viene definito manualmente (tramite signal()o sigaction()), viene eseguita la funzione registrata, in caso contrario viene eseguita l' azione predefinita del segnale . L'azione predefinita dipende dal segnale inviato.

Ad esempio, il SIGSTOPgestore predefinito del segnale cambierà lo stato del processo corrente in TASK_STOPPEDe quindi eseguirà schedule()per selezionare un nuovo processo da eseguire. Si noti che SIGSTOPnon è possibile accedere (come SIGKILL), quindi non è possibile registrare un gestore di segnali manuale. In caso di segnale inattaccabile, verrà sempre eseguita l'azione predefinita.


Alla tua domanda:

Un pianificatore defunto o morto non sarà mai determinato dallo scheduler di essere TASK_RUNNINGnuovamente nello stato. Pertanto il kernel non eseguirà mai il gestore del segnale (predefinito o definito) per il segnale corrispondente, qualunque sia il segnale. Pertanto exit_signalnon verrà mai più impostato. Il segnale viene "consegnata" al processo impostando il signal_structin task_structdel processo, ma niente altro accadrà, perché il processo non potrà mai funzionare ancora. Non c'è codice da eseguire, tutto ciò che rimane del processo è quella struttura del processo.

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


Sì, ma il comando killstesso restituisce 0 o 1?
Anthony Rutledge,

9

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

Non puoi segnalarli perché nessuno può agire su questo segnale. Anche segnali fatali come KILL sono inutili poiché il processo ha già terminato la sua esecuzione. Puoi provare te 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 quando dorme o appena esce 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

Vorrei trovare il passaggio pertinente nel codice sorgente del kernel per te, ma faccio fatica a trovarlo ...
lgeorget,

@Igeorget Grazie ma va bene, non ho bisogno di vedere il codice del kernel.
user137481

Semplice programma Ruby che avvia un processo e il bambino esce immediatamente ruby -e "loop while fork { exit! }"... imgur.com/SoRXErm
S.Goswami
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.