2013-04-11 51 views
10

考虑下面的琐碎的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

什么是让一个线程安全的库并正确确保正确的/最佳方式相互操作与它相链接的代码?

回答

4

我没有在时刻访问任何Solaris机器,所以我不能对此进行测试。但是,如果将#define _POSIX_C_SOURCE 200112L作为第一行test.c(包含<errno.h>之前),会发生什么情况?如果您的Solaris符合POSIX标准,那么应该使errno扩展为线程安全版本。这是因为POSIX如下定义errno

对于过程的每个线程,错误号的值不应当由函数由其他线程调用或者分配到错误号影响。

因此,这是移植到任何兼容POSIX的系统。事实上,如果你想要写POSIX兼容的应用程序代码,那么你应该总是定义_POSIX_C_SOURCE适合您的目标POSIX的最低版本的值。在包含任何标题之前,定义应位于每个源文件的顶部。从2001版本的标准:

严格符合POSIX应用程序是一个应用程序,只需要在IEEE标准1003.1-2001中描述的设施。这样的应用:

...

8。对于C编程语言,应规定_POSIX_C_SOURCE是200112L包括

+0

'cc -D_POSIX_C_SOURCE = 200112L -E test.c | grep return - > return(*(___errno()));' 事实上,它按预期工作;你是否也推荐在POSIX兼容库的头文件中定义这个? – 2013-04-11 16:14:10

+0

@AlexanderChernyakhovsky:不,它永远不需要在任何头文件中定义。它应该始终在源文件的顶部定义。这是确保在包含任何标题之前定义它的唯一方法。您也可以在命令行上对其进行定义,就像您在示例中所做的那样。这是可以接受的。我更喜欢在源文件中看到它,但这部分只是一个品味问题。 – 2013-04-11 16:27:33

+0

有趣。这听起来像你主张仍然在我的库头中检查'#define',因为我仍然不希望最终开发者最终得到错误的errno。 – 2013-04-11 17:17:32

0

如果你的库使用autoconf的任何头之前,你可能想使用AC_USE_SYSTEM_EXTENSIONS宏。该宏设置了一些启用POSIX +扩展语义的特定于目标的定义。我目前没有Solaris系统进行测试,但我相信_POSIX_PTHREAD_SEMANTICS应该启用线程安全的errno。至少它启用了POSIX _r()函数,而不是Solaris默认提供的烦人提供的POSIX-draft _r()变体。

相关问题