免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 8534 | 回复: 13

preempt_disable的问题 [复制链接]

论坛徽章:
0
发表于 2010-12-09 22:29 |显示全部楼层
各位,我对preempt_disable加barrier()的方式不理解,preempt_disable需要控制指令的执行顺序,这个容易理解。比如
preempt_disable();
a++;
preempt_enable();
如果不控制的话,a++可能在抢占被禁用之前就执行,使preempt_disable失去意义。但是内核的实现怎么只用一个barrier(),这个只能控制编译器不乱序吧,如果cpu乱序执行怎么办? 望大侠指点。

附上preempt_disable的代码:
30 #define preempt_disable() \
31 do { \
32     inc_preempt_count(); \
33     barrier(); \
34 } while (0)

论坛徽章:
0
发表于 2010-12-10 09:42 |显示全部楼层
这个只能控制编译器不乱序吧,如果cpu乱序执行怎么办?
回复 1# biger410


    你说的cpu乱序执行是什么意思?barrier应该就可以保证之前和之后的在时间序上是有先后的吧。cpu体系结构上不懂,但是就算是pipeline乱序发射,也是在微指令层面上,EIP取指令的时候会遇到这个barrier的内存栅吧。

论坛徽章:
0
发表于 2010-12-10 10:58 |显示全部楼层
barrier的作用保证写入内存的操作结束之前不会执行下面的代码。inc_preempt_count();会修改内存

论坛徽章:
0
发表于 2010-12-10 11:56 |显示全部楼层
barrier() 保证的是 cpu 不乱序执行。而不是编译器吧。

论坛徽章:
0
发表于 2010-12-10 15:19 |显示全部楼层
barrier的实现:
#define barrier() __asm__ __volatile__("": : :"memory")
它不能影响CPU乱序执行,只能保证编译器不乱排指令。

论坛徽章:
0
发表于 2010-12-10 16:41 |显示全部楼层
barrier的实现:
#define barrier() __asm__ __volatile__("": : :"memory"
它不能影响CPU乱序执行,只能 ...
biger410 发表于 2010-12-10 15:19



    这个只是barrier的一种实现吧~ 还有其他的呢?

    我的理解是: barrier要同时避免CPU和编译器的乱序问题。 只避免其一有什么用? 我实在想不到有什么场景是可以容忍编译器乱序却不能容易CPU乱序的,或者反过来。

论坛徽章:
0
发表于 2010-12-10 19:43 |显示全部楼层
回复 6# kouu

barrier只跟编译器有关,与体系结构没有关系。我上面列的是gcc相关的实现

论坛徽章:
0
发表于 2013-04-02 11:44 |显示全部楼层
老帖了,我来结掉吧,希望楼主还在~~
内核抢占发生在硬件中断唤醒一个高优先级任务的时候,当发现当前任务的优先级低于被唤醒的任务的优先级且当前任务的preempt_count为0,那么就会抢占当前任务。因此硬件中断是导火索,也就是说只有在临界区中的指令执行后,preempt_count++未执行前发生一个硬件中断,才有可能发生抢占。但是硬件中断都是precise interrupt,精确中断,即中断发生时的PC指针记录的指令之前的指令都已完成,之后的指令都未执行。这样即使在上述情况发生一个硬件中断,硬件只能有两种方法来保证中断的精确性:

  1. 丢弃临界区中指令的执行结果,将PC指针指向preempt_disable()。这样即使被抢占,临界区中的指令的执行结果也被丢弃了,所以不会出问题。

  2. 等preempt_disable()指令执行后,将PC指针指向临界区中最后完成的指令的下一条指令。这样由于preempt_count被加1了,该任务就不能被抢占了,所以也不会出问题。

  分析到这里,可以看出此处确实不需要内存屏障了,用barrier()保证编译器不乱排临界区中的指令就可以了。但是实际上这里这个barrier()还有另外一个作用,就是保证编译器会产生写内存的指令,把preempt_count的值写到内存。否则编译器可能对preempt_count进行优化,仅仅把更新后的值放到寄存器中。这样会出问题,比如在临界区中产生一个中断,中断处理程序只能从内存中去读preempt_count的值,它可能无法看到内核抢占已经被禁止,而错误的将不能被抢占的任务抢占。这是中断和进程共享数据时用barrier()来保持同步而不用锁的一个很好的例子。

论坛徽章:
0
发表于 2013-04-14 09:31 |显示全部楼层
cengku 发表于 2013-04-02 11:44
老帖了,我来结掉吧,希望楼主还在~~
内核抢占发生在硬件中断唤醒一个高优先级任务的时候,当发现当前任 ...

你分析的场景是在中断发生那一刻,CPU的流水线设计会保证指令的顺序执行,已经偏离了楼主的原意。preempt_enable会在内核的多种场景下使用到,而不是中断发生那一刻。
barrier函数中的memory参数是告诉编译器,不对该处做数据流的编译优化,在这条指令之后,前面内存变量保存在寄存器里的值将全部丢弃。之所以只保证了编译器的编译优化,没有考虑CPU的乱序执行,原因如下:
使用内存屏障强加的严格的CPU内存事件次序,保证程序的执行看上去象是遵循顺序一致性模型。在当前的实现中,wmb() 实际上是一个空操作,这是因为目前Intel的CPU系列都遵循“处理机一致性”,所有的写操作是遵循程序顺序的,不会越过前面的读写操作。但是,由于 Intel CPU系列可能会在将来采用更弱的内存一致性模型并且其他体系结构可能采用其他放松的一致性模型,仍然在内核里必须适当地插入wmb()保证内存事件的正确次序。
详见http://blog.chinaunix.net/uid-21961753-id-1810628.html

论坛徽章:
4
酉鸡
日期:2014-03-21 23:19:50狮子座
日期:2014-08-01 22:11:40酉鸡
日期:2015-01-10 21:31:442015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2013-04-14 09:54 |显示全部楼层
回复 9# junnyg

那部分代码是架构相关的,你只考虑了intel?

   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP