ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PRAXIST 中的 embedded 原子操作回退机制:portable-atomic 中断禁用/临界区模块(interrupt)深度解析

PRAXIST 中的 embedded 原子操作回退机制:portable-atomic 中断禁用/临界区模块(interrupt)深度解析
  • 人工智能
  • AI Agent
  • 多智能体
  • Agent 工作流
  • 深度研究
  • AI 评测

【免费下载链接】PRAXIST

Autonomous research system for measurable, computer-executable research.

项目地址:https://gitcode.com/gh_mirrors/pr/PRAXIST
点击查看免费下载

本文以 PRAXIST 仓库中 vendored 依赖portable-atomic-1.15.0的 中断禁用/临界区回退模块文档 为核心,完整梳理该模块的两种临界区实现策略、各架构的汇编级关中断原语、无需关中断优化的原子操作清单以及相关的unsafe-assume-*安全边界,并结合 vendored 源码与 PRAXIST 示例工程的构建配置,说明这套 embedded 原子操作实现是如何被引入、在何种条件下被编译,以及开发者如何针对自己的目标平台选择正确的 feature。读完本文后,你将能够独立判断一个no-std目标应该使用critical-section、unsafe-assume-single-core还是纯disable-fiq/s-mode/zaamo等特性组合,并理解每次原子操作背后 guard 的生命周期与底层寄存器操作。

模块定位:为什么 portable-atomic 需要"禁用中断"这一层

portable-atomiccrate 的目标是在标准库原子类型不可用的目标平台上补齐完整的原子能力(包括 128 位原子、原子浮点等)。其能力分层为:

  • 对支持原子 CAS 的目标(几乎所有可用std的目标及多数no-std目标),直接提供Atomic{I,U}{8,16,32,64}全类型;
  • 对完全没有原子操作的目标(无 A 扩展的 RISC-V、MSP430、AVR),提供原子 load/store;
  • 对没有原子 CAS 的目标(thumbv6m、v6 之前的 Arm、无 A 扩展的 RISC-V、MSP430、AVR、Xtensa 等),提供原子 CAS(见 vendored README)。

后两类"补能力"的路线中,CAS 在单核/可关中断环境下无法用硬件指令实现,于是src/imp/interrupt/目录提供了基于禁用中断或临界区(critical section)的回退实现,即本文主角。目录内文件布局与模块文档一一对应:

文件内容
mod.rs临界区(guard)骨架、atomic_base!/atomic_int!宏体系、类型分发
armv6m.rsArm M-Profile(PRIMASK)
armv4t.rsv6 之前的 Arm / A-Profile(CPSR 的 I/F 位)
avr.rsAVR(SREG 的 I 位)
msp430.rsMSP430(SR 的 GIE 位)
riscv.rsRISC-V(mstatus/sstatus)
xtensa.rsXtensa(PS 特殊寄存器)

模块顶部注释明确了分工:mod.rs存放基于临界区的回退骨架,各架构文件存放各自"关中断"的具体实现(见 mod.rs 文件头注释)。

两种临界区实现策略与启用条件

模块文档(README.md)指出,本模块支持两种不同的临界区实现:

  1. 内置"禁用所有中断"实现
    • 在 MSP430 和 AVR 上,硬件始终是单核且没有非特权模式,因此默认启用;
    • 在 Armv6-M(thumbv6m)、v6 之前的 Arm(如thumbv4t、thumbv5te)、无 A 扩展的 RISC-V 以及 Xtensa 上,系统可能是多核或存在非特权模式,因此只有当用户显式声明"系统是单核且可用特权指令"时才启用,即打开unsafe-assume-single-corefeature(或 cfgportable_atomic_unsafe_assume_single_core)。
  2. 调用critical-sectioncrate 的实现(允许用户自行插入任意临界区实现)
    • 当用户通过critical-sectionfeature 显式请求时启用(见 vendored README 的可选特性章节)。

此外,unsafe-assume-privilegedfeature(cfgportable_atomic_unsafe_assume_privileged)也会复用本模块中"禁用中断"实现的那一部分。

