僵尸进程示例分析------顺便说说linux中的wait和waitpid

972 篇文章 327 订阅
148 篇文章 34 订阅

        在之前的博文中, 我们说过孤儿进程, 在unix/linux中, 如果子进程活着, 而父进程死亡, 那么子进程就是孤儿进程, 很形象吧。 

 

        今天我们说说僵尸进程, 在unix/linux中, 如果父进程还活着, 但子进程死了, 那么unix/linux不会轻易地让子进程消息得一干二净, 而是会让子进程变成僵尸进程, 目的是为了保存一些子进程的信息, 便于让父进程知道。 否则, 子进程干干净净彻彻底底地不留痕迹地死去, 父进程会伤心的。

        僵尸进程的存在, 是为了遗留信息和线索, 但会占据系统资源, 所以应该由父进程来收尸, 获取僵尸中的信息, 知道子进程是怎么死去的, 说不定会找公安机关去报案呢。

 

        我么来看看程序:

 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

int main()
{
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        printf("fork error\n");
        return 1;
    }

	if (pid == 0)  // 子进程
    {
        printf("this is child process\n");
        return 0;
    }

	// 父进程继续
    printf("this is father process\n");

	sleep(3);  // 让子进程先退出
	system("ps -o pid,ppid,state,tty,command");
    getchar();
    
    return 0;
}

       看看结果:

 

 

xxxxxx:~> ./a.out 
this is child process
this is father process
  PID  PPID S TT       COMMAND
17330 17328 S pts/0    -bash
19219 17330 S pts/0    ./a.out
19220 19219 Z pts/0    [a.out] <defunct>
19221 19219 R pts/0    ps -o pid,ppid,state,tty,command

        看到那个Z(zombie)没? 这就是僵尸进程。 此时, 父进程依然活着。  如果此时杀死父进程, 那么已经成为僵尸进程的子进程会变成“僵尸孤儿进程”, 会被pid为1的init进程进程领养, 而init进程比较特殊, 它会对自己的子僵尸进程进行自动收尸, 应用程序不用管。

 

       那么, 应用程序该如何对自己的子僵尸进程进行收尸呢? 其实很简单, wait和waitpid就是用来替僵尸进程收尸的。

       既然wait和waitpid都是用来收尸的, 那么他们有什么区别呢? 这样的问题, 网上到处都是介绍, 我就不再啰嗦了。 我只想说, waitpid是更高级版的wait, 功能更全量, 更强大, 所以, 与waitpid比起来, wait是个鸡肋。 在fork模型的多进程服务器中, 为了处理僵尸进程, waitpid能做到wait所做不到的事情, 看Stevens网络编程就知道这点。

       所以, 我们直接说waitpid,  看看代码:

 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>

void sig_child(int signo)
{
     pid_t pid;
     int stat;

	 // 循环收尸(非阻塞)
     while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
     {
           printf("kill zombie child %u \n", pid);
	 }
}

int main()
{
    pid_t pid;
    signal(SIGCHLD, sig_child);
    pid = fork();
    if (pid < 0)
    {
        printf("fork error:");
       	return 1;
    }

	if (pid == 0)
    {
        printf("this is child process, pid is %u\n",getpid());
        return 0;
    }

    printf("this is father process\n");
	sleep(3);
	system("ps -o pid,ppid,state,tty,command");
    getchar();
    
    return 0;
}

       结果:

 

 

xxxxxx:~> ./a.out 
this is child process, pid is 20571
kill zombie child 20571 
this is father process
  PID  PPID S TT       COMMAND
17330 17328 S pts/0    -bash
20570 17330 S pts/0    ./a.out
20572 20570 R pts/0    ps -o pid,ppid,state,tty,command

       可见, 僵尸的子进程被彻底清除, 没有Z了。

 


       说到这里, 大家应该想到, 回收僵尸进程, 除了用wait和waitpid之外, 直接杀死其父进程不就可以吗? 是的。 杀死父进程后, 僵尸的子进程被init进程领养, 进而在死时被init自动收尸。 所以, 我们自然可以想到如下模型: 

       爷爷进程----父亲进程----孙子进程。 杀死父进程, 让爷爷进程来收尸, 然后孤单的孙子进程自动由init进程领养(继父), 不需要年迈的爷爷进程过问了。

       所以, 两次fork搞起:

 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>

int main()
{
	// 爷爷进程
    pid_t  pid;
    pid = fork();
    if (pid < 0)
    {
        printf("fork error 1\n");
        return 1;
    }

    if (pid == 0) // 父亲进程
    {
        printf("father process, pid:%u, ppid:%u\n", getpid(), getppid());

		pid = fork();
        if (pid < 0)
        {
            printf("fork error 2\n");
            return 1;
        }

        if (pid > 0) // 还是父亲进程
        {
            printf("father process over\n");
            return 0; // 父亲进程退出, 变成僵尸进程, 让爷爷进程来收尸
        }

		// 孙子进程, 被init进程领养
        printf("grandson process, pid: %u, ppid:%u\n",getpid(), getppid());

		getchar();
        return 0;
    }

    // 爷爷进程对父亲进程进程收尸
    if (waitpid(pid, NULL, 0) != pid)
    {
        printf("waitepid error\n");
        return 1;
    }

	printf("grandpa process over\n");
    return 0;
}

       结果:

 

 

xxxxxx:~> ./a.out 
father process, pid:22851, ppid:22850
grandson process, pid: 22852, ppid:22851
father process over
grandpa process over
xxxxxx:~> 

       再看进程:

 

 

xxxxxx:~> ps -ef | grep a.out 
1000     22852     1  0 02:19 pts/0    00:00:00 ./a.out
1000     22865 17665  0 02:19 pts/1    00:00:00 grep a.out

       可以看到, 爷爷进程和父亲进程都over了(也不会存在僵尸了), 而孙子进程还活得好好的, 被进程号为1的init进程所领养。 此后, 如果孙子进程挂了, init进程会自动来收尸的。

 

       先说这么多。

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值