由preempt_disable的实现想到的

  #define preempt_disable() /
  do { /
      inc_preempt_count(); /
      barrier(); /
  } while (0)

  实现很简单,就两行代码,第一行把进程描述符中的preempt_count加1,第二行加一个优化屏障。展开inc_preempt_count()后就是:

  current->thread_info->preempt_count++;

  barrier();

  看到这个实现的时候,觉的很奇怪,用个优化屏障就够了吗?似乎应该用内存屏障mb()。优化屏障只能保证编译器不乱排指令,而如果CPU乱序执行,被preempt_disable保护的临界区中的指令岂不是有可能在preempt_count++之前执行。但可以肯定这不是个BUG,这么重要的地方不可能出现这么低级的BUG。那么就结合preempt_disable的应用环境分析一下,以下为个人的分析,并未得到权威认证,如有错误还请指正。

  既然没有BUG,那只能有两种可能,第一就是禁用抢占后临界区中的指令,不会先于preempt_count++执行。第二就是在临界区中的指令先执行且preempt_count++尚未执行的时候,不能发生抢占或者抢占不会有问题。第一种情况由于cpu的乱序执行显然无法保证。那么只能是第二种。为什么呢?接着分析。

  内核抢占发生在硬件中断唤醒一个高优先级任务的时候,当发现当前任务的优先级低于被唤醒的任务的优先级且当前任务的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()来保持同步而不用锁的一个很好的例子。

  终于分析完了,不由的感叹,内核真是太精妙了,简单的两行代码,隐藏着这么深的问题。要想成为linux hacker,要走的路还有很长,努力吧。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值