构建线程安全的多平台C库的正确方法是什么?

时间:2013-04-11 09:01:39

标签: c gcc thread-safety pthreads

考虑以下简单的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_REENTRANTgcc -pthread。但是,对于库,指定这些多线程选项似乎很差,因为它会对线程运行时注入不必要的依赖。但是,如果库需要是线程安全的(包括errno),那么在库的编译时和派生代码的编译时都需要线程安全的语义。在Linux上,这很容易,因为errno总是线程局部的,但是在其他系统上并不能保证这一点。

这导致了一个问题:如何通过头文件正确编译和分发线程安全库?一个选项是主标头中的#define _REENTRANT,但如果在包含库标头之前发生#include <errno.h>,则会导致问题。另一种选择是使用-D_REENTRANT编译库,如果未定义#error则使用主标题_REENTRANT

制作线程安全库并确保它与链接的代码正确互操作的正确/最佳方法是什么?

2 个答案:

答案 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()变体。