FreeRTOS学习(10)--临界段

前文提要
FreeRTOS学习(1)—为什么使用RTOS
FreeRTOS学习(2)-链表和节点之结构体分析
FreeRTOS学习(3)-链表和节点程序分析
FreeRTOS学习(4)-什么是任务?
FreeRTOS学习(5)-任务之静态创建函数解析
FreeRTOS学习(6)-任务之静态创建函数解析2(重点)
FreeRTOS学习(7)-任务切换理论分析(重点)
FreeRTOS学习(8)–pendsv和systick优先级分析以及任务切换场景
FreeRTOS学习(9)–任务调度汇编函数分析(难点)

1、什么是临界段?

    临界段(Critical Section)就是一段在执行过程中不可以被其他事情打断的代码。能够打断的一个是系统调度,还有一个就是外部中断。在FreeRTOS,系统调度SVC或者是systick产生系统调度,都要使用pendsv中断。因此总结来说,临界段就是一段执行过程中不可以被中断打断的代码。因此FreeRTOS 对临界段的保护也就是对中断的开和关的控制

2、临界段的使用场景

    临界段(Critical Section)在实时操作系统(RTOS)中是指一段需要独占访问共享资源的代码。临界段的目的是防止多个任务或中断同时访问共享资源,从而导致数据不一致或竞争条件。使用临界段的主要场景包括:

(1)访问共享数据

当多个任务需要读取或写入共享数据时,必须确保在任意时刻只有一个任务能访问该数据。否则,会出现数据竞争和不一致的问题。比如一个变量存在多个任务函数进行处理,某一时刻只能有一个任务进行处理。

(2)更新全局指针或资源状态

全局指针(如当前任务控制块指针 pxCurrentTCB)或其他全局状态变量的更新需要被保护,以防止任务切换过程中出现不一致的情况。

(3)硬件访问

某些硬件资源不能被多个任务同时访问,比如某些外设寄存器、传感器、显示设备等。在访问这些硬件资源时,需要使用临界段保护,以确保访问的原子性。

3、Cortex-M3 的屏蔽寄存器组

在这里插入图片描述

在 FreeRTOS 中,对中断的开和关是通过操作 BASEPRI 寄存器来实现的,即大于等于 BASEPRI 的值的中断会被屏蔽,小于 BASEPRI 的值的中断则不会被屏蔽。用户可以设置 BASEPRI 的值来选择性的给一些非常紧急的中断留一条后路。

4、临界区代码保护函数

(1)在这里用图表进行显示,先弄清楚外部轮廓怎么使用,在进行细致的分析。

在这里插入图片描述

(2)对上面的任务级和中断级进行解释:

在这里插入图片描述

(3)他们的使用方式

①没有嵌套的情况下

在这里插入图片描述

②在有嵌套的情况下

嵌套进入临界区时,需要确保每次退出临界区时的调用次数和进入临界区的调用次数相同。在中断级中我们要保存并恢复每次进入临界区的中断状态。
在这里插入图片描述

5、特点

(1)临界区成对使用

(2)支持嵌套

(3)尽量保持临界段消耗时间很短,不然处理过长会导致中断延时。因为临界区直接屏蔽了中断,系统任务调度靠中断,ISR也靠中断。

6、接下来详细分析任务级函数进入临界区taskENTER_CRITICAL()

在这里插入图片描述

经过两层嵌套后,实际上是vPortEnterCritical()这个函数。

(1)vPortEnterCritical()函数分析

在这里插入图片描述

在这里插入图片描述

(2)portDISABLE_INTERRUPTS();函数分析

在这里插入图片描述

(3)关中断-无返回值函数分析vPortRaiseBASEPRI()

①源代码

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
		dsb
		isb
	}
}

②问题1:此处为啥使用static portFORCE_INLINE ?

使用 static 修饰符有以下几个作用:

