【问题标题】:Bizarre memory issue?奇怪的记忆问题?
【发布时间】:2013-01-05 07:01:11
【问题描述】:

我遇到了一个有趣的问题,我希望这完全是我的错。

我有从队列中读取的代码,如下所示:

 do {
    evt       = &newevts[ evt_head++ ];
    evt_head &=  MAX_EVENTS;

    if (evt->index <= 0 || evt->index > MAX_INDEX) {
         printf("RX EVENT BAD NDX: ndx=%d h=%d\n",evt->index, evt_head);
         continue;
    }

    //... etc ...

 } while(evt_head != evt_tail) ;

奇怪的问题是 if 语句可以评估为 evt->index 是一个错误的值,但是当 printf 显示时它显示一个完全有效的值!示例:

RX EVENT BAD NDX: ndx=1 h=64

if 语句清楚地表明条件必须是 1024(最大索引)。更糟糕的是,这种情况只会偶尔发生一次。我正在使用 GCC,Centos 6.3。除此线程外,没有线程接触 evt_head。 (为了确定,我已经重命名了几次并重新编译。)

尾部由一个函数处理,该函数将项添加到队列中,方法与头部删除它们的方式相同(递增然后 AND)。我还在事件结构本身内添加了一个计数器来记录头/尾值,因为事件被放入队列中,并且没有发现丢失或跳过的值。从字面上看,我好像读到了一些糟糕的记忆。但这太荒谬了——如果是这样的话,我希望系统崩溃或至少程序崩溃。

你有什么想法可以在世界上偶尔发生吗? (频率约为 100 次读取中的 1 次)感谢您的任何意见!

typedef struct {
    int    index;
    int    event;
} EVENT;

#define  MAX_EVENTS  0x01ff
#define  MAX_INDEX   1024

没有线程或其他代码触及 evt_head。只有这个循环。队列永远不会接近满员。我也碰巧在进入添加到队列的例程时有一个“SPIN LOCK”(为稍后被其他线程访问做准备),在退出时有一个 UNLOCK。

【问题讨论】:

  • 一方面,您在printf的格式字符串后面缺少第三个参数。
  • 能否请您说明您对MAX_EVENTSnewevts 的定义?
  • 另外,您确定MAX_INDEX 是== 1024 吗?也许我们也应该看到evt 指向的struct 定义。
  • 如果队列在一个线程中被填充并在另一个线程中被清空,您可能会意外覆盖您的事件,因为您在使用数据之前修改了evt_head。这可能会给写入线程一种印象,即实际上是一个完整的队列中有一个空闲空间。
  • 您确定evt_head 有效吗?你想对声明 evt_head &amp;= MAX_EVENTS; 做什么? evt_head 还怎么修改?

标签: c linux memory gcc


【解决方案1】:

我的猜测是,在写入 index 字段之前,将事件添加到尾部的函数将更改 evt_tail。这使您的读者可以访问仍在编写过程中的事件。

【讨论】:

  • 是的,我在事件删除代码周围添加了一个 SPIN_LOCK,问题就消失了。以前从未见过:我确信这是因为在我使用 ASM 优化了很多代码之后,它确实加快了速度并允许发生这种冲突。在优化之前,我猜这两者在时间上相距甚远,以至于它从未完全发生过。
  • 阅读器中没有任何自旋锁,您可以简单地先编写事件,然后才将新值传播到evt_tail?
  • 我想过这个问题,但是(我想)当前正在使用的 evt_head 事件仍有可能被访问。你肯定一针见血。
  • 除非您也只在读取循环的最后和continue 条件处增加evt_head。在队列满的情况下也可以保护您。如果 evt_headevt_tail 仅在完成各自的操作后才递增,则可以保证您的阅读器中没有自旋锁就可以了。
  • 也许其他人可以对此发表评论,但我认为我们这里有一个案例也需要内存围栏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-04
  • 2019-06-13
相关资源
最近更新 更多