在双核嵌入式系统中,由于多种原因,core0 需要中断 core1。枚举与联合配对用于传递中断的原因和数据。代码是用C编写的。
#include <stdatomic.h>
typedef enum { // the interrupt reason, also indicates which member is valid in the union
kData0,
// ...
} InterruptId;
typedef struct {
_Atomic InterruptId interruptId;
union { // data associated with interruptId
int data0;
// ...
};
} SharedMemory;
// core0
void InterruptCore1(); // write some registers in ARM GIC to interrupt core1
void PassData0ToCore1(SharedMemory *mem, int data0) {
mem->data0 = data0;
atomic_store_explicit(&mem->interruptId, kData0, memory_order_release);
InterruptCore1();
}
// core1
void ProcessData0(int data0);
void OnCore0Interrupt(SharedMemory *mem) {
InterruptId id =
atomic_load_explicit(&mem->interruptId, memory_order_acquire);
switch (id) {
case kData0:
ProcessData0(mem->data0);
break;
}
}
上面的代码似乎不正确,因为 core1 可能会看到过时的
interruptId
值。据我了解,释放-获取顺序并不能保证加载可以读取最新值。也许atomic_thread_fence就是这里的解决方案。
typedef struct {
InterruptId interruptId;
union {
int data0;
// ...
};
} SharedMemory;
// core0
void InterruptCore1(); // write some registers in ARM GIC to interrupt core1
void PassData0ToCore1(SharedMemory *mem, int data0) {
mem->data0 = data0;
mem->interruptId = kData0;
atomic_thread_fence(memory_order_release);
InterruptCore1();
}
// core1
void ProcessData0(int data0);
void OnCore0Interrupt(SharedMemory *mem) {
atomic_thread_fence(memory_order_acquire);
InterruptId id = mem->interruptId;
switch (id) {
case kData0:
ProcessData0(mem->data0);
break;
}
}
atomic_thread_fence
的发布版本和获取版本都编译为dmb ish
,这在机器级别似乎是正确的。但后来我读了C++版本的atomic_thread_fence
,发现它也需要使用原子变量,并且只有当load真正读取到释放的值时同步才有效。
这是否意味着第二个解决方案与第一个解决方案具有相同的问题(core1 可能读取过时的值)?如何保证core1能读取到最新的值?非常感谢。
据我了解,释放-获取顺序并不能保证加载可以读取最新值。
所有内存顺序保证所有线程看到“最新”写入所有原子对象。差异源于您在其他对象中可以看到的状态,即对其他对象的写入是否可以围绕原子读取或写入重新排序。