LinuxでゾンビプロセスにSIGKILLを送信するとどうなりますか?


10

Linuxでは、子プロセスが終了し、その親がまだその上で待機していない場合、ゾンビプロセスになります。子の終了コードはpid記述子に格納されます。

a SIGKILLが子に送信された場合、何の影響もありません。

これは、終了コードがによって変更されない、SIGKILLまたは終了コードが変更されて、子がSIGKILL

回答:


14

その質問に答えるには、シグナルがプロセスに送信される方法と、プロセスがカーネルにどのように存在するかを理解する必要があります。

各プロセスはtask_structカーネル内で表されます(定義はsched.hヘッダーファイルにあり、ここから始まります)。その構造体はプロセスに関する情報を保持します。たとえば、pid。重要な情報は、関連する信号が格納される行1566にあります。これは、シグナルがプロセスに送信された場合にのみ設定されます。

デッドプロセスまたはゾンビプロセスには、まだがありtask_structます。構造体は、親プロセス(自然または養子縁組による)が子プロセスをwait()取得SIGCHLDするために受け取った後に呼び出されるまで残ります。シグナルが送信されると、signal_structが設定されます。この場合、シグナルがキャッチ可能かどうかは関係ありません。

シグナルは、プロセスが実行されるたびに評価されます。または正確は、プロセス実行される前に。その後、プロセスはそのTASK_RUNNING状態になります。カーネルは、schedule()そのスケジューリングアルゴリズムに従って次の実行中のプロセスを決定するルーチンを実行します。このプロセスが次の実行プロセスであると仮定すると、処理signal_structされる待機シグナルがあるかどうかに関係なく、の値が評価されます。シグナルハンドラが手動で(signal()またはを介してsigaction())定義されている場合、登録された関数が実行されます。そうでない場合は、シグナルのデフォルトアクションが実行されます。デフォルトのアクションは、送信される信号によって異なります。

たとえば、SIGSTOPシグナルのデフォルトハンドラーは、現在のプロセスの状態をに変更してからTASK_STOPPED実行schedule()し、実行する新しいプロセスを選択します。通知は、SIGSTOP(のような捕捉可能ではないSIGKILLので、手動シグナルハンドラを登録する可能性はありません)。キャッチできないシグナルの場合、デフォルトのアクションが常に実行されます。


あなたの質問に:

機能しないプロセスまたはデッドプロセスは、スケジューラーによってTASK_RUNNING再びその状態にあると決定されることはありません。したがって、カーネルは、対応するシグナルのシグナルハンドラー(デフォルトまたは定義済み)を実行しません。したがって、exit_signalが再び設定されることはありません。シグナルは、プロセスのsignal_structin task_structを設定することによってプロセスに「配信」されますが、プロセスが再度実行されることはないため、他に何も起こりません。実行するコードはなく、プロセスに残っているのはそのプロセス構造体だけです。

ただし、親プロセスがその子をwait()で刈り取る場合、プロセスが受け取る「終了コード」は、プロセスが「最初に」停止したときのものです。処理されるのを待っているシグナルがあってもかまいません。


はい、でもコマンドkill自体は0または1を返しますか?
Anthony Rutledge

9

ゾンビプロセスは基本的にすでに死んでいます。唯一のことは、誰もその死を認めていないため、プロセステーブルのエントリと制御ブロック(Linuxカーネルがアクティビティ内のすべてのスレッドに対して維持する構造)を占有し続けていることです。ファイルの強制ロック、共有メモリセグメント、セマフォなどのその他のリソースは再利用されます。

誰もこのシグナルに応じることができないので、あなたはそれらにシグナルを送ることができません。プロセスはすでにその実行を終了しているため、KILLのような致命的なシグナルでさえ役に立たない。あなたは自分で試すことができます:

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

ここでは、子を待つ前にフォークしてスリープするプロセスを開始します。子供は少し寝るだけです。眠っているとき、または退出した直後に子供を殺して、違いを確認できます。

$ 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

カーネルソースコードの関連箇所を見つけたいのですが、見つけるのに苦労しています...
lgeorget

@Igeorgetありがとうございます。問題ありません。カーネルコードを確認する必要はありません。
user137481

プロセスを分岐し、子がすぐに終了する単純なRubyプログラムruby -e "loop while fork { exit! }"... imgur.com/SoRXErm
S.Goswami
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.