3. 进程等待
1. 是什么
通过系统调用 wait/waitpid 对子进程的退出状态进行检测和回收的功能
2. 为什么
僵尸进程无法杀死,通过进程等待来杀掉它,进而解决内存泄漏的问题
(一)进程等待的方法
a. wait :
代码
wait :
等待任意一个子进程
注意:
如果子进程不退出,父进程调用wait的时候,也不会返回,父进程会一直等待子进程,默认状态为阻塞状态
b. waitpid
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相等的子进程
2. status:
- WIFEXITED(status): 若为正常终止子进程返回的状态,则为真(查看进程是否是正常退出)
- WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码(查看进程的退出码)
3. options:
等待方式
options = 0 , 进行阻塞等待 (父进程会一直等待它的子进程)
options = WNOHANG 非阻塞轮询
- WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID 。若函数调用失败,则返回值小于0
注意:
- 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退
出信息
- 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞
- 如果不存在该子进程,则立即出错返回
(二)获取子进程 status
wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充
- 如果传递NULL,表示不关心子进程的退出状态信息;否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程
- status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特
位)
4. 进程程序替换
(一)替换原理
- 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数
- 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变
- 发生程序替换,环境变量的信息不会被替换
单进程版程序替换
注意:
- 替换的是数据和代码,没有创建新的进程PCB
- 程序替换成功之后,exec函数后面的代码不会被执行;替换失败,exec函数后面的代码继续被执行(成功没有返回值,失败才有返回时)
(二)替换函数
有六种以exec开头的函数,统称exec函数
#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
execl 举例
l : 像列表一样列出来 (arg 参数)
path : 代表所执行的进程的路径 , arg : 代表要执行的进程以及指令(以 NULL 结尾)
execlp 举例
p : 在PATH 路径下找需要的进程
execle 举例
e : 必须自己传环境变量参数
注意:
如果我们传入自定义的环境变量参数,则替换进程的环境变量直接被参数覆盖
execv 举例
v : 像 vector一样存储(以 NULL 结尾)
(三)函数解释
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回 ;如果调用出错则返回-1
所以exec函数只有出错的返回值而没有成功的返回值