目录
进程等待
进程等待的必要性
进程等待的方法
wait方法
等待一个进程(阻塞等待)
waitpid方法
任意等待多个进程(阻塞等待)
父进程获取子进程的退出信息
非阻塞轮询等待
进程等待
进程等待的必要性
- 之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
- 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
- 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
- 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
总结:进程等待就是父进程解决僵尸进程的
进程等待的方法
父进程通过wait/waitpid系统调用来实现
wait方法
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
- 返回值:成功返回被等待进程pid,失败返回-1。
- 参数:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
等待一个进程(阻塞等待)
#include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<sys/wait.h>
5 #include<stdlib.h>
6 void Worker()
7 {
8 int cut =5 ;
9 while(cut)
10 {
11 printf(" am a child process; p id : %d ppid : %d ,cut : %d \n",getpid( ),getppid(),cut--);
12 sleep(1);
13 }
14 }
15 int main()
16 {
17 pid_t id = fork();
18 if(id==0)
19 {
20 //child
21 Worker();
22 exit(0);
23 }
24 else{
25 //father
26 sleep(7);
27 printf("wait before\n");
28 pid_t rid = wait(NULL);
29 printf("wait after\n");
30 if(rid == id )
31 {
32 printf("wait success , pid %d rpid:%d\n",getpid(),rid);
33 }
34 sleep(2);
35 }
36 return 0;
37 }
总结:
- 父进程通过wait子进程可以回收子进程的僵尸状态
- 如果子进程没有退出,父进程必须进行阻塞等待(等待软件资源就绪),直接到子进程僵尸,wait回收,返回。
- 父子进程水仙运行我们不直到是由于调度器决定的,但是我们可以很清楚的直到是父进程最后退出的。
waitpid方法
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int* status, int options);
返回值
- 当正常返回的时候waitpid返回收集到的子进程的进程ID;
- 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
- 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数
pid
- pid=-1,等待任一个子进程。与wait等效。
- pid>0.等待其进程ID与pid相等的子进程。
status
- WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
- WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
- 0:表示父进程以阻塞的方式等待子进程,即子进程如果处在其它状态,不处在僵尸状态(Z状态),当子进程运行结束,操作系统会检测到,把父进程重新唤醒,然后回收子进程;
- WNOHANG:非阻塞轮询等待,若 pid 指定的子进程没有结束,处于其它状态,则 waitpid() 函数返回0,不予等待。若正常结束,则返回该子进程的 ID;
任意等待多个进程(阻塞等待)
#include <sys/types.h>
5 #include <sys/wait.h>
6
7 #define N 5
8 void Worker(int i)
9 {
10 int cnt = 5;
11 while(cnt--)
12 {
13 printf("I am child, pid:%d, ppid:%d, %d\n", getpid(), get ppid(), i);
14 sleep(1);
15 }
16 return;
17 }
18 int main()
19 {
20 int i=0;
21 for( i = 0; i < N; i++)
22 {
23 //创建一批子进程
24 pid_t id = fork();
25 if(id == 0)
26 {
27 // child
28 Worker(i);
29 exit(0);
30 }
31 // father
32
33 }
34 //等待子进程
35 int j=0;
36 for(j=0;j<N;j++)
37 {
38 pid_t rid = waitpid(-1,NULL,0);
39 if(rid>0)
40 {
41 printf("wait %d success\n",rid);
42 }
43 }
父进程获取子进程的退出信息
在上篇文章进程的终止我们提到,创建进程肯定是完成某一项任务的;任务的结果就三种情况:
- 代码运行完毕,结果正确
- 代码运行完毕,结果不正确
- 代码出异常了,被操作系统发送信号终止
因此,作为父进程通过等待获取这个退出信息;这个信息就是在传入的参数status中被写入。
我们可以看到这个参数的类型是一个整形指针,需要我们在父进程执行的代码块中定义一个整形变量,将这个整型变量取地址作为wait/waitpid的参数交给操作系统,操作系统会将子进程的退出码填充给这个参数。
wait
和waitpid
都有一个status
参数,该参数是一个输出型参数,由操作系统填充。如果传递 NULL,表示不关心子进程的退出状态信息。
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
status
不能简单的当做整形来看待,可以当做位图来看待,具体细节如下图(只需要关注 status 低16比特位)
退出的情况可以总结为两种,正常终止(0:代表成功;非0:代表失败),异常终止;
前7位代表异常终止时的终止信号,后8位代表我们正常终止的退出状态;
操作系统没有0号信号,因此,如果低七位是0说明子进程没有收到任何信号。
我们要想拿到这两个数字就要进行位运算
- 退出码:exit_code = (status>>8)&0xFF
- 信号码: sig_code= (status)& 0x7F
这样的方式非常的麻烦;我们可以使用系统中提供的转化方法获取退出码。
- WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
- WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
#include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6 void Worker()
7 {
8 int cnt = 5;
9 while(cnt)
10 {
11 printf("I am child process,pid: %d,ppid : %d\n,cnt: %d",getpid(),getppid(),cnt-- );
12 }
13 }
14 int main()
15 {
16 pid_t id = fork();
17 if(id == 0)
18 {
19 //child
20 Worker();
21 // exit(0);
22 exit(10);
23 }
24 else{
25 sleep(10);
26 //father
30 int status = 0;
31 printf("wait before\n");
32 // pid_t rid = wait(NULL );
33 pid_t rid = waitpid(id , &status ,0);
34
35 printf("wait after\n");
36 if(rid==id)
37 {
38 printf("wait success,pid: %d",getpid());
39 }
40 // printf("%d\n",status);
41 if(WIFEXITED(status))
42 {
43 printf("child process normal quit , exit code : %d \n",WEXITSTATUS(status));
44 }
45 else
46 {
47 printf("child process quit except!!!\n");
48 }
49 }
50 return 0;
51 }
非阻塞轮询等待
前面说过如果子进程没有进入僵尸状态,父进程什么也不做就一直阻塞等待子进程。采用非阻塞的方式轮询等待的方式,子进程在没有进程僵尸状态的区间父进程可以进行其他的事情。
#include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6 void Worker()
7 {
8 int cnt =3 ;
9 while(cnt--)
10 {
11 printf("am a child process pid : %d ppid %d num %d \n",getpid(),getppid(),cnt);
12 sleep(2);
13 }
14 }
15 int main()
16 {
17 pid_t id = fork();
18 if(id==0)
19 {
20 //child
21 Worker();
22 exit(0);
23 }
24 else
25 {
26 //father
27 while(1)
28 {
29 pid_t rid = waitpid(id,NULL,WNOHANG );
30 if(rid == 0)
31 {
32 printf("wait success,but process no quit\n");
33 printf("father process , to do athor thing.......\n");
34 sleep(1);
35 }
36 else if(rid < 0)
37 {
38 printf("wait fail\n");
39 break;
40 }
41 else
42 {
43 printf("wait success\n");
44 break;
45 }
46 }
47 }
48
49 return 0;
50 }
第三个参数设置为WNOHONG后,可以通过循环语句控制父进程轮询等待;在子进程未就绪期间父进程可以执行其他的一些简单代码。
今天对Linux下进程的等待的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!!