一文带你深度了解FreeRTOS的任务切换之PendSV异常

RTOS系统的核心是任务管理,而任务管理的核心是任务切换,任务切换决定了任务的执行顺序,任务切换效率的高低也决定了一款系统的性能,尤其是对于实时操作系统。而对于想深入了解 FreeRTOS系统运行过程的同学其任务切换是必须掌握的知识点。本章记录我学习我FreeRTOS的任务切换过程之PendSV,本章分为如下几部分:
1 PendSV 异常
2 FreeRTOS任务切换场合

3 PendSV中断服务函数

目录

一、PendSV 

二、FreeRTOS任务切换场合

(一)、执行系统调用 

(二)、 系统滴答定时器(SysTick)中断

三、PendSV中断服务函数 


一、PendSV 

这是我从网上整理的PendSv概念:

FreeRTOS 是一个流行的开源实时操作系统(RTOS),广泛用于嵌入式系统开发。它提供了任务调度、时间管理、互斥量、信号量等多种实时操作系统的基本功能。在基于 ARM Cortex-M 架构的系统中,FreeRTOS 利用了该架构特有的一些特性来实现其功能,其中包括 PendSV(Pendable Service Call)异常。

PendSV 异常简介

PendSV(可挂起的服务调用)异常是 ARM Cortex-M 处理器提供的一种特殊类型的异常,用于支持操作系统级的上下文切换。在 FreeRTOS 中,PendSV 主要用于触发上下文切换,以便于操作系统从一个任务切换到另一个任务。

PendSV 在 FreeRTOS 中的作用

  1. 任务切换: 在 FreeRTOS 中,当一个任务需要让出 CPU 使用权,以便另一个任务可以运行时,会触发 PendSV 异常来实现任务的上下文切换。这个过程涉及保存当前任务的状态(寄存器、堆栈指针等)并恢复即将运行的任务的状态,从而实现无缝的任务切换。

  2. 低优先级: PendSV 设计为具有最低的优先级。这确保了所有其他更高优先级的中断和异常处理完毕后,才执行上下文切换,从而减少对实时性能的影响。

  3. 可控制的触发: 通过设置相应的控制寄存器,软件可以灵活地触发 PendSV 异常。这意味着 FreeRTOS 可以根据任务调度算法的需要,在合适的时机主动触发上下文切换。

  4. 高效的上下文切换: 利用 PendSV 异常进行上下文切换是非常高效的,因为它允许直接利用处理器的特性来保存和恢复任务的状态,避免了不必要的操作和延迟。

总结

PendSV 异常是 ARM Cortex-M 处理器提供的一种强大功能,FreeRTOS 利用这一机制实现了高效、可靠的任务切换。通过将 PendSV 设置为最低优先级,FreeRTOS 确保了系统的实时性能和响应性,同时也展示了操作系统与硬件架构紧密合作的一个典型例子。

PendSV(可挂起的系统调用)异常对FreeRTOS操作非常重要,其优先级可以通过编程设置。可以通过将中断控制和状态寄存器ICSR 的 bit28,也就是PendSV的挂起位置1来触发PendSV中断。与SVC异常不同,它是不精确的,因此它的挂起状态可在更高优先级异常处理内设置,且会在高优先级处理完成后执行。


利用该特性,若将PendSV设置为最低的异常优先级,可以让PendSV异常处理在所有其他中断处理完成后执行,这对于上下文切换非常有用,也是各种OS设计中的关键。
在具有嵌入式OS 的典型系统中,处理时间被划分为了多个时间片。若系统中只有两个任务,这两个任务会交替执行,如图所示:

上下文切换被触发的场合可以是:执行一个系统调用系统滴答定时器(SysTick)中断。
在OS 中,任务调度器决定是否应该执行上下文切换,如图中任务切换都是由SysTick
中断中执行,每次它都会决定切换到一个不同的任务中。


若中断请求(IRQ)在SysTick 异常前产生,则SysTick 异常可能会抢占IRQ的处理,在这种情况下,OS不应该执行上下文切换,否则中断请求IRQ处理就会被延迟,而且在真实系统中延迟时间还往往不可预知一一任何有一丁点实时要求的系统都决不能容忍这种事。

对于Cortex-M3和 Cortex-M4处理器,当存在活跃的异常服务时,设计默认不允许返回到线程模式.若存在活跃中断服务,且OS试图返回到线程模式,则将触发用法 fault,如图所示。

在一些OS设计中,要解决这个问题,可以在运行中断服务时不执行上下文切换,此时可以检查栈帧中的压栈xPSR或NVIC中的中断活跃壮态寄存器。不过,系统的性能可能会受到影响,特别时当中断源在SysTick中断前后持续产生请求时,这样上下文切换可能就没有执行的机会了。


