目录
进程等待是什么?
为什么要进程等待?
如何进程等待?
wait
阻塞等待
进程等待是什么?
- 进程终止会把进程退出的数据(退出码和退出信号)存放到进程的PCB中保存下来,让父进程进行等待。
- 任何一个子进程在退出时,默认情况下,必须被父进程等待。
- 任何子进程,在退出的情况下马,一般必须要被父进程进行等待。
- 3号手册是库函数
- 2号手册是系统调用函数
若这个子进程的父进程被杀死或父进程不存在,则这个子进程被称为孤儿进程。- 孤儿进程会被OS领养,所以严格来讲父进程也存在,父进程就是OS。
子进程在退出的时候,如果父进程不管不顾(父进程不等待)。
子进程退出之后,就处于Z状态(僵尸状态),一直处于Z状态。
很多进程处于Z状态且一直处于Z状态,进程的PCB存在非常大的占空间,非常大的结构体对象,则会造成内存泄露问题。
为什么要进程等待?
❓为什么要进程等待
- 为了获得子进程的退出信息(子进程的任务执行的结果怎么样用户需要知道)
- 防止内存泄露
- 父进程通过等待,解决子进程退出的僵尸问题,回收系统资源(一定要考虑的)
- 获取子进程的退出信息,知道子进程是因为什么原因退出的(可选功能)
- 后面有一些不用等待的场景,后面谈。
如何进程等待?
- 父进程在子进程运行期间一般都在等待。
- 父进程在子进程运行的期间也可以做别的事情,一般都让其等待。
- 父进程等待子进程用系统调用函数
- wait & waitpid
- man 2 wait/waitpid
- 系统调用函数作用:等待一个子进程的状态发生变化
- #include <sys/types.h> #include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
wait()
和waitpid()
是 Unix/Linux 系统调用,用于等待一个或多个子进程的终止。成功这些函数返回一个进程pid,表示已终止的子进程的pid。如果出错,则返回-1。- 参数后面也会详细讲解和举例。参数暂时设为NULL。
在Unix和Linux编程中,
wait
(或更常见的waitpid
)函数用于使父进程等待一个或多个子进程结束,并获取其结束状态。这些函数的返回值和参数在不同的上下文中有所不同,但我会为你解释它们的基本含义。waitpid 函数
waitpid
函数的原型如下:
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
参数:
pid
:要等待的子进程的ID。如果pid
小于-1,则等待任何组ID等于abs(pid)
的子进程。如果pid
等于-1,则等待任何子进程。如果pid
为0,则等待与调用进程同组的任何子进程。如果pid
大于0,则等待进程ID等于pid
的子进程。status
:一个指向整数的指针,该整数将接收关于子进程状态的信息。你可以使用宏(如WIFEXITED
,WEXITSTATUS
,WIFSIGNALED
,WTERMSIG
等)来检查这个状态值。options
:影响函数行为的选项。常见的选项有WNOHANG
(如果子进程没有结束,则立即返回),WUNTRACED
(如果子进程因信号而停止,则也返回),以及WCONTINUED
(如果子进程因SIGCONT
信号而恢复,则也返回)。返回值:
- 如果成功,
waitpid
返回等待的子进程的PID。- 如果设置了
WNOHANG
并且没有子进程可用,则返回0。- 如果发生错误,则返回-1,并设置
errno
以指示错误。wait 函数
wait
函数是waitpid
函数的一个特例,它仅等待任何子进程结束,而不提供额外的选项。其原型如下:
#include <sys/wait.h>
pid_t wait(int *status);
这个函数的参数和返回值与
waitpid
类似,但没有pid
和options
参数。总的来说,
wait
和waitpid
函数用于在父进程中同步子进程的结束,并获取其状态信息。这在需要处理子进程输出、检查其退出状态或确保子进程正确结束的场景中非常有用。
wait
- 请看下面代码☞☞
- 父进程等待子进程耗费的时间❓
- fork之后创建子进程,父子进程是同时执行各自的任务吗❓
- main函数没有return或者exit也会终止吗❓
- 子进程运行5ms退出,进程终止了
- 父进程在子进程运行5ms是也sleep5ms,子进程终止之后,继续sleep5ms
- 这里就存在一个5ms的窗口时间:子进程已经终止了,父进程还在休眠,短暂的看到子进处于僵尸状态,Z状态。
- 当父进程结束休眠,等待子进程,可以看到子进程的僵尸状态消失了且PCB被回收了。
- 以上就证明了等待是会解决子进程的僵尸问题的
- 等待的本质就是:获取子进程退出信息(退出码&退出信号)并让OS把PCB释放掉(就是把子进程的Z状态☞X状态)
myprocess.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<string.h>
4 #include<stdlib.h>
5 #include<sys/types.h>
6 #include<sys/wait.h>
7
8 void ChildRun()
9 {
10 int cnt = 5;
11 while(cnt--)
12 {
13 printf("I am child,pid: %d,ppid: %d,cnt: %d\n",getpid(),getppid(),cnt);
14 sleep(1);
15 }
16 }
17
18 int main()
19 {
20 printf("I am father,pid: %d,ppid: %d\n",getpid(),getppid());//父进程
21 pid_t id = fork();
22 if(id == 0)//child子进程
23 {
24 //子进程循环运行
25 ChildRun();
26 printf("Child quit...\n");
27 exit(0);//终止进程,子进程直接僵尸
28 }
29 //子5s
30 //父10s
31 //father父进程,父进程在子进程运行期间先休眠10s再等待子进程....
32 sleep(10);
33 pid_t rid = wait(NULL);
34 if(rid > 0)
35 {
36 printf("wait success,rid: %d\n",rid);
37 }
38 sleep(3);
39 printf("father quit ... \n");
40 }
阻塞等待
- 我们修改以下代码,请看☞☞
- 父进程在子进程运行期间5ms,一直在等待,没有做任何其他的事情。
- 只有当子进程终止时(状态变化S☞☞Z时),父进程通过系统调用接口wait等待子进程,拿到子进程的PCB中的退出信息,告诉OS释放掉PCB。
- 子进程没有终止之前,父进程一直处于阻塞等待中........
- 子进程本身就是软件,父进程的本质时在等待某种软件条件就绪......(子进程终止,状态从S☞☞Z)
- 前面我们也讲过阻塞状态,是某个进程等待硬件资源就绪,等待被调度,此进程的PCB处于该硬件资源的等待队列中,该进程处于阻塞状态。(类似理解)
- 一个进程等待某种资源就绪
- 硬件资源就绪
- 软件条件就绪
如何理解父进程阻塞等待子进程条件就绪呢❓
- 阻塞等待子进程
- 父进程不在被调度,父进程的状态设为S状态。(休眠状态)
- 父进程的PCB被链入到子进程的等待队列中。
- 一旦子进程终止.....
- OS就把等待队列中的父进程唤醒,父进程的状态设为R状态(运行状态)。
- 继续向后执行。
- 注意❗无论是硬件还是软件在操作系统OS中都是数据结构的对象,一切皆对象。(这个在后面C++中也会讲到)
myprocess.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<string.h>
4 #include<stdlib.h>
5 #include<sys/types.h>
6 #include<sys/wait.h>
7
8 void ChildRun()
9 {
10 int cnt = 5;
11 while(cnt--)
12 {
13 printf("I am child,pid: %d,ppid: %d,cnt: %d\n",getpid(),getppid(),cnt);
14 sleep(1);
15 }
16 }
17
18 int main()
19 {
20 printf("I am father,pid: %d,ppid: %d\n",getpid(),getppid());//父进程
21 pid_t id = fork();
22 if(id == 0)//child子进程
23 {
24 //子进程循环运行
25 ChildRun();
26 printf("Child quit...\n");
27 exit(0);//终止进程,子进程直接僵尸
28 }
29 //father
30 //父进程,父进程在子进程运行期间5ms一直在等待....
31 pid_t rid = wait(NULL);
32 if(rid > 0)
33 {
34 printf("wait success,rid: %d\n",rid);
35 }
36 sleep(3);
37 printf("father quit ... \n");
38 }
🙂感谢大家的阅读,若有错误和不足,欢迎指正。下篇讲解重点waitpid&非阻塞等待。