僵尸进程
子进程退出后,资源没有释放,处于僵死状态。
产生原因: 子进程优先于父进程退出,父进程正在执行其他操作,没有关注子进程退出,这时候操作系统为了保护子进程退出的原因,不会释放子进程中的资源,子进程既没有运行,也没有退出,处于僵死状态,成为僵尸进程。
通常情况下解决僵尸进程的办法就是:
waitpid(pid_t pid,int * status,int options);
pid:子进程的操作句柄
- pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。
- pid=-1 等待任何子进程,相当于 wait()。
- pid=0 等待进程组识别码与目前进程相同的任何子进程。
- pid>0 等待任何子进程识别码为 pid 的子进程。
status:保存子进程退出原因。
- 如果不需要进程退出原因,则设置为NULL
options:存储waitpid函数的等待方式。
- 为0默认阻塞等待。
- WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。
- WUNTRACED 若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。WIFSTOPPED(status)宏确定返回值是否对应与一个暂停子进程。
子进程的结束状态返回后存于 status,底下有几个宏可判别结束情况: - WIFEXITED(status)如果若为正常结束子进程返回的状态,则为真;对于这种情况可执行WEXITSTATUS(status),取子进程传给exit或_eixt的低8位。
- WEXITSTATUS(status)取得子进程 exit()返回的结束代码,一般会先用
WIFEXITED 来判断是否正常结束才能使用此宏。 - WIFSIGNALED(status)若为异常结束子进程返回的状态,则为真;对于这种情况可执行WTERMSIG(status),取使子进程结束的信号编号。
返回值:成功返回子进程的pid,失败返回0。
//使用waitpid等待子进程退出
int main()
{
signal(SIGCHLD,sigcb);
pid_t pid = fork();
if(pid == 0)
{
sleep(5);//子进程睡眠5秒钟后退出
exit(0);
}
wait(-1,NULL,0);
while(1)
{
printf("this is parents\n");
}
return 0;
}
如果waitpid为阻塞等待,则父进程一直在等子进程退出,直到子进程退出后才执行自己的操作,有没有一种父进程在执行自己的操作,当子进程退出信号发出时,父进程再来释放子进程。
如果这样我们需要先知道子进程退出时,操作系统给父进程发送什么样的信号,来通知父进程。
17号信号:SIGCHLD
但是这个SIGCHLD为进程默认忽略处理信号,这下我们知道为什么父进程不会注意子进程退出。
现在我们知道子进程退出时,操作系统会给父进程发送17号信号,但是我们如何自定义处理该信号呢?
signal
自定义信号处理函数。
//我们通过信号处理函数来通知父进程
void sigcb(int signo)
{
wait(-1,NULL,0);
}
int main()
{
signal(SIGCHLD,sigcb);
pid_t pid = fork();
if(pid == 0)
{
sleep(5);//子进程睡眠5秒钟后退出
exit(0);
}
while(1)
{
printf("this is parents\n");
}
}
但是这样做会产生一个问题:如果父进程有多个子进程呢,由于SIGCHLD信号为非可靠信号,在信号的集合中只保存一份,所以在调用signal函数时,会出现想要释放多个子进程,但事实上只释放一个,导致子进程没有完全处理,同样会产生僵尸进程。
解决办法:循环调用waitpid函数,防止出现进程没有完全处理的情况。
void sigcb(int signo)
{
while(wait(-1,NULL,0)>0);//如果有子进程退出,则循环处理
}
int main()
{
signal(SIGCHLD,sigcb);
pid_t pid = fork();
if(pid == 0)
{
sleep(5);//子进程睡眠5秒钟后退出
exit(0);
}
while(1)
{
printf("this is parents\n");
}
}