目录
int wait(int *status)------阻塞接口
int waitpid(pid_id,int *status,int option)
前言:
首先我们需要知道为什么要进行进程等待:
另外,进程一旦变成僵尸状态,那就刀枪不入, “ 杀人不眨眼 ” 的 kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。最后,父进程派给子进程的任务完成的如何,我们需要知道。子进程运行完成,结果对还是不对,或者是否正常退出。父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
进程等待的方法:
操作系统总共给我们提供了两个接口:![]()
在了解这两个接口之前,我们先来看看阻塞和非阻塞的区别:
阻塞:为了完成某个操作,发起调用,但是若不具备操作的完成条件,则调用一直等待。
非阻塞:为了完成某个操作,发起调用,但是若不具备操作的完成条件 ,立即报错返回。
int wait(int *status)------阻塞接口
功能:等待任意一个子进程退出,若当前没有子进程退出,则一直等待。
成功则返回值大于0,是退出子进程的pid;若出错,则返回-1
参数:int *status---整形空间的地址;保存退出子进程的退出状态。
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 #include<sys/wait.h> 5 int g_val=0; 6 int a=8; 7 8 int main() 9 { pid_t id=fork(); 10 if(id<0){ 11 perror("fork"); 12 return 0; 13 } 14 else if(id==0){ 15 int cut=5; 16 while(cut){ 17 printf("我是子进程,我还剩下%d秒\n",cut--); 18 sleep(1); 19 } 20 } 21 else 22 { 23 24 pid_t id=wait(NULL); 25 if(id>0) 26 { 27 printf("获取成功,退出子进程的pid是%d\n",id); 28 } 29 else if(id=-1) { 30 printf(" 等待失败\n"); 31 } 32 while(1){ 33 printf("我是父进程,我在等待子进程退出!\n"); 34 sleep(1); 35 36 } 37 return 0; 38 } 39 }
这时我们可以看到,当子进程退出后,父进程读取了子进程的退出信息,子进程也就不会变成僵尸进程了。 由此得知我们可以通过wait()的方案解决回收子进程Z状态,让子进程进入X。
int waitpid(pid_id,int *status,int option)
功能:若pid参数设置为-1,则表示等待任意子进程退出,否则就是等待指定子进程退出
参数:
pid:-1等待任意子进程退出,大于0表示等待指定子进程
status:获取退出子进程的状态
option:0-默认阻塞操作;WNOHANG-非阻塞操作(没有子进程退出立即返回)
返回值:若处理了指定子进程的退出,则返回值大于0;若等于0则表示没有子进程退出;出错则返回值小于0;
waitpid(-1,NULL,0)等价于wait(NULL);
注意点:wait&waitpid获取的是已经退出的子进程状态
>>调用的时候已经有退出的,则直接处理
>>调用的时候已经有多个退出的,则只处理一个
下面我们先看看阻塞操作:
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 #include<sys/wait.h> 5 int g_val=0; 6 int a=8; 7 8 int main() 9 { pid_t id=fork(); 10 if(id<0){ 11 perror("fork"); 12 return 0; 13 } 14 else if(id==0){ 15 int cut=5; 16 while(cut){ 17 printf("我是子进程,我还剩下%d秒\n",cut--); 18 sleep(1); 19 } 20 } 21 else 22 { 23 24 pid_t id=waitpid(-1,NULL,0); 25 if(id>0) 26 { 27 printf("获取成功,退出子进程的pid%d\n",id); 28 } 29 else if(id=-1) { 30 printf(" 等待失败\n"); 31 } 32 while(1){ 33 printf("我是父进程,我在等待子进程退出!\n"); 34 sleep(1); 35 36 } 37 return 0; 38 } 39 }
下面是非阻塞操作:
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 #include<sys/wait.h> 5 int g_val=0; 6 int a=8; 7 8 int main() 9 { pid_t id=fork(); 10 if(id<0){ 11 perror("fork"); 12 return 0; 13 } 14 else if(id==0){ 15 int cut=5; 16 while(cut){ 17 printf("我是子进程,我还剩下%d秒\n",cut--); 18 sleep(1); 19 } 20 } 21 else 22 { 23 24 pid_t id=waitpid(-1,NULL,WNOHANG); 25 if(id>0) 26 { 27 printf("获取成功,退出子进程的pid是%d\n",id); 28 } 29 else if(id=-1) { 30 printf(" 等待失败\n"); 31 } 32 while(1){ 33 printf("我是父进程,我在等待子进程退出!\n"); 34 sleep(1); 35 36 } 37 return 0; 38 } 39 }
我们会发现依然产生了僵尸进程。因为当我非阻塞操作的时候,当父进程发现子进程还没有退出,此时它就会去运行自己的代码,但当子进程推出之后,父进程依然再做自己的事情,那么就会忽略子进程的退出,所以为了让非阻塞操作也能完成等待任务,通常我们都要循环进行非阻塞操作。
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 #include<sys/wait.h> 5 int g_val=0; 6 int a=8; 7 8 int main() 9 { pid_t id=fork(); 10 if(id<0){ 11 perror("fork"); 12 return 0; 13 } 14 else if(id==0){ 15 int cut=5; 16 while(cut){ 17 printf("我是子进程,我还剩下%d秒\n",cut--); 18 sleep(1); 19 } 20 } 21 else 22 { 23 pid_t id; 24 while((id=waitpid(-1,NULL,WNOHANG)==0)) 25 { 26 printf("有子进程,但是还没退出!\n"); 27 sleep(1); 28 } 29 printf("子进程已经退出,退出子进程的pid是%d\n",id); 30 while(1) 31 { 32 printf("我是父进程,我正在等待子进程退出!\n"); 33 sleep(1); 34 } 35 return 0; 36 } 37 }
我们再看就没有僵尸进程了
获取子进程status
wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
如果传递NULL,表示不关心子进程的退出状态信息。
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):解释上图:
- 在status的低16比特位当中,高8位表示进程的退出状态,即退出码。进程若是被信号所杀,则低7位表示终止信号,而第8位比特位是core dump标志。
我们通过一系列位操作,就可以根据status得到进程的退出码和退出信号。
exitCode = (status >> 8) & 0xFF; //退出码 exitSignal = status & 0x7F; //退出信号
对于此,系统中提供了两个宏来获取退出码和推出信号。
- WIFEXITED(status):用于查看进程是否是正常退出,本质是检查是否收到信号。
- WEXITSTATUS(status):用于获取进程的退出码
exitNormal = WIFEXITED(status);//是否正常退出 exitCode = WEXITSTATUS(status);//获取退出码
需要注意的是,当一个进程非正常退出时,说明该进程是被信号所杀,那么该进程的退出码也就没有意义了。
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<unistd.h> 4 #include<sys/wait.h> 5 int main() 6 { 7 pid_t id=fork(); 8 if(id<0) 9 { 10 perror("fork"); 11 return 0; 12 }else if(id==0) 13 { 14 sleep(5); 15 exit(99); 16 }else 17 { 18 int status; 19 pid_t ret=waitpid(-1,&status,0); 20 if(ret<0) 21 { 22 perror("waitpid"); 23 } 24 else if(ret>0) 25 { 26 printf("子进程已经退出,退出子进程的pid是%d,退出码是%d,退出信号是%d\n",ret,WEXITSTATUS(status),WIFEXITED(status)); 27 }else 28 { 29 printf("当前没有子进程退出\n"); 30 } 31 while(1) 32 { 33 printf("我是父进程,我在等待\n"); 34 sleep(1); 35 } 36 } 37 return 0; 38 }
问:一个进程退出的时候,父进程会拿到退出码和退出信号,那到底先看谁呢?
- 一旦进程出现异常,只关心退出信号,退出码没有任何意义。