序言:
之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。因此,为了解决这个问题,就需要用到有关 “进程等待” 的基本知识!!!
(一)进程的等待必要性
进程等待通常是指父进程等待子进程的执行完成。当一个进程创建了一个子进程,并希望在子进程执行完成后再继续执行自己的任务时,父进程需要等待子进程的完成。
以下是一些常见的原因和情况,需要进程等待:
- 避免僵尸进程:如果父进程不等待子进程完成而直接退出,子进程可能会成为僵尸进程。僵尸进程是已经完成执行但尚未被父进程回收的子进程。为了避免僵尸进程的产生,父进程需要等待子进程完成并回收子进程的资源
- 父子进程间通信:在使用fork()系统调用创建子进程时,父进程通常需要等待子进程完成,以确保获取子进程的执行结果或处理子进程的退出状态;
- 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
因此,我们可以得出进程等待就是:
- 通过系统调用,获取子进程退出码或者退出信号的方式,顺便释放内存问题
(二)进程等待的方法
进程等待可以通过操作系统提供的函数来实现,主要有以下两种方法来进程等待操作:
- waitpid函数:等待指定的子进程完成。可以设置选项参数来指定等待条件。
- wait函数:等待任意一个子进程完成并获取其退出状态。
1、wait方法
遇到新的函数,如果不认识,就先去系统中查询一下:
【说明】
- 返回值:成功返回被等待进程pid,失败返回-1。
- 参数:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
接下来,通过代码简单的
int main() { pid_t id = fork(); if(id == 0) { //子进程 int cnt = 5; while(cnt) { printf("我是子进程,我还活着呢,我还有%dS, pid: %d, ppid%d\n", cnt --, getpid(), getppid()); sleep(1); } exit(0); } sleep(10); // 父进程 pid_t ret_id = wait(NULL); printf("我是父进程,等待子进程成功, pid: %d, ppid: %d, ret_id: %d\n",getp id(), getppid(), ret_id); sleep(5); }
输出显示:
【解释】
- 上述代码是一个简单的示例,展示了父进程创建子进程,并使用
wait
函数等待子进程完成。
进阶着又有一个问题:那就是父进程在 wait的时候,如果子进程没有退出,那么父进程在干什么呢?
我们把代码改一下看输出结果。代码如下:
int main() { pid_t id = fork(); if(id == 0) { //子进程 int cnt = 5; while(cnt) { printf("我是子进程,我还活着呢,我还有%dS, pid: %d, ppid%d\n", cnt --, getpid(), getppid()); sleep(1); } exit(0); } // 父进程 pid_t ret_id = wait(NULL); printf("我是父进程,等待子进程成功, pid: %d, ppid: %d, ret_id: %d\n",getp id(), getppid(), ret_id); sleep(5); }
输出显示:
2、waitpid方法
wait 和 waitpid 的头文件一样。具体如下:
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
1、当正常返回的时候waitpid返回收集到的子进程的进程ID;
2、如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
3、 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
1. Pid=-1,等待任一个子进程。与wait等效。
2. Pid>0.等待其进程ID与pid相等的子进程。
status:
1.WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
2.WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
1.WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
- 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
- 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
- 如果不存在该子进程,则立即出错返回。
(三)获取子进程status
- wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
- 如果传递NULL,表示不关心子进程的退出状态信息。
- 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
- status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位)
具体可以向下述这样理解:
判断一个进程是否正常退出,只需要判断是否接收到进程终止信号。大于0,异常退出,有进程终止信号;等于0,正常退出。
代码演示:
int main() { pid_t id = fork(); if(id == 0) { //子进程 int cnt = 5; while(cnt) { printf("我是子进程,我还活着呢,我还有%dS, pid: %d, ppid: %d\n", cnt--, getpid(), getppid()); sleep(1); } exit(111); } // 父进程 int status =0 ; pid_t ret_id = waitpid(id, &status, 0); printf("我是父进程,等待子进程成功, pid: %d, ppid: %d, ret_id: %d,child exit code: %d, child exit signal: %d\n"\ ,getpid(), getppid(), ret_id,(status>>8)&0xFF, status & 0x7F); sleep(2); }
输出结果:
1、进程的阻塞等待方式
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.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; }
输出展示:
此运行结果是子进程正常退出的场景
2、进程的非阻塞等待方式
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 #include <sys/types.h> 5 #include <sys/wait.h> 6 7 int main() 8 { 9 pid_t pid; 10 pid = fork(); 11 if(pid < 0){ 12 printf("%s fork error\n",__FUNCTION__); 13 return 1; 14 }else if( pid == 0 ){ //child 15 printf("child is run, pid is : %d\n",getpid()); 16 sleep(5); 17 exit(1); 18 } else{ 19 int status = 0; 20 pid_t ret = 0; 21 do 22 { 23 ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待 24 if( ret == 0 ){ 25 printf("child is running\n"); 26 } 27 sleep(1); 28 }while(ret == 0); 29 if( WIFEXITED(status) && ret == pid ){ 30 printf("wait child 5s success, child return code is :%d.\n",WEXITS TATUS(status)); 31 }else{ 32 printf("wait child failed, return.\n"); 33 return 1; 34 } 35 } 36 return 0; 37 } 38
输出展示:
调用 waitpid 函数(非阻塞,WNOHANG)父子进程交替打印进行各自的循环,大概 5s 之后子进程, 可以看到子进程由 S+ 变成 Z+。
若WIFEXITED(status)为真---->进程正常退出
若WEXITSTATUS(status)>0---->WEXITSTATUS(status)表示子进程的退出码
总结
以上便是关于进程等待的全部知识了。感谢大家的观看与支持!!!