6020|6

10

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

请教ucos一个小问题?! [复制链接]

 看很多书上的例程时,他们都会在建立一个新任务的程序内加这么一句
#if OS_CRITICAL_METHOD ==3
     OS_CPU_SR  cpu_sr
#endif

字面就是如果开关中断的方法是第三种,那么创建个cpu状态寄存器? 有什么实际意思和作用呢?我没用程序也跑起来了啊

最新回复

学习了  详情 回复 发表于 2016-7-14 09:47
点赞 关注(2)
 

回复
举报

10

帖子

0

TA的资源

一粒金砂(中级)

沙发
 
坛子没人了么 还是?
 
 
 

回复

30

帖子

0

TA的资源

一粒金砂(中级)

板凳
 
本帖最后由 yunfenglw 于 2014-4-29 16:22 编辑

看的一篇帖子很好,把他贴过来:熟悉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.      
  8. }
复制代码

会出现什么情况?在我的实验中,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. }
复制代码

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

回复

86

帖子

0

TA的资源

一粒金砂(中级)

4
 
大神:我也是不太清楚为什么要使用方式3,可是关于汇编的,小弟看不懂啊,悲剧~~~
这个该如何补?大神能提供点意见吗?
 
 
 

回复

253

帖子

1

TA的资源

一粒金砂(高级)

5
 
大意就是说,如果使用汇编的话,编译器可能会忽略堆栈的改变,也就是在返回的时候错位了。而将寄存器的状态声明成局部变量保存的话,这个变量就会被编译器处理,而不会出现堆栈错误。对吧。汇编看小甲鱼的汇编视频教程,先学80x86汇编
 
 
 

回复

8

帖子

0

TA的资源

一粒金砂(中级)

6
 
如果在51上跑,你的体会多一些,
 
 
 

回复

8

帖子

0

TA的资源

一粒金砂(初级)

7
 
学习了
 
 
 

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

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表