考虑下面的琐碎的C程序,构建线程安全的多平台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
。
什么是让一个线程安全的库并正确确保正确的/最佳方式相互操作与它相链接的代码?
'cc -D_POSIX_C_SOURCE = 200112L -E test.c | grep return - > return(*(___errno()));' 事实上,它按预期工作;你是否也推荐在POSIX兼容库的头文件中定义这个? – 2013-04-11 16:14:10
@AlexanderChernyakhovsky:不,它永远不需要在任何头文件中定义。它应该始终在源文件的顶部定义。这是确保在包含任何标题之前定义它的唯一方法。您也可以在命令行上对其进行定义,就像您在示例中所做的那样。这是可以接受的。我更喜欢在源文件中看到它,但这部分只是一个品味问题。 – 2013-04-11 16:27:33
有趣。这听起来像你主张仍然在我的库头中检查'#define',因为我仍然不希望最终开发者最终得到错误的errno。 – 2013-04-11 17:17:32