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

标签 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_REENTRANT , 和 gcc 一样的 -pthread .然而,对于一个库来说,指定这些多线程选项似乎很糟糕,因为它注入(inject)了对线程运行时的不必要依赖。但是,如果库需要是线程安全的(包括 errno),那么在库和派生代码的编译时都需要线程安全的语义。在 Linux 上,这很容易,因为 errno 始终是线程本地的,但正如刚刚演示的那样,在其他系统上不能保证这一点。

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

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

最佳答案

目前我无法访问任何 Solaris 机器,所以我无法对此进行测试。但是当你输入 #define _POSIX_C_SOURCE 200112L 时会发生什么?作为 test.c 中的第一行(在包含 <errno.h> 之前)?如果您的 Solaris 是 POSIX 兼容的,那么这应该是 errno扩展到线程安全版本。这是因为 POSIX 定义了 errno如下:

For each thread of a process, the value of errno shall not be affected by function calls or assignments to errno by other threads.

因此,这可以移植到任何 POSIX 兼容系统。事实上,如果您想编写符合 POSIX 标准的应用程序代码,那么您应该始终定义_POSIX_C_SOURCE。为适合您所针对的最低 POSIX 版本的值。在包含任何标题之前,定义应该位于每个源文件的顶部。来自 2001 版标准:

A Strictly Conforming POSIX Application is an application that requires only the facilities described in IEEE Std 1003.1-2001. Such an application:

...

8. For the C programming language, shall define _POSIX_C_SOURCE to be 200112L before any header is included

关于c - 构建线程安全的多平台 C 库的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15944664/

相关文章:

Java try-finally 竞争条件?

c - noNameFunc1 在这里如何真正工作以及如何向它传递值?

c - Yocto 构建 - loadlocale.c#130

java - 使用什么方法使 Servlet 线程安全?

c++ - 在 C++ 中重用内存

c++ - 为什么对于这个涉及求幂的简单函数,clang 生成的代码比 gcc 快得多?

java - 如何确保我的 map 在我的构建器模式中设置后永远不会被修改?

c - 如何停止 CSE 不要重复 CSE

c - 用于嵌入式系统的轻量级 Web 身份验证

c - 符号循环错误 : libgobject-2. 0.so.0:undefined symbol: g_bytes_unref