
🎬 个人主页 :艾莉丝努力练剑
❄专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平
🎬 艾莉丝的简介:

文章目录
- 前言
- [1~> 逻辑拆解](#1~> 逻辑拆解)
-
- [(1) 局部变量的本质](#(1) 局部变量的本质)
- [(2) 原子操作的作用](#(2) 原子操作的作用)
- [(3) 不可重入的判定标准](#(3) 不可重入的判定标准)
- [(4) 核心结论](#(4) 核心结论)
- [2 ~> 从两个维度,深入理解可重入性(Reentrancy)](#2 ~> 从两个维度,深入理解可重入性(Reentrancy))
-
- [(1) 变量类型与重入性的关系](#(1) 变量类型与重入性的关系)
- [(2) 原子操作在其中的角色](#(2) 原子操作在其中的角色)
- [(3) 核心对比表](#(3) 核心对比表)
- [(4) 为什么"加锁"不能解决重入问题?](#(4) 为什么“加锁”不能解决重入问题?)
- [(5) 总结:编写可重入函数的准则](#(5) 总结:编写可重入函数的准则)
- 结尾
前言
- 在一个函数中若对局部变量进行了原子操作,则这个函数一定是不可重入的
这个表述正确吗?
这个观点是不正确的。"使用了原子操作"与"函数是否可重入"之间没有必然的因果关系 。 事实上,在一个函数中对局部变量进行原子操作,通常并不会导致函数变得不可重入。
1~> 逻辑拆解
(1) 局部变量的本质
局部变量(非 static 修饰)存储在每个线程独立的 **栈(Stack)**空间中。
-
当多个线程同时调用同一个函数时,每个线程都有自己独立的局部变量副本。
-
既然每个线程操作的是完全不同的内存地址,它们之间不存在资源竞争,自然也就不会破坏重入性。
(2) 原子操作的作用
原子操作(Atomic Operations)的主要目的是解决多个执行流(线程或中断处理程序)访问共享资源(如全局变量、静态变量、堆内存)时的竞态条件。
-
如果在函数内对全局变量进行原子操作,该函数依然可能是不可重入的(虽然它是线程安全的)。
-
但如果在函数内仅对局部变量进行原子操作,这通常是多此一举,因为栈上数据本身就是线程私有的,不需要原子性保护。
(3) 不可重入的判定标准
一个函数之所以不可重入,通常是因为它满足了以下条件之一:
使用了静态(static)数据结构或全局变量。
调用了 malloc 或 free(它们通常维护全局的堆链表)。
调用了标准 I/O 函数(其内部使用了全局数据结构)。
返回了指向静态数据的指针。
(4) 核心结论
判定重入性的核心在于对共享状态的依赖。
-
可重入函数:即使被中断并在中断处理程序中再次调用,返回后原调用逻辑依然正确。
-
示例:
cpp
void func(int x) {
std::atomic<int> local_val = x; // 局部变量
local_val++; // 对局部变量的原子操作
// 这个函数依然是可重入的,因为所有操作都在栈上
}
总结 :只要函数不操作全局/静态变量,不调用其他不可重入函数,即使内部有原子操作,它依然是可重入的。原子操作通常是为了实现"线程安全",而"可重入"是比"线程安全"更严格的要求。
2 ~> 从两个维度,深入理解可重入性(Reentrancy)
接下来,艾莉丝想细说一下可重入函数和不可重入函数,艾莉丝打算结合包括什么局部变量、全局变量、静态变量等变量类型、原子操作等知识点,帮助理解什么是可重入函数、不可重入函数。
要深入理解可重入性(Reentrancy) ,必须区分两个维度:代码逻辑的隔离性 (是否使用共享资源)和执行流的原子性(操作是否会被打断导致状态破坏)。
我们可以通过内存布局和资源访问权限来拆解。
(1) 变量类型与重入性的关系
函数是否可重入,核心取决于它如何处理数据。
**局部变量(栈变量)**加粗样式
每个线程/进程在调用函数时,都会在自己的私有栈中分配局部变量。不同执行流之间的数据互不干扰。
- 结论 : 只使用局部变量的函数通常是可重入的。
全局变量与静态变量(数据段):
它们存储在全局数据段,被所有执行流共享。如果函数修改了这些变量,当它被中断并再次进入(重入)时,第二次执行会覆盖第一次执行的中间结果,导致第一次执行恢复时数据错乱。
- 结论 : 修改全局或静态变量的函数通常是不可重入的。
堆变量(malloc/new)
虽然堆空间由程序员分配,但其底层管理(空闲链表)是全局共享的。
- 结论 : 调用
malloc或free的函数通常是不可重入的,因为堆管理器本身维护着全局状态。
(2) 原子操作在其中的角色
原子操作(Atomic Operation)是指不可被中断的操作。它在并发场景下有两个层面的意义:
1、解决竞态条件 : 在多个线程访问同一个全局变量时,原子操作能保证数据的"原子性"(即线程安全),但它不等于可重入。
2、原子性不代表逻辑一致性:
-
假设函数
update()使用原子操作修改全局变量g_count。 -
线程 A 执行到一半,
g_count被原子地修改了。 -
此时中断发生,中断处理函数也调用了
update(),再次修改了g_count。 -
线程 A 恢复后,虽然
g_count的数值是正确的(没有出现数据撕裂),但如果线程 A 依赖于g_count在中断前后的一致性,其逻辑依然会崩溃。
结论: 原子操作主要用于解决"线程安全"问题,而可重入性要求更高------它要求函数即便在任何点被"切走"再"切回",逻辑必须完好如初。
(3) 核心对比表

(4) 为什么"加锁"不能解决重入问题?
这是一个常见的误区。为了线程安全,我们常给全局变量加锁(Mutex)。
-
如果一个函数在持有锁的情况下被中断。
-
中断处理函数(同一个执行流)尝试再次获取该锁。
-
由于锁已经被自己(在被中断的状态下)持有,函数会陷入死锁。
结论: 加锁可以实现线程安全,但往往会破坏重入性。可重入函数的设计原则是**"不共享"**而非"保护共享"。
(5) 总结:编写可重入函数的准则
- (1) 不使用全局变量或静态变量。
- (2) 不返回指向静态数据的指针。
- (3) 所有数据由调用者通过参数提供(使用调用者的栈空间)。
- (4) 不调用任何不可重入的库函数。
你之前提到的"对局部变量进行原子操作"之所以不影响重入性,是因为局部变量本身就在隔离的栈中,即便没有原子保护,它们也是天然隔离的。
结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 艾莉丝努力练剑 C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日练剑达成!"
"技术之路难免有困惑,但同行的人会让前进更有方向。" |
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!
往期回顾:
【Linux系统:多线程】Linux 内核与多线程深度强化干货25条
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა
