You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多核嵌入式Rust中,使用static mut实现单向跨核数据共享是否合适?

多核嵌入式Rust中用static mut实现单写单读跨核数据共享是否可行?

问题背景

在多核嵌入式Rust开发场景中,是否适合使用static mut实现单核心写入、另一核心读取的单向数据共享?以下是基于embassy框架的示例代码:

#![no_std]
// …

static mut CORE1_STACK: Stack<4096> = Stack::new();
static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
static EXECUTOR1: StaticCell<Executor> = StaticCell::new();

static mut one_way_data_exchange:u8 = 0;


#[cortex_m_rt::entry]
fn main() -> ! {
    spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || {
        let executor1 = EXECUTOR1.init(Executor::new());
        executor1.run(|spawner| unwrap!(spawner.spawn(core1_task())));
    });

    let executor0 = EXECUTOR0.init(Executor::new());
    executor0.run(|spawner| unwrap!(spawner.spawn(core0_task())));
}

#[embassy_executor::task]
async fn core0_task() {
    info!("Hello from core 0");
    loop {
        unsafe { one_way_data_exchange = 128; } // sensor value
    }
}

#[embassy_executor::task]
async fn core1_task() {
    info!("Hello from core 1");
    let sensor_val:u8 = 0;
    loop {
        unsafe { sensor_val = one_way_data_exchange; }

        // continue with rest of program
        }
    }
}

已知双核心同时写入会产生竞态,但如果仅核心0写入、核心1读取,且业务上允许错过部分输入(不严格要求读写顺序),这种情况下是否还存在问题?


核心分析

直接用static mut实现跨核单写单读是不安全且不可靠的,核心原因如下:

  • 内存可见性问题:现代CPU的缓存架构会导致核心0写入的值可能仅停留在本地缓存,核心1无法立即感知到最新值。同时编译器可能因无法识别跨核访问,对读写操作做优化(比如将读取缓存到寄存器,不会每次都从内存取值),导致核心1持续读取旧数据。
  • Rust unsafe契约违反:static mut的unsafe使用要求同一时间只能有一个线程/核心访问,跨核并行读写(哪怕是单写单读)已经违反了这个契约,属于未定义行为,编译器可能生成不符合预期的机器码。
  • 潜在的原子性隐患:虽然示例中是u8(多数ARM架构下是单字节原子操作),但如果后续换成更大的类型(比如非对齐的u32或自定义结构体),跨核读写可能出现部分写入/读取的情况,拿到不完整的数据。即使是u8,也不能依赖硬件原子性来保证长期可靠性,因为没有显式告知编译器和CPU这是跨核共享数据。

适合的替代方案

既然业务允许错过部分输入,推荐用以下更安全的方式:

原子类型(最适合轻量场景)

使用core::sync::atomic模块下的原子类型(比如AtomicU8),它能保证内存可见性和原子性,无需unsafe即可安全跨核读写。示例修改如下:

static ONE_WAY_DATA_EXCHANGE: AtomicU8 = AtomicU8::new(0);

#[embassy_executor::task]
async fn core0_task() {
    info!("Hello from core 0");
    loop {
        ONE_WAY_DATA_EXCHANGE.store(128, Ordering::Relaxed);
        // 业务逻辑延迟
    }
}

#[embassy_executor::task]
async fn core1_task() {
    info!("Hello from core 1");
    loop {
        let sensor_val = ONE_WAY_DATA_EXCHANGE.load(Ordering::Relaxed);
        // 数据处理逻辑
        // 业务逻辑延迟
    }
}

这里使用Ordering::Relaxed就足够,因为业务不关心严格的读写顺序,只需要保证读取到的是某个时刻的有效值。

单生产者单消费者队列(SPMC)

如果需要保留多个输入值而非直接覆盖,可以使用嵌入式场景下的SPMC队列(比如embassy自带的队列或第三方实现),但如果仅允许覆盖最新值,原子类型的开销更小。


结论

即使是单写单读的跨核场景,也绝对不能用static mut实现数据共享,会引入未定义行为和潜在的稳定性问题。原子类型是这种轻量、允许丢失部分输入的跨核单向共享场景的最优选择。

内容的提问来源于stack exchange,提问作者risingtiger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 00:48:38