errno宏定义为一个int类型的左值, 用于传达标准C函数的错误码,在多线程的情况下当前线程调用标准C函数到访问errno之间可能会切换到其它线程执行标准C函数,导致当前线程访问errno不可信。这就需要不同的线程有自己的errno。
Zephyr的errno
Zephyr的标准C库可以使用自己的minimal libc,也可以配置为使用newlib-c,二者使用的方式大致一样, 最后都是访问的z_errno()。
minimal libc
在zephyr\lib\libc\minimal\include\errno.h中定义了errno宏
1 | #define errno (*z_errno()) |
在Zephyr的minimal/socket/fs中的标准C函数内,将直接访问写errno
newlib-c
在zephyr\lib\libc\newlib\libc-hooks.c中定义了errno函数
weak int *__errno(void)
{
return z_errno();
}
在newlib-c代码中libc/include/sys/errno.h中定义了errno宏,newlib-c中将直接访问写errno
1 | #define errno (*__errno()) |
z_errno()
zephyr\include\sys\errno_private.h的代码片段可以看到CONFIG_ERRNO_IN_TLS=y
的情况下将使用__thread声明z_errno_var,z_errno返回的是该变量的地址,这是TLS变量每个线程一份。更多TLS详情可参考Zephyr TLS线程本地存储的实现一文。
1 | #ifdef CONFIG_ERRNO_IN_TLS |
zephyr\kernel\errno.c中定义了z_impl_z_errno函数,非TLS的情况下将使用struct k_thread
中的errno_var
成员存放errno
1 | #ifdef CONFIG_ERRNO |
编译后脚本会产生系统调用关系z_errno会调用到z_impl_z_errno。
我们再观察一下thread.h中关于errno_var
1 | #if defined(CONFIG_ERRNO) && !defined(CONFIG_ERRNO_IN_TLS) |
可以看到要支持多线程errno,需要配置CONFIG_ERRNO=y
,是否使用GCC支持的TLS由CONFIG_ERRNO_IN_TLS
决定
总结一下:
- 使用多线程errno, 配置
CONFIG_ERRNO=y
- TLS errno: 配置
CONFIG_ERRNO_IN_TLS=y
,z_errno()返回z_errno_var的地址 - 非TLS errno:
CONFIG_ERRNO_IN_TLS=n
, z_errno()返回struct k_thread
中的errno_var
的地址