同事在RTOS"临界区嵌套使用"栽了跟头~


1

裸机与RTOS的理解

首先这里只针对单核CPU架构的芯片展开讨论,大部分是MCU吧,而多核CPU的讨论相对比较复杂,暂不涉及~

玩RTOS的朋友都知道,裸机与OS的最大区别就是实现多任务的并发,其实你说裸机就不能实现任务的并发吗 ? 这个需要看所站的角度吧,只是说RTOS并发的粒度可以更加细,因为你把裸机的任务拆分成多块运行,其实也是一种并发方式。

从宏观上虽然RTOS的每个任务都是在并发执行,其实微观上还是一条一条指令在顺序执行着,如下图所示:

而对于目前主流的RTOS,如UCOS或者FreeRTOS,所实现的都是多任务,更多的是一种多线程的并发执行而非多进程,所以对应到Linux平台上称他们为thread。

2

并发带来的问题

并发的好处就是能够在更细的粒度来尽可能的提高CPU的利用率,这里不能说使用了多线程就一定能提高,这与你所设计的任务划分和处理有着直接的关系,只能说多线程相比裸机更有这个能力。

而任何事物都有其利弊,多个任务在没有同步处理的情况下,任务之间是无序运行的,无序也就意味着状态的多样性和复杂度。

当然bug菌这里所说的无序是一个相对的过程,比如对于CPU而言,它就是顺序的去执行一条一条的指令,所以在这个层面它是有序的、确定的。

而我们把过程放大,比如执行一条C语言语句,一般它是由多条汇编指令组成,对于目前的抢占式内核,在一段时间内其多个任务就有可能指令交替执行,当这些指令都去操作同一块内存,那么内存的最终结果由于顺序不同而不同,最终难以确定。

状态的不确定就有可能造成异常行为,也就是大家经常遇到的:"怎么跑着跑着就有问题,还没啥规律~","这段代码怎么看也没问题呀~"

所以对比看来RTOS确实会带来编程上的难度~

3

临界区

既然有难度,我们就要解决,把不确定性部分通过一些手段来变得确定,而造成这些不确定因素的动力是什么呢?是中断~

bug菌一直觉得,其实对于裸机而言,如果把中断服务函数看成一个更高优先级的抢占式任务,其实裸机主任务与中断任务就形成了一种两任务的并发,所以中断与任务之间也是有共享问题需要类似处理的。

为了解决这些不确定因素,我们只需要在这段代码区域限制中断的发生即可,这一段区域就是临界区,说得直白点 : 关中断与开中断。

1ENTER_CRITICAL();//进入临界区
2
3//临界区代码
4
5EXIT_CRITICAL();//退出临界区

4

临界区嵌套

临界区的使用没啥可说的,但是在你的代码中怎么加临界区确实一门技巧,可是说很多3~5年的工程师也并不一定处理得好,本文暂不展开,后面bug菌整理以后再分享给大家,今天只聊聊临界区嵌套使用的问题,毕竟很多朋友在这里掉过坑~

参考伪代码:

 1/*********************************************
 2 * Function: Fuction1
 3 * Description:功能函数
 4 * Author: bug菌
 5 ********************************************/
 6void Fuction1(void)
 7{
 8    ENTER_CRITICAL();//进入临界区
 9
10    //do something~
11
12    EXIT_CRITICAL();//退出临界区
13}
14/*********************************************
15 * Function: Fuction2
16 * Description: 功能函数
17 * Author: bug菌
18 ********************************************/
19void Fuction2(void)
20{
21    ENTER_CRITICAL();//进入临界区
22
23    ......
24    Fuction1();
25
26    ......
27    //do something~
28
29    EXIT_CRITICAL();//退出临界区
30}

这种临界区的使用是很多朋友常犯的错误,当然这里的临界区操作仅仅只是开关中断,许多自己公司写的,或者裁剪的都是这种简约开关中断版本,所以当调用Function1函数以后,后面的代码就不在临界区内了,此时就有可能会存在共享问题。

