免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5351 | 回复: 14
打印 上一主题 下一主题

volatile 和 内存屏障 [复制链接]

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-11-01 15:47 |只看该作者 |倒序浏览
本帖最后由 zylthinking 于 2011-11-01 17:25 编辑

事隔多日, 又在这里开始弄不清楚了:
内存屏障似乎不影响编译器怎么编译吧;
那么如果一下伪码:

int a = 0;
mov eax,  a
-------------------> 发生切换
mb();
-------------------> 或者这里发生切换
test eax, ebx

在另一个线程中
a = 10;
wmb();

那么执行 test eax, ebx 中, eax 中能保证为 10 吗?
内存屏障影响的是高速缓存, 那么影响已经 load 进 CPU 寄存器中的值吗?

我的理解:
在单CPU下, 寄存器中的值在切换时会被保存到内存, 切换回后再次从内存中恢复
多CPU下, CPU1 发出的 invalid request 影响的是高速缓存, 但进入寄存器中的值怎么知道自己对应的内存已经变了? 不可能为每一个寄存器中的值保留一份相对应的内存地址吧

那么, 执行到 test eax, ebx 中, 说不定这个还真是 0
也就是说, 内存屏障影响的是 mov eax, a, 如果 wmb() 执行了, 那么CPU高速缓存中的数据保证失效, 从而读内存, wmb 同时保证内存中已经是新值了, 从而可以保证完整性;
但如果执行到 mov eax, a 之后再发生 a = 10, wmb(), 那么只会导致CPU 高速缓存失效, 内存被更新; 但 test eax, ebx 确实寄存器间的操作, 会不会导致数据错误???

而 voaltile, 正是约束编译器的, 也就是说它在编译层面保证 mov eax, a   test eax, ebx 不会被编译为2条指令, 而时编译为 test *(&a), ebx???

现在再接着推理:
1。 在UP下, a = 10;
wmb();
执行时, int a = 0;
mov eax,  a
-------------------> 发生切换
mb();
-------------------> 或者这里发生切换
test eax, ebx
中所有的寄存器内容其实都保存在 task 相关结构中, 那么 wmb时, CPU作为操作系统下层, 肯定没能力自己去找 task结构做更新; 因此, 等很久以后执行 test eax, ebx 时, 我基本肯定 eax中是 旧值了

2。 在SMP下, 假设两个CPU分贝同时在执行以下代码
int a = 0;

CPU1:                              CPU2:
a += 1;                           a += 2;
wb();                               wb();

如果发生了这种情况, 两个CPU在相同的时间内执行了不同的赋值语句, 那么 CPU1 和 CPU2谁向谁同步呢???
估计是个随机情况了吧???

论坛徽章:
6
金牛座
日期:2013-10-08 10:19:10技术图书徽章
日期:2013-10-14 16:24:09CU十二周年纪念徽章
日期:2013-10-24 15:41:34狮子座
日期:2013-11-24 19:26:19未羊
日期:2014-01-23 15:50:002015年亚洲杯之阿联酋
日期:2015-05-09 14:36:15
2 [报告]
发表于 2011-11-01 16:53 |只看该作者
回复 1# zylthinking
前段时间看这块内容,看的都快恶心了。。。
简单点说就是:
volatile应该是优化屏障 保障编译的时候不会乱序
内存屏障是保证CPU执行的时候不会乱序

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
3 [报告]
发表于 2011-11-01 16:55 |只看该作者
回复  zylthinking
前段时间看这块内容,看的都快恶心了。。。
简单点说就是:
volatile应该是优化屏障 ...
瀚海书香 发表于 2011-11-01 16:53


你说的这两条, 之前我已经总结出来了; 现在是新情况

论坛徽章:
6
金牛座
日期:2013-10-08 10:19:10技术图书徽章
日期:2013-10-14 16:24:09CU十二周年纪念徽章
日期:2013-10-24 15:41:34狮子座
日期:2013-11-24 19:26:19未羊
日期:2014-01-23 15:50:002015年亚洲杯之阿联酋
日期:2015-05-09 14:36:15
4 [报告]
发表于 2011-11-01 16:59 |只看该作者
回复 1# zylthinking

内存屏障默认都是有优化屏障的.

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
5 [报告]
发表于 2011-11-01 17:05 |只看该作者
回复  zylthinking

内存屏障默认都是有优化屏障的.
瀚海书香 发表于 2011-11-01 16:59


你还是没明白我在想什么, 你说的这三条, 我都知道, 然而我还是有1楼的疑问。

论坛徽章:
6
金牛座
日期:2013-10-08 10:19:10技术图书徽章
日期:2013-10-14 16:24:09CU十二周年纪念徽章
日期:2013-10-24 15:41:34狮子座
日期:2013-11-24 19:26:19未羊
日期:2014-01-23 15:50:002015年亚洲杯之阿联酋
日期:2015-05-09 14:36:15
6 [报告]
发表于 2011-11-01 17:07 |只看该作者
回复 1# zylthinking