为了解决这个问题,PendSV异常将上下文切换请求延迟到所有其他 IRQ处理都已经完成后,此时需要将PendSV设置为最低优先级。若OS需要执行上下文切换,他会设置PendSV的挂起壮态,并在 PendSV异常内执行上下文切换。如图所示:

图中事件的流水账记录如下:
(1)任务A呼叫SVC来请求任务切换(例如,等待某些工作完成)
(2)OS接收到请求,做好上下文切换的准备,并且 pend一个PendSV异常。

(3)当CPU退出SVC后,它立即进入Pendsv,从而执行上下文切换。
(4)当PendSV执行完毕后,将返回到任务B,同时进入线程模式。

(5)发生了一个中断,并且中断服务程序开始执行
(6)在ISR执行过程中,发生 SysTick 异常,并且抢占了该ISR。
(7) OS执行必要的操作,然后pend起 PendSV异常以作好上下文切换的准备。

(8)当SysTick 退出后,回到先前被抢占的ISR中,ISR继续执行。
(9)ISR执行完毕并退出后,PendSV服务例程开始执行,并且在里面执行上下文切换。

(10)当PendSv执行完毕后,回到任务A,同时系统再次进入线程模式。
讲解PendSV异常的原因就是让大家知道,FreeRTOS系统的任务切换最终都是在PendSv中断服务函数中完成的,UcOS也是在 PendSV中断中完成任务切换的。

在FreeRTOS中,ISR代表中断服务程序(Interrupt Service Routine)。中断服务程序是一段特殊的代码,用于处理硬件引发的中断事件。当硬件设备触发了一个中断,系统会立即跳转执行与该中断相关联的中断服务程序。在FreeRTOS中,可以使用中断服务程序来处理实时操作系统所需的硬件中断,例如定时器中断、串行通信中断等。通过适当地编写和管理中断服务程序,可以实现对系统资源的高效利用和实时任务的调度。

 二、FreeRTOS任务切换场合

前面我们介绍PendSV中断的时候提到过上下文任务切换触发的场合:

1、可以执行一个系统调用;

2、系统滴答定时器(SysTick)中断。

(一)、执行系统调用 

执行系统调用就是执行FreeRTOS系统提供的相关API函数,比如任务切换函数taskYIELD(),FreeRTOS有些API函数也会调用函数taskYIELD(),这些API函数都会导致任务切换,这些API函数和任务切换函数 taskYIELD()都统称为系统调用。函数 taskYIELD()其实就是个宏,在文件task.h中有如下定义:

#define taskYIELD()  portYIELD()

函数portYIELD()也是个宏,在文件portmacro.h中有如下定义:

(1)、通过向中断控制和状态寄存器ICSR的bit28写入1挂起PendSv来启动PendSV中断。这样就可以在 PendSV中断服务函数中进行任务切换了。
中断级的任务切换函数为portYIELD_FROM_ISR(),定义如下:

可以看出portYIELD_FROM_ISR()最终也是通过调用函数portYIELD()来完成任务切换的。

(二)、 系统滴答定时器(SysTick)中断

FreeRTOS中滴答定时器(SysTick)中断服务函数中也会进行任务切换,滴答定时器中断服务函数如下:

在滴答定时器中断服务函数中调用了FreeRTOS的API函数xPortSysTickHandler(),此函数源码如下:

(1)、关闭中断

(2)、通过向中断控制和壮态寄存器ICSR的bit28写入Ⅰ挂起PendSV来启动PendSV中断。这样就可以在 PendSV中断服务函数中进行任务切换了。
(3)、打开中断。

三、PendSV中断服务函数 

前面说了FreeRTOS任务切换的具体过程是在PendSV中断服务函数中完成的,接下来我们就来学习一个PendSV的中断服务函数,看看任务切换过程究竟是怎么进行的。PendSV中断服务函数本应该为PendSV_Handler(),但是FreeRTOS使用#define重定义了,如下:
 

#define xPortPendSVHandler    PendSV_Handler

函数xPortPendSVHandler() 源码如下:

(1)、读取进程栈指针,保存在寄存器R0里面。


(2)和(3),获取当前任务的任务控制块,并将任务控制块的地址保存在寄存器R2里面。

(4)和(5)、判断任务是否使用了FPU,如果任务使用了FPU的话在进行任务切换的时候就需要将FPU寄存器s16~s31手动保存到任务堆栈中,其中 sO~s15和FPSCR是自动保存的。


(6)、保存s16~s31这16个FPU寄存器。
(7)、保存r4~r11和R14这几个寄存器的值。


