免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: zylthinking
打印 上一主题 下一主题

为什么用户空间程序不用考虑内存屏障? [复制链接]

论坛徽章:
4
戌狗
日期:2013-08-15 18:22:43技术图书徽章
日期:2013-08-21 13:48:45巨蟹座
日期:2013-09-26 17:06:39处女座
日期:2013-12-25 11:26:10
1 [报告]
发表于 2011-10-14 16:09 |显示全部楼层
进程切换的时候,必然是需要一个同步点的,这是一个一般原则。

multipul processor memory consistency model (?好像是这个名字),又缩写为WRL-95-9的一篇文章里,专门有一章thread migration,谈了半天这个问题。

论坛徽章:
4
戌狗
日期:2013-08-15 18:22:43技术图书徽章
日期:2013-08-21 13:48:45巨蟹座
日期:2013-09-26 17:06:39处女座
日期:2013-12-25 11:26:10
2 [报告]
发表于 2011-10-14 16:23 |显示全部楼层
回复 5# asuka2001


   
基本原理概括为两句话:
1) CPU2若看到CPU1执行了某条指令,则必可看到CPU1中这条指令之前的所有指令。
2) CPU3若看到CPU2执行了某条指令,而此时CPU2若看到CPU1执行了某条指令,则CPU3必可看到CPU1中的这条指令。该原则递归。



另外啊,可别引用我的帖子了,我说的不够好。


这两句话的详细准确解释,在arm architecture reference manual ARM-v7a中,里边有解释dmb这个同步指令的段落。

他们不叫“看到”,而是叫“observed”,并对何为observed进行了解释; 然后将所有访问分成GROUP A和GROUP B,进行了全面的讨论。

大意就是这两句话的意思,但是显然人家说的更全面和准确

论坛徽章:
4
戌狗
日期:2013-08-15 18:22:43技术图书徽章
日期:2013-08-21 13:48:45巨蟹座
日期:2013-09-26 17:06:39处女座
日期:2013-12-25 11:26:10
3 [报告]
发表于 2011-10-14 17:27 |显示全部楼层
草,我写了半天,争取能回答你那三个问题

结果由于现在在华为这个地方出差,不知怎的把我网络重置了,靠,写的那些没有了。



等周日再写吧,我下班了,现在要去东莞了。

论坛徽章:
4
戌狗
日期:2013-08-15 18:22:43技术图书徽章
日期:2013-08-21 13:48:45巨蟹座
日期:2013-09-26 17:06:39处女座
日期:2013-12-25 11:26:10
4 [报告]
发表于 2011-10-14 19:13 |显示全部楼层
就等你呢, 那么现在一个基本问题: 缺乏 barrier 造成的代码重排, 可能导致
thread A:

ptr->buf  ...
zylthinking 发表于 2011-10-14 16:22



1) 先说thread A和thread B,单核。

     我认为thread A是需要barrier的,但是thread B是不需要的。

    thread B两个指令间,存在control dependency,这种情况就我知道的知识范围中,编译器是不会重新排序的。


2) 然后说CPU A和CPU B,多核。

   x86我不大了解,搞不定。只说使用weak order的CPU。




   CPU A需要加wmb()或者mb(),这个指令要求buf先写完后,再写flag。   

   如果不加wmb/mb的话:

   a) 在ARM等基于bus boardcast的SMP中,向各个CPU提交本次写操作所对应的invalidate request这个动作,是原子的,即所有CPU同时收到这个invalidate request。因此,所有CPU以同样的顺序,要么先看到写buf对应的invalidate request,要么先看到写flag对应的invalidate request。

   b) 在NUMA结构中,向各个CPU提交本次写操作所对应的invalidate request这个动作,它不是原子的。即有的CPU会先看到buf对应的invalidate request,有的CPU会先看到flag对应的invalidate request。只有加入wmb()/mb(),才能保证所有CPU先收到buf对应的invalidate request,再收到flag对应的invalidate request。



   到了CPU B这里,CPU B同样也会看到这两条指令间的control dependency,而且第二条指令是个写操作。

   大部分CPU都有预读这个功能,但是好像没有CPU会预写,因为预写写错了的话,没法撤销。

   所以CPU B会老老实实的先执行读flag。


   
   但是即使CPU B先读flag,还是有的CPU不要求有rmb()/mb(),有的则要求。

   先说不要求的,大部分CPU在逻辑上,有且仅有一个incoming queue,里边存放收到的各invalidate request,以及read reply等。

   这个incoming queue的真实情况是相邻的invalidate request可以重新排序,但是invalidate request及read reply之间,绝对不能重新排序。为了好理解,你可以认为这个incoming queue从不重新排序,这个是最直接的设计方法。

  如果从不重新排序的话,那么就可以保证,在看到后提交的invalidate request时,必然已经看到了先提交的invalidate request。如果CPU A加了wmb()/mb()的话,就是说CPU B看到对flag的写时,必然已经看到wmb()之前的写操作。

   这个的典型就是cortex-a9。

  
   但是linux kernel的内存模型是基于alpha的,alpha的CPU B中,必须加入rmb()/mb()。

   因为CPU对cache的读写端口越多越好,可以同时执行更多的读写操作。但是作为cache的RAM,其读写端口越多就设计越复杂,所以alpha用了两个RAM,使读写端口数量增加了一倍,带来的问题是有了两个incoming queue,因此没办法保证处理invalidate request及read reply的顺序,或者说它的incoming queue总是乱排序的,这样就不能保证看到后提交的invalidate request时,必然已经看到了先提交的invalidate request。

   解决的办法就是在读完flag后,程序主动执行rmb(),主动的处理所有invalidate request,这样才能保证看到flag的新值时,必然能看到buf的新值。

   体现在kernel中,就是RCU的读,好像叫rcu_dereference????   
  
   RCU写时,写结构,执行mb(),写结构的指针。
  
   RCU读时,一般的CPU只要能读到结构的指针,则必然能读到结构的内容;但是alpha则不然,它在读结构的指针和读结构之间,必须加入一个rmb()。

