FreeRTOS任务.docx
- 文档编号:11029176
- 上传时间:2023-02-24
- 格式:DOCX
- 页数:24
- 大小:53.39KB
FreeRTOS任务.docx
《FreeRTOS任务.docx》由会员分享,可在线阅读,更多相关《FreeRTOS任务.docx(24页珍藏版)》请在冰豆网上搜索。
FreeRTOS任务
标签:
freertos
转:
FreeRTOS任务管理分析转自:
blog.ednchina./bluehacker
freertos是一个轻量级的rtos,它目前实现了一个微核,并且port到arm7,avr,pic18,coldfire等众多处理器上;目前已经在
rtos的市场上占有不少的份额。
它当然不是一个与vxworks之类的rtos竞争的操作系统,它的目标在于低性能小RAM的处理器上。
整个系统只有3个文件,外加上port的和处理器相关的两个文件,实现是很简洁的。
与ucosii不同,它是free的,ucosii不是free的,虽然它的代码是公开的。
FreeRTOS提供的功能包括:
任务管理、时间管理、信
号量、消息队列、存管理。
FreeRTOS核支持优先级调度算法,每个任务可根据重要程度的不同被赋予一定的优先级,CPU总是让处于
就绪态的、优先级最高的任务先运行。
FreeRTOS核同时支持轮换调度算法,系统允许不同的任务使用相同的优先级,在没有更高优
先级任务就绪的情况下,同一优先级的任务共享CPU的使用时间。
这一点是和ucosii不同的。
另外一点不同是freertos既可以配置为可抢占核也可以配置为不可抢占核。
当FreeRTOS被设置为可剥夺型核时,处于就绪态的高优
先级任务能剥夺低优先级任务的CPU使用权,这样可保证系统满足实时性的要求;当FreeRTOS被设置为不可剥夺型核时,处于就绪态
的高优先级任务只有等当前运行任务主动释放CPU的使用权后才能获得运行,这样可提高CPU的运行效率。
这篇文章是以freertosv5.0版本的代码为例子分析下它的任务管理方面的实现。
时间关系可能没有太多时间写的很详细了。
1.链表管理
freertos里面的任务管理,queue,semaphore管理等都借助于双向链表,它定义了个通用的数据结构
structxLIST_ITEM
{
portTickType
xItemValue;//
链表节点的数据项,通常用在任务延时,
表
示
//一个任务延时的节拍数
volatilestructxLIST_
ITEM*pxNext;//
通过这两个成员变量将所有节点
volatilestructxLIST_
ITEM*pxPrevious;//
成双向链表
void*pvOwner;
//指向该item的所有者,通常是任务控制块
void*pvContainer;//
指向此链表结点所在的链表
};
这个数据结构定义了一个通用的链表节点;下面的数据结构定义了一个双向链表
typedefstructxLIST
{
volatileunsignedportBASE_TYPEuxNumberOfltems;//表示该链表中节点的数目
volatilexListItem*pxIndex;//用于遍历链表,指向上次访问的节点
volatilexMiniListItemxListEnd;//链表尾结点
}xList;
而下面这个数据结构用在xList中,只是为了标记一个链表的尾,是一个marker
structxMINI_LIST_ITEM
{
portTickTypexItemValue;
volatilestructxLIST_ITEM*pxNext;
volatilestructxLIST_ITEM*pxPrevious;
};
typedefstructxMINI_LIST_ITEMxMiniListItem;
对于链表的操作也定义了一系列的函数和宏,在list.c文件中。
如初始化个链表,吧一个节点插入链表等。
初始化链表:
voidvListlnitialise(xList*pxList)
{
/*Theliststructurecontainsalistitemwhichisusedtomarkthe
astheonlylistentry.*/
pxList->pxlndex=(xListItem*)&(pxList->xListEnd);
/*Thelistendvalueisthehighestpossiblevalueinthelisttoensureitremainsattheendofthelist.*/pxList->xListEnd.xltemValue=portMAX_DELAY;
/*Thelistendnextandpreviouspointerspointtoitselfsoweknow
whenthelistisempty.*/
pxList->xListEnd.pxNext=(xListItem*)&(pxList->xListEnd);pxList->xListEnd.pxPrevious=(xListItem*)&(pxList->xListEnd);
pxList->uxNumberOfltems=0;
}
把一个节点插入到链表尾部:
voidvListInsertEnd(xList*pxList,xListItem*pxNewListItem)
{
volatilexListItem*pxIndex;
/*InsertanewlistitemintopxList,butratherthansortthelist,
makesthenewlistitemthelastitemtoberemovedbyacalltopvListGetOwnerOfNextEntry.ThismeansithastobetheitempointedtobythepxIndexmember.*/
pxIndex=pxList->pxlndex;
pxNewListltem->pxNext=pxlndex->pxNext;
pxNewListltem->pxPrevious=pxList->pxIndex;
pxIndex->pxNext->pxPrevious=(volatilexListItem*)pxNewListltem;pxIndex->pxNext=(volatilexListItem*)pxNewListltem;
pxList->pxIndex=(volatilexListItem*)pxNewListltem;
/*Rememberwhichlisttheitemisin.*/
pxNewListltem->pvContainer=(void*)pxList;
(pxList->uxNumberOfltems)++;
}
这些就不多说了。
2.任务控制块
typedefstructtskTaskControlBlock
#if(portCRITICAL_NESTING_IN_TCB==1)
unsignedportBASE_TYPEuxCriticalNesting;
#endif#if(configUSE_TRACE_FACILITY==1)
unsignedportBASE_TYPEuxTCBNumber;//用于trace,debug时候提供方便
#endif
#if(configUSE_MUTEXES==1)
unsignedportBASE_TYPEuxBasePriority;//当用mutex发生优先级反转时用
#endif
#if(configUSE_APPLICATION_TASK_TAG==1)
pdTASK_HOOK_CODEpxTaskTag;
#endif
}tskTCB;
其中uxBasePriority用于解决优先级反转,freertos采用优先级继承的办法解决这个问题,在继承时,将任务原先的优先级保存在
这个成员中,将来再从这里恢复任务的优先级。
3.系统全局变量
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;/* memorynotyetfreed.*/ staticvolatileunsignedportBASE_TYPEuxTasksDeleted=(unsignedportBASE_TYPE)0; 一个任务被删除的时候加入到xTasksWaitingTermination链表中,uxTasksDeleted跟中系统中有多少任务被删除(即加到 xTasksWaitingTermination链表的任务数目). staticxListxSuspendedTaskList;/* currentlysuspended.*/ 这个链表记录着所有被xTaskSuspend挂起的任务,注意这不是那些等待信号量的任务。 staticvolatileunsignedportBASE_TYPEuxCurrentNumberOfTasks;记录了当前系统任务的数目 staticvolatileportTickTypexTickCount;是自启动以来系统运行的ticks数 staticunsignedportBASE_TYPEuxTopUsedPriority;记录当前系统中被使用的最高优先级, staticvolatileunsignedportBASE_TYPEuxTopReadyPriority;记录当前系统中处于就绪状态的最高优先级。 staticvolatilesignedportBASE_TYPExSchedulerRunning;表示当前调度器是否在运行,也即核是否启动了 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-thisisguardedbefore use.*/ #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,usStackDepthsizeof(portSTACK_TYPE)); } } returnpxNewTCB; } 再看任务删除。 freertos的任务删除分两步完成,第一步在vTaskDelete中完成,FreeRTOS先把要删除的任务从就绪任务链表和事件等待链表中删除,然后把此任务添加到任务删除链表(即那个xTasksWaitingTermination),若删除的任务是当前运行任务,系统就执行任务调度函数第2步则是在idle任务中完成,idle任务运行时,检查xTasksWaitingTermination链表,如果有任务在这个表上,释放该任务占用的存空间,并把该任务从任务删除链表中删除。 /**************************************************************** **参数: pxTaskToDelete是一个指向被删除任务的句柄,这里其实就是等价于任务控制块 **如果这个句柄==NULL则表示要删除当前任务 *******************************************************************/ voidvTaskDelete(xTaskHandlepxTaskToDelete) { tskTCB*pxTCB; taskENTER_CRITICAL(); { /*如果删除的是当前任务,则删除完成后需要进行调度*/ if(pxTaskToDelete==pxCurrentTCB) { pxTaskToDelete=NULL; } /*通过传进来的任务句柄得到对应的tcb*/ pxTCB=prvGetTCBFromHandle(pxTaskToDelete); traceTASK_DE
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- FreeRTOS 任务