概念
- 僵尸进程是一个比较特殊的状态。当子进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵尸进程。
- 僵尸进程会以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。
- 只要子进程退出,父进程还在运行,但是父进程没有读取子进程状态,子进程进入僵尸状态。
实现一个维持30秒的僵尸进程代码:
/*这是一个30秒僵尸进程的示例代码 (严格来说,僵尸状态只能维持25秒)*/
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
int main()
{
pid_t pid ;
pid = fork();
if(pid < 0)
{
std::cout<<"fork error!!!"<<std::endl;
return -1;
}
else if(pid == 0)
{
std::cout<<"i am chilren!!!"<<"pid: "<<getpid()<<std::endl;
sleep(5);
}
else
{
std::cout<<"i am parent!!!"<<"pid: "<<getpid()<<std::endl;
sleep(30);
}
return 0;
}
危害
进程的退出状态必须被维持下去,父进程如果一直不读取,那子进程就一直处于Z状态。
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中。
换句话说,Z状态一直不退出,PCB一直都要维护,会造成内存资源的浪费,有内存泄露问题。
解决方法
父进程通过 进程等待 的方式,回收子进程的资源,获取子进程退出信息。
wait 方法
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置为 NULL
#include<sys/wait.h>
#include<iostream>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
int main()
{
pid_t pid;
pid = fork();
if(pid < 0)
{
std::cout<<"fork error!!!"<<std::endl;
return -1;
}
else if(pid == 0)
{
std::cout<<"i am chilren!!!"<<std::endl;
sleep(20);
exit(10);
}
else
{
int sta;
int ret = wait(&sta);
if(ret > 0 && (sta & 0x7f)==0)
{
//正常退出
std::cout<<"child exit code:"<<((sta>>8)&0xff)<<std::endl;
}
else if(ret > 0)
{
//异常退出
std::cout<<"sig code:"<<(sta & 0x7f)<<std::endl;
}
}
return 0;
}
waitpid方法(一般使用非阻塞方法)
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进
程的ID。
- 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
- 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
- 如果不存在该子进程,则立即出错返回。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid = fork();
if(pid < 0){
printf("%s fork error\n",__FUNCTION__);
return 1;
}else if( pid == 0 ){ //child
printf("child is run, pid is : %d\n",getpid());
sleep(5);
exit(1);
} else{
int status = 0;
pid_t ret = 0;
do
{
ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
if( ret == 0 ){
printf("child is running\n");
}
sleep(1);
}while(ret == 0);
if( WIFEXITED(status) && ret == pid ){
printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
}else{
printf("wait child failed, return.\n");
return 1;
}
}
return 0;
}