在 mod.rs 中可以看到这两个 unsafe 开关如何组合:当portable_atomic_unsafe_assume_privileged成立而portable_atomic_unsafe_assume_single_core不成立时,整个items!主体(含 guard)被排除,仅保留unsafe-assume-privileged路径所需的关中断部件。架构选择则通过一组#[cfg_attr(..., path = ...)]在编译期完成(mod.rs 第 12–31 行):target_arch = "arm"时按mclasstarget feature 分派到armv6m.rs或armv4t.rs;avr/msp430/riscv32|riscv64/xtensa各对应独立文件;而critical-sectionfeature 打开时,arch子模块整体不参与编译。

unsafe-assume-single-core 的 unsound 边界

模块文档用加粗标出了一条重要警告:unsafe-assume-single-core的实现依赖特权指令关中断,因此通常不能工作在非特权模式下;在"特权指令不可用"或"所用指令不足以关断系统中所有中断"的环境中启用该 feature,通常被认为是unsound(细节因系统而异)。对无法使用unsafe-assume-single-core(或对应 cfg)的系统,文档的建议是改用critical-sectionfeature。

guard 模式:一次原子操作的完整生命周期

理解本模块最直接的方式,是看 mod.rs 中的guard模块。它被单独放在一个子模块里,目的注释写得明确——"Put this in its own module to prevent guard creation"(防止在错误的作用域意外构造 guard):

// critical-section implementations might use locks internally. #[cfg(feature = "critical-section")] const IS_ALWAYS_LOCK_FREE: bool = false; // 基于关中断的单核原子操作被视为 lock-free #[cfg(not(feature = "critical-section"))] const IS_ALWAYS_LOCK_FREE: bool = true; mod guard { // Note: The caller must NOT explicitly modify registers containing fields modified by disable/restore. #[inline(always)] pub(super) fn disable() -> Guard { Guard { #[cfg(feature = "critical-section")] // SAFETY: the state will be restored in the subsequent `release`. state: unsafe { critical_section::acquire() }, #[cfg(not(feature = "critical-section"))] // Get current interrupt state and disable interrupts. state: super::arch::disable(), } } pub(super) struct Guard { /* ... */ } impl Drop for Guard { #[inline(always)] fn drop(&mut self) { #[cfg(feature = "critical-section")] unsafe { critical_section::release(self.state) } #[cfg(not(feature = "critical-section"))] unsafe { super::arch::restore(self.state) } } } }

要点有四:

  • RAII 临界区:disable()返回的Guard在Drop时自动restore/release,中断状态(或critical-section的RestoreState)不会泄漏;
  • 两条实现路径由 feature 编译期二选一:critical-section开则走critical_section::acquire()/release(),关则走架构相关的arch::disable()/restore();
  • IS_ALWAYS_LOCK_FREE的语义差异:内置关中断路径认为单核关中断的原子操作是 lock-free(注释中还特别说明这与 Linux 内核为 pre-v6 Arm 提供的类似原子操作一致,因此同样视为 lock-free);而critical-section实现"内部可能使用锁",故置为false;
  • 寄存器纪律:注释要求调用方不得显式修改被disable/restore触碰的寄存器字段(其余架构文件的注释进一步解释了算术标志位为何可以安全地被整寄存器覆写)。

compare_exchange:CAS 的"软件模拟"

在没有任何硬件 CAS 的目标上,compare_exchange就是 guard 加"读-比-写"三步(mod.rs 第 139–155 行):

pub(crate) fn compare_exchange( &self, current: $value_type, new: $value_type, success: Ordering, failure: Ordering, ) -> Result<$value_type, $value_type> { crate::utils::assert_compare_exchange_ordering(success, failure); let guard = disable(); let prev = self.read(&guard); if prev == current { self.write(new, &guard); Ok(prev) } else { Err(prev) } }

模块内注释解释了为什么回退实现里必须有 CAS:CAS 是"万能 RMW",如果硬件已有 CAS 指令,该回退代码根本不会被编译进来("If the CAS instruction exists, all atomic operations can be implemented by it, so this code will not be used")。compare_exchange_weak直接委托给强版本(第 161–169 行),符合"回退路径无需弱版本优化"的定位。

宏体系与操作分层

