Chinaunix

标题: spin_lock 能否起到内存屏障作用? [打印本页]

作者: zylthinking    时间: 2013-02-01 12:27
标题: spin_lock 能否起到内存屏障作用?
如下:
在此先忽略spin_lock 用在这个例子是否有必要。


CPU 0                                  CPU 1
a = 1;
b = 2;
spin_lock(&lock);
a = 0;
wmb();
b = 0;
spin_unlock(&lock);           spin_lock(&lock);
                                       if (b == 0) assert(a == 1);
                                       spin_unlock(&lock);

我的分析是不能, 也就是说 CPU 1 的代码是有 bug 的, 正确代码应该是

spin_lock(&lock);
if (b == 0) { rmb(); assert(a == 1); }
spin_unlock(&lock);

如果我的分析是没有问题的, 似乎 rcu 里面有点代码就让我不放心了, 先看上面那个结论是否正确, 如果正确, 我再问问 rcu 那个逻辑。
作者: liuiang    时间: 2013-02-01 12:42
Z神已经走火入魔了。

如果你加了rmb,就不需要CPU1上的那对spinlock了。
作者: zylthinking    时间: 2013-02-01 12:44
liuiang 发表于 2013-02-01 12:42
Z神已经走火入魔了。

如果你加了rmb,就不需要CPU1上的那对spinlock了。


请参见从第三个字开始的那一句话
作者: liuiang    时间: 2013-02-01 12:49
你是指这句“先忽略spin_lock”,我当然看到了,关键是CPU0的spinlock不能忽略,CPU2的可以,当然前提是你要加rmb。
作者: zylthinking    时间: 2013-02-01 12:54
liuiang 发表于 2013-02-01 12:49
你是指这句“先忽略spin_lock”,我当然看到了,关键是CPU0的spinlock不能忽略,CPU2的可以,当然前提是你要 ...

你认为去掉 rmb 然后保留2对 spinlock, 这代码就没有 bug 了吗, 我正是觉得有 bug 才发帖的
作者: liuiang    时间: 2013-02-01 12:58
哦理解你的意思了,没有bug,因为锁隐含有内存屏障。
作者: liuiang    时间: 2013-02-01 12:59
如果加锁的话,cpu0的wmb也是不需要的。
作者: 瀚海书香    时间: 2013-02-01 13:02
回复 1# zylthinking
spin_lock起到内存屏障的作用

btw:LZ对内存屏障着魔了

   
作者: zylthinking    时间: 2013-02-01 13:09
liuiang 发表于 2013-02-01 12:58
哦理解你的意思了,没有bug,因为锁隐含有内存屏障。


不是, 第一, 锁隐含内存屏障这个说法我分析不出来道理, 第二, 就算隐藏, 似乎还是存在 bug
spin_lock 经典做法就是锁总线;
而根据塑料袋之前的说法,  wmb 保证的是 invalidate request 按次序到达 CPU 1,  那么, 可以推断 CPU 1 spinlock 成功后, 定然收到了两个 invalidate request; 但还是根据他的说法, 收到 invalidate request 和真正invalidate 之间还存在一个时间窗口, 那么在这个窗口期, 执行了 if (b == 0) assert(a == 0) 似乎没问题, 但这两个其实是可以乱序的, 假设先访问a呢, 由于还没有真正 invalidate , 则获取到的 a 为 1, 然后真正 invalidate 了 b, 然后 b 会取到最新的 0, 从而形成 b ==0, a == 1 的结果。

这个结果就和锁隐含内存屏障的说法矛盾了;

第二个更简单,甚至就是第一个完全一样, 如果隐藏内存屏障, 它隐藏在哪里? 很显然是 spin_lock 里面, 那么, 代码会变成这个样子:

rmb();
if (b == 0) { assert(a == 1); }

这个照样是 bug
作者: zylthinking    时间: 2013-02-01 13:10
本帖最后由 zylthinking 于 2013-02-01 13:11 编辑
瀚海书香 发表于 2013-02-01 13:02
回复 1# zylthinking
spin_lock起到内存屏障的作用

无法彻底理解, 只能是不断思考啊, 你试着解释一下我上一楼的分析?
spin_lock起到内存屏障的作用 就像定理一样, 背是可以的, 但真的必须证明后才能真正知道是怎么回事
作者: liuiang    时间: 2013-02-01 13:17
spin_lock和spin_unlock都隐含有屏障,并且都是mb,因为锁本身保证读写。

对于CPU1来说,当获得spin_lock之后,自己队列里面必然会有两个inv请求,此时,spin_lock里面的mb执行时,就会强行inv掉两个请求。

