RTOS中临界区嵌套保护的实现原理(基于RT-Thread)

0 前言

什么是临界区(临界段)?
裸机编程中由于不涉及线程和线程切换,因此没有临界区这一个概念。在RTOS中由于存在线程切换等场景,便有了临界区这个概念。简单来说,临界区就是不允许被中断的代码区域。什么时候代码执行的过程会被打断:一个是线程切换,一个是异常事件。对于RT-Thread而言实际上都是异常事件导致代码执行过程被中断,因为RT-Thread是在PendSV事件内进行线程切换的。
什么样的代码区域应该作为临界区保护起来?
假如我们有2个线程A和B,它们都有可能使用同一个串口向外打印数据,而我们希望线程A和B在执行printf这个操作时是不会被打断的,否则我们看到的串口打印数据将杂乱无章。下面是一个例子:

void thread_a(void *arg)
{for (;;){printf("A thread!\r\n");}
}void thread_b(void *arg)
{for (;;){printf("B thread!\r\n");}
}

假设线程A和线程B是按照时间片轮转进行调度的,那么就有可能出现线程A正在打印"A thread!\r\n"时被线程B获得CPU使用权进而打印线程B的"B thread!\r\n",最后呈现在串口上的数据杂乱无章。这不是我们希望看到的结果,因此我们需要将printf作为临界区保护起来,以RT-Thread临界区保护API为例,修改后的代码如下:

