考虑以下简单的 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
一样。然而,对于一个库来说,指定这些多线程选项似乎很糟糕,因为它对线程运行时注入了不必要的依赖。但是,如果库需要是线程安全的(包括 errno),那么在库和派生代码的编译时都需要线程安全语义。在 Linux 上,这很容易,因为 errno 始终是线程本地的,但正如刚才演示的那样,在其他系统上并不能保证这一点。gcc
-pthread
这导致了一个问题:线程安全库如何正确编译和分发头文件?一种选择是#define _REENTRANT
在主标题中,但如果#include <errno.h>
在库标题包含之前发生,这会导致问题。另一种选择是使用 编译库,如果未定义-D_REENTRANT
,则使用主标头。#error
_REENTRANT
制作线程安全库并确保它与链接的代码正确互操作的正确/最佳方法是什么?