等到执行a b读的时候,都是从新获取的值。这里,在mb的过程中,inv请求在没有完全inv之前,肯定不会执行任何一个a b读请求的。
作者: zylthinking    时间: 2013-02-01 13:21
liuiang 发表于 2013-02-01 13:17
spin_lock和spin_unlock都隐含有屏障,并且都是mb,因为锁本身保证读写。

对于CPU1来说,当获得spin_loc ...


spinlock 若这样说, 我还有点信, 取决于 lock 总线到底是不是还做了 flush 那个什么 queue 的逻辑; 若说 spin_unlock 包含屏障, 你解释解释:
96#define __raw_spin_unlock_string \
  97        "movb $1,%0" \
  98                :"+m" (lock->slock) : : "memory"
  99
100
101static inline void __raw_spin_unlock(raw_spinlock_t *lock)
102{
103        __asm__ __volatile__(
104                __raw_spin_unlock_string
105        );
106}

屏障在哪里?
作者: 塑料袋    时间: 2013-02-01 13:33
看ARM或者mips的spinlock实现,这些体系上屏障的使用很明显,理论和实际严格一致。
x86上,晦涩,繁琐,超级不利于理解体系结构。
作者: zylthinking    时间: 2013-02-01 13:39
塑料袋 发表于 2013-02-01 13:33
看ARM或者mips的spinlock实现,这些体系上屏障的使用很明显,理论和实际严格一致。
x86上,晦涩,繁琐,超 ...


嗯, 看看 arm 确实看到了 spinlock 的第一句话就是 smp_mb,  另问一句, rmb 的语意是不是 flush 那个什么 queue? 还是什么
作者: zylthinking    时间: 2013-02-01 13:45
本帖最后由 zylthinking 于 2013-02-01 13:46 编辑

终于又明白点为什么锁的写法一般是:

加锁:
操作锁变量
mb();

a = 0;

解锁:
mb();
操作锁变量
的原因了;

这个 mb  的目的是 操作锁变量的语句 和锁内的 a = 0 做同步。
作者: liuiang    时间: 2013-02-01 13:46
回复 14# zylthinking


    以我现在的理解,rmb过程中,flush/drain那个inv队列是必须的。
作者: zylthinking    时间: 2013-02-01 13:51
liuiang 发表于 2013-02-01 13:46
回复 14# zylthinking


貌似是的, 现在正在逐渐加深理解中, 从塑料袋第一次讲解相关内容 http://bbs.chinaunix.net/thread-3593865-1-1.html 到现在, 已经一年多了, 总算越来越接近真相了
作者: liuiang    时间: 2013-02-01 13:55
这东西复杂度确实高,一年半载很正常,我也是研究了一两年了,现在一些问题无法完全连贯起来,更别提自己写程序了。
作者: yiifburj    时间: 2014-06-01 21:24
本帖最后由 yiifburj 于 2014-06-01 21:26 编辑

linux内核文档 Documentation/memory-barriers.txt

锁暗含内存屏障, 和 smp_rmb smp_wmb不同
lock     //lock之后的内存操作一定在lock之后开始,lock之前的内存操作可能在lock之后提交或完成

unlock  //unlock之前的内存操作要在unlock之前完成,unlock之后的内存操作可能在unlock之前提交或完成

unlock 后面带一个 lock 相当于完全内存屏障的作用。smp_mb
lock  code unlock 相当于限制code中的内存操作在lock后开始,unlock前完成,副作用,lock之前的内存操作在unlock之前一定会完成,只能越过lock不会越过unlock, unlock之后的内存操作一定在lock之后完成,只能越过unlock不能越过lock

所以对于锁保护的区间, 不需要内存屏障。
以上仅是个人理解,不保证正确性,内核文档可以好好读读。

我们公司的同事基本都是加锁,很少有人研究内存屏障, 实践表明应该是没有问题的, 忘记加锁,倒是出现过问题,就是一个添加链表,另一个恰好遍历到那个地方, 如果加了内存屏障,不加锁也不会出问题。不过这也是小概率时间,很不容易遇到。
作者: wth0722    时间: 2014-06-03 14:46
unlock 後面帶一個 lock 相當於完全內存屏障的作用。smp_mb
lock  code unlock 相當於限制code中的內存操作在lock後開始,unlock前完成,副作用,lock之前的內存操作在unlock之前一定會完成,只能越過lock不會越過unlock, unlock之後的內存操作一定在lock之後完成,只能越過unlock不能越過lock


好文,記錄一下




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2