我们常常在main函数的最后看到 return 0
问:main函数中的return 与普通函数中的return有什么区别??
答:main函数的return值是进程的退出码
非main函数退出只是函数返回
如:main函数中的 return 0,表示退出当前进程,退出码是0(0表示正常退出)
函数的return 0,表示退出当前函数,函数的返回值是0
目录
一、进程退出
进程退出的时候,会返回一个退出码,这个退出码 代表当前进程的退出状态
每一个退出码对应 不同的退出状态信息
退出码由父进程接收,让父进程了解子进程的退出信息
运行pwd命令本质上都是 创建子进程,然后运行对应的程序,退出码会由bash接收
1、进程退出的场景
- 正常退出,结果正确/不正确
- 代码异常退出
2、进程退出的常见方法
(1) main函数中使用return
int main()
{
return 2;
}
echo $? 打印距离现在最近一次进程的退出码
(2) 调用exit函数
exit函数的参数是 当前进程要返回的退出码,可以在任意位置调用,即便是在普通的函数中也可以调用。(main函数退出的时候,即便不加 return 0,也会隐式调用exit;非main函数不会隐式调用exit函数)
(3) 调用 _exit 函数
_exit函数的参数是 当前进程要返回的退出码,同样可以在任意位置调用,即便是在普通的函数中也可以调用
(4) exit函数和_exit函数的关系
exit 最后也会调用 _exit函数,但exit在中止进程的同时,还会刷新缓冲区、关闭流等
二、进程等待
前面提到这么一个问题
父进程在忙,子进程结束了,但无人回收,这样就造成了“死亡”的子进程一直占用资源
这个时候的子进程被称为“僵尸进程”
为了解决这个问题,最初的思路是:让父进程停下,等待子进程执行完,然后回收子进程
1、进程等待函数waipid
当子进程中出现exit 或者 _exit函数的时候,子进程结束
waitpid 可以接收指定子进程或者任意子进程的退出码
参数:
pid:pid = -1时,表示等待任意一个子进程,和wait等价
pid > 0,等待指定子进程
status:输出型参数,获取子进程的退出状态码
options:
0:阻塞等待,默认行为(也就是干等着子进程运行完,什么都不干)
WNOHANG:非阻塞等待,相当于隔一小段时间去检测子进程是否结束,如果结束了,
则回收子进程;如果没有结束,则继续忙自己的事
返回值:
回收子进程以后,返回收集到的子进程的pid
如果设置了WNOHANG,若检测到子进程还在运行,则返回0
若检测到子进程运行结束,返回子进程的pid
2、关于参数status
status不能简单的当作整型来看,要从二进制的角度来看,32位下,整型转化为二进制有32个bit位
但是我们仅关注低16位
(1) 正常退出时
进程正常退出时,子进程会返回退出码,即退出状态,8~15位记录着正常退出时的退出码,既然是正常退出就不会收到中止信号,所以0~7位都是 0
(2) 异常退出时
进程异常退出时,一般会收到一个中止进程的信号,而且不会执行到return 这句,所以自然就没有退出码,为了知道发生了何种异常,我们使用低 7 位,也就是 0~6 来记录 “中止信号”
注意:core dump是指在进程异常退出的时候,进程会把用户空间的内存数据保存到磁盘文件,文件名为core
(3) 如何取出退出码和中止信号
方式一:
中止信号是低7位,也就是0~6 的位置,我们可以让status按位与0x7F,那么0111 1111 中1对应的位置会被保留 ——》 status & 0x7F
退出码是高8位,我们先将这8位二进制退出码右移8位到 较低的8位,然后像上面那样按位与0xFF
那么1111 1111 中1对应的位置会被保留 ——》 (status >> 8) & 0xFF
方式二:
使用系统提供的宏函数WIFEXITED(status)、 WEXITSTATUS(status)
WIFEXITED(status) :判断进程是否正常退出,如果正常退出,返回true
WEXITSTATUS(status): 返回子进程的退出码
WIFSIGNALED(status):判断子进程是否被信号结束
WTERMSIG(status):获取结束子进程的信号类型
3、进程阻塞等待的方式
第11行,子进程退出,返回退出码为3
第21行,成功回收子进程,打印子进程的退出码,结果为3
4、进程非阻塞等待的方式
第12行,子进程退出,返回退出码为3
第22行,父进程反复检测子进程是否结束运行,若未结束,则打印"child process is running"
(这句话可以替换为父进程要执行的任务)
采用非阻塞等待的方式,在解决僵尸进程的同时,也处理了父进程自己的任务