在 Linux 中将 SIGKILL 发送到 Zombie Process 时会发生什么?

use*_*481 10 linux process signals zombie-process

在 Linux 中,当一个子进程终止并且它的父进程还没有等待它时,它就变成了一个僵尸进程。孩子的退出代码存储在 pid 描述符中。

如果将 aSIGKILL发送给孩子,则不应该有任何效果。

这是否意味着退出代码不会被 修改,SIGKILL或者退出代码是否会被修改以指示子进程退出是因为它收到了SIGKILL?

cha*_*aos 15

要回答这个问题,您必须了解信号如何发送到进程以及进程如何存在于内核中。

每个进程都表示为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(),则它收到的退出代码是进程“最初”死亡时的退出代码。是否有信号等待处理并不重要。


lge*_*get 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;
}
Run Code Online (Sandbox Code Playgroud)

在这里,我启动了一个进程,在等待它的孩子之前分叉和睡眠。孩子什么都不做,只是睡了一会儿。您可以在孩子睡觉时或刚退出时杀死孩子以查看差异:

$ 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
Run Code Online (Sandbox Code Playgroud)