博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C errno是否是线程安全的
阅读量:7121 次
发布时间:2019-06-28

本文共 3503 字,大约阅读时间需要 11 分钟。

本文同时发表在

在使用多线程时,遇到了一个问题:线程例程中如果需要使用errno全局变量,如何保证errno的线程安全性?例如一个简单的线程池代码:

for(int i=0;i

关于C中错误处理的问题,可以参考,简单的说很多系统调用只会返回成功或者失败,具体失败的原因会设置全局变量errno供调用方自己读取,所以引发了多线程里errno线程安全的问题。

如何解决这个问题?毕竟设置errno的过程我们不能干预。上网搜了才发现,,重定义了errno,使之为线程安全的变量:

Redefinition of errno

In POSIX.1, errno is defined as an external global variable. But this definition is unacceptable in a multithreaded environment, because its use can result in nondeterministic results. The problem is that two or more threads can encounter errors, all causing the same errno to be set. Under these circumstances, a thread might end up checking errno after it has already been updated by another thread.
To circumvent the resulting nondeterminism, POSIX.1c redefines errno as a service that can access the per-thread error number as follows (ISO/IEC 9945:1-1996, §2.4):
Some functions may provide the error number in a variable accessed through the symbol errno. The symbol errno is defined by including the header <errno.h>, as specified by the C Standard ... For each thread of a process, the value of errno shall not be affected by function calls or assignments to errno by other threads.

颠覆了我的世界观呀,那这怎么实现的全局变量能够线程安全呢?

在error.h中(vim下按gf跳到库文件),看到如下定义:

/* Declare the `errno' variable, unless it's defined as a macro by   bits/errno.h.  This is the case in GNU, where it is a per-thread   variable.  This redeclaration using the macro still works, but it   will be a function declaration without a prototype and may trigger   a -Wstrict-prototypes warning.  */#ifndef errnoextern int errno;#endif

如果bits/error.h中没有定义errno,才会定义errno。

在bits/errno.h中,找到关于errno定义部分:

extern int *__errno_location (void) __THROW __attribute__ ((__const__));#  if !defined _LIBC || defined _LIBC_REENTRANT/* When using threads, errno is a per-thread value.  */#   define errno (*__errno_location ())#  endif

可以清晰的看到,bits/errno.h对errno进行了重定义。从 __attribute__ ((__const__))推测出__errno_location ()会返回与参数无关的与线程绑定的一个特定地址,应用层直接从该地址取出errno的。(关于__attribute__用法可以参考)。但是上面使用了条件编译,也就是有两种方法可以使得gcc重定义errno:

  • 不定义宏_LIBC
  • 定义宏_LIBC_REENTRANT

但是很有意思的是,我们在编译时,压根不能设置_LIBC,感兴趣的可以自己试一下:

gcc -D_LIBC a.cIn file included from /usr/include/gnu/stubs.h:9:0,                 from /usr/include/features.h:385,                 from /usr/include/stdio.h:28,                 from a.c:1:/usr/include/gnu/stubs-64.h:7:3: error: #error Applications may not define the macro _LIBC  #error Applications may not define the macro _LIBC   ^

我们在编译时设置了宏_LIBC,但是编译失败,原来在gnu/stubs-64.h中会检测,如果有_LIBC宏定义,直接报错终止预编译:

#ifdef _LIBC #error Applications may not define the macro _LIBC#endif

也就是在正常情况下,我们使用gcc编译的程序,全局变量errno一定是线程安全的。现在只剩下了一个问题,__errno_location是怎么实现的。遗憾的是,我并没有找到这个函数的实现,我们可以写个小程序反汇编看一下在哪实现的:

#include 
#include
int main(){ int i = errno; printf("%d",i); return 0;}

反汇编代码:

0x0000000000400588 in main ()=> 0x0000000000400588 
: e8 7b fe ff ff callq 0x400408 <__errno_location@plt>

从@plt看出应该是动态库延迟绑定,进去看看:

0x0000003dc8e148c0 in _dl_runtime_resolve () from /lib64/ld-linux-x86-64.so.2=> 0x0000003dc8e148c0 <_dl_runtime_resolve+0>:  48 83 ec 38     sub    $0x38,%rsp

呃呃,难道__errno_location定义在linux代码中?算了不看了,到这已经基本解决了我的问题:多线程如何保证errno全局变量的线程安全性,哈哈。猜测实现方式应该跟thread-local有关。

最后,虽然在多线程中我们不用保证errno的线程安全,但是如果需要编写信号处理函数时,我们仍然要保证errno的安全性,因为操作系统可能不会新创建一个线程来处理信号处理函数:

void handle_signal(int sig){    int savedErrno;    savedErrno = errno;    /* Do something when recevied this sig */    errno = savedErrno;}

参考资料:

转载于:https://www.cnblogs.com/zhangyachen/p/10054689.html

你可能感兴趣的文章
区块链软件公司:区块链技术的应用
查看>>
PHP 的魔术方法及其应用
查看>>
(九)企业分布式微服务云SpringCloud SpringBoot mybatis-服务链路追踪(Spring Cloud Sleuth)...
查看>>
springcloud(四):熔断器Hystrix
查看>>
Centos6.3搭建cacti&nagios
查看>>
[SQL Server][FILESTREAM] -- Using INSERT, UPDATE and DELETE to manage SQL Server FILESTREAM Data
查看>>
成为Linux内核高手的四个方法
查看>>
教你使用squid搭配dante做代理
查看>>
ecshop调用文章显示上一篇下一篇_无需整理
查看>>
cisco ***笔记
查看>>
执行php程序的时候,报错Allowed memory size of 134217728 bytes exhausted (tried to allocate 83 bytes)...
查看>>
春Phone计划 51cto沙龙上海站
查看>>
HDFS体系结构详解
查看>>
我的友情链接
查看>>
[转]我对CTO的理解
查看>>
RH413-Linux系统下umask测试
查看>>
MYSQLmy-innodb-heavy-4G.cnf配置文件注解
查看>>
HTML5 Audio/Video 标签,属性,方法,事件汇总
查看>>
Android 学习笔记【基础扫盲篇】
查看>>
shiro filter
查看>>