Linux的进程等待
在学习进程等待之前,我们要弄清楚,两个问题,一个是为什么要等待,还有一个是怎么等待?
首先:为什么要进程等待?
进程终止或者退出的时候,进程就会关闭所有文件描述符,然后释放在用户空间的分配的内存,但是进程的PCB会暂时保留,因为PCB中还保存有进程的退出状态,如果进程是正常退出,那么退出状态就是进程的退出码,如果是异常退出,那进程的退出状态就是导致进程退出的信号,因为父进程必须要回收子进程,如果父进程在子进程之前退出,那么子进程就成了孤儿进程,所以父进程要拿到子进程的退出码,然后释放子进程。(来一点专业的描述:当一个进程终止的时候,内核会向其父进程发送信号,通知父进程要等待子进程,父进程调用wait)。
再者,怎么进程等待?
在此之前,我们先总结一下进程退出的状态:①代码运行结束,结果正确,退出码为正常退出路径的退出码
②代码运行结束,结果错误,退出码为非正常退出路径的退出码
③代码没有运行结束,原因是收到某种退出信号
现在我们就要介绍两个系统调用函数: wait 和waitpid
一、wait系统调用:pid_t wait (int* status)
参数:status, 是一个输出型参数,指向的空间存放着一个int类型的数字。高八位是程序的退出码,低七位是接收 到的信号(如果程序有接收到信号)
如果程序是代码跑完,正常结束,那么status里面的高八位存着的就是程序的退出码,低八位就是0, 也就是什么信号都没有接收到。
如果程序是异常终止,那么存着程序退出码的高八位就是0,第七位存放着接收到的信号
如果不关心,那么也可以传NULL进去。
返回值:返回值>0,那么返回的就是等待进程的pid,这时候,我们就可以使用刚才的输出型参数status来查看进程的退出状态。
返回值 == -1 ,那么调用失败,可能是因为没有子进程
wait的等待方式,是阻塞型等待,也就是说,只有等到有子进程给他返回退出状态的时候,他才会继续,如果没有子进程给他返回退出状态,那么父进程就会一直停止,处于等待的状态,如果有多个子进程,那么就会接收最先返回退出状态的子进程
二、waitpid系统调用:pid_t waitpid(pid_t pid,int* status,int options)
参数解析:pid:要等待子进程的pid,(这也是和wait的区别之一),如果输入:2554,那么就会等待pid为2554的进程返回的状态,
Tip:如果pid传进去的是-1,那么就表示等待任意一个进程的结束。
status :和wait的statu是同样的,可以参考wait的status。
options: options设置成1,那么就是阻塞式等待,和wait的效果一样。
options设置成WNOHANG:那就是非阻塞式等待。当发现等待的进程没有退出,就返回0。
返回值:返回值>0,等待成功,返回的是等待进程的pid
返回值==0, 等待的进程没有返回退出状态,也就是等待的那个进程没有退出
返回值<0,调用失败
现在我们给出wait调用的代码:
该例子中,我们让子进程sleep 5s,并且返回一个比较明显的返回值,123,父进程调用wait,之后接收到子进程的返回状态,并把相关信息打印出来
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(){
pid_t id = fork();
if(id == 0){
printf("i am a child, pid:%d,ppid:%d \n",getpid(),getppid());
sleep(5);
exit(123);
}
else if(id > 0){
printf("i am father,pid:%d,ppid:%d\n",getpid(),getppid());
int status = 0;
int ret = wait(&status);
if(ret>0){
printf("wait success! ret:%d,exitcode:%d,sign:%d\n",ret,(status>>8)&0xff,status&0xff);
}
}
else {
perror("wait");
}
return 0;
}
wait的调用结果:
在打印 “wait success! ret:2623,exitcode:123,sign:0” 这句话之前,停顿了5s,此时父进程就是在等待子进程给他返回退出码。
再看看waitpid的调用代码:
这个例子中,我们让子进程sleep 5s,标记一个明显的退出码123,此时调用waitpid,当子进程sleep的时候,父进程读到的是一个0的返回值,就会进入相应的循环分支
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(){
pid_t id = fork();
if(id == 0){
printf("i am child,pid:%d,ppid:%d\n",getpid(),getppid());
sleep(5);
exit(123);
}
else if(id<0){
perror("wait");
}
else{
printf("i am father,pid:%d,ppid:%d\n",getpid(),getppid());
int status = 0;
do{
int ret = waitpid(id,&status,WNOHANG);
if(ret>0){
printf("wait success!id = %d,exitcode = %d,sign = %d \n",ret,(status>>8)&0xff,status&0xff);
break;
}
else if(ret == 0){
printf("father do other thing \n");
sleep(2);
}
else{
perror("wait");
break;
}
}while(1);
}
return 0;
}
waitpid的运行结果:
打印 “father do other thing”,就说明进入了ret == 0的分支,
在打印 “father do other thing”之后,就会sleep 2s,再去打印下一句。
Tip:限于编者水平,文章难免有缺漏之处,欢迎大家指出。
如需转载,请注明出处!