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

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

硬件环境:cortex m4

FreeRTOS版本:v8.0.1

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

 

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指针。

转载于:https://my.oschina.net/u/3699634/blog/1544909

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值