errno是否是thread safe的
errno是线程安全的吗? 假设有A, B两个线程都执行系统调用, 其中A返回EIO, B返回EAGAIN, 在判断返回值时是否会引起混淆?
简单的通过man errno就可以获取答案: errno is thread-local; setting it in one thread does not affect its value in any other thread.
但是errno究竟是如何实现的? 为什么errno还可能是一个宏? 带着疑问我们来研究下glibc. 官网下载到最新的是2.25版本的源码, 我们就以glibc-2.25为例一探究竟.
先看对外暴露的stdlib/errno.h:
1 #include <bits/errno.h> 2 #undef __need_Emath 3 #ifndef errno 4 extern int errno; 5 #endif
这里的注释指明两点:
1. bits/errno.h是系统相关头文件, 在该文件中会测试__need_Emath与_ERRNO_H宏.
2. 如果bits/errno.h未定义errno为宏则声明外部变量errno.
sysdeps/unix/sysv/linux/bits/errno.h中将其定义为函数:
1 #ifdef _ERRNO_H 2 # ifndef __ASSEMBLER__ 3 extern int *__errno_location (void) __THROW __attribute__ ((__const__)); 4 # if !defined _LIBC || defined _LIBC_REENTRANT 5 # define errno (*__errno_location ()) 6 # endif 7 # endif 8 #endif
搞清楚errno的定义后再来看看errno的修改. 由于不同架构系统调用部分相同部分不同, glibc使用脚本来动态生成系统调用函数的封装, sysdeps/unix/make-syscalls.sh即生成函数封装的脚本. 它会先去读取syscalls.list保存在calls变量中, 通过sed将注释行与空行删除, 将得到的文件按行输入(读入的前三个参数分别为file caller rest)并判断对应架构目录下是否存在$file.c $file.S $caller.c $caller.S(如果$caller不为-)文件中一个, 如果有则记录在calls变量中. 接下来根据系统调用类型及参数配置不同参数, 最后将其输出, 注意line 256开始的宏定义与包含的文件. 此处有点不明白, 输出的文件是怎么确定的?
make-syscalls.sh脚本输出的信息有何作用? 上文中line 256可以解答这个问题. 先来看下系统调用的模板(defined in sysdeps/unix/syscall-template.S):
1 #define T_PSEUDO(SYMBOL, NAME, N) PSEUDO (SYMBOL, NAME, N) 2 #define T_PSEUDO_END(SYMBOL) PSEUDO_END (SYMBOL) 3 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS) 4 ret 5 T_PSEUDO_END (SYSCALL_SYMBOL)
syscall-template.S定义了一组宏用于定义系统调用的接口, 这里仅分析最常见的情况.
看下以PSEUDO开头命名的宏(defined in sysdeps/unix/sysv/linux/arm/sysdep.h):
1 #undef PSEUDO 2 #define PSEUDO(name, syscall_name, args) \ 3 .text; \ 4 ENTRY (name); \ 5 DO_CALL (syscall_name, args); \ 6 cmn r0, $4096; 7 #undef PSEUDO_END 8 #define PSEUDO_END(name) \ 9 SYSCALL_ERROR_HANDLER; \ 10 END (name)
因以PSEUDO开头命名的宏较多, 此处仅分析下PSEUDO与PSEUDO_END, 可见两个宏需成对使用, 分别用于系统调用与错误返回, 继续分析DO_CALL(defined in sysdeps/unix/sysv/linux/arm/sysdep.h):
1 #undef DO_CALL 2 #define DO_CALL(syscall_name, args) \ 3 DOARGS_##args; \ 4 ldr r7, =SYS_ify (syscall_name); \ 5 swi 0x0; \ 6 UNDOARGS_##args
DO_CALL宏有三条注释, 分别说明:
1. ARM EABI用户接口将系统调用号放在R7中, 而非swi中传递. 这种方式更加高效, 因为内核无需从内存中获取调用号, 这对于指令cache与数据cache分开的架构比较麻烦. 因此swi中必须传递0.
2. 内核通过R0-R6共传递7个参数, 而编译器通常只使用4个参数寄存器其余以入栈方式传参(见AAPCS), 此处需要做转换防止栈帧毁坏并保证内核正确获取参数.
3. 由于缓存系统调用号在发生系统调用时必须保存并恢复R7.
根据注释理解代码就方便多了, 先保存R7并将参数传递给对应寄存器, 将系统调用号传递给R7并调用swi 0x0, 最后恢复寄存器. DOARGS_#args根据传入args值不同展开为不同的宏(都定义在同一文件下), 此处仅分析DOARGS_7情况(UNDOARGS_#args类似, 不展开分析):
1 #undef DOARGS_7 2 #define DOARGS_7 \ 3 .fnstart; \ 4 mov ip, sp; \ 5 push {r4, r5, r6, r7}; \ 6 cfi_adjust_cfa_offset (16); \ 7 cfi_rel_offset (r4, 0); \ 8 cfi_rel_offset (r5, 4); \ 9 cfi_rel_offset (r6, 8); \ 10 cfi_rel_offset (r7, 12); \ 11 .save { r4, r5, r6, r7 }; \ 12 ldmia ip, {r4, r5, r6}
先将当前栈指针保存在IP中, 将R4-R7依次入栈, 最后通过IP将已经入栈的参数传递给R4-R7. 中间以cfi开头的宏都是伪指令(defined in sysdeps/generic/sysdep.h), 用于debugger分析程序调用间寄存器状态, 不详细分析了, 具体可参见(http://dwarfstd.org/doc/DWARF5.pdf).
SYS_ify宏(defined in sysdeps/unix/sysv/linux/arm/sysdep.h)用于拼接字符串生成对应的调用号(生成的即是内核定义的系统调用号的宏):
#define SYS_ify(syscall_name) (__NR_##syscall_name)
再回头看PSEUDO_END, 其展开即调用SYSCALL_ERROR_HANDLER(sysdeps/unix/sysv/linux/arm/sysdep.h)然后声明函数结束. SYSCALL_ERROR_HANDLER根据不同预处理宏有不同定义, 此处仅分析使用libc的errno且架构不支持THUMB_INTERWORK情况:
1 #define SYSCALL_ERROR_HANDLER \ 2 __local_syscall_error: \ 3 push { lr }; \ 4 cfi_adjust_cfa_offset (4); \ 5 cfi_rel_offset (lr, 0); \ 6 push { r0 }; \ 7 cfi_adjust_cfa_offset (4); \ 8 bl PLTJMP(C_SYMBOL_NAME(__errno_location)); \ 9 pop { r1 }; \ 10 cfi_adjust_cfa_offset (-4); \ 11 rsb r1, r1, #0; \ 12 str r1, [r0]; \ 13 mvn r0, #0; \ 14 POP_PC;
代码还是比较简单的, 首先将LR压栈, 再将R0压栈(注意此时R0为系统调用返回值). 然后获取errno的地址, PLTJMP宏表明__errno_location符号是由程序链接表指定而非静态生成的. 由于函数返回值保存在R0, 出栈时使用R1保存系统调用返回值, 又系统调用返回值为复数, 此处再做一次减法取正, 再将其保存在R0给定的地址上(errno). 最后将R0设置为-1, 将LR出栈并跳转.
待续......
转载于:https://www.cnblogs.com/Five100Miles/p/8459193.html
errno是否是thread safe的相关推荐
- mysql thread safe_Windows环境下完全手工配置Apache、MySQL和PHP(Thread Safe)
happydagui:现在LAMP(Linux.Apache.MySQL.PHP/Perl/Python的简称)已经很流行了.在Windows下也有类似的,比如 WAMP(Apache, MySQL, ...
- PHP5 VC9、VC6、Thread Safe、Non Thread Safe各个版本区别
2019独角兽企业重金招聘Python工程师标准>>> 一.如何选择PHP5.3的VC9版本和VC6版本 网站推广 VC6版本是使用Visual Studio 6编译器编译的,如果你 ...
- PHP版本VC6与VC9/VC11/VC14、Thread Safe与None-Thread Safe等的区别
原文:PHP版本VC6与VC9/VC11/VC14.Thread Safe与None-Thread Safe等的区别 最近正好在弄一个PHP的程序,在这之前一直没有怎么以接触,发现对PHP版本知识了解 ...
- php5.6non thread safe 区别,PHP版本Non Thread Safe和Thread Safe如何选择?区别是什么?
PHP版本分为Non Thread Safe和Thread Safe,Non Thread Safe是指非线程安全,Thread Safe是指线程安全,区别是什么?如何选择? Non Thread S ...
- PHP关于VC11,VC9,VC6以及Thread Safe和Non Thread Safe版本选择
2019独角兽企业重金招聘Python工程师标准>>> 这里是我在搭建php环境时收集的资料供大家参考: 现在PHP官网上下载PHP安装包都有VC11或VC9的字样,这是什么含义,我 ...
- PHP 5.3 下载时 VC9、VC6、Thread Safe、Non Thread Safe 是什么意思?
我最近在 PHP 官网上看到又有新版的 PHP 下载了,于是上去找找 For Windows 的版本,可是一看确傻眼了,一共给了四个版本,VC9 x86 Non Thread Safe.VC9 x86 ...
- PHP版本选择讲解:VC6与VC9,Thread Safe与None-Thread Safe等的选择
October 28, 2010 | 作者:白菜 最近发现很多PHP程序员对PHP版本知识了解不是很清楚,自己也看了不少类似的文章,还是感觉不够明确和全面,网上的结论又都是模棱两可,在此,给出最完整甚 ...
- non thread safe php vc11,PHP 中什么线程安全(TS)和非线程安全(NTS)
显示行号 | 选择喜欢的代码风格 默认 GitHub Dune LakeSide Plateau Vibrant Blue Eighties Tranquil Windows 版的 PHP 从版本 P ...
- 关于VC9和VC6以及Thread Safe和Non Thread Safe版本选择的问题
一.如何选择PHP5.3的VC9版本和VC6版本 VC6版本是使用Visual Studio 6编译器编译的,如果你的PHP是用Apache来架设的,那你就选择VC6版本. VC9版本是使用Visua ...
- PHP版本VC6与VC9、Thread Safe与None-Thread Safe等的区别
转载:http://www.cnblogs.com/whoknows/articles/2425841.html 最近发现很多PHP程序员对PHP版本知识了解不是很清楚,自己也看了不少类似的文章,还是 ...
最新文章
- 手机安全卫士——软件管理-用户程序和系统程序
- 锁优化:逃逸分析、自旋锁、锁消除、锁粗化、轻量级锁和偏向锁
- 阿里云专家手把手教你重塑 IT 架构!
- java map存放班级和姓名_Java 创建一个HashMap对象,并在其中添加学生的姓名和成绩,键为学生姓名,值为学生成绩,使用增强for循环遍历该HashMap,并输出学生成绩。...
- 工作108:精准的v-for和if
- 鸿蒙os来了,华为操作系统“鸿蒙OS”来了!
- Python之进程+线程+协程(并发与并行、GIL锁、同步锁、死锁、递归锁)
- Linux C编程Makefile编写初步-转
- 如何修改apache 2最大连接数
- 【电子商务安全与支付实验】数字证书的申请及使用
- 软件评测师-13.软件测试技术与应用
- 正则表达式提取HTML中IMG标签的SRC地址
- HDLBits在线练习题之Exams/ece241 2014 q7b
- 在Windows中查看文件的MD5值
- 一鸣心所向:想成功?变身蝙蝠侠吧
- PageOffice 在线打开 word 文件并添加水印
- python批量检索文献pubmed_PubMed快速检索文献,学学这些技巧!
- 数据结构算法 - ConcurrentHashMap 源码解析
- yii 添加,操作成功,但数据并没有插入到数据库中
- HTML学习第十二章------布局和排版