前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux孤儿进程和僵尸进程详解(wait和watipid)

Linux孤儿进程和僵尸进程详解(wait和watipid)

作者头像
Ch_Zaqdt
发布2020-03-04 18:53:47
3.2K0
发布2020-03-04 18:53:47
举报
文章被收录于专栏:Zaqdt_ACMZaqdt_ACM

       当一个进程使用了fork函数会创建一个新的子进程,那么就会存在两个问题,一个是子进程没有结束但是父进程结束了,另一个是子进程结束了但是父进程没有回收子进程的资源。这两种情况就产生了孤儿进程和僵尸进程。下面会通过实际进程运行的示例来进行说明。首先先来明确一个知识点,在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。

孤儿进程

       当子进程还没有结束的时候,父进程先结束了,那么此时的子进程就叫做孤儿进程(毕竟没有了爸爸),那么好在系统中也有福利院,此时系统中的1号进程init就相当于福利院收养了这个子进程,下面我们可以通过ps ajx命令看一下init进程,然后会通过一个代码示例来观察一下子进程是不是被1号进程收养了。

       然后我们看到了这个init,然后我们通过下面的代码来验证一下孤儿进程是不是会被init收养,也就是查看孤儿进程的PPID是否是1就好了,这里用桌面版的Ubuntu和服务器版的Ubuntu的运行结果不同,如果用比较新(旧版的应该没有问题)的桌面版的Ubuntu会发现孤儿进程的PPID并不是1,那么为什么图形化的Ubuntu的孤儿进程没有被init收养可以看下这篇博客:传送门,那么这里我就用服务器版的Ubuntu进行演示,代码如下:

代码语言:javascript
复制
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int main(void)
{
        pid_t pid = fork();
        if(pid > 0){         // 父进程输出pid并睡眠2s退出
                printf("parent pid is %d\n", getpid());
                sleep(2);
        }
        else if(pid == 0){   // 子进程循环打印pid和ppid
                for(int i=0;i<10;i++){
                        printf("my pid is %d, my ppid is %d\n", getpid(), getppid());
                        sleep(1);
                }
        }
        else {
                perror("fork");
                exit(1);
        }
        return 0;
}

       运行的结果如下图所示:

僵尸进程

       任何一个子进程在结束后,并不是马上消失掉,而实留下一些资源等待父进程处理,那么僵尸进程就是当子进程比父进程先结束,而父进程又没有释放子进程占用的资源,此时子进程将成为一个僵尸进程。可以通过下面的代码来看一下僵尸进程,代码如下:

代码语言:javascript
复制
/*
    我们让父进程一直循环,子进程打印出pid和ppid后就退出
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int main(void)
{
	pid_t pid = fork();
	if(pid > 0){
		while(1){
			printf("parent pid is %d\n", getpid());
			sleep(2);
		}
	}
	else if(pid == 0){
		printf("my pid is %d, my parent pid is %d\n", getpid(), getppid());
		sleep(1);
	}
	else {
		perror("fork");
		exit(1);
	}
	return 0;
}

       我们运行这个程序后,再打开一个终端使用ps ajx命令来查来一下,最终结果如下图所示:

       我们可以发现子进程退出后,但是它的pid仍然存在,而且状态为Z+,那么Z就是Zombie的意思,说明此时该进程就已经是一个僵尸进程了。僵尸进程的危害:可想而知僵尸进程会造成一定的资源浪费,占用不必要的资源,还有就是当你的进程id达到了最大值的时候,因为有僵尸进程的存在,占用了部分进程id,使得无法再打开新的进程。

       那么为什么系统要让子进程结束的时候等待父进程来处理其资源,而不是直接结束呢?因为父进程有时候需要获取到子进程的退出状态,如果是正常退出,可以直接将其释放,如果是异常退出,又可以根据异常信息进行进一步的相关操作。

wait函数和waitpid函数

       为了解决僵尸进程的问题,可以使用wait函数和waitpid函数来处理,我们先看一下这两个函数的原型:

代码语言:javascript
复制
       #include <sys/types.h>
       #include <sys/wait.h>

       pid_t wait(int *wstatus);

       pid_t waitpid(pid_t pid, int *wstatus, int options);

       对于wait函数,父进程调用该函数的时候,如果子进程还没有运行结束,那么父进程就会阻塞在这里,直到有子进程结束变为僵尸进程后,会获取子进程的退出信息,并将它销毁返回。如果我们不需要查看子进程的退出状态,那么参数设置为NULL就可以了,如果需要获取子进程的退出信息就传入一个int引用就好了。如果成功wait函数会返回子进程的pid,如果当前进程没有子进程,就会失败,返回-1并设置errno为ECHILD。

       对于waitpid函数来说,它的作用和wait函数是一样的,只不过多了两个参数,可以让用户更灵活的去操作。首先第一个参数是一个pid(进程号),它有四种形式:

        1. 当pid > 0时,只等待进程ID等于pid的子进程,那么此时的waitpid函数就有了针对性,只等待和pid相同进程号的子进程。

        2. 当pid = -1时,等待任何一个子进程退出,那么此时的waitpid函数和wait函数的作用相同。

        3. 当pid = 0时,等待和父进程相同进程组中的任何子进程。

        4. 当pid < -1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

       这里再解释一下什么是进程组ID,对于一个父进程,它的进程组ID就是它本身的PID,那么它的所有子进程的组进程ID也都是父进程的PID(可以理解为一个家族),那么假如一个父进程的PID为1000,子进程的PID为1001、1002、1003....,那么这父子进程的组进程ID都是1000,那么我们在使用waitpid函数时,设置pid为-1000的意思就是等待组进程ID为1000的子进程结束。

       第三个参数options,有时候父进程一直阻塞在那里会导致程序的性能降低,那么我们在第三个参数上使用WNOHANG的话,此时如果子进程还正在运行,父进程不会阻塞在这里并返回0,如果子进程已经结束,返回子进程的PID,如果没有子进程返回-1并设置errno。

waitpid有wait没有的三个功能:

1. waitpid能等待一个特定的子进程,而wait只能等待任意的子进程。

2. 系统一旦调用wait函数就会阻塞父进程来等待,直到子进程的退出才停止阻塞,而waitpid提供了非阻塞方式的等待,也就是          WNOHANG参数。

3. waitpid支持作业控制,提供用于检查wait和waitpid返回状态的宏,这两个函数返回的子进程的状态都保存在status指针中。     WIFEXITED(status): 若为正常终止, 则为真. 此时可执行 WEXITSTATUS(status): 取子进程传送给exit或_exit参数的低8位。     WIFSIGNALED(status): 若为异常终止, 则为真.此时可执行 WTERMSIG(status): 取使子进程终止的信号编号。     WIFSTOPPED(status): 若为当前暂停子进程, 则为真. 此时可执行 WSTOPSIG(status): 取使子进程暂停的信号编号。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-02-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 孤儿进程
  • 僵尸进程
  • wait函数和waitpid函数
  • waitpid有wait没有的三个功能:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档