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