RTOS任务调度流程——基于FreeRTOS在Cortex-m4上的实现

一. FreeRTOS任务调度流程
  • RTOS调度流程中主要关注以下几个点:创建任务、开启任务调度、任务调度;下面将以FReeRTOS在Cortex-m4的实现为例,展示一下RTOS任务调度的大体流程。
    在这里插入图片描述
二. 创建任务
  • 创建任务之前,为了防止被其他中断或异常打断,通常会开辟一个临界段。在Cortex-m4中,是通过操作basepri寄存器,来实现临界段的功能的。
    在这里插入图片描述
  • 此处采用静态方式创建任务,即任务栈空间需要用户提供。因此创建任务时主要提供的参数是任务主体函数、任务名字、任务栈地址及空间大小、任务优先级以及任务控制块。任务控制块即为描述任务信息的一个结构体,如下
typedef struct tskTaskControlBlock
{
		volatile StackType_t 		*pxTopOfStack;		/* 栈顶指针,作为TCB的第一个成员 */
		ListItem_t					 xStateListItem;	/* 任务节点 */
		StackType_t					 *pxStack;			/* 任务栈起始地址 */
		char						 pcTaskName[configMAX_TASK_NAME_LEN ];	/* 任务名称 */
		TickType_t					 xTicksToDelay;		/* 阻塞延时时间 , 单位为SysTick的中断周期 */
		UBaseType_t          		 uxPriority;		/* 任务优先级	*/
		/*..............................*/
}tskTCB;
  • 创建任务主要是做以下几件事:
  • 根据任务栈的基地址以及栈大小得到栈顶地址(向下生长),并且将栈顶地址进行8字节对齐(一般32位内核CPU只要4字节对齐,因为考虑到后续兼容浮点运算);
  • 保存任务名字至TCB控制块,初始化TCB的任务优先级;
  • 初始化TCB任务控制块所在的节点,并将该节点的拥有者设置为TCB;
  • 初始化任务堆栈,使得xPSR寄存器24位置1(使用Thumb指令),同时规定好PC、LR寄存器等的值(将PC寄存器设为任务主体函数地址),最后返回指向空闲栈的栈顶指针;
  • 将任务按照优先级添加至对应的任务就绪列表,同时将优先级位号图对应位置1(若第一次创建任务,则会初始化任务就绪、任务阻塞延时列表);
  • 任务创建成功后,任务栈及任务就绪列表如下
    在这里插入图片描述
    在这里插入图片描述
三. 开启任务调度
  • 开启任务调度器前,会首先去创建一个优先级最低的任务——空闲任务,当没有其他任务运行时,才会运行该任务。
  • 初始化时基计数器,该计数器用来控制任务运行节拍。
  • 接着初始化sysTick(之所以选择这个定时器,是为了方便Cortex-m4内核的处理器间移植),选择合适的定时周期。
  • 配置PendSV异常。
  • 调用SVC异常,在异常处理函数中启动第一个任务
  • 获取当前任务控制块的栈顶指针
  • 将任务控制块的栈中”r4-r11”寄存器的值,加载到CPU的r4-r11寄存器
  • 将任务控制块的栈顶指针更新至psp
  • 设置r14/LR寄存器,使其异常返回时使用psp指针、thumb指令、用户模式
  • bx r14异常返回,此时CPU自动将psp中剩下内容加载到CPU寄存器:xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参)
  • 此时,将会跳转到任务函数去执行
四. 任务调度
  • 当前运行的任务被调度成其他任务,主要有两种情况:一是将自己挂起进入阻塞状态,主动让出CPU使用权;二是当sysTick一个节拍来临后,有更高优先级任务而被剥夺CPU使用权。
  • 使用vTaskDelay阻塞延时函数 —— 阻塞任务,让出CPU使用权
  • 将任务移出就绪列表,若该优先级就绪列表没有其他任务,则将优先级位号图对应的位清零
  • 根据传入的延时时间,计算任务解锁时间,并作为节点排序值,插入到阻塞延时列表(若时间溢出,插入到溢出阻塞延时列表;否则插入普通阻塞延时列表)
  • 更新最小的任务阻塞延时到期时间
  • 调用PendSV异常
  • sysTick中断服务函数 —— 找出就绪任务,并进行任务切换
  • 更新系统时基计数器的值
  • 若时基计数器的值溢出,则交换阻塞延时列表,设置下一个任务解锁时间。若该列表没有其他任务,则设为最大0xFFFFFFFF;否则设为下一个任务到期的时间。
  • 如果有任务到期,则将其从阻塞列表移除,加入就绪列表。直到列表为空(设置下一个任务解锁时间为最大) 或还有没到期的任务(设置下一个任务解锁时间为此时间)。
  • 调用PendSV异常
  • PendSV异常服务函数 —— 任务切换(对比与SVC,多了上文保存和上下文切换的过程)
  • 上文保存:将CPU的r4-r11寄存器保存到当前任务的任务栈中
  • 上下文切换:根据前导零计数指令clz配合优先级位号图,得到当前最高优先级的就绪任务;将当前任务块指针pxCurrentTCB指向该优先级就绪列表第一个节点的任务控制块。
  • 下文切换:将切换后任务的”r4-r11”寄存器保存到CPU的r4-r11寄存器中;同时更新psp指针。
  • 异常返回,此时CPU自动将psp中剩下内容加载到CPU寄存器,:xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参)
  • 此时将会跳转到当前任务控制块对应的任务函数去执行。
五. 参考资料
  • FreeRTOS源码
  • 野火《FreeRTOS 内核实现与应用开发实战指南》
  • 《ARM体系结构与编程》

以上是我在学习过程中的总结,不当之处请在评论区指出。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值