当然目前的开源OS都会提供一种把相关嵌套标记保存在局部变量中的处理方式,如下代码所示:

 1//来源于ucos源码
 2#define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR())
 3#define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr))
 4
 5/*********************************************
 6 * Function: Fuction1
 7 * Description:功能函数
 8 * Author: bug菌
 9 ********************************************/
10void Fuction1(void)
11{
12    int cpu_sr;
13
14    OS_ENTER_CRITICAL();//进入临界区
15
16    //do something~
17
18    OS_EXIT_CRITICAL();//退出临界区
19}
20
21/*********************************************
22 * Function: Fuction2
23 * Description: 功能函数
24 * Author: bug菌
25 ********************************************/
26void Fuction2(void)
27{
28    int cpu_sr;
29
30    OS_ENTER_CRITICAL();//进入临界区
31
32    Fuction1(void);
33
34    OS_EXIT_CRITICAL();//退出临界区
35
36}

为了更好的理解,我写了一下下面的伪代码,供大家参数~

 1//中断寄存器register原本是1, 向register写0关中断,向register写1开中断
 2
 3void Fuction2(void)
 4{
 5    int cpu_sr1 = 0;
 6
 7    cpu_sr1   = register; 
 8    register  = 0; //register == 0;cpu_sr1 == 1;
 9
10           void Fuction1(void)
11           {
12               int cpu_sr1 = 0;
13
14               cpu_sr2   = register; 
15               register  = 0; //register == 0;cpu_sr2 == 0;
16
17
18               register  = cpu_sr2; 
19               cpu_sr2   = 0;//register == 0;cpu_sr2 == 0;
20           }
21
22    register  = cpu_sr1; 
23    cpu_sr1   = 0;//register == 1;cpu_sr1 == 0;
24
25}

不同的OS可能具体实现有所差异,大体上都一样~

最后

好了,兄弟别下次了,就这次给个吧~~


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

嵌入式Linux

微信扫描二维码,关注我的公众号

### CMSIS RTOS 中的临界区实现与使用 在嵌入式系统开发中,特别是在多任务环境中管理共享资源时,确保线程安全至关重要。对于基于 Cortex-M 架构微控制器并采用 CMSIS-RTOS API 编写的程序来说,进入和退出临界区的操作可以通过特定函数来完成。 #### 进入临界区 当需要阻止其他更高优先级的任务抢占当前正在执行的关键代码段时,可以调用 `osKernelLock()` 函数[^1]。这会使得内核暂停调度活动直到对应的解锁操作被执行为止。然而,在某些情况下,可能只需要简单地屏蔽中断而不是完全锁定整个操作系统核心;这时就可以利用底层硬件特性如 ARM Cortex-M 处理器中的 PRIMASK 寄存器来进行控制[^3]: ```c // 禁止所有可屏蔽中断 (进入临界区) __set_PRIMASK(1); ``` 上述指令通过设置 PRIMASK 来关闭所有的可屏蔽中断请求 IRQs ,从而防止任何外部事件打断正在进行的重要计算过程或数据更新动作。 #### 退出临界区 一旦完成了对敏感区域内的操作,则应该尽快恢复正常的中断响应能力以维持系统的实时性能表现。为此,应当立即清除之前所设下的屏障条件——即重新允许接收到来自外设或其他来源发出的新颖信号通知: ```c // 允许所有可屏蔽中断 (离开临界区) __set_PRIMASK(0); ``` 值得注意的是,虽然直接操控寄存器能够提供最细粒度级别的干预手段,但在实际项目里更推荐遵循官方提供的接口规范去实施此类功能。因此,在 CMSIS-RTOS 应用场景下建议使用如下所示的标准库函数组合方式来定义一段受保护的时间窗口: ```c uint32_t interruptState; interruptState = osKernelGetInterruptState(); // 获取当前状态 if (!interruptState){ osKernelLock(); } /* 执行关键部分 */ ... if (!interruptState){ osKernelUnlock(); } ``` 这段伪代码展示了如何优雅地围绕着潜在的竞争条件下包裹住必要的业务逻辑片段,并且还考虑到了并发环境里的特殊情况处理策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值