浅谈FreeRTOS任务启动与切换流程

一个轻量级操作系统最核心的地方就在于任务的执行与切换,像FreeRTOS和ucOSⅢ在任务启动与切换方面都差不多,本文主要从枝干而省去了所有细枝末节让你最快的了解操作系统的任务创建与切换。
以正点原子FreeRTOS移植实验代码为例,该代码主要移植了FreeRTOS操作系统,并创建了4个任务,开始任务、LED闪烁1、LED闪烁2、浮点运算。
1.先从.s启动文件说起
启动文件里面有三个中断是操作系统必要的:
在这里插入图片描述
即SVC_Handler,PendSV_Handler,SysTick_Handler。其中:SVC是用于触发一个PendSV异常来进行一个上下文切换,具体切换过程在PendSV_Handler里完成,SysTick_Handler则是为操作系统提供一个时基,进行一系列操作比如延时时切换任务等。
简单点来说:
SVC_Handler是用于启动第一个任务的中断;
PendSV_Handler是用于每次任务切换中断;
SysTick_Handler是一个定时器,比如一个任务运行3s,这3s就是用这个定时器来计时得到的。
至于为什么这些操作要放在这几个中断内进行,这是因为CM3内核有两种模式:用户和特权,模式不同,权限不同,有些操作需要在特权模式下进行。
具体的代码分析放在后面,现在只了解存在这三个中断。(想了解的可以搜索:CM3的两种模式,两种权限,以及两个指针MSP与PSP,中断与异常)
接下来直接进入到main函数
在这里插入图片描述
在main里面关于操作系统主要存在于三个函数内:delay_init,xTaskCreate,vTaskStartScheduler
其中delay_init中主要开启了SysTick中断:

在这里插入图片描述
然后xTaskCreate是创建函数任务,里面主要操作是为任务申请堆栈空间
在这里插入图片描述
其中分为两种情况:堆栈向上和向下生长两种情况,不做具体讨论。
在为任务申请完空间之后,还有很重要的两个操作:
在这里插入图片描述
填充任务控制块信息和把新创建的任务加入就绪列表。
其中填充任务控制块中比较重要的是:
1获取栈顶地址pxTopOfStack
在这里插入图片描述
protSTACK_GROWTH<0即栈为向下生长,栈顶在高地址,然后需要8字节对齐。
可以看出来pxTopOfStack = 为任务申请的堆栈(数组)首地址+堆栈(数组)大小-1
2填充堆栈区域
在这里插入图片描述
上面的函数即为填充堆栈:
在这里插入图片描述
在这里插入图片描述

至此任务初始化完成。简单来说,任务创建过程就是:

  1. 申请内存
  2. 填充内存
    至于为什么要这么填充内存,其实和取用的时候有关,后面会详细解释。
    接下来就是开始任务调度:
    在这里插入图片描述
    在开启任务调度里面比较重要的是:
    1创建了空闲函数
    2开启任务切换
    在这里插入图片描述
    在这里插入图片描述
    xPortStartScheduler()是开启任务调度,里面除了设置相关内容,最重要的函数如下:
    在这里插入图片描述

即开始第一个任务。
在这里插入图片描述

该函数主要两个作用:

  1. 复位MSP值(MSP存在向量表的起始地址,而向量表起始地址存在SCB_VTO寄存器里(地址:0xE000ED08))
  2. 触发SVC
    SVC的内容如下:
    在这里插入图片描述

该函数主要内容:
8字节对齐
加载pxCurrentTCB(任务控制块,存储每个任务的相关信息)的地址到R3
加载pxCurrentTCB的地址里面的内容到R1
因为pxCurrentTCB的地址里面的内容还是一个地址,所以又加载这个地址里面的内容到R0
R0=pxCurrentTCB首地址内容即栈顶地址,pxCurrentTCB是一个结构体,结构体第一个成员为任务控制块栈顶地址
在这里插入图片描述

所以现在R0=栈顶地址,然后以R0为基址,向高地址++,把地址内的内容依次加载到R4、R5、R6、R7、R8、R9、R10、R11、R14等寄存器
然后把R0此时的地址赋给psp
然后R0=0
basepri=R0
然后用BX指令,把以psp为基址,向高地址++,里面的的内容依次加载到R0、R1、R2、R3、R12、R14、R15、xPSR寄存器内
从这里就可以理解了初始化任务堆栈时,为什么要按顺序填充内存,因为取的时候是按顺序取的。
然后任务的切换是在滴答定时器中断内进行的:
在这里插入图片描述

可以看到里面就一个函数:
在这里插入图片描述

函数里面最重要的是通过设置相应寄存器触发了PendSV中断。
PendSV里面的内容如下:
在这里插入图片描述

主要内容:

  • 把当前任务运行现场保存在当前任务的任务堆栈内
  • 把下个要运行的任务的任务堆栈里面的内容加载到寄存器内
    (s16~s31是开启专门运算浮点的模块时,需要用到的寄存器)。

其实上面两条,1和任务刚初始化时做的一样,只不过任务初始化时是初始化为0,而现在是把当前寄存器内值存起来,保存的位置都一样。2和启动第一个任务一样,把任务堆栈内的内容一一对应加载到对应寄存器内。
至此,操作系统最基本的任务流程就完成了。
总结一下
一. 开启系统滴答定时器
二. 为任务分配运行空间,把任务启动时每个寄存器里面的值按规定好的顺序存在任务分配的运行空间里
三. 触发SVC,开始第一个任务,即把第一个任务运行空间里面的存的寄存器的值按顺序加载到寄存器里
四. 系统滴答定时器中断触发,把现在寄存器内的所有值存进当前任务的运行空间里,加载下个要运行的任务运行空间里面的值到寄存器里。
在这里插入图片描述

  • 14
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值