纯粹技术干货,喜欢水的朋友可能看不进去,准备好了没^_^。
我们在linux下编程时,比如说shell脚本或C程序(包括c++)等,程序退出时都会给操作系统传回一个值做为返回值,此值不能超过255,就此问题咱们深入到内核源码看一下。
程序退出时的参数便是返回值,通常父进程或操作系统会根据此返回值判断子程序执行的结果。
退出分为正常退出和异常退出两类。
正常退出有3种:
return返回
调用C库函数exit
调用系统调用_exit
异常退出两种:
主动调用abort函数
信号终止
下面从头说,这是为后面的结论做铺垫。
_exit函数的接口定义如下:
#include
void _exit(int status)
_exit函数中status参数定义了进程的终止状态,父进程可以通过wait()来获取该状态值。需要注意的是返回值,虽然status是int型,但是仅有低8位可以被父进程所用。所以写exit(-1)结束进程时,在终端执行“$?”会发现返回值是255。
return是一种更常见的终止进程的方法。执行return(n)等同于执行exit(n),因为调用main() 的运行时函数会将main的返回值当作exit的参数。
比如如下代码:
#include
int main() {
exit(88);
}
给操作系统的返回值将是88,执行如下:
或者用return也一样:
首先return并不是系统调用和库函数,是纯粹的C语法关键字。
下面用strace跟踪,证明return确实是调用了exit:
最后调用exit_group(99),其中的99就是return 99。这个为什么不是exit(99)?
首先,我们来分析C库的退出函数exit,代码如下:
void exit (int status) {
__run_exit_handlers (status, &__exit_funcs, true);
}
C库的exit主要用来执行所有注册的退出函数,比如使用atexit或on_exit注册的函数。执行完注册的退出函数后,__run_exit_handlers会调用_exit,代码如下:
void _exit (status)
int status;
{
while (1) {
#ifdef __NR_exit_group
INLINE_SYSCALL (exit_group, 1, status); #endif
INLINE_SYSCALL (exit, 1, status); #ifdef ABORT_INSTRUCTION
ABORT_INSTRUCTION; #endif
}
}
上面的代码很简单,当平台有exit_group时,就调用exit_group,否则就调用exit,这两个指的都是系统调用,并不是C库中的exit。从Linux内核2.5.35版本以后,为了支持线程,就有了exit_group。这个系统调用不仅仅是用于退出当前线程,还会让所有线程组的线程全部退出。
如果是shell相关的编程,shell可能需要获取进程的退出值,那么退出值最好不要大于128。如果退出值大于128,会给shell带来困扰。POSIX 标准规定了退出状态及其含义如下:
127以内是命令本身相关的返回值,128以上则是和操作系统相关。
1~125之间是由各个命令自己定义的。
比如脚本中用exit 传递返回值,exit 133表示返回值是133,可以通过$?变量查看返回值。
8位二进制可表示的范围是0~255,超过范围后就会回卷。比如,返回值若为256,二进制就是100000000,那么由于只用低8位,那么返回值则为0.
返回值若为257,二进制就是100000001,那么由于只用低8位,那么返回值则为1.
128以上的返回值,是由内核来管理的,比如用中断信号终止一个程序运行,该程序的返回值就是130,如下:
若用terminate信号杀掉程序,返回值为143.
模拟以上情况,可在执行sleep 99后,在另一窗口向这个程序发15信号即可。
c程序也一样,以下代码:
用strace跟这个程序,运行到最后,如图中,exited with 0,这说明确实以低8位做为返回值。
下面就不跟踪了,直接运行看结果。
看过本文的朋友,给大伙儿拜年啦,过年好!
领取专属 10元无门槛券
私享最新 技术干货