ucos中的三种临界区管理机制
熟悉ucos,或者读过Jean.J.Labrosse写过的ucos书籍的人,一定会知道ucos中著名的临界去管理宏:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。
同样是通过关中断来保护临界区,OS_ENTER_CRITICAL/OS_EXIT_CRITICAL一共实现了三种实现方式,如下所示:
- #if OS_CRITICAL_METHOD == 1
- #define OS_ENTER_CRITICAL() __asm__("cli")
- #define OS_EXIT_CRITICAL() __asm__("sti")
- #endif
- #if OS_CRITICAL_METHOD == 2
- #define OS_ENTER_CRITICAL() __asm__("pushf \n\t cli")
- #define OS_EXIT_CRITICAL() __asm__("popf")
- #endif
- #if OS_CRITICAL_METHOD == 3
- #define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR())
- #define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr))
- #endif
第一种方式,OS_ENTER_CRITICAL()简单地关中断,OS_EXIT_CRITICAL()简单地开中断。这种方式虽然简单高效,但无法满足嵌套的情况。如果有两层临界区保护,在退出内层临界区时就会开中断,使外层的临界区也失去保护。虽然ucos的内核写的足够好,没有明显嵌套临界区的情况,但谁也无法保证一定没有,无法保证今后没有,无法保证在附加的驱动或什么位置没有,所以基本上第一种方法是没有人用的。
第二种方式,OS_ENTER_CRITICAL()会在关中断前保存之前的标志寄存器内容到堆栈中,OS_EXIT_CRITICAL()从堆栈中恢复之前保存的状态。这样就允许了临界区嵌套的情况。但现在看来,这种方法还存在很大的问题,甚至会出现致命的漏洞。
在OS_CRITICAL_METHOD=2的情况下,假设有如下代码:
- function_a()
- {
- int a=(1<<31);
- OS_ENTER_CRITICAL();
- function_b(a);
- OS_EXIT_CRITICAL();
- }
会出现什么情况?在我的实验中,OS_EXIT_CRITICAL()之后,会出现处理器异常。为什么会出现处理起异常,让我来模拟一下它的汇编代码。之所以是模拟,并非是我虚构数据,而是因为我实际碰到问题的函数复杂一些,理解起来就需要更多的代码。而这个问题是有普遍意义的,所以请允许我来浅显地揭示这个隐藏的bug。
- 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()宏定义并没有提供传递状态参数的功能。所以它的临界去必须这么用:
- 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失去信心的。我们可以参考下一般的实时操作系统是如何实现关中断临界区的,就是以显式的方式用局部变量保存中断状态。
- 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()的可以用汇编更高效地实现,也可以选择只恢复中断标志的状态。这种方法让我们显示地管理状态保存的情况,我觉得至少要比宏定义清楚多了。
ucos中的三种临界区管理机制相关推荐
- css中的三种基本定位机制
css中的三种基本定位机制 a.普通文档流 b.定位:相对定位 绝对定位 固定定位 c.浮动 1.普通流中,元素位置由文档顺序和元素性质决定,块级元素从上到下依次排列,框之间的垂直距离由框的垂直mar ...
- Hibernate中的三种数据持久状态和缓存机制
Java三大框架之--Hibernate中的三种数据持久状态和缓存机制 Hibernate中的三种状态 瞬时状态:刚创建的对象还没有被Session持久化.缓存中不存在这个对象的数据并且数据库中没 ...
- Oracle的join默认为,Oracle中的三种Join方法详解
这里将为大家介绍Oracle中的三种Join方法,Nested loop join.Sort merge join和Hash join.整理出来以便帮助大家学习. 基本概念 Nested loop j ...
- oracle hash join outer,CSS_浅谈Oracle中的三种Join方法,基本概念
Nested loop join:
Outer - phpStudy...
浅谈Oracle中的三种Join方法 基本概念 Nested loop join: Outer table中的每一行与inner table中的相应记录join,类似一个嵌套的循环. Sort mer ...
- MySQL buffer pool中的三种链
三种page.三种list.LRU控制调优 一.innodb buffer pool中的三种页 1.free page:从未用过的页 2.clean page:干净的页,数据页的数据和磁盘一致 3.d ...
- .net core 注入中的三种模式:Singleton、Scoped 和 Transient
从上篇内容不如题的文章<.net core 并发下的线程安全问题>扩展认识.net core注入中的三种模式:Singleton.Scoped 和 Transient 我们都知道在 Sta ...
- 广播泛洪_一分钟了解交换机四种报文中的三种:广播、组播、未知单播
随着互联网的发展,各种应用层出不穷,日新月异,对网络设备的要求也越来越高,对广大设备厂商也提出更多的要求.不过这些事情都与二层交换机没有关系:) ,因为各种应用的数据到达二层交换机时,都只会变成一个一 ...
- (POST请求中的三种数据请求格式.application/x-www-form-urlencoded和multipart/form-data和application/json)
(POST请求中的三种数据请求格式.application/x-www-form-urlencoded和multipart/form-data和application/json) applicatio ...
- 061 hive中的三种join与数据倾斜
一:hive中的三种join 1.map join 应用场景:小表join大表 一:设置mapjoin的方式: )如果有一张表是小表,小表将自动执行map join. 默认是true. <pro ...
最新文章
- zend framework mysql_Zend Framework连接Mysql数据库实例分析
- EOJ_1049_三元组稀疏矩阵相加
- ?????nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for pr
- save product in COMMPR01的调试和调用栈
- psychopy 音频时长代码_PsychoPy入门_03_视频和音频的呈现
- 数学--数论--HDU 6128 Inverse of sum (公式推导论)
- 轻松掌握使用 SQL Server 浏览器,解决SQL Server 2005跨网段不能连接问题
- python爬取会议论文pdf_【python2.7】爬取知网论文
- 【2022】多线程并发编程面试真题
- bzoj2662:[BeiJing wc2012]冻结
- “王者对战”之 MySQL 8 vs PostgreSQL 10
- php查看mysql最近执行过的sql_PHP获取MySQL执行sql语句的查询时间
- openssl以及openssh升级
- 用SeaMonkey写cnblogs博客时碰到的问题
- 5分钟搭一个FastDFS--Linux篇
- SetWindowsHookEx 其他进程的 记录
- [4/10]指定结进程名称的命令taskkill.exe
- 数据治理--元数据--元数据的作用
- PSPICE仿真数据转MATLAB
- 计算机上分辨率怎么设置在哪里设置方法,windows7如何设置分辨率_windows7设置分辨率的方法...