内存屏障似乎不影响编译器怎么编译吧;

如果内存屏障包含优化屏障,怎么会不影响编译器编译呢。

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
7 [报告]
发表于 2011-11-01 17:28 |只看该作者
本帖最后由 zylthinking 于 2011-11-01 17:40 编辑
回复  zylthinking


如果内存屏障包含优化屏障,怎么会不影响编译器编译呢。
瀚海书香 发表于 2011-11-01 17:07


原来纠结这个, 我说的影响不是影响指令重排, 而是会不会 a += 1 这样的代码会被编译成3条指令; 而 voaltile int a; a += 1 只编译成一条指令
或者事实是
volatile int a = 0;

// 这个语句可以被编译成任意多个宏指令, 这些宏指令之间还可以进行任务切换, 这些指令之间, 仍然使用寄存器值, 因此切换回来后, 依旧使用旧值 0 进行运算, 最后写到内存的还是 1; 如果在切换期间被改变, 比如 10, 则10 最终被丢失
a += 1;
             // 如果在这里被切换, 并被改为 10, 如果任务切换时已经反应到内存, 则由于volatile性质, 切换回来执行完最后一句后,  a == 11;
                  
// 这里必然重新从内存取
a += 1;

论坛徽章:
2
CU十二周年纪念徽章
日期:2013-10-24 15:41:34处女座
日期:2013-12-27 22:22:41
8 [报告]
发表于 2011-11-01 18:18 |只看该作者
这都乱七八糟的什么啊。

volatile最一般的作用是:告诉编译器,每次引用变量时都访问内存,而不要把内存中的值读到寄存器后续对变量的引用直接用寄存器里的值。因为memory-mapped寄存器可以能异步变化。

自己的经历:某个寄存器的值会随着CPU时钟加1,准备在一个时间点读取该寄存器值,再另一个时间点再读一次,从而得到两点间时间间隔。結果算出的时间间隔总是0。后来加volatile就好了,道理就是上面所说。

由于看不懂LZ所说,LZ纠结的可能是volitale其它用法。

论坛徽章:
0
9 [报告]
发表于 2011-11-01 18:19 |只看该作者
但如果执行到 mov eax, a 之后再发生 a = 10, wmb(), 那么只会导致CPU 高速缓存失效, 内存被更新; 但 test eax, ebx 确实寄存器间的操作, 会不会导致数据错误???

我认为这样的代码本身就没有同步逻辑,test eax, ebx的时候,eax里面装是是a的新值还是a的旧值根本就没关系。
换个说法,这个地方的代码没有考虑test a的时候是否执行过a = 10,那么CPU或者编译器自然也不需要考虑这个问题。

而 voaltile, 正是约束编译器的, 也就是说它在编译层面保证 mov eax, a   test eax, ebx 不会被编译为2条指令, 而时编译为 test *(&a), ebx???

我不知道X86里面test指令是否支持取内存,但是RISC机器应该是不支持这样做的。所以不管怎样,test a的总有1条指令不能搞定的时候。
在这个问题上,我觉得voaltile跟优化屏障并没有区别。

如果发生了这种情况, 两个CPU在相同的时间内执行了不同的赋值语句, 那么 CPU1 和 CPU2谁向谁同步呢???
估计是个随机情况了吧???

我觉得这个跟第一个场景是类似的,代码里面根本就没有同步逻辑,谁先发生谁后发后都是有可能的。那么最终的结果如何,CPU或者编译器当然也不会去保证。

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
10 [报告]
发表于 2011-11-01 18:32 |只看该作者
本帖最后由 zylthinking 于 2011-11-01 18:33 编辑
我认为这样的代码本身就没有同步逻辑,test eax, ebx的时候,eax里面装是是a的新值还是a的旧值根本就没关 ...
kouu 发表于 2011-11-01 18:19



是这样的, 我最终的结论也就是因为代码本身无同步, 那么一切自然而然的显得很优雅的就没问题了。
另一个结论就是我猜测的, volatile 保证的是C语句之间的严格从内存读, 但如果一条C语句编译成若干汇编语句, volatile 也不能保证这几个汇编语句之间使用寄存器。

一个比较纠结的是这样

volatile int a = 0;

THREAD 1:
for(int i = 0; i < 4; ++i){
    ++a;
}

THREAD 2:
a = 10;

main(){
    start thread 1;
    start thread 2;

    wait both thread terminate:
    printf("%d\n", a);
}

得到的值很可能是 4;
这个如果我对volatile的结论是正确的, 那么这个明显在C层面显得不可能的结果却称为事实输出(只要理想的认为线程切换不会导致任何情况的内存屏障)
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP