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.rhkb.cn/news/294300.html

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

相关文章

代码随想录算法训练营第39天|62.不同路径 |63. 不同路径 II

代码随想录算法训练营第39天|62.不同路径 |63. 不同路径 II 详细布置 62.不同路径 本题大家掌握动态规划的方法就可以。 数论方法 有点非主流&#xff0c;很难想到。 https://programmercarl.com/0062.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84.html 视频讲解&#xff1a;https…

网络钓鱼升级 Darcula如何窃取用户信息

近日&#xff0c;网络安全领域一种名为 “Darcula” 的网络钓鱼欺诈&#xff08;PhaaS&#xff09;悄然兴起。这种新型钓鱼方式不同于传统的手段&#xff0c;它巧妙地利用了谷歌信息和 iMessage 的富通信服务&#xff08;RCS&#xff09;&#xff0c;成为了网络犯罪分子的新手段…

dockerfile制作-pytoch+深度学习环境版

你好你好&#xff01; 以下内容仅为当前认识&#xff0c;可能有不足之处&#xff0c;欢迎讨论&#xff01; 文章目录 文档内容docker相关术语docker常用命令容器常用命令根据dockerfile创建容器dokerfile文件内容 docker问题&#xff1a;可能的原因和解决方法示例修改修改后的D…

vscode调试Unity

文章目录 vscode调试UnityC#环境需求开始调试 Lua添加Debugger环境配置联系.txt文件配置Java环境 添加调试代码断点不生效的问题 vscode调试Unity C# 现在使用vscode调试Unity的C#代码很简单&#xff0c;直接在vscode的EXTENSIONS里面搜索“Unity”&#xff0c;第一个就是&am…

YOLOv8改进 | 细节涨点篇 | 利用YOLOv8自带的RayTune进行超参数调优

一、本文介绍 本文给大家带来的改进机制是利用Ray Tune进行超参数调优,在YOLOv8的项目中目前已经自带了该超参数调优的代码,我们无需进行任何的改动,只需要调用该方法输入我们的一些指令即可,当然了,这些超参数的设置还是比较又学问的,本文的内容也是应群友的需求进行发…

研发设计人员能力级别定义

研发设计人员能力&级别定义 1. 源由2. 级别定义3. 级别能力3.1 助理工程师3.1.1 工作内容3.1.2 级别晋升3.1.3 详细描述 3.2 初级工程师3.2.1 工作内容3.2.2 级别晋升3.2.3 详细描述 3.3 高级工程师3.3.1 工作内容3.3.2 级别晋升3.3.3 详细描述 3.4 资深工程师3.4.1 工作内…

【Entity Framework】EF中的增删改查

【Entity Framework】EF中的增删改查 文章目录 【Entity Framework】EF中的增删改查一、概述二、DbContext数据上下文三、EntityState五个状态值四、EF添加数据4.1 EF Add方式4.2 EF 通过改变对象的状态为 Added4.3 调用方sql4.4 调用存储过程 五、EF修改数据5.1 不查询数据库&…

仿真黑科技EasyGo DeskSim 2022

DeskSim2022的FPGA支持多种solver的混合应用&#xff0c;对于每一种solver可以采用不同的仿真步长&#xff0c;以下图模型为例&#xff0c;模型运行在FPGA上&#xff0c;FPGA解算方式采用的是Power Electronic & FPGA Coder解算&#xff0c;其中电力电子电路部分采用了两种…

ssm013小型企业办公自动化系统的设计和开发+vue

小型企业办公自动化系统的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对小型企业办公信息管理混乱&am…

wps 开发插件

官方文档参考wps官方文档参考 1.环境安装 安装wps https://www.wps.cn/ 安装Node.js https://nodejs.org/en 安装代码编辑器 Visual Studio Code https://code.visualstudio.com/ 环境检查-进入cmd查看 node -v2.demo 2.1 demo下载 打开vscode&#xff0c;新建终端 安装…

C#学习笔记 面试提要

冒泡 for (int m 0; m < arr.Length; m) { for (int n 0; n < arr.Length - 1 - m; n) { if (arr[n] > arr[n1]) { int temp arr[n]; arr[n] arr[n 1]; arr[n1] temp; } } } 选择 for (int m 0; m < arr.Length; m) { int index 0; for (int n 1; n < …

HackTheBox-Mist

整体思路 端口扫描->Pluck CMS组件文件读取漏洞->文件上传获取shell->创建指向exe的快捷方式来提权-> 信息收集&端口利用 namp -sSVC 10.10.11.17目标只开放了80端口&#xff0c;将mist.htb加入到hosts文件后&#xff0c;访问mist.htb Pluck CMS文件读取 在…

中科驭数超低时延网络解决方案入选2023年度金融信创优秀解决方案

近日&#xff0c;由中国人民银行领导、中国金融电子化集团有限公司牵头组建的金融信创生态实验室发布「2023年度第三期金融信创优秀解决方案」&#xff0c;中科驭数超低时延网络解决方案从众多方案中脱颖而出&#xff0c;成功入选&#xff0c;代表了该方案的技术创新和金融实践…

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

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

算法题->盛最多水的容器C语言和JAVA双指针解法

盛最多水的容器C语言和JAVA双指针解法 题目描述: 力扣链接:https://leetcode.cn/problems/container-with-most-water/description/ 题意: 根据数组中的值(高)和下标差值(宽),求能容纳最多的体积V. 例子: 输出49的求解过程,根据木桶效应,存储水的高度由短木板决定,故 V 短…

瑞_23种设计模式_迭代器模式

文章目录 1 迭代器模式&#xff08;Iterator Pattern&#xff09;★★★1.1 介绍1.2 概述1.3 迭代器模式的结构1.4 中介者模式的优缺点1.5 中介者模式的使用场景 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 JDK源码解析 &#x1f64a; 前言&#xff1a;本文…

NBU备份oracle详细配置文档(含常见报错处理方法)

​前提 NBU master和media服务器已经配置OK&#xff0c;现在需要oracle主机安装agent并配置备份任务。 NBU master版本8.3.0.2 Oracle OS版本redhat 6.8 Oracle版本 11.2.0.4 1.Oracle 安装agent 下载安装档 https://www.veritas.com/content/support/zh_CN 选择…

产品经理的进阶之路

点击下载《产品经理的进阶之路》 1. 前言 本文深入剖析了产品经理这一职业从产品专员起步,逐步晋升为产品经理、高级产品经理,直至产品总监的整个职业发展路径。在每个阶段,产品经理都需承担不同的工作职责,展现出独特的职业特点。 2. 产品专员 关键词【产品需求/原型/文…

【操作系统】FCFS、SJF、HRRN、RR、EDF、LLF调度算法及python实现代码

文章目录 一、先来先服务调度算法&#xff08;FCFS&#xff09; 二、短作业优先调度算法&#xff08;SJF&#xff09; 三、高响应比优先调度算法&#xff08;HRRN&#xff09; 四、轮转调度算法&#xff08;RR&#xff09; 五、最早截至时间优先算法&#xff08;EDF&#…

windows下部署llama.cpp

下载cmake 下载地址 解压&#xff0c;设置Path环境变量D:\CMake\bin 打开cmd输入cmake -version 安装mingw powershell下执行 Set-ExecutionPolicy RemoteSigned -Scope CurrentUser iex "& {$(irm get.scoop.sh)} -RunAsAdmin" scoop bucket add extras s…