【学习日记】【FreeRTOS】临界段的保护

news2024/11/25 13:36:01

写在前面

本文主要是对于 FreeRTOS 中临界段的保护的详细解释,代码大部分参考了野火 FreeRTOS 教程配套源码,作了一小部分修改。

一、什么是临界段

临界段就是一段在执行的时候不能被中断的代码段

**临界段(Critical Section)**是指在多任务或多线程环境下,一段代码或一组代码,在执行期间对共享资源进行访问或操作的临界区域。临界段中的代码只能被一个任务或线程单独执行,以确保对共享资源的访问是正确和一致的。

二、为什么要保护临界段

在进入临界段之前,任务或线程需要获得对应的同步机制(如互斥锁、信号量等)的控制权。一旦获得控制权,任务或线程可以安全地访问临界段中的共享资源。在临界段执行完成后,任务或线程释放同步机制的控制权,让其他任务或线程可以进入临界段执行。

三、要保护临界段,我们需要做什么?

1. 了解临界段什么时候会被打断

  1. 系统调度
  2. 外部中断
  • 因此,对临界段的保护的实质——对中断的开和关的控制

2. 了解Cortex-M内核中断指令的开关指令

①一些缩写的解释

  • NMI:Non-Maskable Interrupt(不可屏蔽中断)
  • CPSID:Change Program Status and Interrupt Disable(修改程序状态并禁用中断)
  • CPSID 后面跟着的 I 或者 F 分别表示 Interrupt(普通的可屏蔽中断)和 FAULT Interrupt(异常中断)
  • Primask是一个特殊的寄存器,全称是 “PrIMask”,其含义是 “Priority Mask”,即优先级屏蔽
  • basepri"代表的是"Base Priority",即基本优先级

②中断操作指令

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

③寄存器说明

在这里插入图片描述

3. 可嵌套的中断开启与关闭函数对保护临界区的作用

野火的教程在这方面语焉不详,笔者自行理解了一下:

所谓可嵌套的中断操作函数,指的是该中断函数有返回值,返回该函数操作前的中断屏蔽等级,便于其他函数使用该返回值恢复现场。

对于可嵌套的中断操作函数在保护临界区方面有如下作用:

进入临界区时,关闭中断,此时可嵌套的关闭中断函数可以保存关闭前的屏蔽等级,在退出临界区可以使用该保存的屏蔽等级进行屏蔽等级的恢复设置。

代码例程如下:

// 全局共享资源
volatile int counter = 0;

// 中断处理程序
void interrupt_handler()
{
    // 进入临界区(关闭中断)
    Interruption_Mask_Level = disable_interrupt();

    // 访问共享资源
    counter++;

    // 退出临界区(打开中断)
    enable_interrupt(Interruption_Mask_Level);
}

// 主程序
int main()
{
    // 启用中断

    while (1)
    {
        // 执行其他任务
    }

    return 0;
}

四、临界段相关操作函数详解

主要分为三类函数:

  1. 关中断函数
  2. 开中断函数
  3. 进出临界段函数

其中开关中断函数被进出临界段函数调用。

1. 关中断函数

  • 宏定义
//关中断函数
	//不带返回值的关中断函数,不能嵌套,不能在中断里面使用
#define portDISABLE_INTERRUPTS()				vPortRaiseBASEPRI()
	//带返回值的关中断函数,可以嵌套,可以在中断里面使用
#define portSET_INTERRUPT_MASK_FROM_ISR()		ulPortRaiseBASEPRI()

//中断屏蔽等级的设置
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191   /* 高四位有效,即等于0xb0,或者是11 */

  • 不带返回值的关中断函数,原理就是改写 BASEPRI 寄存器的值屏蔽指定等级的中断