作用域限制:函数只能在定义它的源文件中可见,避免了命名冲突和不必要的全局可见性。
优化:编译器可以更好地优化静态函数,因为它知道这些函数不会在其他源文件中使用。

portFORCE_INLINE

#define portFORCE_INLINE __forceinline
portFORCE_INLINE 是一个宏,用于强制内联函数。内联函数在调用时不通过函数调用机制,而是直接将函数体插入到调用处,这样可以减少函数调用的开销。

内联函数的优点包括:

减少函数调用开销:避免了函数调用时的参数传递、栈操作等开销。
减少函数调用的延迟:对于实时系统,减少延迟非常重要。
增强编译器优化:编译器可以更好地优化内联函数,因为它可以看到完整的函数代码。

在 FreeRTOS 中,static portFORCE_INLINE 常用于定义一些小的、频繁调用的函数,比如临界段入口和出口函数。这种做法确保了这些函数在每次调用时不会有函数调用开销,并且可以被编译器更好地优化,从而提高系统的性能和响应速度。

③代码解析

这个函数仅仅用于提高 BASEPRI 寄存器的值,从而屏蔽一定优先级范围内的中断。

④问题1:vPortRaiseBASEPRI这个函数为什么不能用于中断?这也是taskENTER_CRITICAL()为啥不用于中断的原因。

a.优先级管理限制

在中断处理程序中,中断优先级已经确定,并且中断处理程序本身具有更高的优先级。如果在中断处理程序中调用vPortRaiseBASEPRI,试图修改BASEPRI寄存器,可能会干扰其他中断的优先级管理,导致不可预见的中断屏蔽行为。

b.嵌套中断问题

中断处理程序中通常会涉及嵌套中断。如果在ISR中调用vPortRaiseBASEPRI,会改变中断屏蔽级别,可能导致更高优先级的中断被不恰当地屏蔽,从而影响系统的实时响应。

7、分析任务级函数退出临界区taskEXIT_CRITICAL()

在这里插入图片描述

经过两层嵌套后,实际上是vPortExitCritical()这个函数。

(1)vPortExitCritical()函数分析

在这里插入图片描述
在这里插入图片描述

(2)portENABLE_INTERRUPTS();函数分析

在这里插入图片描述

8、分析中断级函数进入临界区taskENTER_CRITICAL_FROM_ISR()

在这里插入图片描述

经过两层嵌套后,实际上是ulPortRaiseBASEPRI()这个函数。

(1)ulPortRaiseBASEPRI()函数分析

在这里插入图片描述

(2)代码解释

在这里插入图片描述

(3)为啥ulPortRaiseBASEPRI函数可以用于中断?也就是taskENTER_CRITICAL_FROM_ISR()可以作为中断使用的原因

    函数设计目的是为了临时提升 BASEPRI 寄存器的值,进入临界区,而不是完全禁用所有中断。它只提升 BASEPRI 到指定的优先级(configMAX_SYSCALL_INTERRUPT_PRIORITY),高优先级的中断仍然可以响应。
    函数保存当前的 BASEPRI 值并在函数结束时返回。这样,在函数调用后,可以恢复之前的 BASEPRI 值,确保中断级别的上下文一致性。

9、分析中断级函数退出临界区taskEXIT_CRITICAL_FROM_ISR( x )

在这里插入图片描述

经过两层嵌套后,实际上是vPortSetBASEPRI(x)这个函数。

(1)vPortSetBASEPRI(x)函数分析

在这里插入图片描述
通过x的赋值给basepri寄存器,退到原有的优先级。

10、总结

学习临界段及其在 FreeRTOS 中的实现和使用方法和原理。掌握了任务级和中断级临界段的具体实现和使用方法。也理解了临界段的使用场景和需求。

补上目录供读者查看

个人学习文档,有问题欢迎大家评论交流,如果感到有用的话点个赞吧。ヽ(。◕‿◕。)ノ゚

  • 23
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值