FreeRTOS思路梳理
前置知识
中断屏蔽:芯片会有个中断屏蔽相关的寄存器,往这个寄存器中写入一个值,芯片会自动将优先级小于这个值的中断屏蔽掉
滴答定时器:为OS提供心跳,计时,本质仍然是中断
PendSV异常:本质是中断,它的特点是可以缓期执行——在ISR被占用时可以暂时挂起
PSP和MSP:有些芯片会提供有两个堆栈指针,MSP用于OS内核和中断异常处理,PSP用于任务
列表:可以看成是双向链表
列表项:可以看成列表里面的节点
关于任务调度
在FreeRTOS中,任务被抽象成了一个TCB_t结构体,这个结构体中有这个任务所有的信息
在TCB中有两个列表项,一个表示状态,一个表示事件
列表项中存储一个指向所属TCB的指针,所以移动列表项就可以完成对TCB状态的更改——就像衣架,移动钩子,钩子下面的衣服也就跟着移动了
同时列表项中还有一个指向所属列表的指针,它一般用来表示在等待事件、信号量、队列等
FreeRTOS定义了很多种列表,主要分状态列表和事件列表,列表按value值排序,value表示被处理的优先度
状态列表主要是用来表示任务状态,有就绪列表、等待延时列表、挂起列表和等待唤醒列表
唤醒任务、阻塞任务等操作均是通过操作TCB中的列表项到相应的列表中完成
FreeRTOS在PendSV中执行任务调度和任务切换,在每一次进入滴答定时器中断后,会自动悬起一个PendSV中断(P.S.因为PendSV优先级允许缓期执行,所以会ISR未被占用后进行任务调度,适合做任务调度)
在PendSV中断中,OS直接选择就绪列表中优先级最高的那个任务进行切换执行;在任务切换时,会进行上下文切换,即保护现场(将运行环境,寄存器变量等压入任务堆栈中),恢复现场(从任务堆栈中按规则取出数据和寄存器的值进行恢复)
当任务阻塞时,如果有等待时间则将任务状态TCB移动到延时等待列表中,延时列表根据延时时间排序
在每个滴答定时器中断中都会更新系统时间,并检查是否有延时到的任务需要唤醒,唤醒流程——将列表项移到就绪列表中,将此TCB的任务列表项归属置NULL
关于队列和信号量
队列是利用一片内存来存储数据,队列结构体中有两个列表,一个表示入队阻塞的任务(队列满了),一个表示出队阻塞的任务(队列为空)
队列为空时,要出队的任务的事件列表项将被挂载在队列中的列表中,如有等待时间移动状态列表项到延时等待列表中,如果死等则挂起任务
队列满时,入队的任务操作流程相同
当可以入队时,将消息拷贝到消息队列中,然后按优先级唤醒相应因出队阻塞的任务;可以入队时,操作类似
各种信号量底层均是队列,利用队列完成信号量的上锁等待等操作
互斥信号量较其他信号量多了优先级翻转,在等待时,互斥信号量会将正在持有互斥量的任务优先级提高到和自己一样
关于事件组
事件组的实现和队列类似,事件组的结构体中有一个列表,列表存储等待事件组某些事件发生的任务
当任务调用等待某一事件组的某些事件时,会将此任务的事件列表项挂载在此列表下,列表项的value值存储等待哪些事件的发生,同时将其状态切换为延时阻塞或挂起
当事件组被写入某些事件时,遍历列表中的所有任务,查看是否满足要求,满足则唤醒,最后根据要求清除相应的事件标志位
关于任务通知
修改TCB中的元素的值和任务通知的状态变量