//不带返回值,不能嵌套
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		msr basepri, ulNewBASEPRI	//宏定义中 basepri = 11,也就是优先级大于11的中断不被响应
		dsb
		isb
	}
}
  • 带返回值的关中断函数,原理就是先记录 BASEPRI 寄存器的值作为函数返回值,然后再改写 BASEPRI 寄存器的值屏蔽指定等级的中断
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		mrs ulReturn, basepri		//先把basepri中的值保存在返回值中
		msr basepri, ulNewBASEPRI	//再设置basepri的值
		dsb
		isb
	}

	return ulReturn;
}

2. 开中断函数

  • 宏定义
    • 不可嵌套的开中断函数就是将 BASEPRI 寄存器的值设置为 0,使任何一个中断都不被屏蔽
    • 可嵌套的开中断函数就是将 BASEPRI 寄存器的值设置为 可嵌套的关中断函数的返回值,使中断屏蔽等级在进出临界区后和之前保持一致
//开中断函数
	// 不可嵌套
#define portENABLE_INTERRUPTS()					vPortSetBASEPRI( 0 )
	// 可嵌套
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)
  • 具体函数实现
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
	__asm
	{
		/* Barrier instructions are not used as this function is only used to
		lower the BASEPRI value. */
		msr basepri, ulBASEPRI
	}
}

3. 进出临界段函数

进出临界段函数抽象了好几层,如下:
task.h
可以看到,不带中断保护版本多抽象了一层,而带中断保护版本直接对开关中断进行操作

/* ==========进入临界段,不带中断保护版本,不能嵌套=============== */                                
#define taskENTER_CRITICAL() portENTER_CRITICAL()
/* ==========进入临界段,带中断保护版本,可以嵌套=============== */
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()	
/* ==========退出临界段,不带中断保护版本,不能嵌套=============== */
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
/* ==========退出临界段,带中断保护版本,可以嵌套=============== */
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )

portmacro.h

  • 下面是不带中断保护版本的进出临界段函数的抽象:
//进出临界段
#define portENTER_CRITICAL()					vPortEnterCritical()
#define portEXIT_CRITICAL()						vPortExitCritical()

展示不带中断保护版本的进出临界段函数代码:

  1. vPortEnterCritical(): 这个函数的作用是进入临界区。它首先调用portDISABLE_INTERRUPTS()函数来禁用中断,阻止其他中断干扰。然后,它将uxCriticalNesting计数器加一,表示进入了一个新的临界区。如果uxCriticalNesting计数器的值为1,即当前进入的是第一个临界区,那么可以使用configASSERT()来进行断言检查,确保该函数不是在中断上下文中调用。

  2. vPortExitCritical(): 这个函数的作用是退出临界区。它首先检查uxCriticalNesting计数器的值,确保当前确实在一个临界区中。然后,它将uxCriticalNesting计数器减一,表示退出当前的临界区。如果uxCriticalNesting计数器的值减为0,即没有进入任何临界区了,那么可以调用portENABLE_INTERRUPTS()函数来重新启用中断,允许其他中断恢复执行。

//进入临界段
void vPortEnterCritical( void )
{
	portDISABLE_INTERRUPTS();
	uxCriticalNesting++;

	/* This is not the interrupt safe version of the enter critical function so
	assert() if it is being called from an interrupt context.  Only API
	functions that end in "FromISR" can be used in an interrupt.  Only assert if
	the critical nesting count is 1 to protect against recursive calls if the
	assert function also uses a critical section. */
	if( uxCriticalNesting == 1 )
	{
		//configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
	}
}


//退出临界段
void vPortExitCritical( void )
{
	//configASSERT( uxCriticalNesting );
	uxCriticalNesting--;
    
	if( uxCriticalNesting == 0 )
	{
		portENABLE_INTERRUPTS();
	}
}
  • 而带中断保护版本的就是可嵌套开关中断函数:
//进出临界段
//带返回值的关中断函数,可以嵌套,可以在中断里面使用
#define portSET_INTERRUPT_MASK_FROM_ISR()		ulPortRaiseBASEPRI()
//可嵌套开中断函数
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)

五、临界段操作函数如何使用

1. 中断场合

在这里插入图片描述

2. 非中断场合

在这里插入图片描述

后记

如果您觉得本文写得不错,可以点个赞激励一下作者!
如果您发现本文的问题,欢迎在评论区或者私信共同探讨!
共勉!

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

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

相关文章

文心一言 VS 讯飞星火 VS chatgpt (74)-- 算法导论7.2 2题

二、如果用go语言,当数组A的所有元素都具有相同值时,QUICKSORT的时间复杂度是什么? 文心一言: 在Go语言中,对一个所有元素都相等的数组进行快速排序(QuickSort)的时间复杂度是O(n log n)。 快速排序是一…

栈和队列修炼指南(基本操作+OJ练习)

栈和队列修炼指南 1. 栈 1. 1 概念及结构 栈:是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素的操作。进行数据插入和删除操作的一端称为栈顶,另一端为栈底。 栈中的数据元素遵守后进先出原则(LIFO)原则 压栈:栈的…

【Git】—— git的配置

目录 (一)忽略特殊⽂件 (二)给命令配置别名 (一)忽略特殊⽂件 在⽇常开发中,我们有些⽂件不想或者不应该提交到远端,⽐如保存了数据库密码的配置⽂件,那怎么让Git知道呢…

为什么金鸣识别不做成离线版?

来百度APP畅享高清图片 在众多的用户咨询中,金鸣识别客服常常会被用户问及为何不做成离线版的问题,下面我就在这里跟大伙说说其中的原因吧。 离线版的OCR准确率相对于网络版可能会较低,主要有以下几个原因: 1. 数据量和模型更新…

WLRYJ-300型微流控芯片真空热压机

WLRYJ-300型微流控芯片真空热压机 关键词:微流控芯片,热压封合,LNP合成芯片 WLRYJ-300型微流控芯片真空热压机是一款应用于PMMA、PC、PP、COP、COC、BOPET、CBC、树脂(部分)、聚乙烯(部分)等硬质…

干货丨学完网络安全专业,我掌握了哪些技能?

andy Ng在我校完成网络防御与司法大专(Diploma in Network Defense and Forensic Countermeasures)之后,顺利升入我校的网络安全本科课程,目前她就职于一家金融机构并担任安全操作中心的分析专员。在进入我校就读之前,Sandy在建筑行业领域工作…

tsup打包如何更改outFileName

首先简单介绍一下,什么是tsup tsup是一个打包工具,类似的有rollup tsup的官网:https://tsup.egoist.dev/ tsup的优点:打包速度快,配置少,与ts兼容良好 安装 npm i tsup -D # Or Yarn yarn add tsup --…

决策规划仿真平台搭建

决策规划仿真平台搭建 自动驾驶决策规划算法第二章第一节 决策规划仿真平台搭建 这部分的主要难点在于多个软件的连通与适配,环境的搭建总是折磨人的,主要是 4 个软件,各软件版本如下 Visual Studio2017PreScan8.5.0CarSim2019.0MATLAB2019b…

【黑马头条之xxl-Job分布式任务调度】

本笔记内容为黑马头条项目的分布式任务调度热点文章部分 目录 一、今日内容 1、需求分析 2、实现思路 3、定时计算 4、定时任务框架-xxljob 二、分布式任务调度 1、什么是分布式任务调度 2、xxl-Job简介 3、XXL-Job-环境搭建 4、配置部署调度中心-docker安装 5、xx…

美团外卖红包入口在哪里找到如何免费领取美团外卖红包天天神券?