(8)、将寄存器RO的值写入到寄存器R2所保存的地址中去,也就是将新的栈顶保存在任务控制块的第一个字段中。此时的寄存器RO 保存着最新的堆栈栈顶指针值,所以要将这个最新的栈顶指针写入到当前任务的任务控制块第一个字段,而经过(2)和(3)已经获取到了任务控制块,并将任务控制块的首地址写如到了寄存器R2中。


(9)、将寄存器R3的值临时压栈,寄存器R3中保存了当前任务的任务控制块,而接下来要调用函数vTaskSwitchContext(),为了防止R3的值被改写,所以这里临时将R3的值先压栈。


(10)和(11)、关闭中断,进入临界区
(12)、调用函数vTaskSwitchContext(),此函数用来获取下一个要运行的任务,并将pxCurrentTCB更新为这个要运行的任务。
(13)和(14)、打开中断,退出临界区。


(15)、刚刚保存的寄存器R3的值出栈,恢复寄存器R3的值。注意,经过(12)步,此时pxCurrentTCB的值已经改变了,所以读取R3所保存的地址处的数据就会发现其值改变了,成为了下一个要运行的任务的任务控制块。


(16)和(17)、获取新的要运行的任务的任务堆栈栈顶,并将栈顶保存在寄存器R0中。(18)、R4~R11,R14出栈,也就是即将运行的任务的现场。
(19)、(20)和(21)、判断即将运行的任务是否有使用到FPU,如果有的话还需要手工恢复FPU的s16~s31寄存器。


(22)、更新进程栈指针PSP的值。
(23)、
执行此行代码以后硬件自动恢复寄存器RO~-R3、R12、LR、PC和xPSR的值,确定异常返回以后应该进入处理器模式还是进程模式,使用主栈指针(MSP)还是进程栈指针(PSP)。很明显这里会进入进程模式,并且使用进程栈指针(PSP),寄存器PC值会被恢复为即将运行的任务的任务函数,新的任务开始运行!至此,任务切换成功。

 

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

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

相关文章

项目管理软件:如何确保项目启动顺利?

对所有项目经理来说,了解如何启动项目是最关键的技能之一。项目都是从小事开始,逐渐发展为更大型、更复杂的。好的开始是成功的一半,对项目管理来说更是如此。 启动项目的 10 个简单步骤 即使是最复杂的项目,也可以分解成简单的…

爬虫练习:获取某网站的房价信息

一、相关网站 二、相关代码 import requests from lxml import etree import csv with open(房天下数据.csv, w, newline, encodingutf-8) as csvfile:fieldnames [名称, 地点,价格,总价,联系电话]writer csv.DictWriter(csvfile, fieldnamesfieldnames)writer.writeheader…

计算机丢失msvcp140.dll是什么意思,电脑自带dll修复安装下载

在使用电脑的过程中那个大家是不是有遇到过电脑丢失某个文件,导致电脑的程序不能继续运行,那么出现这样的问题有什么办法可以解决呢?其实解决办法还是有很多的!今天这篇文章就教大家如果电脑丢失的msvcp140.dll文件那么该怎么办&a…

基于PHP的餐厅管理系统APP设计与实现

目 录 摘 要 I Abstract II 引 言 1 1 相关技术 3 1.1 MVC 3 1.2 ThinkPHP 3 1.3 MySQL数据库 3 1.4 uni-app 4 1.5 本章小结 4 2 系统分析 5 2.1 功能需求 5 2.2 用例分析 7 2.3 非功能需求 8 2.4 本章小结 8 3 系统设计 9 3.1 系统总体设计 9 3.2 系统详细设计 10 3.3 本章小…

qt如何配置ros环境

在Qt5.7的版本可以使用bash -i -c来启动qt,让Qt自己识别系统环境,不知道为什么Qt在之后的版本,这样使用都失效了。因为它会默认把CMAKE_PREFIX_PATH修改掉。 网上还有安装ros插件版本的qt creator,感觉失去了一些灵活性。 自己测试…

学c还行,学Python很累,还有其他语言适合我吗?

学c还行,学Python很累,还有其他语言适合我吗? 在开始前我分享下我的经历,我刚入行时遇到一个好公司和师父,给了我机会,一年时间从3k薪资涨到18k的, 我师父给了一些 电气工程师学习方法和资料&a…

基于SSM的网络教学系统设计与实现

目 录 摘 要 I Abstract II 引 言 1 1 相关技术 3 1.1 B/S架构技术 3 1.2 Ajax技术 3 1.3 JavaScript 4 1.4 jQuery 4 1.5 SSM框架 4 1.5.1 Spring 5 1.5.2 Spring MVC 5 1.5.3 MyBatis 5 1.6 本章小结 6 2 系统分析 7 2.1 需求分析 7 2.2 系统用例分析 8 2.3 非功能需求分析 …

