【发布时间】:2015-10-08 20:45:26
【问题描述】:
我正在使用基于写入索引和读取索引的简单软件队列。
介绍详情;语言:C,编译器:GCC 优化:-O3 带额外参数,架构:Armv7a,CPU:多核,2 Cortex A-15,L2 缓存:共享和启用,L1 缓存:每个 CPU,启用,架构应该是缓存连贯。
CPU 1 负责写入,CPU 2 负责读取。下面是非常简化的示例代码。您可以假设索引的初始值为零。
常见:
#define QUE_LEN 4
unsigned int my_que_write_index = 0; //memory
unsigned int my_que_read_index = 0; //memory
struct my_que_struct{
unsigned int param1;
unsigned int param2;
};
struct my_que_struct my_que[QUE_LEN]; //memory
CPU 1 运行:
void que_writer
{
unsigned int write_index_local;
write_index_local = my_que_write_index; //my_que_write_index is in memory
my_que[write_index_local].param1 = 16; //my_que is my queue and stored in memory also
my_que[write_index_local].param2 = 32;
//similar writing stuff
++write_index_local;
if(write_index_local == QUE_LEN) write_index_local = 0;
my_que_write_index = write_index_local;
}
CPU 2 运行:
void que_reader()
{
unsigned int read_index_local, param1, param2;
read_index_local = my_que_read_index; //also in memory
while(read_index_local != my_que_write_index)
{
param1 = my_que[read_index_local].param1;
if(param1 == 0) FATAL_ERROR;
param2 = my_que[read_index_local].param2;
//similar reading stuff
my_que[read_index_local].param1 = 0;
++read_index_local;
if(read_index_local == QUE_LEN) read_index_local = 0;
}
my_que_read_index = read_index_local;
}
好的,在正常情况下,永远不会发生致命错误,因为队列的 param1 始终以常量值 16 存储。但不知何故,队列的 param1 发生了 0 并发生了致命错误。
很明显,这在某种程度上是一个竞争条件问题,但我不知道它是如何发生的。索引由 CPU 单独更新。
我不想在不了解问题核心的情况下用内存屏障填充我的代码。你知道这是怎么回事吗?
详细说明:这是一个baremetal系统,这些代码是禁止中断的,没有抢占和任务切换。
【问题讨论】:
-
不要发布“简化版”,而是 minimal reproducible example。你甚至不显示声明。
-
您知道编译器可能会随意重新排序代码吗?您将需要
stdatomic.h。有很多文章,包括实现线程安全缓冲区的code-sn-ps;我强烈建议您先仔细阅读它们。例如:为什么读取代码实际上要重新读取写入索引,因为从它的角度来看它没有改变? -
为了确定,您已在两个内核上启用了 ACTLR SMP 位,是吗?它们是否都使用相同的页表,以及设置的内存类型和属性是什么?至于了解您需要障碍的位置和原因(假设您实际上并未使用未缓存的强排序内存),我建议您尝试理解this document。
-
你能从你的工具链中发布这段代码的反汇编汇编代码,然后你可能会知道编译器做了什么重新排序。
标签: c gcc embedded race-condition armv7