考虑以下简单的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
。但是,对于库,指定这些多线程选项似乎很差,因为它会对线程运行时注入不必要的依赖。但是,如果库需要是线程安全的(包括errno),那么在库的编译时和派生代码的编译时都需要线程安全的语义。在Linux上,这很容易,因为errno总是线程局部的,但是在其他系统上并不能保证这一点。
这导致了一个问题:如何通过头文件正确编译和分发线程安全库?一个选项是主标头中的#define _REENTRANT
,但如果在包含库标头之前发生#include <errno.h>
,则会导致问题。另一种选择是使用-D_REENTRANT
编译库,如果未定义#error
则使用主标题_REENTRANT
。
制作线程安全库并确保它与链接的代码正确互操作的正确/最佳方法是什么?
答案 0 :(得分:4)
我目前无权访问任何Solaris计算机,因此无法对此进行测试。但是,如果您将#define _POSIX_C_SOURCE 200112L
作为test.c
中的第一行(包含<errno.h>
之前),会发生什么?如果您的Solaris符合POSIX,则应使errno
扩展为线程安全版本。这是因为POSIX将errno
定义如下:
对于进程的每个线程, errno 的值不受其他线程的函数调用或 errno 赋值的影响。
因此,这可以移植到任何符合POSIX的系统。实际上,如果您想编写符合POSIX标准的应用程序代码,那么您应该始终将_POSIX_C_SOURCE
定义为适合您所定位的最低POSIX版本的值。在包含任何标头之前,定义应位于每个源文件的顶部。从2001版标准:
严格遵守POSIX应用程序的应用程序仅需要IEEE Std 1003.1-2001中描述的功能。这样的申请:
...
8。对于C编程语言,在包含任何头之前,应将_POSIX_C_SOURCE定义为200112L
答案 1 :(得分:0)
如果您的库使用autoconf,您可能想要使用AC_USE_SYSTEM_EXTENSIONS宏。该宏设置了一些特定于目标的定义,这些定义启用了POSIX +扩展语义。我目前还没有要测试的Solaris系统,但我相信_POSIX_PTHREAD_SEMANTICS应该启用线程安全的errno。至少它启用了POSIX _r()函数,而不是默认情况下Solaris烦人提供的POSIX-draft _r()变体。