Open chenpengcong opened 6 years ago
我们知道当执行系统调用或库函数调用出现错误时,errno的值将被根据错误类型被置为相应的值(非0),且errno是线程安全的,现在我们来探究errno的本质。
首先,写个demo
#include "errno.h" int main() { errno = 1; return 0; }
对文件进行预处理 $ gcc -E test2.c -o test2.i
$ gcc -E test2.c -o test2.i
预编译后的文件内容
# 1 "test2.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 1 "<command-line>" 2 # 1 "test2.c" # 1 "/usr/include/errno.h" 1 3 4 # 28 "/usr/include/errno.h" 3 4 # 1 "/usr/include/features.h" 1 3 4 # 367 "/usr/include/features.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4 # 410 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4 # 411 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4 # 368 "/usr/include/features.h" 2 3 4 # 391 "/usr/include/features.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 1 3 4 # 10 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/gnu/stubs-64.h" 1 3 4 # 11 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4 # 392 "/usr/include/features.h" 2 3 4 # 29 "/usr/include/errno.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/errno.h" 1 3 4 # 24 "/usr/include/x86_64-linux-gnu/bits/errno.h" 3 4 # 1 "/usr/include/linux/errno.h" 1 3 4 # 1 "/usr/include/x86_64-linux-gnu/asm/errno.h" 1 3 4 # 1 "/usr/include/asm-generic/errno.h" 1 3 4 # 1 "/usr/include/asm-generic/errno-base.h" 1 3 4 # 5 "/usr/include/asm-generic/errno.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/asm/errno.h" 2 3 4 # 1 "/usr/include/linux/errno.h" 2 3 4 # 25 "/usr/include/x86_64-linux-gnu/bits/errno.h" 2 3 4 # 50 "/usr/include/x86_64-linux-gnu/bits/errno.h" 3 4 # 50 "/usr/include/x86_64-linux-gnu/bits/errno.h" 3 4 extern int *__errno_location (void) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__)); # 36 "/usr/include/errno.h" 2 3 4 # 58 "/usr/include/errno.h" 3 4 # 2 "test2.c" 2 # 2 "test2.c" int main() { # 4 "test2.c" 3 4 (*__errno_location ()) # 4 "test2.c" = 1; return 0; }
可以看到errno经过预处理变成了*__errno_location(),表示对__errno_location()函数返回值取地址内容。
*__errno_location()
那么__errno_location()函数内部是怎么实现的呢? 首先我们需要找到__errno_location函数在哪里定义
这里有个小技巧,利用gdb调试查看进入哪个文件
(gdb) s __GI___errno_location () at errno-loc.c:26 26 errno-loc.c: No such file or directory
可以看到__errno_location定义在errno-loc.c文件,且本机没有该文件,这里我们可以到github上查看glibc源码
可以看到errno-loc.c文件内容如下
#include <errno.h> #include <tls.h> int * __errno_location (void) { return &errno; } libc_hidden_def (__errno_location)
__errno_location函数返回errno变量的地址,那么此处的errno又是什么,查看该文件包含的头文件(在glibc/include目录下)
__errno_location
可以看到如下一行
extern __thread int errno attribute_tls_model_ie;
定义了一个整型变量errno
所以__errno_location最终就是返回了一个整型变量的地址
值得注意的是该变量使用了__thread关键字
__thread
查阅维基百科,看到C已经在与语言层面实现了thread local storage (TLS),其中GNU C使用的就是__thread关键字
Tip:除了语言层面支持,pthreads的API也支持使用线程本地存储,详见维基百科
参考: http://blog.csdn.net/js072110/article/details/44855565 https://en.wikipedia.org/wiki/Thread-local_storage
我们知道当执行系统调用或库函数调用出现错误时,errno的值将被根据错误类型被置为相应的值(非0),且errno是线程安全的,现在我们来探究errno的本质。
首先,写个demo
对文件进行预处理
$ gcc -E test2.c -o test2.i
预编译后的文件内容
可以看到errno经过预处理变成了
*__errno_location()
,表示对__errno_location()函数返回值取地址内容。那么__errno_location()函数内部是怎么实现的呢? 首先我们需要找到__errno_location函数在哪里定义
这里有个小技巧,利用gdb调试查看进入哪个文件
可以看到__errno_location定义在errno-loc.c文件,且本机没有该文件,这里我们可以到github上查看glibc源码
可以看到errno-loc.c文件内容如下
__errno_location
函数返回errno变量的地址,那么此处的errno又是什么,查看该文件包含的头文件可以看到如下一行
定义了一个整型变量errno
所以
__errno_location
最终就是返回了一个整型变量的地址值得注意的是该变量使用了
__thread
关键字查阅维基百科,看到C已经在与语言层面实现了thread local storage (TLS),其中GNU C使用的就是__thread关键字
Tip:除了语言层面支持,pthreads的API也支持使用线程本地存储,详见维基百科
参考: http://blog.csdn.net/js072110/article/details/44855565 https://en.wikipedia.org/wiki/Thread-local_storage