考虑以下简单的 C 程序,
#include <errno.h>
int
main(int argc, char* argv[]) {
return errno;
}
在 Solaris 上编译时,此代码的行为取决于 -D_REENTRANT
的存在.
solaris$ cc -E test.c | grep return
return errno;
solaris$ cc -D_REENTRANT -E test.c | grep return
return ( * ( ___errno ( ) ) );
后一个版本是线程安全的。如果我们在 Linux 上编译相同的代码,我们会得到与 -D_REENTRANT
无关的相同行为。
linux$ gcc -E test.c | grep return
return (*__errno_location ());
linux$ gcc -D_REENTRANT -E test.c | grep return
return (*__errno_location ());
Solaris 的 cc
有选项 -mt
,这意味着 -D_REENTRANT
, 和 gcc
一样的 -pthread
.然而,对于一个库来说,指定这些多线程选项似乎很糟糕,因为它注入(inject)了对线程运行时的不必要依赖。但是,如果库需要是线程安全的(包括 errno),那么在库和派生代码的编译时都需要线程安全的语义。在 Linux 上,这很容易,因为 errno 始终是线程本地的,但正如刚刚演示的那样,在其他系统上不能保证这一点。
这导致了一个问题:一个线程安全的库如何正确编译和分发头文件?一种选择是 #define _REENTRANT
在主标题中,但这会导致问题,如果 #include <errno.h>
发生在库头包含之前。另一种选择是使用 -D_REENTRANT
编译库, 并有主标题 #error
如果_REENTRANT
未定义。
创建线程安全库并确保它与其链接的代码正确互操作的正确/最佳方法是什么?
最佳答案
目前我无法访问任何 Solaris 机器,所以我无法对此进行测试。但是当你输入 #define _POSIX_C_SOURCE 200112L
时会发生什么?作为 test.c
中的第一行(在包含 <errno.h>
之前)?如果您的 Solaris 是 POSIX 兼容的,那么这应该是 errno
扩展到线程安全版本。这是因为 POSIX 定义了 errno
如下:
For each thread of a process, the value of errno shall not be affected by function calls or assignments to errno by other threads.
因此,这可以移植到任何 POSIX 兼容系统。事实上,如果您想编写符合 POSIX 标准的应用程序代码,那么您应该始终定义_POSIX_C_SOURCE
。为适合您所针对的最低 POSIX 版本的值。在包含任何标题之前,定义应该位于每个源文件的顶部。来自 2001 版标准:
A Strictly Conforming POSIX Application is an application that requires only the facilities described in IEEE Std 1003.1-2001. Such an application:
...
8. For the C programming language, shall define _POSIX_C_SOURCE to be 200112L before any header is included
关于c - 构建线程安全的多平台 C 库的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15944664/