看到一篇ucos3种临界模式的帖讲的很清楚

看的一篇帖子很好,把他贴过来:熟悉ucos,或者读过Jean.J.Labrosse写过的ucos书籍的人,一定会知道ucos中著名的临界去管理宏:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。
同样是通过关中断来保护临界区,OS_ENTER_CRITICAL/OS_EXIT_CRITICAL一共实现了三种实现方式,如下所示:
  1. #if OS_CRITICAL_METHOD == 1
  2. #define OS_ENTER_CRITICAL() __asm__("cli")
  3. #define OS_EXIT_CRITICAL() __asm__("sti")
  4. #endif

  5. #if OS_CRITICAL_METHOD == 2
  6. #define OS_ENTER_CRITICAL() __asm__("pushf \n\t cli")
  7. #define OS_EXIT_CRITICAL() __asm__("popf")
  8. #endif

  9. #if OS_CRITICAL_METHOD == 3
  10. #define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR())
  11. #define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr))
  12. #endif
第一种方式,OS_ENTER_CRITICAL() 简单地 关中断,OS_EXIT_CRITICAL() 简单地 开中断。这种方式虽然简单高效,但 无法满足 嵌套 的情况。如果有两层临界区保护,在退出内层临界区时就会开中断,使外层的临界区也失去保护。 虽然ucos的内核写的足够好,没有明显嵌套临界区的情况,但谁也无法保证一定没有,无法保证今后没有,无法保证在附加的驱动或什么位置没有 ,所以基本上第一种方法是没有人用的。
   第二种方式,OS_ENTER_CRITICAL()会在关中断前 保存 之前的 标志寄存器内容 堆栈 中,OS_EXIT_CRITICAL()从 堆栈 恢复 之前保存的状态。这样就允许了临界区嵌套的情况。但现在看来,这种方法 还存在很大的问题,甚至会出现致命的漏洞
      在OS_CRITICAL_METHOD=2的情况下,假设有如下代码:
  1. function_a()
  2. {
  3.      int a=(1<<31);
  4.      OS_ENTER_CRITICAL();
  5.      function_b(a);
  6.      OS_EXIT_CRITICAL();   
  7. }
会出现什么情况?在我的实验中,OS_EXIT_CRITICAL()之后,会出现 处理器异常 。为什么会出现处理起异常,让我来模拟一下它的汇编代码。之所以是模拟,并非是我虚构数据,而是因为我实际碰到问题的函数复杂一些,理解起来就需要更多的代码。而 这个问题是有普遍意义 的,所以请允许我来 浅显地揭示这个隐藏的bug
  1. function_a:
  2.      push ebp
  3.      mov ebp, esp
  4.      sub esp, 8
  5.      mov 4(esp), 0x80000000
  6.      pushfd
  7.      cli
  8.      mov edi, 4(esp)
  9.      mov (esp), edi
  10.      call function_b
  11.     popfd
  12.     mov esp, ebp
  13.     ret

这是参照了gcc编译结果的汇编模拟,无论是否加优化选项这一问题都存在。这个问题的起因很简单, gcc想聪明一点,一次把堆栈降个够,然后它就可以在栈上随意放参数 去调用其他函数。尤其是在调用函数较多的时候,这种做法就更有意义。而且,gcc这种聪明与优化选项O好像没有太大关系,好像没有什么能禁止它这么做。但问题是, gcc不知道我们的OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()是操作了堆栈的 ,我尝试过使用__asm__ __volatile__("pushfd \n\tcli":::"memory")来通知gcc 内存数据改变 了,但显然 gcc不认为堆栈也改变 了。于是, OS_ENTER_CRITICAL()保存在栈上的状态就被冲掉 了,比如被这里调用参数a的值。在恢复时,是否会引发异常,会引发什么异常,这个就要靠运气了。但我相信一个人的运气不会总是那么好的,所以最后别使用OS_CRITICAL_METHOD=2。
  第三种,在关中断前,使用 局部变量 保存 中断状态 。这也是几乎所有实时操作系统共有的选择。但ucos是一朵奇葩,为了兼容前两种方式,OS_ENTER_CRITICAL()/ OS_EXIT_CRITICAL()宏定义并没有提供传递状态参数的功能。所以它的临界去必须这么用:
  1. function_a()
  2. {
  3. #if OS_CRITICAL_METHOD == 3
  4.     int cpu_sr;
  5. #endif
  6.       int a = 1<<31;
  7.       OS_ENTER_CRITICAL();
  8.       function_b(a);
  9.       OS_EXIT_CRITICAL();
  10. }
这种代码怎么看怎么别扭,可能是因为在函数体内加了宏定义吧。然后,第三种方法对同一个函数体内的嵌套临界区无法支持,这在一些很长大的函数中使用时或许会造成一定困扰。希望对你有帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值