Jul
27
全局变量errno: Thread-Local Storage
在ISO C规范中有有一个很诡异的东西,那就是传说中的errno,一个有左值的int。当库函数出错的时候,它很可能会被设置为非0值;且没有任何一个库函数或系统调用会把它置为0。
刚接触到errno,可能会认为这是个在errno.h中用
幸而,ISO C并没有规定,errno必须是一个int,如果看errno的manpage (man errno) 会看到:实际上GNU的glibc也就是这么实现的,(通常)在/usr/include/bits/errno.h里头:
于是errno就是个有左值的int了。至于这个__errno_location,在glibc的源码的 csu/errno-loc.c 里头:
而这里的真正的errno,在 csu/errno.c 里头:
由于包含的都是errno.h,所以对于用户程序而言,可见的只有errno.h中的"errno",实际上是(*(__errno_location()),而__errno_location()返回的是errno.c中的errno的地址。可真绕啊。可是偏偏又不能把 __thread int errno; 放在errno.h中,为什么呢?其实原因很简单也很复杂——C语言真是个纠结的语言。
最后,解释一下“__thread”修饰符:__thread defines number to be a thread local variable. 定义thread-local的变量。详情参见Thread-local_storage的wiki页:http://en.wikipedia.org/wiki/Thread-local_storage
转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: https://www.felix021.com/blog/feed.php 。
刚接触到errno,可能会认为这是个在errno.h中用
int errno;
定义的整型。但是一旦开始写多线程的程序,再看到errno的时候,就会抑郁了。如果两个线程调用库函数都出错,那errno怎么办...?幸而,ISO C并没有规定,errno必须是一个int,如果看errno的manpage (man errno) 会看到:
引用
errno may be a macro. errno is thread-local; setting it in one thread does not affect its value in any other thread.
#define errno (*__errno_location ())
于是errno就是个有左值的int了。至于这个__errno_location,在glibc的源码的 csu/errno-loc.c 里头:
int *
#if ! USE___THREAD
weak_const_function
#endif
__errno_location (void)
{
return &errno;
}
#if ! USE___THREAD
weak_const_function
#endif
__errno_location (void)
{
return &errno;
}
而这里的真正的errno,在 csu/errno.c 里头:
__thread int errno;
由于包含的都是errno.h,所以对于用户程序而言,可见的只有errno.h中的"errno",实际上是(*(__errno_location()),而__errno_location()返回的是errno.c中的errno的地址。可真绕啊。可是偏偏又不能把 __thread int errno; 放在errno.h中,为什么呢?其实原因很简单也很复杂——C语言真是个纠结的语言。
最后,解释一下“__thread”修饰符:__thread defines number to be a thread local variable. 定义thread-local的变量。详情参见Thread-local_storage的wiki页:http://en.wikipedia.org/wiki/Thread-local_storage
欢迎扫码关注:
转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: https://www.felix021.com/blog/feed.php 。
我写了这么个函数:
bool err_can_ignore(int errno)
{
//..
}
然后用的时候一直提示不能把int转换为int*(*)() ……
郁闷了好久,才想起这个errno,换了个名字,正常。同时顺便也就了解到了errno的本质……