Linux C库函数的可重入与不可重入版本说明

2026-10-19 10:05:43 14阅读

在多线程编程中,线程安全(Thread-Safety)是非常重要的概念,而可重入性(Reentrancy)是确保线程安全的关键因素之一。

C 标准库中有很多函数都存在可重入(Reentrant)和不可重入版本。命名上,不可重入函数通常是简单的函数名,而可重入版本在函数名后带 _r,表示该版本为可重入。

  • 不可重入函数:通常依赖共享的全局状态,或使用静态变量存储中间结果。多个线程并发调用时,可能发生数据竞争或资源争用,导致不安全行为。
  • 可重入函数:设计上避免共享状态,通常只使用局部变量,或通过传递上下文指针存储状态,每个线程拥有自己的副本,避免竞争条件。

我们在 C 库中常遇到一些不可重入的函数,多线程环境中可能出现问题,例如:

  • asctime() 与 asctime_r()
  • ctime() 与 ctime_r()
  • localtime() 与 localtime_r()

这些函数的 _r 版本是可重入的,能在多线程环境下安全使用。通过 man 手册可查询每个库函数的线程安全性标注,例如 man 3 ctime 可查看 ctime() 和 ctime_r() 的信息,手册的 "ATTRIBUTES" 部分会显示函数是否线程安全。

MT-Safe 与 MT-Unsafe 标记

函数的线程安全性通常以 MT-Safe 或 MT-Unsafe 标记:

  • MT-Safe(线程安全):该函数多线程安全,可被多个线程同时调用而不会产生竞态条件或数据竞争。
  • MT-Unsafe(线程不安全):该函数不是线程安全的,多个线程同时调用可能导致数据不一致或其他不安全行为。

值得注意的是,即使某些函数被标记为 MT-Safe,它们可能还带有附加条件(如 env 或 locale 标签),表示在某些情况下这些函数可能失去线程安全性。asctime_r()、ctime_r()、localtime_r() 等通常标记为 MT-Safe env locale,意味着在环境变量和区域设置条件满足时线程安全,若条件不满足(如环境变量变化)仍可能出现线程不安全。

env 与 locale 条件标签

通过 man 7 attributes 可以查询 env、locale 等标签的确切含义:

env 标签表示函数依赖进程的环境变量。环境变量是全局的,多个线程共享同一个进程的环境变量,若一个线程修改了环境变量,其他线程的读取行为可能产生不可预知的结果。例如 getenv() 读取环境变量,多个线程同时调用不会冲突,但若某个线程调用 setenv() 或 putenv() 修改环境变量,其他线程的 getenv() 结果就可能不确定。

locale 标签表示函数依赖区域设置。区域设置是进程级设置,控制程序如何处理语言和文化相关信息(日期格式、货币符号、字符编码等)。若多个线程同时更改进程的区域设置,会影响所有线程。例如 setlocale() 设置或查询当前区域设置,若某线程更改区域设置,其他依赖区域设置的函数(如 strcoll())可能受影响。

有些函数是绝对线程安全的,不依赖任何共享资源,也不受环境变量和区域设置影响,例如数学库的 sqrt() 函数,没有任何附加标签。

可重入 ≠ 线程安全

需要强调的是,可重入函数并不一定就是线程安全的(两者有重叠)。例如函数只使用局部变量、不依赖全局状态,通常是可重入的,但不一定线程安全,尤其当函数内部涉及系统调用时。

可重入函数在每次调用时不会依赖外部状态(如全局变量、静态变量),即便在中断上下文或多个线程并发执行时也能安全执行;线程安全函数则意味着多个线程可同时调用而不发生数据竞争,通常通过同步机制(如锁)确保正确执行。

用 ctime() 和 ctime_r() 具体说明区别:ctime() 不可重入,返回的字符串存储在静态缓冲区、所有线程共享同一个缓冲区,多个线程同时调用会导致缓冲区数据被覆盖、结果不可靠:

#include 
#include

void* unsafe_function(void* arg) {
time_t now = time(NULL);
printf("ctime: %s
", ctime(&now)); // 使用静态缓冲区,线程不安全
return NULL;
}

ctime_r() 是可重入版本,允许用户传递缓冲区,线程间不共享状态,因此线程安全:

#include 
#include

void* safe_function(void* arg) {
time_t now = time(NULL);
char buffer[26]; // 用户传入的缓冲区
printf("ctime_r: %s
", ctime_r(&now, buffer)); // 线程安全
return NULL;
}

C 标准库中很多函数都有可重入与不可重入版本。多线程编程中使用可重入函数是确保线程安全的关键之一,但可重入并不总是等同于线程安全。通过理解 man 手册中的 MT-Safe 与 MT-Unsafe 标记,以及带 env 或 locale 的条件性标签,开发者可以更好地选择合适函数,避免多线程环境中的潜在问题。


作者:不脱发的程序猿
链接:https://www.aiddithome.com/p/89e513c9ff3eb.html
本作遵循 [CC BY 4.0 署名] 许可,版权归作者所有。若存在违规行为,您可以点击 “举报”。


登录 后发表评论
3条评论
阿强焊点总飞
1楼 · 2天前

ctime 和 ctime_r 的例子特别直观,静态缓冲区的坑踩过的都懂。

MT-Safe 和 env、locale 标签这块讲得很专业,嵌入式多线程可以参考。

探头看一眼
3楼 · 2天前

平时写多线程真得留意这些 _r 版本函数,谢谢整理。