孤儿进程与僵尸进程

孤儿进程:父进程先于子进程结束(遇到return、exit、异常终止等情况时),则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。可以通过getppid函数来查看孤儿进程的父进程ID,即init进程的ID,init进程的ID具体是多少取决于操作系统对进程的调度,其值是不确定的。在操作系统中,init进程也不止一个,可通过ps aux详细查看。

僵尸进程:进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。

特别注意,僵尸进程是不能使用kill命令清除掉的。因为kill命令只是用来终止进程的,而僵尸进程已经终止。

// orphan.c  //shell产生子进程执行该程序

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{	
    pid_t pid;
    pid = fork();  //创建子进程

    if (pid == 0) {
        while (1) {
            printf("I am child, my parent pid = %d\n", getppid());
            sleep(1);  //子进程一直运行
        }
    } else if (pid > 0) {
            printf("I am parent, my pid is = %d\n", getpid());
            sleep(9);
            printf("------------parent going to die------------\n");  //父进程先于子进程结束,子进程变为孤儿进程
    } else {
        perror("fork");
        return 1; //等价于exit(1),都是结束进程,且进程结束状态置1表示出错
    }

    return 0;
}

[root@localhost wait]# ./orphan

I am parent, my pid is = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

------------parent going to die------------ //父进程正常结束

I am child, my parent pid = 1  //子进程为孤儿进程,被init进程领养,即其父进程为init进程

[root@localhost wait]# I am child, my parent pid = 1 //父进程结束,shell进程收回前台,等待命令交互

I am child, my parent pid = 1 //子进程一直执行,为孤儿进程

I am child, my parent pid = 1

I am child, my parent pid = 1

I am child, my parent pid = 1

I am child, my parent pid = 1

//zoom.c

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

int main(void)
{
    pid_t pid;
    pid = fork(); 

    if (pid == 0) {
            printf("---child, my parent= %d, going to sleep 10s\n", getppid());
            sleep(10);
            printf("-------------child die--------------\n"); //子进程先正常结束
    } else if (pid > 0) {
        while (1) {
            printf("I am parent, pid = %d, myson = %d\n", getpid(), pid);
            sleep(1);
        }    //父进程一直运行
    } else {
        perror("fork");
        return 1;
    }

    return 0;
}

[root@localhost wait]# ./zoom

I am parent, pid = 27152, myson = 27153

---child, my parent= 27152, going to sleep 10s

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

-------------child die--------------  //子进程死亡

I am parent, pid = 27152, myson = 27153 //父进程一直运行,一直占据前台,shell进程无法获得前台交互  且子进程结束后,父进程没有对子进程残留在内核中的PCB进行回收,从而子进程变为僵尸进程。

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

[root@localhost wait] # ps aux

root     27152  0.0  0.0   4160   352 pts/1    S+   03:11   0:00 ./zoom

root     27153  0.0  0.0      0     0 pts/1    Z+   03:11   0:00 [zoom] <defunct>

root      27155  0.0  0.0      0     0 ?        R    03:12   0:00 [kworker/3:0]

root      27163  0.0  0.0 107892   360 ?        S    03:12   0:00 sleep 60

root      27164  0.0  0.0 123360  1384 pts/0    R+   03:12   0:00 ps aux

[root@localhost wait]# kill 27152 

由上可以看出,父进程27152,为S+,表示该进程在后台运行(注意,ps aux命令是在另一个终端执行的,因此相对于另一个shell终端,父进程在后台运行);子进程27153,为Z+,表示僵尸进程,说明该进程终止后,其残留在内核的PCB资源没有被父进程回收;而ps aux这个命令的进程为R+,表示在前台运行,即就在pts/0设备终端的前台运行。而前两个进程属于pts/1设备。[zoom] <defunct>  defunct表示已故的,不复存在的,但其痕迹仍然残留在内核中,占用内存资源,因此需要做到及时对僵尸进程回收和清除。

总结:在每个进程退出的时候, 内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。 另外,如果父进程一直不结束(不终止),在不调用wait或waitpid的情况下,其子进程结束后会变为僵尸进程,残留在内核中,此时若父进程结束了,那么这些僵尸进程因为没有了父进程,就会变为孤儿进程被init进程领养,init进程就会对这些僵尸进程进行回收,然后清除。因此,父进程结束了,其子进程会被回收。孤儿进程结束后,也会被init进程回收。如果要回收一个进程,除了通过其父进程调用wait或waitpid函数外,还可以杀死其父进程,让其变为孤儿进程,被init进程回收。

系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。可以fork两次, 父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值