void thread_a(void *arg)
{for (;;){rt_enter_critical(); // 进入临界区printf("A thread!\r\n");rt_exit_critical(); // 退出临界区}
}void thread_b(void *arg)
{for (;;){rt_enter_critical(); // 进入临界区printf("B thread!\r\n");rt_exit_critical(); // 退出临界区}
}

1 RTOS中临界区保护的实现原理(基于RT-Thread)

前面我们已经知道导致代码段被中断的原因就是发生了异常事件,为了实现对临界区的保护可以选择最简单直接的方法:关闭全部中断(NMI fault和硬fault除外),为了避免直接关闭中断带来的不良影响,RT-Thread为我们提供了2种方案:
(1)开关全部中断
(2)使能/失能线程调度

1.1 开关全部中断

ARM的M3/M4/M7内核为了快速开关中断,专门设置了一条CPS指令,共有以下4种用法:

CPSID I ;PRIMASK=1;关中断
CPSIE I ;PRIMASK=0;开中断
CPSID F ;FAULTMASK=1, ;关异常
CPSIE F ;FAULTMASK=0 ;开异常

有关上述寄存器的定义如下:
在这里插入图片描述
这里我们并不想关闭硬件fault,因此只会使用CPSID I和CPSIE I这2条指令。对于没有出现临界区嵌套的情况,直接在进入临界区前关中断,退出临界区后关中断即可实现对临界区的保护。但如果临界区出现嵌套的话,只是单纯使用开关中断显然无法满足我们要求,看看RT-Thread如何解决这个问题的:

// 关全部中断
rt_base_t rt_hw_interrupt_disable(void);
;/*
; * rt_base_t rt_hw_interrupt_disable();
; */
rt_hw_interrupt_disable    PROCEXPORT  rt_hw_interrupt_disableMRS     r0, PRIMASKCPSID   IBX      LRENDP
// 开全部中断
void rt_hw_interrupt_enable(rt_base_t level);
;/*
; * void rt_hw_interrupt_enable(rt_base_t level);
; */
rt_hw_interrupt_enable    PROCEXPORT  rt_hw_interrupt_enableMSR     PRIMASK, r0BX      LRENDP

可以看到,我们使能中断时是需要传入一个rt_base_t类型参数,这个就是RT-Thread实现临界区嵌套保护的关键。我们使用时只需要按照如下格式进行嵌套保护即可,下面是一个一重临界区嵌套保护的示例:

rt_base_t level1;
rt_base_t level2;
void thread_a(void *arg)
{for (;;){level1 = rt_hw_interrupt_disable(); printf("A thread!\r\n");level2 = rt_hw_interrupt_disable();printf("A thread!!\r\n");rt_hw_interrupt_enable(level2); printf("A thread!!!\r\n");rt_hw_interrupt_enable(level1);}
}

结合前面的汇编代码,我们很容易可以发现,只有在执行最后一个rt_hw_interrupt_enable操作时才会打开全部中断,真正实现嵌套临界区的保护。
它的实现原理就是使用全局变量保存进入临界区之前的全部中断开关状态,然后退出临界区时根据这个开关状态来进行全部中断开关。根据临界区嵌套保护语句的顺序,显然只有执行了最后一个rt_hw_interrupt_enable才会打开全部中断。

1.2 使能/失能线程调度

相比前面直接开关中断来实现临界区保护,使能/失能线程调度则显得“温柔”很多,它并不会长时间关闭中断对我们的一些中断事件(例如SysTick时间基准)造成影响,如果我们的线程并不会被中断服务函数内的操作影响,推荐使用使能/失能线程调度的方法去实现临界段保护,RT-Thread提供的相关函数如下:

// 失能线程调度
/*** This function will lock the thread scheduler.*/
void rt_enter_critical(void)
{register rt_base_t level;/* disable interrupt */level = rt_hw_interrupt_disable();/** the maximal number of nest is RT_UINT16_MAX, which is big* enough and does not check here*/rt_scheduler_lock_nest ++;/* enable interrupt */rt_hw_interrupt_enable(level);
}
;/*
; * rt_base_t rt_hw_interrupt_disable();
; */
rt_hw_interrupt_disable    PROCEXPORT  rt_hw_interrupt_disableMRS     r0, PRIMASKCPSID   IBX      LRENDP
// 使能线程调度
/*** This function will unlock the thread scheduler.*/
void rt_exit_critical(void)
{register rt_base_t level;/* disable interrupt */level = rt_hw_interrupt_disable();rt_scheduler_lock_nest --;if (rt_scheduler_lock_nest <= 0){rt_scheduler_lock_nest = 0;/* enable interrupt */rt_hw_interrupt_enable(level);if (rt_current_thread){/* if scheduler is started, do a schedule */rt_schedule();}}else{/* enable interrupt */rt_hw_interrupt_enable(level);}
}
;/*
; * void rt_hw_interrupt_enable(rt_base_t level);
; */
rt_hw_interrupt_enable    PROCEXPORT  rt_hw_interrupt_enableMSR     PRIMASK, r0BX      LRENDP

(1)rt_enter_critical函数
首先关闭全部中断,将rt_scheduler_lock_nest自增1,然后将中断状态还原到rt_enter_critical函数最开始的状态。
(2)rt_exit_critical函数
首先关闭全部中断,将rt_scheduler_lock_nest自减1,如果rt_scheduler_lock_nest<=0表示临界区保护结束需要退出临界区,这时rt_scheduler_lock_nest=0然后将中断状态还原到函数最开始的状态,如果调度已经开始则发起一次调度尽可能保证更高优先级线程能够得到及时运行。如果rt_scheduler_lock_nest>0表示临界区保护还未结束,将中断状态还原到函数最开始的状态。
实现临界区嵌套保护的核心就是rt_scheduler_lock_nest,借助嵌套临界区保护操作成对出现这一特点,使用rt_scheduler_lock_nest调度锁表征当前临界区保护嵌套执行情况,当rt_scheduler_lock_nest<=0时则可以退出临界区保护,因此rt_exit_critical()和rt_exit_critical()无需人为定义全局变量就可以实现临界区嵌套保护,使用起来更加方便。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/786311.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在 Three.js 中,`USDZExporter` 类用于将场景导出为 USDZ 格式,这是一种用于在 iOS 平台上显示增强现实(AR)内容的格式。

demo 案例 在 Three.js 中&#xff0c;USDZExporter 类用于将场景导出为 USDZ 格式&#xff0c;这是一种用于在 iOS 平台上显示增强现实&#xff08;AR&#xff09;内容的格式。下面是关于 USDZExporter 的入参、出参、方法和属性的讲解&#xff1a; 入参 (Parameters): sc…

HTTP和HTTPS谁传输数据更安全?

1.HTTP HTTP在传输数据时&#xff0c;通常都是明文传输&#xff0c;也就是传输的数据没有进行加密。在这种情况下&#xff0c;如果传输的是一些敏感数据&#xff0c;比如某银行卡密码&#xff0c;就很容易被别人截获到&#xff0c;这就对我们的个人利益产生了威胁。 HTTP传输数…

Mysql数据库故障排查与优化

目录 前言 一、Mysql数据库的单实例故障 1.故障一——拒绝连接数据库 1.1故障内容 1.2问题分析 1.3解决方法 2.故障二——密码错误 2.1故障内容 2.2问题分析 2.3解决方法 3.故障三——数据库处理较慢 3.1故障内容 3.2问题分析 3.3解决方法 4.故障四——数据库表…

k8s 基础入门

1.namespace k8s中的namespace和docker中namespace是两码事&#xff0c;可以理解为k8s中的namespace是为了多租户&#xff0c;dockers中的namespace是为了网络、资源等隔离 2.deployment kubectl create #新建 kubectl aply #新建 更新 升级&#xff1a; 滚动升级&#x…

真实sql注入以及小xss--BurpSuite联动sqlmap篇

前几天漏洞检测的时候无意发现一个sql注入 首先我先去网站的robots.txt去看了看无意间发现很多资产 而我意外发现admin就是后台 之后我通过基础的万能账号密码测试or ‘1‘’1也根本没有效果 而当我注入列的时候情况出现了 出现了报错&#xff0c;有报错必有注入点 因此我…

企业能耗数据分析有哪些优势?怎样进行分析?

随着互联网技术的发展&#xff0c;企业在运营中会出现大量的用能数据&#xff0c;但却做不了精准的用能数据分析&#xff0c;导致数据没有得到有效利用&#xff0c;以及产生能源浪费现象。 为什么企业用能分析总是难&#xff1f; 一、用能分析过程复杂 由于用能分析过于复杂…

采用C#.net6.0+Vue,Ant-Design技术开发的一套大型医院手术麻醉信息系统源码,系统成熟,运行稳定

手术麻醉信息系统源码&#xff0c;C#手麻系统源码&#xff0c;自主版权应用案例&#xff08;适合上项目&#xff09; 手术麻醉信息系统可以实现手术室监护仪、麻醉机、呼吸机、输液泵等设备输出数据的自动采集&#xff0c;采集的数据能据如实准确地反映患者生命体征参数的变化&…

穿山甲广告平台SDK接入效果怎么样?

广告收入是大多数开发者的应用变现收入来源&#xff0c;如何进行流流量变现是从应用设计之初就需要开发者思考的问题。 穿山甲广告平台作为国内第三方广告变现平台&#xff0c;是不少开发者选择的对接平台。 穿山甲广告平台的广告类型较多&#xff0c;有信息流&#xff0c;ba…

苍穹外卖05(Redis入门,下载于安装,服务启动和停止,Redis数据类型面试题,常用命令,Java中操作Redis,店铺营业状态设置)

目录 一、Redis入门 1. Redis简介 1 NoSQL介绍 2 Redis简介 2. Redis下载与安装 1 Redis下载 2 Redis安装 3. Redis服务启动与停止 1 服务启动命令 2 客户端连接命令 3 修改Redis配置文件 3 修改Redis配置文件 4 Redis客户端图形工具 二、Redis数据类型【面试题】…

如何让光猫4个网口都有网络

一般情况光猫只有LAN1口有网络&#xff0c;LAN2、LAN3和LAN4口都是预留给电视用的&#xff0c;那么如何让这3个网口也有网络呢&#xff1f; 使用场景&#xff1a; 光猫在弱电箱内&#xff0c;弱电箱中有三根网线&#xff08;网线1、网线2和网线3&#xff09;分别接入到了三个房…

基于Arduino IDE 野火ESP8266模块 文件系统LittleFS 的开发

一、文件系统LittleFS的介绍 LittleFS是一个为微控制器设计的轻量级、可靠且高性能的文件系统。它专为嵌入式设备打造&#xff0c;拥有占用空间小、对硬件要求低的特点&#xff0c;同时保证在断电情况下数据的完整性和稳定性。 1.设计与特点 LittleFS的设计旨在提供嵌入式系统所…

依赖倒转原则

1.1 MM请求电脑 MM电脑坏了&#xff0c;需要修电脑&#xff0c;是因为每次打开QQ,一玩游戏&#xff0c;机器就死了。出来蓝底白字的一堆莫名奇妙的英文。蓝屏死机了&#xff0c;估计内存有问题。 1.2 电话遥控修电脑 遥控修理电脑&#xff0c;打开内存条&#xff0c;两根内存…

Vivado使用(6)——增量综合( Incremental Synthesis)

目录 一、概述 1.1 增量综合的工作原理 1.2 增量综合的优点 1.3 注意事项 二、设置增量综合 2.1 Write Incremental Synthesis 复选框 2.2 Incremental Synthesis 选择框 2.2.1 自动使用上一次运行的检查点&#xff08;Automatically use the checkpoint from the…

Vue(十二):脚手架配置代理,github案例,插槽

一、脚手架配置代理 老师讲的主要有两种方法&#xff1a; 但是我的没有proxy&#xff0c;只有proxyTable,之前一直不成功&#xff0c;现在我是这样配置的&#xff1a; config文件夹下的index.js: App.vue: 然后就成功了&#xff1a;&#xff08;我真服了&#xff0c;之前在这…

jdbc连SQL server,显示1433端口连接失败解决方法

Exception in thread "main" com.microsoft.sqlserver.jdbc.SQLServerException: 通过端口 1433 连接到主机 localhost 的 TCP/IP 连接失败。错误:“connect timed out。请验证连接属性。确保 SQL Server 的实例正在主机上运行&#xff0c;且在此端口接受 TCP/IP 连接…

零基础入门实战深度学习Pytorch课程下载

本课程旨在帮助零基础学员掌握PyTorch深度学习框架。通过实战项目&#xff0c;学员将学习神经网络基础、模型训练和调优技巧。逐步掌握深度学习核心概念&#xff0c;为未来在人工智能领域打下坚实基础。 课程大小:2.6G 课程下载&#xff1a;https://download.csdn.net/downlo…

【战略前沿】与中国达成生产协议后,飞行汽车即将起飞

【原文】Flying cars edge towards takeoff after Chinese production deal 【作者】Thomas Macaulay 斯洛伐克公司KleinVision签署了一项协议&#xff0c;将大规模生产AirCar。 一辆获得航空认证的飞行汽车向商业化又迈出了一大步。 空中汽车的创造者KleinVision今天宣布出售…

【经验分享】Ubuntu下如何解决问题arm-linux-gcc:未找到命令

【经验分享】Ubuntu下如何解决问题arm-linux-gcc&#xff1a;未找到命令 前言问题分析解决方法 前言 在编译过程中发现一个问题&#xff0c;明明之前安装了gcc-4.6版本&#xff0c;版本信息都是正常显示的&#xff0c;刚安装上去的时候也是可以用的。但不知道什么原因突然不能…

HUAWEI 华为交换机 配置 Eth-Trunk 接口流量本地优先转发示例(堆叠)

组网需求 说明 S5720I-10X-PWH-SI-AC 和 S5720I-6X-PWH-SI-AC 不支持此配置。 如 图 3-23 所示&#xff0c;为了增加设备的容量采用设备堆叠技术&#xff0c;将 Switch3 和 Switch4通过专用的堆叠电缆链接起来&#xff0c;对外呈现为一台逻辑交换机。为了实现设备间的备份、…

Windows基线安全检测-安全配置检测

Windows基线安全检测-安全配置检测 前言 Windows在生产环境中是使用最多的一个系统&#xff0c;大部分为客户端&#xff0c;少部分为服务端&#xff1b; 然而其实很多用户对windows系统不是很了解&#xff0c;安全配置更是如此&#xff1b; 因此我们安全人员要定期对员工的主…