13.4.4 PendSV任务切换

更新于 2026年10月10日 版权声明
13.4.4 PendSV任务切换

在FreeRTOS中,若任务不是第一次切换,则采用PendSV中断实现。由13.2.2小节可知,从就绪态到运行态一定要通过调度才能实现任务切换。那么,如果现有多个任务处于就绪态,FreeRTOS如何进行任务切换?下面将详细讨论。

1.任务调度的步骤

假设13.2.1小节中的第3个任务Task3_led已处于阻塞态,现Task1_led和Task2_led均处于就绪态,任务调度步骤如图13.15所示。先对正在运行的低优先级的任务压栈,然后在就绪列表中查找最高优先级2,再根据优先级数值找到就绪列表pxReadyTasksLists[2],再在列表中查找列表项,找到该任务的任务控制块,最后根据任务控制块,找到堆栈地址并执行出栈操作,把堆栈中的数据弹给CPU,完成任务Task2_led的调度。

2.任务切换的场合

FreeRTOS任务切换有如下3种情况。

(1)任务级调度。

任务在运行过程中,由于等待某种条件而暂停的情况下,发生任务级调度。任务级调度就是用taskYIELD()函数触发一次PendSV中断,通过一个宏实现,定义如下。

图示

图13.15 任务调度步骤

图示

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

图示

(2)中断级调度。

在中断服务程序执行完成,可调用相应函数执行一次中断级任务调度。中断级的任务切换函数为portYIELD_FROM_ISR(),定义如下。

图示(https://www.daowen.com)

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

(3)时间片轮转调度。

若优先级相同,则用时间片轮转调度。要使用时间片轮转调度,宏configUSE_PREEMPTION和宏configUSE_TIME_SLICING必须为1。时间片的长度由宏configTICK_RATE_HZ来确定,一个时间片的长度就是SysTick的中断周期,如果configTICK_RATE_HZ为1000,那么一个时间片的长度就是1 ms。

在FreeRTOS中,SysTick的中断服务函数如下。

图示

在节拍定时器SysTick中断服务函数中调用了FreeRTOS的API函数xPortSysTickHandler(),此函数源码如下。

图示

3.PendSV中断服务函数

PendSV中断服务函数xPortPendSVHandler()是用汇编语言编写的压栈、出栈操作的函数,该函数的原型如下。

图示

PendSV中断服务程序任务切换示意如图13.16所示。

图示

图13.16 PendSV中断服务程序任务切换示意图

首先,将正在运行的低优先级任务压到自己的堆栈中,R4~R11需要手动压栈,R0~R3、R12、LR、PC、xPSR已经自动压入堆栈;紧接着调整指针,指向新的任务控制块中的栈顶指针pxTopOfStack,最后使R4~R11寄存器从堆栈中把数据弹给CPU,中断返回后,自动将R0~R3、R12、LR、PC、xPSR出栈。注意,这里发生PendSV中断自动压栈和自动出栈分别对应处于阻塞态的低优先级任务和即将运行的高优先级任务。

↑上一章 ↓下一章
关注公众号获取验证码
复制内容需要验证码(7.99元/天)