11

考虑以下简单的 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

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

4

2 回答 2

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

于 2013-04-11T10:58:21.450 回答
0

如果您的库使用 autoconf,您可能希望使用AC_USE_SYSTEM_EXTENSIONS宏。该宏设置了一些特定于目标的定义,这些定义启用了 POSIX +扩展语义。我目前没有要测试的 Solaris 系统,但我相信 _POSIX_PTHREAD_SEMANTICS 应该启用线程安全的 errno。至少它启用了 POSIX _r() 函数,而不是 Solaris 默认提供的 POSIX 草案 _r() 变体。

于 2014-10-13T04:49:38.800 回答