目录
引言
进程等待的必要性
见见猪跑:是什么
怎么办
多个子进程时
阻塞等待
非阻塞轮询
参数一:
参数二
进程等待的原理
进程退出相关的宏
第三个参数option(设置等待的方式)
引言
在Linux操作系统中,进程控制是核心功能之一。进程的创建、执行、终止以及资源管理都是操作系统管理任务的重要组成部分。特别是在多进程编程中,进程间的同步和资源回收显得尤为重要。本文将重点介绍Linux系统中进程等待的机制,特别是wait
和waitpid
这两个系统调用。
进程等待的必要性
见见猪跑:是什么
先建立一个多进程的程序,观察现象。
perror:将错误码转化成错误码描述
如果打开文件错误,perror("file:"),那么打印结果是file: No such file or directory
现象
子进程跑完循环之后退出,父进程一直执行。
发现子进程变成了僵尸,状态是Z,后面还跟着defunct备注。
怎么办
处理:wait等待子进程使之从z变成x
头文件在sys/types.h和sys/wait.h
等待成功之后返回pid,等待失败返回-1。子进程未结束返回0(wait默认情况下是阻塞等待)。
wait需要传入一个指针参数(后续讲解)
现象:
子进程由S----Z----正确退出释放。
多个子进程时
创建时,子进程执行结束接着退出,所以父进程执行继续循环,会创建多个子进程。
fork success是父进程的输出,可以看到,父子进程并不是有严格的执行规律
后去全部可以等待成功。
wait等待是一种泛型等待,只要你是父进程的子进程,那么wait就可以去等待。
阻塞等待
如果wait等待的子进程都不结束,那么父进程会一直停留在wait处等待子进程。
因此进程阻塞不只是进程在等待硬件资源的响应,在等待软件资源的响应时,也会造成阻塞!
非阻塞轮询
为了让父进程“有活干”,就不能让父进程一直阻塞在wait处。waitpid()接口可以实现非阻塞轮询。
参数一:
直接传入子进程的pid(限制了等待对象)
参数二
这是一个输出型参数。获取进程的信息是通过status参数获取的
这两个接口的status含义一致
可以观察到status是256,并不是我们所“期待的 1”。
原因
进程的退出状态有三种
当进程异常终止时(没执行完毕 )0-7bit为代表终止信号(signal信号)
信号最大为62,2^6所以用7个bite位表示(低七位)
第八位:这是一个core dump标志,暂时不关心
次低八位:表示退出状态(退出码)
获得信息
exit signal(退出信号):status & 0x7F (7F是0111 1111,按位与可得到低七位信息)
exit code(退出码) : (status >> 8 )& 0xFF (FF是1111 1111 ,按位与 可得到次低八位信息)
不可以建立一个全局变量去获得相关的信息(写时拷贝、进程的独立性)
进程等待的原理
父进程获取子进程的内核pcb,并且将z状态改为x状态(在x状态下进行资源的释放)
为什么不直接去内核数据结构访问呢?父进程必须通过接口去访问子进程的pcb信息,OS不相信任何人
什么时候进程等待失败(不返回pid,返回-1)?只需要让第一个参数pid 随意 加减一个数字,让父子进程不匹配即可。(父进程只能回收自己的子进程)
进程退出相关的宏
WIFEXITED:检测子进程正常退出,返回非零值,否则返回0(wait if exited)
WEXITSTATUS :正常退出时,获取退出码(wait exit status)
注意:前两个宏都得是退出的前两种状况:进程正常退出,而不是被信号所杀。
第三个参数option(设置等待的方式)
这就是我们所提及的非阻塞轮询。
如果第三个参数是0,那么仍然是阻塞轮询。
0:阻塞等待方式
用0的方式本身就会让父进程去等待子进程,让自己进入阻塞状态。
如果第三个参数设置为WNOHANG(wait no hang禁止等待),那么将不会停留在waitpid处。
非阻塞轮询由:非阻塞 + 循环构成
非阻塞ret有三种返回方式(都是立即返回,不会阻塞): 1.pid等待成功返回pid(跳出循环) 2.等待失败返回 -1 (跳出循环) 3.子进程还在运行返回0
阻塞等待
阻塞等待是指父进程在调用wait()或waitpid()函数时,如果没有子进程退出,父进程会暂停执行,直到有子进程退出。这是最常见的等待方式,适用于大多数IO类函数。在阻塞等待期间,父进程处于等待状态,无法执行其他任务。
非阻塞轮询
非阻塞等待是指父进程在调用waitpid()函数时,通过设置WNOHANG选项,即使子进程没有退出,父进程也不会暂停执行,而是立即返回继续执行其他任务。这种方式允许父进程在等待子进程的同时,执行其他操作,从而提高了程序的效率。
无论是阻塞等待还是非阻塞等待,父进程都是最后结束,因为父进程需要等待回收子进程(否则变成孤儿进程)。