深度解剖~ FreeRtos阅读笔记2 任务建立、内核链表初始化

2.FREERTOS任务建立、内核链表初始化

硬件环境:cortex m4数组

FreeRTOS版本:v8.0.1函数

今天开始阅读freertos,阅读同时作下笔记,等哪天碰到移植问题再翻出来看看。spa

 

2.1 任务、链表结构体

源码中使用tskTCB来存储一个任务的全部信息,xLIST存储内核链表数据。一个系统最基本的功能是它的任务调度,在任务切换时最重要的则是内核链表,用图描述下这两个结构体,这样看起来比代码更清晰。(TCB中有省略成员)翻译

(TCB结构体)指针

-------------------------------------------------分割线----------------------------------------------------索引

(LIST结构体)图片

一个TCB中包含了两个xLIST_ITEM做为链表节点,操做xLIST_ITEM中的指针指向既为控制一个任务进出某个链表。相比xLIST_ITEM,在xLIST中使用了精简版的结点xMINI_LIST_ITEM。内存

 

2.2 xTaskGeneEricCreate 任务建立流程分析

xTaskGeneEricCreate 函数用来建立一个新任务,在调度器启动前和启动后均可以建立。资源

Freertos在调度器启动后至少会有一个任务(IDLE)处于准备调度状态,即便开发者不去建立本身的任务。开发

xTaskGeneEricCreate源码流程:(拖动能够放大图片)

(任务建立流程图)

 

2.2.1 prvAllocateTCBAndStack 分配空间

Freertos使用pvPortMalloc在堆上分配一块TCB大小的内存空间,分配成功后还要使用pvPortMalloc分配一块内存,当作任务运行所须要的栈空间。这些空间直到任务被删除时才会获得释放。

在栈分配时有参数判断,若建立任务时有传入的栈地址则放弃分配。

分配的栈内存总大小为栈深度(传入参数)与栈宽度乘积。

 

2.2.2 prvInitialiseTCBVariables 执行初始化

prvInitialiseTCBVariables函数中主要执行了任务名字拷贝、优先级保存、两个链表节点初始化。

下图表示TCB中节点和链表初始化后指针指向

(链表及TCB初始化后)

 

2.2.3 pxPortInitialiseStack 执行”压栈”

pxPortInitialiseStack函数执行的代码很是奇特,一开始彻底没法理解,还好在葵花宝典找到了da案,神书!引用M3权威指南上一句翻译:响应异常的第一个步骤是保存现场,硬件自动压栈,压栈后内存分布:

 

 

再对比看下pxPortInitialiseStack源码:

对比下二者动做,这个函数是对任务栈进行了一些处理,而且是模仿异(中断)常发生时所产生的动做。为何必定要模仿异常进行压栈,首先扯一下freertos任务调度工做的大体流程:

当一个任务在运行时,还有一个内部定时器(systick)在一直计数,它的计数值和时钟频率比值能够当作为时间片。时间片到,中断产生,中断里进行上下文切换也就是pxReadyTasksLists中的任务被依次调度。硬件进入中断时便会自动压栈,不须要咱们处理。中断处理完成后到中断返回时硬件还会自动出栈,还原进入异常前的状态。进中断时压入的那些寄存器值都被一一出栈 如:PC、R0、等寄存器。这样pxPortInitialiseStack函数就好理解了,它先对新建立的任务进行手动压栈,还多包括了R4-R11,那么在调度中断结束后这些手动压入的值将被自动出栈,进而使新任务运行起来。

PC位置是传入的任务主程序句柄地址,也就是咱们要任务执行的主要程序,LR(返回寄存器)的位置是prvTaskExitError函数地址,这个函数里是一个for死循环加错误信息打印,也就是一个任务永远不该从它的主程序中跳出,若是跳出则进入prvTaskExitError函数打印错误。通常任务句柄里都会用for(;;)把它写死永远循环执行,须要退出时要将该任务delete掉。

 

2.3 pxReadyTasksLists链表

一个TCB建立并初始化完成后便开始插入pxReadyTasksLists等待被调度。pxReadyTasksLists链表是一个数组,优先级最大数决定它的大小。一个处于空闲状态的TCB(准备好被调度)在插入时是an照优先级做为索引插入的,这里说TCB插入不太准确,应该是TCB上的链表节点插入链表。

举个栗子,第一个任务插入空链表时的情况:

 

看着有些凌乱的话再来张大意图:

 

若是此时又有一个相同优先级任务建立,链表变为:


 

简略图:

 

 

链表将节点依次链接,组成TCB链,调度器运行时会an照须要遍历链表进而控制任务。

链表头部都带有index元素,一开始它指向链表自己,因此咱们上面建立的任务都像是在尾插,事实上调度器运行起来时新节点插入的位置由index决定。

图解:

调度开始后index开始遍历readylist,它指向第一个TCB时,第一个TCB获得cpu资源开始运行,变为:

 

注意红色线条变化,此时若是动态建立了一个优先级相同的任务TCB3,应该把它插在哪里?若是插在TCB后面那对于TCB2来讲是不公平的,由于人家排队等待cpu的时间确定比TCB3长,其实仔细考虑下插在链表头部或尾部都是不规律的,只有利用index。Freertos将其插在TCB前面,以保证是当前链表最后一个获得cpu资源的位置:

 

 

新TCB进入链表,任务建立流程就快结束了。在程序尾部有些优先级判断,若是建立的任务比当前运行的任务优先级要高则使能PendSV中断。若是调度器是中止的则直接更改当前TCB指针。

相关文章
相关标签/搜索