首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

ucos中的三种临界区管理机制(2)

ucos中的三种临界区管理机制(2)

会出现什么情况?在我的实验中,OS_EXIT_CRITICAL()之后,会出现处理器异常。为什么会出现处理起异常,让我来模拟一下它的汇编代码。之所以是模拟,并非是我虚构数据,而是因为我实际碰到问题的函数复杂一些,理解起来就需要更多的代码。而这个问题是有普遍意义的,所以请允许我来浅显地揭示这个隐藏的bug。[cpp] view plaincopyprint?

  • function_a:  
  •      push ebp  
  •      mov ebp, esp  
  •      sub esp, 8  
  •      mov 4(esp), 0x80000000  
  •      pushfd  
  •      cli  
  •      mov edi, 4(esp)  
  •      mov (esp), edi  
  •      call function_b  
  •     popfd  
  •     mov esp, ebp  
  •     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()宏定义并没有提供传递状态参数的功能。所以它的临界去必须这么用:
[cpp] view plaincopyprint?

  • function_a()  
  • {  
  • #if OS_CRITICAL_METHOD == 3
  •     int cpu_sr;  
  • #endif
  •       int a = 1<<31;  
  •       OS_ENTER_CRITICAL();  
  •       function_b(a);  
  •       OS_EXIT_CRITICAL();  
  • }  

这种代码怎么看怎么别扭,可能是因为在函数体内加了宏定义吧。然后,第三种方法对同一个函数体内的嵌套临界区无法支持,这在一些很长大的函数中使用时或许会造成一定困扰。

    好吧,如果有了问题,就要有解决方案,毕竟我不是为了让大家对ucos失去信心的。我们可以参考下一般的实时操作系统是如何实现关中断临界区的,就是以显式的方式用局部变量保存中断状态。
[cpp] view plaincopyprint?

  • int int_lock()  
  • {  
  •    int cpu_sr;  
  •     __asm__ __volatile__("pushfd \n\t pop %0\n\t cli":"=r"(cpu_sr));  
  •     return cpu_sr;  
  • }  

  • void int_unlock(int cpu_sr)  
  • {  
  •      __asm__ __volatile__("push %0\n\t popfd"::"r"(cpu_sr));  
  • }  

  • function_a()  
  • {  
  •    int a, cpu_sr;  
  •    a=1<<31;  
  •    cpu_sr = int_lock();  
  •    function_b(a);  
  •    int_unlock(cpu_sr);  
  • }  


   int_lock()和int_unlock()的可以用汇编更高效地实现,也可以选择只恢复中断标志的状态。这种方法让我们显示地管理状态保存的情况,我觉得至少要比宏定义清楚多了。
继承事业,薪火相传
返回列表