论坛徽章:
4
戌狗
日期:2013-08-15 18:22:43技术图书徽章
日期:2013-08-21 13:48:45巨蟹座
日期:2013-09-26 17:06:39处女座
日期:2013-12-25 11:26:10
5 [报告]
发表于 2011-10-14 19:27 |显示全部楼层
下雨也不能阻挡我去东莞的心!

我去长安镇的康城名仕桑拿了,那里有情景房,我预订了个教室房,一会有老师上课。

论坛徽章:
4
戌狗
日期:2013-08-15 18:22:43技术图书徽章
日期:2013-08-21 13:48:45巨蟹座
日期:2013-09-26 17:06:39处女座
日期:2013-12-25 11:26:10
6 [报告]
发表于 2011-10-15 18:49 |显示全部楼层
LZ不懂多线程编程,建议好好看看 POSIX多线程程序设计 这书。
另你这段程序有问题,没有判断malloc是否成功 ...
smalloc 发表于 2011-10-15 11:36



不相关或独立性,并不是乱序的根源。

而且以CPU的视角来说,根本不存在不相关的指令,即使他们看起来毫无关联。



即使两个指令不相关,但是他们有可能对同地址进行读写,如果有对同地址的W-->W,W-->R.... 这也称之为dependency,前者叫output dependency,后者为anti dependency。此时load/store unit必须知道指令的顺序,才能产生正确结果。

而且我们认为计算目标地址的过程是,基地址+偏移 ---> 虚拟地址,虚拟地址+TLB ---> 物理地址。所以不到计算出来物理地址的最后一刻,CPU根本不知道那些指令间存在output dependency,anti dependency......

比如说有一条store指令的物理地址还没出来之前,则store后边的load/store指令,从本质上说是不能发射的。



但是站在cache的视角来观察load / store,则即使存在control dependency,true data dependency(后边指令的操作地址或者操作数依赖于前边),则这些指令也是可以乱序的。



粗略的原理概述就是:

load / store unit总是顺序的收到各load / store指令,或者是知道各load / store指令的顺序信息。

load / store unit乱序的计算各指令的目标物理地址

当目标物理地址确定后,load / store unit又知道指令间的顺序信息,所以可以判断那些指令间存在output dependency或者anti dependency。

load / store unit既然顺序的判断了指令间的dependency,现在可以顺序的将load / store的操作提交给下一级。下一级是个缓冲区,原始的就一个store buffer,复杂的称为LSQ,这个缓冲区直接和cache打交道。

LSQ还是顺序的看到load / store,但是LSQ却乱序的将load /store提交给cache,或者根本不提交。

对于LSQ来说,不仅对不同地址的操作可以随便乱序;即使对相同地址的操作,同样可以乱序。比如说将两个同地址的W合为一个,可以将W的数据直接返回给后边对同地址的R........


LSQ之所以敢于这样乱序的原因是,LSQ中的R/W都是必须要完成的,即使CPU由于中断等,发生了roll-back,这些R/W也不会被撤消;而且,load / store unit在看cache时,只要考虑到LSQ,将LSQ与cache的数据联合在一起看,则load / store unit看到的结果是顺序的。

任何时候,只要将LSQ中的数据写回了cache,则cache就得到了一种顺序RW的结果。

论坛徽章:
4
戌狗
日期:2013-08-15 18:22:43技术图书徽章
日期:2013-08-21 13:48:45巨蟹座
日期:2013-09-26 17:06:39处女座
日期:2013-12-25 11:26:10
7 [报告]
发表于 2011-10-16 00:28 |显示全部楼层
总结一下吧:
操作系统对线程切换时会不会做一个内存屏障的问题, 现在感觉没必要做了, 至少没必要针对这个问题专门做了---单CPU不需要, 多核心做了也没有太大意义
zylthinking 发表于 2011-10-15 22:50



不是这样的。

对于这个问题的准确描述,见《Memory Consistency Models for Shared-Memory Multiprocessors》---by Kourosh Gharachorloo的212页,

5.7 Interaction with Thread Placement and Migration


里边解释的很详细,而且这个论文算是多核标杆了。

论坛徽章:
4
戌狗
日期:2013-08-15 18:22:43技术图书徽章
日期:2013-08-21 13:48:45巨蟹座
日期:2013-09-26 17:06:39处女座
日期:2013-12-25 11:26:10
8 [报告]
发表于 2011-10-19 22:30 |显示全部楼层
回复 85# zylthinking


    你是不是在深圳啊???

论坛徽章:
4
戌狗
日期:2013-08-15 18:22:43技术图书徽章
日期:2013-08-21 13:48:45巨蟹座
日期:2013-09-26 17:06:39处女座
日期:2013-12-25 11:26:10
9 [报告]
发表于 2011-10-19 22:48 |显示全部楼层
咱这两天的讨论中,都没涉及到随机预读。

实际上,预读加进去的话,啥屏障也保证不了读操作按照顺序发生,能够做的只是在屏障之后,验证一下读操作读的数值正确。

这几天我在华为横竖没事干,打算写个文档总结下;要不找个周末,我们一起去东莞,继续我上次想召开,却没开成的常平镇美宝桑拿linux kernel技术研讨会。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP