目录
进程终止
进程退出有三种情况
- 代码运行完毕,结果正确
- 代码运行完毕,结果不正确
- 代码异常终止
进程常见退出方法
正常终止(可以通过 echo $? 查看进程退出码)
- 从main返回
- 调用exit
- _exit
异常退出
- ctrl+c,信号终止 kill -9
_exit函数和exit函数的区别
_exit函数
#include <unistd.h>void _exit(int status);参数: status 定义了进程的终止状态,父进程通过 wait 来获取该值
exit函数
#include <unistd.h>void exit(int status);
这两者之间属于调用与被调用的关系,exit最后也会调用_exit,但在调用_exit之前,还做了其他工作,如下为exit函数的执行流程:
- 执行用户通过atexit或on_exit定义的清理函数
- 关闭所有打开的流,所有的缓存数据均被写入
- 调用_exit
进程等待
什么是进程等待
通过系统调用wait/waitpid,来进行状态检测与回收的功能
为什么需要进程等待?
- 子进程退出,父进程如果不管不顾,可能会造成僵尸进程的问题,僵尸进程无法被杀死,因此只有通过进程等待来杀掉该进程,进而解决内存泄漏问题。
- 我们需要通过进程等待,获得子进程的退出情况(任务完成的怎么样了)。
进程等待的方法
wait方法
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status)
返回值:
等待任意一个进程,有进程退出就返回,成功返回被等待进程id,失败返回-1;
参数:
输出型参数,获取子进程退出状态,不关心则可以设置称为NULL
如果子进程不退出,父进程默认在wait的时候,调用这个系统调用的时候,也就不返回,默认叫做阻塞状态。
waitpid方法
pid_ t waitpid(pid_t pid, int *status, int options);返回值:当正常返回的时候,waitpid返回收集到的子进程的进程ID;如果设置了WHOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;如果调用中出错,则返回-1,这时errono会被设置成相应的值,以指示错误所在;参数:pid:Pid=-1,等待任一个子进程。与wait 等效。Pid>0.等待其进程ID 与 pid 相等的子进程。status:WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)这个相当于提取status的后七位进行判断,等价于 !status&0x7F 。WEXITSTATUS(status): 若WIFEXITED 非零,提取子进程退出码。(查看进程的退出码)options:WNOHANG: 若pid 指定的子进程没有结束,则 waitpid() 函数返回 0 ,不予以等待。若正常结束,则返回该子进程的ID默认0,阻塞等待子进程
获取子进程status
wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
status是一个整数,是32位,我们只考虑它的低16位。
- 正常终止的情况下,低16位的前8位标识进程退出码
- 被信号所杀的情况下,低7位标识终止信号
进程替换
替换函数
其中有六种以exec开头的函数,统称exec函数:
#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
- 函数名带l的表示一个列表,参数以列表的形式填入
int execlp(const char *file, const char *arg, ...);
- 函数名带p的,表示PATH,execp自己会在默认的PATH环境变量中查找,传参的时候只需要传入要执行的文件就可以了
int execle(const char *path, const char *arg, ...,char *const envp[]);
- e表示enviroment环境变量,需要传入环境变量
int execv(const char *path, char *const argv[ ]);
- v可以理解为vector,与l不同的是第二个参数传入字符串指针数组
int execvp(const char *file, char *const argv[ ]);
int execve(const char *path, char *const argv[ ], char *const envp[]);
这里总结一下:
- l(list):表示参数使用列表
- v(vector):表示参数使用数组
- p(path):有p自动搜索环境变量PATH
- e (env):表示自己维护环境变量
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序,当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序启动例程开始执行,调用exec并不创建新的进程,所以调用exec前后该进程的id并未改变。
TIPS:
- 程序替换成功之后,exec*后续的代码不会被执行,替换失败,才执行原本后续的代码,exec*函数只有失败返回值,没有成功返回值
- Linux中形成的可执行文件被称为ELF文件,可执行文件的表头指明了程序执行入口地址
- makefile一次要形成多个可执行文件可以引入一个伪目标all,如下:
.PHONY:all
all:otherExe mycommand
otherExe:otherExe.cpp
g++ -o $@ $^ -std=c++11
mycommend:mycommand.c
gcc -o $@ $^ -std=c99
.PHONY:clean
clean:
rm -f otherExe mycommand
环境变量在进行进程替换的时候不会被替换,而是继承下去,父进程所包含的环境变量,子进程会全部继承,子进程新增的环境变量,父进程并不会拥有。