【Linux】僵尸进程(第十一篇)
目录
1.僵尸进程概述
2.回收僵尸进程
1.wait()函数
2.waitpid()函数
1.僵尸进程概述
由一个例子引入:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
int main()
{
pid_t pid = fork();
if(pid >0){
printf("parent process \n");
while(1) sleep(1);
}
else if(pid ==0){
printf("child process \n");
exit(0);
}else
{
perror("fork failed");
exit(0);
}
}
这份代码子进程通子进程先于父进程终止。这种内存泄漏由僵尸进程导致的(残留的子进程PCB)【思考:为什么会残留】 exit里面调用_exit() 进程结束时负责释放进程资源 将0-3G都释放,内核空间释放部分PCB残留此为僵尸进程。除非父进程通过wait回收僵尸进程。
2.回收僵尸进程
1.wait()函数
wait()阻塞函数,子进程存在,但未产生僵尸,wait阻塞等待,产生僵尸后立即回收。
注意:wait每调用一次只能回收一次
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
if(pid >0)
{
printf("parent process pid [%d]\n",getpid());//pid =getpid() 获取当前进程id
//pid = getppid() 获取父进程的id
pid_t zid = wait(NULL); //等待子进程结束回收,释放残留pcb,即僵尸进程
printf("child process pid [%d]",zid);
}else if(pid ==0)
{
printf("child process\n");
exit(0);
}
while(1)sleep(1);
return 0;
}
wait返回值:1.回收成功,wait返回值僵尸进程的pid 2.回收失败,wait 返回-1(什么时候失败? .没有子进程)
为什么不回收干净呢?怕父进程出现野id (比如 张三孩子死了还直接处理了,父亲不知道),父进程通过pcb 知道子进程的终止信息 父进程通过子进程的终止信息,可以知道子进程的退出原因,如果是正常终止,退出码可以获取到,如果是一场终止,例如被别人杀死,可以获取杀死进程的信号编号以及其进程的id wait(int *status) 对status进行校验就可以获悉详细的子进程退出信息 常见的子进程退出原因 1.子进程正常退出(exit、return) 2.异常退出(硬件错误异常进程终止,其他进程利用信号杀死) 3.进程被人为挂起 一个整型里面要存储20个option,每一个选项对应一位,0or1表示开启或关闭,位域 比如open(path,o_read|o_write|o_create) 用于校验status的一系列函数: WIFEXITED(status) #判断子进程是否正常退出,正常退出返回真 WEXITSTATUS(status) #该函数从status提取退出码并返回
WIFSIGNALED(status) #判断子进程是否是被信号杀死的,如果是则返回真 WTERMSIG(status)#该函数可以从status提取 终止子进程的信号编码并返回
//wait中的status 可以查看子进程的退出原因
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int status;
pid_t pid = fork();
if(pid >0)
{
printf("parent process pid [%d]\n",getpid());
pid_t zid = wait(&status);
if(WIFEXITED(status))
{
printf("parent process wait successfully,zombie [%d] exit code [%d] \n",getpid(),WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
printf("parent process wait successfully,zombie [%d] signal [%d] \n",getpid(),WTERMSIG(status));
}
}else if(pid ==0)
{
printf("child process\n");
//exit(0); //正常退出
while(1) sleep(1);//信号退出 kill -2 pid
}
while(1)sleep(1);
return 0;
}
注意printf输出必须要加\n结束,否则输出不出来。
2.waitpid()函数
waitpid 回收函数 (比wait功能性更强,回收更为灵活) waitpid(pid_t pid,int* status,int option);
1)pid:可以通过该参数指定waitpid回收
小于-1 跨组回收,指定组id,回收指定进程组种的(僵尸)子进 程,-5000(指定组id) -1 回收任意子进程if 0 同组回收,父进程只能回收与自己同组的子进程
大于0 点名回收,指定子进程的pid,回收子进程3000 前面三个可以回收多个,>0回收一个 2)status 回收成功,传出子进程的退出信息,无论是wait还是waitpid,int* status 可以传NULL,不接收子进程退出信息
3)options: 可以通过选项参数,设计waitpid的工作状态以及回收方式
WNOHANG=可以通过该选项,将waitpid设置为非阻塞回收,没有子进程退出立即返回:
waitpid 返回值: 1)回收成功,子进程id
2)回收失败,wait返回-1 (没有子进程) 3)非阻塞回收,返回0,表示当前的子进程没有结束,无需回收
阻塞回收策略 和非阻塞回收策略 阻塞回收:阻塞工作需要挂起唤醒,如果挂起唤醒频繁,增大系统开销 非阻塞回收: 无挂起唤醒开销 阻塞回收:阻塞回收会对父进程造成一定影响,父进程自身的任务被搁置 无法进行推进 非阻塞回收:轮询回收,定时查看子进程是否需要回收,如果不需要返回执行自己的任务,如果需要则回收释放PCB残留 这两种回收策略都不完善,都会对父进程核心工作造成影响
示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void Parent_job()
{
printf("Parent job..id [%d]...\n",getpid());
}
int main()
{
pid_t zid;
pid_t pid = fork();
if(pid >0)
{
while((zid = waitpid(-1,0,WNOHANG))!= -1)
{
if(zid ==0)
{
Parent_job();
}else if(zid >0)
{
printf("parent process wait child process %d\n",zid);
}
sleep(2);//
}
}else if(pid ==0)
{
printf("child process id [%d]\n",getpid());
sleep(5);
exit(0);
}else
{
perror("fork failed\n");
exit(0);
}
return 0;
}