系统编程-回收子进程-孤儿和僵尸进程,wait和waitpid方法

系统编程-回收子进程-孤儿和僵尸进程,wait和waitpid方法

在这里插入图片描述

1. 孤儿进程

孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。
危害:没什么危害…

产生孤儿进程的举例:

#include <stdio.h>
#include <unistd.h>

int main(){
	pid_t pid = fork();
    if(pid == 0){// child
		while(1){//强制不让子进程退出,变为孤儿进程
       		printf("I am child,PID:%d,PPID:%d\n",pid,getpid(),getppid());
			sleep(1);
		}
    }else if(pid>0){// parent
        printf("I am parent,PID:%d,PPID:%d\n",pid,getpid(),getppid());
		sleep(5);
		printf("I am parent,I will die!\n");
    }
	return 0;
}

2. 僵尸进程

僵尸进程: 子进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,此子进程便成为僵尸(zombie)进程。
注意:僵尸进程是不能使用kill命令清除掉的。因为kill命令只是用来终止进程的,而僵尸进程已经终止。

产生僵尸进程的举例:

#include <stdio.h>
#include <unistd.h>

int main(){
	pid_t pid = fork();
    if(pid == 0){// child
       		printf("I am child,PID:%d,PPID:%d\n",pid,getpid(),getppid());
			sleep(2);
			printf("I am child,I will die!\n");
		}
    }else if(pid>0){// parent
		while(1){//强制不让父进程退出,子进程变为僵尸进程
        	printf("I am parent,PID:%d,PPID:%d\n",pid,getpid(),getppid());
			sleep(5);
		}
    }
	return 0;
}

危害:僵尸进程会占用系统资源,如果很多,则会严重影响服务器的性能;
如何解决僵尸进程
①杀死他的父进程使其变成孤儿进程,进而被系统处理。
②wait函数

3. 子进程回收

  一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用waitwaitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用waitwaitpid得到它的退出状态同时彻底清除掉这个进程。

3.1 wait

父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:
① 阻塞等待子进程退出
② 回收子进程残留资源
③ 获取子进程结束状态(退出原因)。

wait函数的原型是:

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);

  进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸进程的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个这样的进程出现为止

  参数status用来保存被回收进程退出时的一些状态,如果我们不想知道这个子进程是如何死掉的,只想把它消灭掉的话,那么我们可以设定这个参数为NULL,就像下面这样:

pid_t pid = wait(NULL);

  如果成功,wait会返回被回收子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD
  返回的status只是一个整型变量,不能很精确的描述出状态,因此需要借助宏函数来进一步判断进程终止的具体原因。经常用到的宏函数为如下两组:

//正常死亡WIFEXITED
if(WIFEXITED(status)){
	pfintf("%d:"WEXITSTATUS(status));
}
//非正常死亡
if(WIFSIGNALED(status)){
	pfintf("%d:"WTERMSIG(status));
}
  1. WIFEXITED(status) 为非0 → 进程正常结束

WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (return/exit的参数)

  1. WIFSIGNALED(status) 为非0 → 进程异常终止

WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(){
	pid_t pid = fork();
	if(pid==0){
		printf("I am child,will die!\n");
		sleep(2);
		//while(1){
			printf("I am child,Come and hit me!!\n");
			sleep(1);
		//}
		return 101;
	}
	else if(pid>0){
		printf("I am parent,wait for child die\n");
		int status;
		pid_t wpid = wait(&status);
		printf("wait ok,wpid = %d,pid = %d\n",wpid,pid);
		//正常死亡WIFEXITED
		if(WIFEXITED(status)){
			printf("child exit with %d\n:",WEXITSTATUS(status));
		}
		//非正常死亡
		if(WIFSIGNALED(status)){
			printf("child killed by %d:",WTERMSIG(status));
		}
	}
}

子进程正常死亡运行结果:
在这里插入图片描述
子进程一直运行时(while(1)),kill掉之后运行结果:
在这里插入图片描述

3.2 waitpid

作用同wait,但可指定pid进程清理,可以不阻塞。
waitpid函数的原型是:

#include <sys/types.h>
#include <sys/wait.h>

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

返回值:
成功:返回清理掉的子进程ID
失败:-1(出错,无子进程);
返回0值,参3传WNOHANG,且子进程正在运行。

pid
从参数的名字pid和类型pid_t 就可以看出,这里需要的是一个进程ID。当pid取不同的值时,在这里有不同的意义。

  • pid >0时,回收指定ID的子进程,只等待进程ID等于pid的子进程,不管其他已经有多少个子进程运行结束退出了,只要指定的子进程还没结束,waitpid就会一直等下去;
  • pid = -1时,回收任意子进程(相当于wait,等待任何一个子进程退出,没有 任何限制;
  • pid = 0时,回收和当前调用waitpid一个组的所有子进程,如果 子进程已经加入了别的进程组,waitpid不会对它做任何理睬;
  • pid < -1时,回收指定进程组内的任意子进程,这个进程组的ID等于pid的绝对值;

options

  • 0:(相当于wait)阻塞回收
  • WNOHANG:非阻塞回收,用轮询结构回收。

3. wait和waitpid的区别

  • 在一个子进程终止前,wait使其调用者阻塞,而waitpid则提供了非阻塞版本;
  • waitpid等待一个指定的子进程,而wait等待第一个终止的子进程;
  • waitpid支持作业控制(以WUNTRACED选项,由pid指定的任一子进程状态,且其状态自暂停以来还未报告过,则返回其状态);

注意:一次waitwaitpid调用只能清理一个子进程,清理多个子进程应使用循环。

wait循环回收:
在这里插入图片描述
运行结果:
在这里插入图片描述
waitpid循环回收:
在这里插入图片描述
运行结果:
在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值