1. 原子操作的本质与重要性
在嵌入式开发领域,特别是使用STM32这类资源受限的微控制器时,理解原子操作的概念至关重要。原子操作就像是一个不可分割的"量子"操作——要么完整执行,要么完全不执行,不存在中间状态。这个概念在金融交易中很容易理解:银行转账必须保证扣款和入账两个操作同时成功或同时失败,不能出现钱扣了但对方没收到的情况。
对于STM32这样的单核处理器,虽然单条汇编指令通常是原子的(因为处理器保证指令执行的完整性),但问题出在高级语言与机器指令的映射关系上。我们写的C语言语句count++看似简单,但编译器会将其分解为多个机器指令。这就好比把一封信的投递过程拆分为"取出信件"、"走到邮箱"、"投入信箱"三个步骤——如果在"走到邮箱"的途中被打断,就可能出现信件丢失的情况。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. count++操作的深度解析
2.1 从C语言到汇编的转换过程
让我们深入分析这个看似简单的count++操作。假设我们有一个16位的全局变量count存储在内存地址0x20000000处。在C语言层面,这只是一行代码:
c复制count++; // 看似简单的自增操作
但在ARM Cortex-M3架构下,编译器会生成如下汇编指令序列:
assembly复制LDRH r0, [r0, #0x00] ; 1. 从内存加载count值到寄存器r0
ADD r0, r0, #1 ; 2. 将寄存器r0的值加1
STRH r0, [r0, #0x00] ; 3. 将新值写回内存
这个"读-改-写"三步骤的操作序列,就是导致非原子性的根本原因。就像是一个需要三个动作才能完成的机械操作——抬起手柄、旋转齿轮、按下开关——如果在旋转齿轮时被打断,整个操作就会处于不一致状态。
2.2 数据竞争的实际案例分析
让我们通过一个具体场景来说明问题。假设:
- 主程序正在执行
count++(当前值=10) - 同时串口中断发生,需要执行
count--
时间线会这样展开:
- 主程序读取:执行
LDRH指令,将count值10加载到寄存器r0 - 中断发生:在ADD指令执行前,串口中断触发
-
中断服务程序执行:
