Linux僵尸进程与孤儿进程
僵尸进程
1.概念
简单来说:子进程先于父进程退出,子进程会变成僵尸进程,进入到Z状态。
2.模拟代码
创建一个僵尸进程例子
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int ret = fork();
if(ret < 0){
perror("fork");
return -1;
}
else if(ret == 0){ //child
printf("I am child : %d!, ret: %d\n", getpid(), ret);
}else{ //father
while(1){
printf("I am father : %d!, ret: %d\n", getpid(), ret);
sleep(1);
}
}
return 0;
}
编译运行
在另一个终端下启动监控
3.原因
子进程在退出的时候,会告知父进程,父进程会忽略处理,父进程并没有回收子进程的退出状态信息
4.危害
子进程的PCB得不到释放,造成了内存泄露,如果僵尸进程太多,浪费的内存就越大
5.解决方案
注意:程序员针对僵尸进程,使用kill命令是不能够杀死的(kill [pid])
- 杀死父进程(不推荐)
- 重启操作系统(不推荐)
- 父进程进行进程等待(推荐!最合理的解决方案):父进程进行进程等待,等待子进程退出以后,回收子进程的退出状态信息,防止子进程变成僵尸进程
- 具体操作:在父进程的分支逻辑当中调用进程等待的函数,进行等待子进程退出,回收子进程的退出状态信息
调用进程等待的函数
- wait函数原型:pid_t waitpid(int sstatus);
- 作用:等待退出的子进程,引申含义就是回收退出的子进程的退出状态信息
- 返回值:成功返回被等待进程pid,失败返回-1.
- 参数:调用wait函数的调用者,想要获取子进程的退出状态信息,但是返回值已经有含义了,所以, wait获取到的子进程的退出状态信息是通过参数进行传递的。
- 函数特性:谁调用,谁等待。直到等待的子进程退出,把这种称之为阻塞,换句话说:当发起阻塞调用之后,如果要完成函数功能,需要等待某种资源,则会一直等待,直到资源到来,完成函数功能后,函数才能返回。
- 细分:如果发起阻塞调用的时候,资源在,无需等待,直接执行函数功能后返回。
如果发起阻塞调用的时候,需要等待资源,则需要等待资源到来后,执行完函数功能后返回- 参数status含义:子进程正常退出:如果传递status的值,在会获取到子进程的退出状态
子进程异常退出:如果传递status的值,则会获取到coredump标志位+退出信号
代码验证:
#include <stdio.h
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.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(257);
} else{
int status = 0;
pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5S
printf("this is test for wait\n");
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;
}
- waitpid函数原型:pid_t waitpid(pid_t pid,int *status,int options);
- 参数:pid:Pid=-1,等待任一个子进程。与wait等效。pid>o.等待其进程ID与pid相等的子进程。
status:子进程的退出信息,同wait
options:WNOHANG:设置waitpid为非阻塞状态若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待若正常结束,则返回该子进程的ID- 返回值:当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANC,而调用中waitpid发现没有退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在
代码验证进程的非阻塞等待方式
#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;
}
孤儿进程
1.概念
父进程先于子进程退出,子进程就会变成孤儿状态
2.模拟代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int ret = fork();
if(ret < 0){
perror("fork");
return -1;
}
else if(ret == 0){ //child
while(1){
printf("I am child : %d!, ret: %d\n", getpid(), ret);
sleep(1);
}
}else{ //father
sleep(1);
printf("I am father : %d!, ret: %d\n", getpid(), ret);
}
return 0;
}
3.原因
父进程先于子进程退出后,既定回收子进程的父进程不在了,所以子进程变成孤儿了,称之为孤儿进程。
4.孤儿进程有危害吗
注意:孤儿进程并没有什么危害!事实上没有孤儿状态!!!
当父进程运行结束了,导致子进程变成了孤儿进程,当前的子进程就会被1号进程领养,孤儿进程的父进程就变成了1号进程
什么是1号进程:/usr/lib/systemd/systemd :操作系统启动的第一个进程,后续有很多进程都是由该进程创建出来的或者是由该进程的子孙创建出来的操作系统初始化的一些工作