atomic_base!宏(第 90–225 行)生成了三种关键行为:

  • (base, ...):定义#[repr(C, align($align))]的原子类型结构体(内部为UnsafeCell<$value_type>),并为其unsafe impl Send/Sync(SAFETY 注释即模块级注释:数据竞争由关中断/原子内建/临界区防止);
  • (native_load_store, ...):不关中断的 load/store——通过Deref把回退类型直接转投为core::sync::atomic中的原生类型(两者布局一致、原子性语义兼容),这是"指针大小或更小的 load/store 无需关中断"这条优化的实现手段;
  • (emulate_load_store, ...)/(emulate_swap, ...):需要 guard 的模拟版本,如load就是assert_load_ordering+disable()+read(&guard)(第 197–211 行)。

atomic_int!宏(第 342–528 行)在此之上做按目标分发的 RMW 选择,关键分支有三处:

  1. feature = "critical-section"时走all_critical_session分支——所有load/store 与 RMW 都套临界区(第 367–372 行、431–443 行),这正对应模块文档"启用critical-sectionfeature 时,所有原子操作都取临界区"一句;
  2. 其他情况下 load/store 走native_load_store(AVR 在极老 rustc、portable_atomic_no_asm时退化为emulate_load_store),RMW 再按架构选择:
    • AVR +rmwtarget feature:emulate_arithmetic(利用硬件 8 位 RMW 指令优化fetch_add/sub/max/min);
    • RISC-V +zaamo(或force-amo/portable_atomic_force_amocfg):32 位(RV32)/{32,64} 位(RV64)直接用 AMO 指令(cas分支为空实现),子字宽按有无zabha分别处理(第 513–527 行);
    • 其余:cas[emulate]=emulate_swap+emulate_arithmetic+emulate_bit,即全部 guard 化模拟(第 508–512 行)。

类型注册段(第 530–612 行)展示了完整的分发矩阵:AVR 因为 16 位原子操作不可拆,AtomicIsize/AtomicUsize及各子字宽类型全部走all_critical_session;非 AVR 目标则load_store_atomic+ RMW 分层。更宽类型(16 位指针目标上的AtomicI32/AtomicU32、无 64 位快速原子的目标上的AtomicI64/AtomicU64、以及AtomicI128/AtomicU128)一律是all_critical_session(第 614–676 行),因为关中断 + 手工字节循环是唯一的通用兜底。

文件末尾是模块自带的单线程测试(第 679–696 行),对AtomicPtr与i8…usize全类型展开test_atomic_ptr_single_thread!/test_atomic_int_single_thread!,验证 guard 骨架在宿主上的行为正确性。

各架构的关中断原语:寄存器与指令映射

模块文档(README.md 第 18–28 行)列出了"通过显式启用 feature 可改变实现"的完整矩阵,下面将其与 vendored 源码逐一对照。每个架构文件都实现同一对函数:disable() -> State(读取并返回原中断状态,同时关中断)与unsafe restore(State)(恢复原状态)。

