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


10

在Linux中,当子进程终止并且其父进程尚未等待时,它将成为僵尸进程。孩子的退出代码存储在pid描述符中。

如果将a SIGKILL发送给孩子,则不会产生任何影响。

这是否意味着退出代码将不会被SIGKILL或修改,否则退出代码将被修改以指示该子项由于接收到而退出了SIGKILL

Answers:


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_structtask_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 '16

一个简单的Ruby程序,它派生一个进程,并且子进程立即退出ruby -e "loop while fork { exit! }"... imgur.com/SoRXErm
S.Goswami 19/12/7
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.