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

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

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

a がSIGKILL子に送信されると、何の効果もありません。

これは、SIGKILL終了コードが変更されないことを意味しますか、それとも終了コードが変更され、子プロセスが終了したことを示すために変更されることを意味しますかSIGKILL

答え1

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

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再び設定されません。シグナルは in を設定してsignal_structプロセスに「転送」されますが、task_structプロセスが再実行されないため、何も起こりません。実行するコードがなく、プロセスに残っているのはプロセス構造です。

ただし、親プロセスが子プロセスを収集する場合、wait()受信される終了コードは、プロセスが「元の」終了したときに持っていた終了コードです。処理待機中の信号があるかどうかは重要ではありません。

答え2

ゾンビのプロセスは基本的に死んだ。唯一の問題は、まだ誰もその死を認めていないため、プロセステーブルのエントリと制御ブロック(各アクティブスレッドに対して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

関連情報