美团外卖红包免费领取入口在哪里找到? 关注「草柴」微信公众号,回复「美团外卖」就可以找到美团外卖红包免费领取入口,获得美团外卖节红包优惠券; 美团外卖节红包优惠券怎么使用享受优惠? 1、通过(上面&a…

2021年12月 C/C++(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题&#xff1a;输出整数部分 输入一个双精度浮点数f&#xff0c; 输出其整数部分。 时间限制&#xff1a;1000 内存限制&#xff1a;65536 输入 一个双精度浮点数f(0 < f < 100000000)。 输出 一个整数&#xff0c;表示浮点数的整数部分。 样例输入 3.8889 样例输出 3…

Vue.js2+Cesium1.103.0 八、动态光墙效果

Vue.js2Cesium1.103.0 八、动态光墙效果 Demo <template><divid"cesium-container"style"width: 100%; height: 100%;"/> </template><script> /* eslint-disable no-undef */ import /utils/dynamicWallMaterialProperty.js exp…

浅谈AI人工智能ChatGpt提升竞彩足球分析准确率最高的分析软件

随着科技的不断进步&#xff0c;人工智能正在扮演着越来越重要的角色。在体育领域&#xff0c;特别是足球竞猜中&#xff0c;AI人工智能ChatGpt正以其卓越的分析能力引起了广泛的关注。作为一款以大数据分析为基础的分析软件&#xff0c;AI人工智能ChatGpt不仅可以提供准确的数…

树莓派命令行运行调用音频文件的函数,不报错,没有声音解决办法

树莓派接上音频首先需要切换音频不是HDMI&#xff0c;然后可以双击运行wav文件可以播放&#xff0c;但是&#xff1a; 命令行直接运行wav文件报错&#xff1a; Playing WAVE twzc.wav : Signed 16 bit Little Endian, Rate 16000 Hz, Mono命令行运行main方法也是无法播放&am…

Linux学习————redis服务

目录 一、redis主从服务 一、redis主从服务概念 二、redis主从服务作用 三、缺点 四、主从复制流程 五、搭建主从服务 配置基础环境 下载epel源&#xff0c;下载redis​编辑 二、哨兵模式 一、概念 二、作用 三、缺点 四、结构 五、搭建 修改哨兵配置文件 启动服务…

骨传导耳机对耳朵有损害吗?最不伤耳的骨传导耳机

骨传导耳机对耳朵有损害吗&#xff1f; 骨传导是除了空气传导之外另一种很重要的听觉方式&#xff0c;它是通过骨骼进行传递声音的&#xff0c;利用骨头振动的原理&#xff0c;声音直接绕过耳膜等神经单元&#xff0c;声音直接传送到内部耳神经。 骨传导耳机的原理就是通过人体…

React 全栈体系(一)

第一章 React入门 一、React简介 1. 是什么&#xff1f; 是一个将数据渲染为HTML视图的开源JavaScript库。 2. 谁开发的&#xff1f; 由Facebook开源 3. 为什么要学&#xff1f; 原生JavaScript操作DOM繁琐&#xff0c;效率低&#xff08;DOM-API 操作 UI&#xff09; 使…

nvidia驱动更新导致驱动版本不匹配

关于nvidia驱动自动更新&#xff1a; 和这篇描述类似&#xff1a;https://blog.csdn.net/weixin_43568307/article/details/128187469 记得记录原先版本 sudo dpkg-reconfigure unattended-upgrades 关闭一下自动更新。 当前主机做好快照备份&#xff0c;重新安装之前的驱…

代码随想录算法训练营第53天|动态规划part11|123. 买卖股票的最佳时机 III、188.买卖股票的最佳时机IV

代码随想录算法训练营第53天&#xff5c;动态规划part11&#xff5c;123. 买卖股票的最佳时机 III、 188.买卖股票的最佳时机IV 123. 买卖股票的最佳时机 III 123. 买卖股票的最佳时机 III 思路&#xff1a; 相比买股票的最佳时机II&#xff0c;限制了买股票的次数&#xf…

高端百度地图开发1:自定义水滴头像(自定义标注覆盖物、Overlay覆盖类)

自定义水滴头像&自定义标注覆盖物 一、引入百度地图JSAPI库二、构建map容器1. CSS样式表2.HTML容器 三、核心代码1.百度地图API功能2.定义构造函数并继承Overlay3.初始化自定义覆盖物4.绘制覆盖物5.添加覆盖物 自定义标注覆盖物&#xff08;Custom Overlay&#xff09;是百…