【问题标题】:Why in Redis source code I didn't see memory barriers?为什么在 Redis 源代码中我没有看到内存屏障?
【发布时间】:2013-01-06 09:21:14
【问题描述】:

在Linux内核源代码中,有很多内存屏障(smp_mb()等)。

但是在redis的源码中,我没有看到。在redis的Makefile中,gcc优化选项是-O2,所以应该对这些指令重新排序。为什么它不使用 mb() 来确保正确的行为?

添加:

例如: 在 Linux 内核的 kfifo 中:

unsigned int __kfifo_put(struct kfifo *fifo,unsigned char *buffer, unsigned int len)   
{   
  unsigned int l;   
  len = min(len, fifo->size - fifo->in + fifo->out);
  smp_mb();
  l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));   
  memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
  ...
  smp_wmb();
  fifo->in += len;
  ...
}

在 Redis 源码中,我研究了整个项目,找不到内存障碍: 例如:

zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj) {
  zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
  unsigned int rank[ZSKIPLIST_MAXLEVEL];
  int i, level;
  ...
  level = zslRandomLevel();
  if (level > zsl->level) {
    for (i = zsl->level; i < level; i++) {
      rank[i] = 0;
      update[i] = zsl->header;
/////need a mb() ???
      update[i]->level[i].span = zsl->length;
    }
    zsl->level = level;
  }
  ...
}

为什么在redis中没有内存屏障有什么特别的吗?
我想可能是我对 mb() 的理解还不成熟,感谢您的评论...

添加:

但是在上面显示的两段代码中,linux内核中的kfifo使用了mb()。它只是更改分配在线程堆栈空间中的变量以及用户 mb() 在 r/w 操作之间。所以它不应该完全与多线程相关......(虽然redis是单线程)

【问题讨论】:

  • 这个问题太模糊了。具体来说,请展示一些您认为需要内存屏障才能正确操作但没有使用内存屏障的 Redis 代码。
  • 我对 Redis 的代码库一无所知。是否有理由认为您展示的跳过列表实现是线程感知的,更不用说线程安全了?
  • 在Multi-Processing(SMP..)中,如果用gcc打开优化,可能会重新排序指令。所以它可能是 CPU 从存储缓冲区读取/写入脏数据,所以需要 mb() 来确保正确的行为? (在我看来)
  • 好吧,如果只有一个线程查看/更改数据,那么所有这些都是完全无关的。不允许编译器以会破坏单线程代码的方式重新排序指令。
  • 那些数据在线程堆栈上,我认为它与线程无关,但与共享数据上的多 CPU 操作有关

标签: c memory gcc redis kernel


【解决方案1】:

Redis 是单线程的,因此不需要内存屏障。

仅当您有多个执行路径(例如在多线程应用程序中)时,它们才有意义。即使使用多线程用户空间应用程序,您通常也不需要自己的内存屏障,因为库(例如 pthreads)在同步 API 中包含内存屏障(例如互斥锁、信号量、条件变量等)。

【讨论】:

  • 但是在上面显示的两段代码中,linux内核中的kfifo使用了mb()。它只是更改分配在线程堆栈空间中的变量以及用户 mb() 在 r/w 操作之间。所以它不应该完全与多线程相关......
  • @Pei WenQian 不,它没有。它从传入的 struct kfifo 读取和写入,一个 struct kfifo 可以在内核中的多个不同任务之间共享,因此代码需要关心 fifo 的并发使用,但在某些情况下,您需要对 __kfifo_put 进行额外的锁定.
  • 哦!是的,我部分关注 len 参数,这些 mb() 不用于同步 len,针对 struct kfifo。非常感谢
【解决方案2】:

意识到很少的代码需要显式的内存屏障可能会有所帮助。此类代码的一些示例包括操作系统内核、线程库和无锁数据结构。

大多数其他代码会:

  1. 不直接与线程交互,因此不需要内存屏障;
  2. 使用操作系统提供的线程库(例如pthreads),并依赖此类库提供的memory ordering guarantees

如果在问这个问题时,您想到了 Redis 代码库的特定部分,请向我们展示相关代码。

【讨论】:

  • 非常感谢,我重新编辑了它。这是我第一次在stackoverflow上提问,请原谅我的疏忽
猜你喜欢
  • 2020-11-24
  • 2021-04-09
  • 1970-01-01
  • 2011-03-30
  • 2018-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-11
相关资源
最近更新 更多