僵尸进程及其预防

1. 僵尸进程的产生原因
1.1 僵尸状态

    在UNIX中使用 fork() 系统调用创建进程时,将复制父进程的地址空间。如果父进程调用 wait() 系统调用,则父进程的执行将暂停,直到子进程终止。在子进程终止时,会生成“ SIGCHLD”信号,该信号会由内核传递给父进程。父进程在收到“SIGCHLD”时从进程表中获取子进程的状态。当子进程被终止,进程表中有一个条目对应存储状态的子进程。父进程收集状态后,此条目将被删除。至此,子进程的所有痕迹都将从系统中删除。如果父进程决定不等待子进程的终止就继续执行其后续任务,则在子进程终止时,退出状态不会被读取。因此,即使在终止子进程之后,进程表中仍保留一个条目。子进程的这种状态称为僵尸状态。

// A C program to demonstrate working of 
// fork() and process table entries. 
#include<stdio.h> 
#include<unistd.h> 
#include<sys/wait.h> 
#include<sys/types.h> 
  
int main() 
{ 
    int i; 
    int pid = fork(); 
  
    if (pid == 0) 
    { 
        for (i=0; i<20; i++) 
            printf("I am Child\n"); 
    } 
    else
    { 
        printf("I am Parent\n"); 
        while(1); 
    } 
} 

    输出:

# gcc test1.c -o test1
# ./test1
I am Parent
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child
I am Child

    现在使用终端中的以下命令检查进程表:
    $ ps -eaf
在这里插入图片描述
    或

# ps aux|grep test
root       2746  102  0.0   4216   352 pts/1    R+   09:42   0:19 ./test1
root       2747  0.0  0.0      0     0 pts/1    Z+   09:42   0:00 [test1] <defunct>
root       2749  0.0  0.0 112808   968 pts/2    S+   09:42   0:00 grep --color=auto test

    这里,[test1] <defunct> 显示了僵尸进程。

1.2 为什么我们需要避免创建僵尸进程?

    每个系统有一个进程表。进程表的大小是有限的。如果生成的僵尸进程太多,则进程表会被填满。也就是说,系统将无法生成任何新的进程,然后系统将陷于停顿。因此,我们需要防止僵尸进程的创建。

2. 防止僵尸进程的不同方法
2.1 使用 wait() 系统调用

    当父进程创建子进程,并调用 wait() 时,就表明父进程将等待子进程完成,并获得子进程的退出状态。父进程被挂起(在等待队列中等待),直到子进程终止。必须理解,在此期间,父进程什么都不做,只是等待。

// A C program to demonstrate working of 
// fork()/wait() and Zombie processes 
#include<stdio.h> 
#include<unistd.h> 
#include<sys/wait.h> 
#include<sys/types.h> 
  
int main() 
{ 
    int i; 
    int pid = fork(); 
    if (pid==0) 
    { 
        for (i=0; i<20; i++) 
            printf("I am Child\n"); 
    } 
    else
    { 
        wait(NULL); 
        printf("I am Parent\n"); 
        while(1); 
    } 
} 
2.2 忽略SIGCHLD信号

    当一个子进程终止时,一个相应的SIGCHLD信号被传递给父进程,如果我们调用 ‘signal(SIGCHLD,SIGIGN)’,那么系统将忽略SIGCHLD信号,并且将从进程表中删除子进程条目。因此,不会创建僵尸。但是,在这种情况下,父进程不能知道子进程的退出状态。

// A C program to demonstrate ignoring  
// SIGCHLD signal to prevent Zombie processes 
#include<stdio.h> 
#include<unistd.h> 
#include<sys/wait.h> 
#include<sys/types.h> 
  
int main() 
{ 
    int i; 
    int pid = fork(); 
    if (pid == 0) 
        for (i=0; i<20; i++) 
            printf("I am Child\n"); 
    else
    { 
        signal(SIGCHLD,SIG_IGN); 
        printf("I am Parent\n"); 
        while(1); 
    } 
} 
2.3 使用 signal 句柄

    父进程为SIGCHLD信号创建一个 signal 句柄。 signal 句柄在其中调用 wait() 系统调用。在这种情况下,当子进程终止时,SIGCHLD将传递给父进程。在收到SIGCHLD时,相应的处理程序被激活,它依次调用 wait() 系统调用。所以,父进程几乎立即收集exit状态,并且清除进程表中的子进程。这样就不会产生僵尸进程。

// A C program to demonstrate handling of 
// SIGCHLD signal to prevent Zombie processes. 
#include<stdio.h> 
#include<unistd.h> 
#include<sys/wait.h> 
#include<sys/types.h> 
  
void func(int signum) 
{ 
    wait(NULL); 
} 
  
int main() 
{ 
    int i; 
    int pid = fork(); 
    if (pid == 0) 
        for (i=0; i<20; i++) 
            printf("I am Child\n"); 
    else
    { 
        signal(SIGCHLD, func); 
        printf("I am Parent\n"); 
        while(1); 
    } 
} 

    输出没有任何 [a.out] defunct,即没有任何僵尸进程被创建。

参考文档

[1]Kishlay Verma.Zombie Processes and their Prevention[EB/OL].https://www.geeksforgeeks.org/zombie-processes-prevention/,2019-08-02.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值