目标/条件状态寄存器与位vendored 源码中的关键指令
Arm M-Profile(Armv6-M 起)PRIMASKmrs {primask}, PRIMASK+cpsid i;恢复用msr PRIMASK, {prev}(armv6m.rs)
Arm 非 M-ProfileCPSR的 I 位(IRQ mask)mrs {prev}, cpsr/orr {new}, {prev}, 0x80/msr cpsr_c, {new}(armv4t.rs)
Arm 非 M-Profile +disable-fiqCPSR的 I 位和 F 位(FIQ mask)掩码变为0xC0(#[cfg(portable_atomic_disable_fiq)]分支,armv4t.rs 第 8–22 行)
RISC-V(M-mode)mstatus的 MIE 位(0x8)csrrci {status}, mstatus, 0x8(riscv.rs)
RISC-V +s-modesstatus的 SIE 位(0x2)csrrci {status}, sstatus, 0x2(同一文件的portable_atomic_s_mode分支,riscv.rs 第 26–45 行)
RISC-V +zaamo(或force-amo)有对应 AMO 指令的 RMW 直接走 AMO,其余仍关中断由mod.rs的atomic_int!分发(见上文第 500–515 行区间)
MSP430状态寄存器SR的 GIE 位mov r2, {sr}+dint { nop;恢复为nop { mov {prev}, r2 { nop(msp430.rs 第 30–86 行)
AVR状态寄存器SREG的 I 位in {sreg}, 0x3F+cli(avr.rs 第 30–56 行)
XtensaPS特殊寄存器rsil a14, 15(INTLEVEL=15 关断全部中断);恢复wsr.ps a14+rsync(xtensa.rs 第 25–63 行)

从这些实现细节中还能读到几条工程取舍:

  • nostack且不用nomem/readonly是刻意的:几乎每个asm!都注释"防止后续内存访问被重排到关中断之前 / 前一次访问被重排到开中断之后",这是保证临界区内存序语义的关键;
  • preserves_flags的取舍:MSP430 的DINT与 AVR 的CLI会修改状态寄存器(即标志位寄存器本身),因此不能标preserves_flags;而 RISC-V/Xtensa/Arm 的关中断指令不碰算术标志,可以标注以减少不必要的保存/恢复;
  • MSP430 的nop前缀:dint { nop与nop { mov ... { nop中的 NOP 序列来自 TI 用户指南的"Enable and Disable Interrupt"说明,用于吸收流水线/取指延迟(msp430.rs 第 37、73 行 的注释);
  • Xtensa 恢复后的rsync是必需的:注释指出"SYNC after WSR is required to guarantee that subsequent RSIL read the written value"(xtensa.rs 第 54–63 行),而关中断方向的SYNC after RSIL则被明确判定为不需要;
  • portable_atomic_no_asm降级路径:MSP430/AVR 等文件保留了旧式llvm_asm!宏作为极老工具链的回退(如 avr.rs 第 48–56 行)。

哪些原子操作可以完全绕过关中断

模块文档(README.md 第 30–38 行)给出了一份"无需关中断的操作"清单,它对应着前文宏体系中native_load_store与cas(真 AMO)分支的启用条件:

  • AVR 以外:指针大小或更小的 load 和 store;
  • AVR:8 位 load 和 store;
  • AVR +rmwtarget feature:额外加上 8 位swap;
  • MSP430:额外支持 {8,16} 位add,sub,and,or,xor,not(不返回旧值的 RMW,硬件指令直接可用);
  • RISC-V +zaamo(portable_atomic_target_feature="zaamo"cfg、force-amofeature 或portable_atomic_force_amocfg 等价):
    • 32 位(RV32)/{32,64} 位(RV64)的swap,fetch_{add,sub,and,or,xor,not,max,min},add,sub,and,or,xor,not,以及 {8,16} 位的fetch_{and,or,xor,not},and,or,xor,not1;
    • AtomicBool的全部操作。

而一旦启用critical-sectionfeature,规则被推翻:所有原子操作都会取临界区(对应mod.rs中all_critical_session分支对 load/store 与全部 RMW 的统一处理,以及IS_ALWAYS_LOCK_FREE = false的取值)。这条设计取舍在 mod.rs 第 549–556 行的注释 中也被解释过:单核上使用临界区模拟 CAS 与硬件原子 load/store 混用是安全的,因为 load/store 是原子的,不受中断影响。

模块文档还留了一句开放邀请:"Feel free to submit an issue if your target is not supported yet."——即该模块的目标支持面是按需扩展的,当前 vendored 快照(1.15.0)覆盖上表所列六类架构。

feature / cfg 速查与安全边界

结合 vendored Cargo.toml 中的 feature 声明与模块文档,与本文主题直接相关的开关如下:

Feature(Cargo feature)对应 cfg作用
unsafe-assume-single-coreportable_atomic_unsafe_assume_single_core声明单核 + 特权指令可用,启用内置关中断 CAS 回退(Avrv/MSP430 默认无需声明)
unsafe-assume-privilegedportable_atomic_unsafe_assume_privileged声明特权指令可用,复用关中断实现部分
critical-sectionportable_atomic_critical_section改用critical-sectioncrate 的可插拔临界区
disable-fiqportable_atomic_disable_fiqArm 非 M-Profile 上同时屏蔽 CPSR 的 I 位与 F 位
s-modeportable_atomic_s_modeRISC-V 用sstatus/SIE 位而非mstatus/MIE 位
force-amoportable_atomic_force_amoRISC-V 有 AMO 指令的 RMW 一律走 AMO
fallbackportable_atomic_fallback启用双字宽及以上的兜底原子(对应all_critical_session的宽类型注册)

使用建议可归纳为一条决策链:目标硬件单核且有特权关中断指令 →unsafe-assume-single-core(或 MSP430/AVR 默认);无法证明单核/特权 →critical-section并自行提供临界区实现;RISC-V 有 A 扩展/zaamo→ 尽量让 RMW 走 AMO;多核且无硬件 CAS → 只能依赖critical-section背后的自旋锁等实现(此时原子操作不再 lock-free)。需要特别强调模块文档的警告:在不满足特权前提的环境启用unsafe-assume-single-core通常被视为unsound,细节依赖具体系统。

它在 PRAXIST 仓库中的位置:为什么这里有一份嵌入式原子操作源码

上述模块出现在 PRAXIST 的 rocket_booster_recovery_rust 示例 的vendor/目录下。它的引入路径是标准的 Cargo 依赖链,而非该 Rust 示例直接依赖portable-atomic:

  1. 示例根 Cargo.toml 依赖ndarray = "0.16"等 CPU 侧数值栈;
  2. vendored 的 ndarray 0.16.1 的 Cargo.toml 中存在条件依赖:[target.'cfg(not(target_has_atomic = "ptr"))'.dependencies]下才引入portable-atomic与portable-atomic-util(第 356–359 行),并且提供portable-atomic-critical-section = ["portable-atomic/critical-section"]的透传 feature(第 343 行);
  3. 示例根 Cargo.lock 锁定了portable-atomic 1.15.0与portable-atomic-util 0.2.7;
  4. .cargo/config.toml 将 crates-io 源整体替换为本地vendor目录([source.crates-io] replace-with = "vendored-sources"),task_linux/.cargo/config.toml 指向../vendor,从而让三个平台任务档案(task_GPU_server/、task_linux/、task_macos/)都能在离线/受控环境下用同一份 vendor 源码构建。

因此对 x86_64/aarch64 Linux 或 Apple Silicon 主机来说,target_has_atomic = "ptr"恒为真,portable-atomic不会被编译进最终二进制,src/imp/interrupt/目录自然也不会参与编译——它作为可复现构建的 vendored 源码完整保存在仓库中;一旦把ndarray(或任何启用require-cas/fallback的依赖)交叉编译到无原子 CAS 的 embedded 目标,本文描述的这套 guard/关中断回退就会自动生效。查看方式很简单:直接阅读上述 vendored 源码与模块文档;构建示例本身则按 README 指引,通过praxist examples install rocket_booster_recovery_rust安装可写副本后再执行cargo build/cargo test(仓库中的vendor/是只读分发资产,不应原地构建修改)。

小结

portable-atomic的interrupt模块是"无硬件 CAS 目标如何获得完整原子语义"这个问题的工程答案:mod.rs提供 feature 二选一的 guard 骨架与按架构分发的宏体系,六个架构文件提供寄存器级的关中断/恢复原语(PRIMASK、CPSR 的 I/F 位、mstatus/sstatus、SR 的 GIE 位、SREG 的 I 位、PS),而模块文档则以清单形式界定了每种 feature 组合下的行为差异与 unsound 风险边界。PRAXIST 将其作为ndarray依赖链的一部分 vendored 进rocket_booster_recovery_rust示例,使这份 embedded 原子操作实现在仓库中可直接检索、可交叉验证——理解它,就是理解单核裸机平台上compare_exchange背后"关中断—读—比—写—开中断"的完整生命周期。


  1. 文档脚注原文:With thezabhatarget feature, {8,16}-bitswap,fetch_{add,sub,max,min},add,subtoo.

    ↩
  • 人工智能
  • AI Agent
  • 多智能体
  • Agent 工作流
  • 深度研究
  • AI 评测

【免费下载链接】PRAXIST

Autonomous research system for measurable, computer-executable research.

项目地址:https://gitcode.com/gh_mirrors/pr/PRAXIST
点击查看免费下载

相关推荐

上一篇:U-Net训练监控终极指南:使用TensorBoard实时跟踪训练过程
下一篇:patcher9x快速指南:3步让Windows 98在新CPU上稳定运行

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进