当一个进程终止的时候,它的资源,比如说PCB,数据等不会被立马清理掉。它会保持在已经终止的状态,这种状态称为“僵尸状态”,直到被父进程确认。父进程wait,即父进程向内核确认子进程已经终止,可以为子进程“收尸”了,内核会把子进程的退出信息传给父进程,然后清理掉子进程的资源,这个时候子进程才算真正地终止了!
总结:
一个进程可以通过调用wait函数等待子进程。wait函数是系统调用函数。
#include
#include
pid_t wait(int *status);
返回值:返回被等待进程的pid,如果等待失败,返回-1。
参数:输出型参数,可以获取子进程的退出状态,如果不需要获取子进程的退出状态,则设置为NULL。
测试:
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <sys/wait.h>
5 #include <stdlib.h>
6
7 int main(void)
8 {
9 pid_t id = fork(); //创建子进程
10 if(id == 0)
11 {
12 //child
13 //执行5秒
14 int cnt = 5;
15 while(cnt)
16 {
17 printf("child[%d] , cnt:%d\n", getpid(), cnt);
18 sleep(1);
19 cnt--;
20 }
21 exit(EXIT_SUCCESS);
22 }
23 sleep(10);
24 pid_t ret = wait(NULL);
25 if(ret > 0)
26 {
27 //wait success, ret is pid;
28 printf("father wait child[%d] success\n", ret);
29 }
30 else{
31 //wait failed.
32 printf("father wait failed\n");
33 }
34 return 0;
35 }
现象:子进程执行5秒后,终止了,但是内核没有立马清理掉它的资源,所以此时是僵尸状态,再过了5秒之后,父进程休眠完毕,然后等待子进程,确认子进程已经终止,返回子进程的pid,然后内核开始清理子进程资源,子进程真正地终止了,又过了5秒后父进程也终止了。
通过wait函数的输出型参数可以获得子进程的退出信息。
waitpid函数也可以使得父进程等待子进程
#include
#include
pid_t waitpid(pid_t pid, int *status, int options);
先不关心第二个和第三个参数,第二个参数可以设置为NULL,第三个参数暂时设置为0。
第一个参数:
1、如果第一个参数pid传的是某个具体的进程的进程ID,表示等待该指定进程
2、如果第一个参数pid传的是-1,表示等待父进程的任意子进程。
第三个参数:
wait(&status) 等价于 waitpid(-1, &status, 0)
【注意事项】
在上述并没有具体解释参数status的作用。
status是一个指向整形的指针。但是一个进程的退出信息那么多,怎么可能会那么简单地用一个整型就知道进程的退出信息了呢?实际上,并不是简单地看待status指向的整形,而是当作位图来看,一个整型有32位,这样就可以全面地描述被等待进程的退出信息了。
只用研究低16个比特位。
进程退出的情况有四种:
1、正常退出(自愿,代码执行完,结果正确)
2、错误退出(自愿,代码执行完,结果不正确)
3、异常退出(非自愿,代码未执行完,退出码无意义)
4、被其他进程终止(非自愿,代码未执行完,退出码无意义)
这四种情况,可以按照进程是否收到信号来分类,第一种和第二种进程未收到信号,第三和第四种进程收到信号。
当被等待进程不是被信号所终止时,低8位全是0,而次低8位则是被等待进程的退出码。
当被等待进程是被信号所终止时,低7位表示被等待进程收到的信号。
如果进程是正常终止,如何显示地知道退出码?
如果进程是收到信号而终止,如何知道它收到了什么信号?直接就是低7位表示的是进程收到的信号,如果是非法的信号,说明它没有收到信号,这个值是无效的。
测试:
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <sys/wait.h>
5 #include <stdlib.h>
6
7 int main(void)
8 {
9 pid_t id = fork(); //创建子进程
10 if(id == 0)
11 {
12 //child
13 //执行5秒
14 int cnt =7;
15 while(cnt)
16 {
17 printf("child[%d] , cnt:%d\n", getpid(), cnt);
18 sleep(1);
19 cnt--;
20 }
21 exit(12);
22 }
23 sleep(10);
24 int status;
25 pid_t ret = waitpid(id, &status, 0);
26 if(ret > 0)
27 {
28 //wait success, ret is pid;
29 printf("father wait child[%d] success\n", ret);
30 }
31 else{
32 //wait failed.
33 printf("father wait failed\n");
34 }
35 printf("get a exit num : %d\n, get a single:%d", (status >> 8) & 0XF FFF, status & 0XFFFF);
36 sleep(2);
37 return 0;
38 }
当然,还可以不需要进行位运算,系统提供了两个宏,可以得到退出信息
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <sys/wait.h>
5 #include <stdlib.h>
6
7 int main(void)
8 {
9 pid_t id = fork(); //创建子进程
10 if(id == 0)
11 {
12 //child
13 //执行5秒
14 printf("i am child precess\n");
15 sleep(5);
16 exit(12);
17 }
18 sleep(3);
19 int status;
20 printf("father begin wait\n");
21 pid_t ret = waitpid(id, &status, 0);
22 if(ret > 0)
23 {
24 //wait success, ret is pid;
25 printf("father wait child[%d] success\n", ret);
26 }
27 else{
28 //wait failed.
29 printf("father wait failed\n");
30 }
31 if(WIFEXITED(status))
32 {
33 printf("exit code : %d\n", WEXITSTATUS(status));
34 }
35 else{
36 printf("get a signal\n");
37 }
38 sleep(2);
39 return 0;
40 }