最全软件测试面试问题和回答,适合全文背诵

求职,类似于打仗,是一场挑战自己的战斗,也是一场跟用人单位的博弈,更是一场千人过独木桥的厮杀、混战。《孙子谋攻篇》中早就说了:"知己知彼,百战不殆;不知彼而知己,一胜一负&a…

安全知识分享域渗透

内网渗透思维导图 密码相关域渗透攻击思路DACL的应用NTLM 中继攻击滥用 ACE 内网渗透思维导图 专注于web漏洞挖掘、内网渗透、免杀和代码审计,感谢各位师傅的关注!网安之路漫长,与君共勉! 密码相关 域渗透攻击思路 常见攻击手法&a…

Paimon新版本核心特性和生产实践解读

最近Apche Paimon发布了最新版本0.7.0,在这个版本中,Paimon对一些新特性进行了增强。 Paimon在数据湖领域发展迅速,未来会在整个数据开发领域占有很重要的地位,今天我们来盘点一下当前能力的特点以及在生产环境中的使用情况。 Loo…

大数据赋能,能源企业的智慧转型之路

在数字洪流中,大数据已经成为推动产业升级的新引擎。特别是在能源行业,大数据的应用正引领着一场深刻的智慧转型。今天,我们就来探讨大数据如何在能源企业中发挥其独特的魅力,助力企业提效降本,实现绿色发展。 动态监控…

BK7231+字库+LCD显示

1、BK7231有2M flash,可以保存1个16*16汉字字库 2、驱动1个8位并口屏,将字库中的汉字中显示出来 一、将gb2312_80.bin打包到烧录镜像中。 1、bk7231u_rtt_sdk\OTAPackage\beken_packager中修改config.json文件: {"magic": "…

有关线性表的算法题

1.回文字符串 判断一个非空字符串是否是回文。 #include <iostream> #include <string> using namespace std;bool judge(string str) {int len 0;for (int i 0; i < 100; i) {if (str[i] < 65 || str[i]>122) {break;}len;//计算字符串的大小}f…

【JAVA】基于HTML与CSS的尚品汇项目

1.代码 index.html <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><!-- 引入页签图标 --><link rel"shortcut icon"…

MySQL--索引类型详解

索引的类型 主键索引&#xff1a; PRIMARY KEY&#xff0c;当一张表的某个列是主键的时候&#xff0c;该列就是主键索引&#xff0c;一张表只允许有一个主键索引&#xff0c;主键所在的列不能为空。 创建主键索引的SQL语法&#xff1a; # 给user表中的id字段创建名为id_ind…

【联邦学习综述:概念、技术】

出自——联邦学习综述&#xff1a;概念、技术、应用与挑战。梁天恺 1*&#xff0c;曾 碧 2&#xff0c;陈 光 1 从两个方面保护隐私数据 硬件层面 可 信 执 行 环 境 &#xff08;Trusted Execution Environment&#xff0c;TEE&#xff09;边 缘 计 算&#xff08;Edge Com…

Error attempting to get column ‘add_time‘ from result set

使用Mybatis-plus 重构项目&#xff0c;报错&#xff1a; Error attempting to get column add_time from result set 当前采用技术 1、数据库字段为 datetime 类型 2、使用了mybatis-plus 框架 3、实体类使用了 LocalDateTime 原因参考&#xff1a; Error attempting to ge…

天软特色因子看板(2024.3 第3期)

该因子看板跟踪天软特色因子A08006近一月日度买卖压力2)&#xff0c;该因子为近一个月个股每日的相对价格位置&#xff0c;用以刻画股票所受买卖压力&#xff0c;取作 个于0~1间&#xff0c;指标值越大&#xff0c;反映股票在价格相对高位停留的时间越长&#xff0c;所面临的买…

SSM整合项目(Vue3 + Element - Plus 创建项目基础页面)

文章目录 1.配置Vue启动端口1.修改vue.config.js2.启动 2.安装Element Plus命令行输入 npm install element-plus --save 3.修改Vue3默认样式并自定义一个组件1.修改App.vue1.删除原有结构2.启动项目查看 2.修改HomeView.vue3.删除HelloWorld.vue组件4.创建一个组件 src/compon…

PostgreSQL数据优化——死元组清理

最近遇到一个奇怪的问题&#xff0c;一个百万级的PostgreSQL表&#xff0c;只有3个索引。但是每次执行insert或update语句就要几百ms以上。经过查询发现是一个狠简单的问题&#xff0c;数据库表死元组太多了&#xff0c;需要手动清理。 在 PG 中&#xff0c;update/delete 语句…