FreeRTOS任务.docx
《FreeRTOS任务.docx》由会员分享,可在线阅读,更多相关《FreeRTOS任务.docx(16页珍藏版)》请在冰豆网上搜索。
FreeRTOS任务
标签:
freertos
转:
FreeRTOS任务管理分析
转自:
是一个轻量级的rtos,它目前实现了一个微内核,并且port到arm7,avr,pic18,coldfire等众多处理器上;目前已经在rtos的市场上占有不少的份额。
它当然不是一个与vxworks之类的rtos竞争的操作系统,它的目标在于低性能小RAM的处理器上。
整个系统只有3个文件,外加上port的和处理器相关的两个文件,实现是很简洁的。
与ucosii不同,它是free的,ucosii不是free的,虽然它的代码是公开的。
FreeRTOS提供的功能包括:
任务管理、时间管理、信号量、消息队列、内存管理。
FreeRTOS内核支持优先级调度算法,每个任务可根据重要程度的不同被赋予一定的优先级,CPU总是让处于就绪态的、优先级最高的任务先运行。
FreeRT0S内核同时支持轮换调度算法,系统允许不同的任务使用相同的优先级,在没有更高优先级任务就绪的情况下,同一优先级的任务共享CPU的使用时间。
这一点是和ucosii不同的。
另外一点不同是freertos既可以配置为可抢占内核也可以配置为不可抢占内核。
当FreeRTOS被设置为可剥夺型内核时,处于就绪态的高优先级任务能剥夺低优先级任务的CPU使用权,这样可保证系统满足实时性的要求;当FreeRTOS被设置为不可剥夺型内核时,处于就绪态的高优先级任务只有等当前运行任务主动释放CPU的使用权后才能获得运行,这样可提高CPU的运行效率。
这篇文章是以freertos版本的代码为例子分析下它的任务管理方面的实现。
时间关系可能没有太多时间写的很详细了。
1.链表管理
freertos里面的任务管理,queue,semaphore管理等都借助于双向链表,它定义了个通用的数据结构
structxLIST_ITEM
{
portTickTypexItemValue;Toinitialisethelistthelistendisinserted
astheonlylistentry.*/
pxList->pxIndex=(xListItem*)&(pxList->xListEnd);
/*Thelistendvalueisthehighestpossiblevalueinthelistto
ensureitremainsattheendofthelist.*/
pxList->=portMAX_DELAY;
/*Thelistendnextandpreviouspointerspointtoitselfsoweknow
whenthelistisempty.*/
pxList->=(xListItem*)&(pxList->xListEnd);
pxList->=(xListItem*)&(pxList->xListEnd);
pxList->uxNumberOfItems=0;
}
把一个节点插入到链表尾部:
voidvListInsertEnd(xList*pxList,xListItem*pxNewListItem)
{
volatilexListItem*pxIndex;
/*InsertanewlistitemintopxList,butratherthansortthelist,
makesthenewlistitemthelastitemtoberemovedbyacallto
pvListGetOwnerOfNextEntry. Thismeansithastobetheitempointedtoby
thepxIndexmember.*/
pxIndex=pxList->pxIndex;
pxNewListItem->pxNext=pxIndex->pxNext;
pxNewListItem->pxPrevious=pxList->pxIndex;
pxIndex->pxNext->pxPrevious=(volatilexListItem*)pxNewListItem;
pxIndex->pxNext=(volatilexListItem*)pxNewListItem;
pxList->pxIndex=(volatilexListItem*)pxNewListItem;
/*Rememberwhichlisttheitemisin.*/
pxNewListItem->pvContainer=(void*)pxList;
(pxList->uxNumberOfItems)++;
}
这些就不多说了。
2.任务控制块
typedefstructtskTaskControlBlock
{
volatileportSTACK_TYPE *pxTopOfStack;统全局变量
freertos将任务根据他们的状态分成几个链表。
所有就绪状态的任务根据任务优先级加到对应的就绪链表中。
系统为每个优先级定义了一个xList。
如下:
staticxListpxReadyTasksLists[configMAX_PRIORITIES]; /*此外,所有延时的任务加入到两个延时链表之一。
staticxListxDelayedTaskList1;
staticxListxDelayedTaskList2;
还定义了两个指向延时链表的指针:
staticxList*volatilepxDelayedTaskList;
staticxList*volatilepxOverflowDelayedTaskList;
freertos弄出两个延时链表是因为它的延时任务管理的需要。
freertos根据任务延时时间的长短按序将任务插入这两个链表之一。
在插入前先把任务将要延时的xTicksToDelay数加上系统当前tick数,这样得到了一个任务延时duetime(到期时间)的绝对数值。
但是有可能这个相加操作会导致溢出,如果溢出则加入到pxOverflowDelayedTaskList指向的那个链表,否则加入pxDelayedTaskList指向的链表。
freertos还定义了个pending链表:
staticxListxPendingReadyList;
这个链表用在调度器被lock(就是禁止调度了)的时期,如果一个任务从非就绪状态变为就绪状态,它不直接加到就绪链表中,而是加到这个pending链表中。
等调度器重新启动(unlock)的时候再检查这个链表,把里面的任务加到就绪链表中
staticvolatilexListxTasksWaitingTermination; /*staticvolatileunsignedportBASE_TYPEuxTasksDeleted=(unsignedportBASE_TYPE)0;
一个任务被删除的时候加入到xTasksWaitingTermination链表中,uxTasksDeleted跟中系统中有多少任务被删除(即加到xTasksWaitingTermination链表的任务数目).
staticxListxSuspendedTaskList; /*这个链表记录着所有被xTaskSuspend挂起的任务,注意这不是那些等待信号量的任务。
staticvolatileunsignedportBASE_TYPEuxCurrentNumberOfTasks;记录了当前系统任务的数目
staticvolatileportTickTypexTickCount;是自启动以来系统运行的ticks数
staticunsignedportBASE_TYPEuxTopUsedPriority;记录当前系统中被使用的最高优先级,
staticvolatileunsignedportBASE_TYPEuxTopReadyPriority;记录当前系统中处于就绪状态的最高优先级。
staticvolatilesignedportBASE_TYPExSchedulerRunning ;表示当前调度器是否在运行,也即内核是否启动了
4.任务管理
freertos与ucosii不同,它的任务控制块并不是静态分配的,而是在创建任务的时候动态分配。
另外,freertos的优先级是优先级数越大优先级越高,和ucosii正好相反。
任务控制块中也没有任务状态的成员变量,这是因为freertos中的任务总是根据他们的状态连入对应的链表,没有必要在任务控制块中维护一个状态。
此外freertos对任务的数量没有限制,而且同一个优先级可以有多个任务。
先看任务创建:
/***************************************************************
**参数:
pvTaskCode---任务函数名称
**pcName---任务名字,可选
**ucStackDepth---任务堆栈的深度,即大小
**pvParamenters---参数,即传给任务函数的参数,所有的任务函数原型是voidtask(void*pvParameters)
**uxPriority—任务优先级
**pxCreatedTask—可选,通过它返回被创建任务的tcb
*******************************************************************/
signedportBASE_TYPExTaskCreate(pdTASK_CODEpvTaskCode,constsignedportCHAR*constpcName,unsignedportSHORTusStackDepth,void*pvParameters,unsignedportBASE_TYPEuxPriority,xTaskHandle*pxCreatedTask)
{
signedportBASE_TYPExReturn;
tskTCB*pxNewTCB;
#if(configUSE_TRACE_FACILITY==1)
staticunsignedportBASE_TYPEuxTaskNumber=0;/*lint!
e956Staticisdeliberate-thisisguardedbeforeuse.*/
#endif
/*动态分配tcb和任务堆栈*/
pxNewTCB=prvAllocateTCBAndStack(usStackDepth);
/*如果分配成功的话*/
if(pxNewTCB!
=NULL)
{
portSTACK_TYPE*pxTopOfStack;
/*初始化tcb*/
prvInitialiseTCBVariables(pxNewTCB,pcName,uxPriority);
/*计算堆栈的顶*/
#ifportSTACK_GROWTH<0
{
pxTopOfStack=pxNewTCB->pxStack+(usStackDepth-1);
}
#else
{
pxTopOfStack=pxNewTCB->pxStack;
}
#endif
/*初始化任务堆栈,并将返回地址保存在tcb中的pxTopOfStack变量*/
pxNewTCB->pxTopOfStack=pxPortInitialiseStack(pxTopOfStack,pvTaskCode,pvParameters);
/*关中断*/
portENTER_CRITICAL();
{/*更新系统的任务数*/
uxCurrentNumberOfTasks++;
if(uxCurrentNumberOfTasks==(unsignedportBASE_TYPE)1)
{
/*如果这是系统中第一个任务,则把它设为当前任务*/
pxCurrentTCB= pxNewTCB;
/*如果这是系统中的第一个任务,那也就意味着内核刚准备启动,实际上这第一个任务一定是idle任务,这个时候我们要做一些系统初始化,即初始化那些全局链表*/
prvInitialiseTaskLists();
}
else
{
/*如果内核还没有运行,则把当前任务设成已经创建的任务中优先级最高的那个,将来内核一旦运行,调度器会马上选择它运行*/
if(xSchedulerRunning==pdFALSE)
{
if(pxCurrentTCB->uxPriority<=uxPriority)
{
pxCurrentTCB=pxNewTCB;
}
}
}
/*我们记录下当前使用的最高优先级,这为了方便任务调度*/
if(pxNewTCB->uxPriority>uxTopUsedPriority)
{
uxTopUsedPriority=pxNewTCB->uxPriority;
}
#if(configUSE_TRACE_FACILITY==1)
{
/*AddacounterintotheTCBfortracingonly.*/
pxNewTCB->uxTCBNumber=uxTaskNumber;
uxTaskNumber++;
}
#endif
/*把新创建的任务加到就绪链表*/
prvAddTaskToReadyQueue(pxNewTCB);
xReturn=pdPASS;
traceTASK_CREATE(pxNewTCB);
}
portEXIT_CRITICAL();
}
/*如果分配内存失败,我们返回错误*/
else
{
xReturn=errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
traceTASK_CREATE_FAILED(pxNewTCB);
}
if(xReturn==pdPASS)
{
if((void*)pxCreatedTask!
=NULL)
{
/*将新创建任务的tcb返回给调用者
*pxCreatedTask=(xTaskHandle)pxNewTCB;
}
/*如果调度器已经运行*/
if(xSchedulerRunning!
=pdFALSE)
{
/*如果新创建的任务的优先级高于当前正在运行的任务,则调度*/
if(pxCurrentTCB->uxPriority {
taskYIELD();
}
}
}
returnxReturn;
}
其中prvAllocateTCBAndStack分配tcb和stack内存,这个里面调用了pvportMalloc和pvPortFree函数来分配和释放内存,这两个函数对应于C标准库里面的malloc和free。
但是标准库中的mallo和free存在以下缺点:
并不是在所有的嵌入式系统中都可用,要占用不定的程序空间,可重人性欠缺以及执行时间具有不可确定性,而且多次反复调用可能导致严重的内存碎片。
因此freertos在内存管理那块自己实现了这两个函数。
statictskTCB*prvAllocateTCBAndStack(unsignedportSHORTusStackDepth)
{
tskTCB*pxNewTCB;
/*AllocatespacefortheTCB. Wherethememorycomesfromdependson
theimplementationoftheportmallocfunction.*/
pxNewTCB=(tskTCB*)pvPortMalloc(sizeof(tskTCB));
if(pxNewTCB!
=NULL)
{
/*Allocatespaceforthestackusedbythetaskbeingcreated.
ThebaseofthestackmemorystoredintheTCBsothetaskcan
bedeletedlaterifrequired.*/
pxNewTCB->pxStack=(portSTACK_TYPE*)pvPortMalloc(((size_t)usStackDepth)*sizeof(portSTACK_TYPE));
if(pxNewTCB->pxStack==NULL)
{
/*Couldnotallocatethestack. DeletetheallocatedTCB.*/
vPortFree(pxNewTCB);
pxNewTCB=NULL;
}
else
{
/*Justtohelpdebugging.*/
memset(pxNewTCB->pxStack,tskSTACK_FILL_BYTE,usStackDepth*sizeof(portSTACK_TYPE));
}
}
returnpxNewTCB;
}
再看任务删除