freertos任务管理教学内容.docx

上传人:b****5 文档编号:6103234 上传时间:2023-01-03 格式:DOCX 页数:14 大小:22.28KB
下载 相关 举报
freertos任务管理教学内容.docx_第1页
第1页 / 共14页
freertos任务管理教学内容.docx_第2页
第2页 / 共14页
freertos任务管理教学内容.docx_第3页
第3页 / 共14页
freertos任务管理教学内容.docx_第4页
第4页 / 共14页
freertos任务管理教学内容.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

freertos任务管理教学内容.docx

《freertos任务管理教学内容.docx》由会员分享,可在线阅读,更多相关《freertos任务管理教学内容.docx(14页珍藏版)》请在冰豆网上搜索。

freertos任务管理教学内容.docx

freertos任务管理教学内容

freertos任务管理

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

{

链表节点的数据项,通常用在任务延时,表示一个任务延

portTickTypexItemValue;//

时的节拍数

通过这两个成员变量将所有节点

volatilestructxLIST_ITEM*pxNext;//

链接成双向链表

volatilestructxLIST_ITEM*pxPrevious;//

void*pvOwner;//指向该item的所有者,通常是任务控制

void*pvContainer;//指向此链表结点所在的链表

};

/*定义一个链表?

*//*一个优先级一个链表?

*/这个数据结构定义了一个通用的链表节点;下面的数据结构定义了一个双向链表typedefstructxLIST

{

volatileunsignedportBASE_TYPEuxNumberOfItems;//表示该链表中节点的数目

volatilexListItem*pxIndex;//用于遍历链表,指向上次访问的节

八、、

volatilexMiniListItemxListEnd;//链表尾结点/*指向链表中的最后一个节点?

*/

}xList;

而下面这个数据结构用在xList中,只是为了标记一个链表的尾,是一个marker

structxMINI_LIST_ITEM

{

portTickTypexItemValue;

volatilestructxLIST_ITEM*pxNext;

volatilestructxLIST_ITEM*pxPrevious;

};

typedefstructxMINI_LIST_ITEMxMiniListItem;

对于链表的操作也定义了一系列的函数和宏,在list.c文件中。

如初始化个链表,吧一个节点插入链表等。

初始化链表:

voidvListInitialise(xList*pxList)

{

/*Theliststructurecontainsalistitemwhichisusedtomarkthe

endofthelist.Toinitialisethelistthelistendisinserted

astheonlylistentry.*/

pxList->pxIndex=(xListItem*)&(pxList->xListEnd);

/*Thelistendvalueisthehighestpossiblevalueinthelistto

ensureitremainsattheendofthelist.*/

pxList->xListEnd.xItemValue=portMAX_DELAY;

/*Thelistendnextandpreviouspointerspointtoitselfsoweknow

whenthelistisempty.*/

pxList->xListEnd.pxNext=(xListItem*)&(pxList->xListEnd);

pxList->xListEnd.pxPrevious=(xListItem*)&(pxList->xListEnd);

pxList->uxNumberOfItems=0;

}

把一个节点插入到链表尾部:

voidvListInsertEnd(xList*pxList,xListItem*pxNewListItem)

{

volatilexListItem*pxIndex;

/*InsertanewlistitemintopxList,butratherthansortthelist,

makesthenewlistitemthelastitemtoberemovedbyacalltopvListGetOwnerOfNextEntry.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.*/

pxNewListltem->pvContainer=(void*)pxList;

(pxList->uxNumberOfltems)++;

}

这些就不多说了。

2.任务控制块

typedefstructtskTaskControlBlock

{

volatileportSTACK_TYPE*pxTopOfStack;//指向堆栈顶

xListItemxGenericListltem;//通过它将任务连入就绪链表或者延时链表或者挂起链表中

xListItemxEventListltem;//通过它把任务连入事件等待链表

unsignedportBASE_TYPEuxPriority;//优先级

portSTACK_TYPE*pxStack;//指向堆栈起始位置

signedportCHARpcTaskName[configMAX_TASK_NAME_LEN];

#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];

/*

此外,所有延时的任务加入到两个延时链表之一

staticxListxDelayedTaskListl;

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.Wherethememorycomesfromdependsontheimplementationoftheportmallocfunction.*/

pxNewTCB=(tskTCB*)pvPortMalloc(sizeof(tskTCB));

if(pxNewTCB!

=NULL)

{

/*Allocatespaceforthestackusedbythetaskbeingcreated.ThebaseofthestackmemorystoredintheTCBsothetaskcanbedeletedlaterifrequired.*/

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;

}

再看任务删除:

freertos的任务删除分两步完成,第一步在vTaskDelete中完成,FreeRTOS先把要删除的任务从就绪任务链表和事件等待链表中删除,然后把此任务添加到任务删除链表(即那个xTasksWaitingTermination),若删除的任务是当前运行任务,系统就执行任务调度函数.第2步

则是在idle任务中完成,idle任务运行时,检查xTasksWaitingTermination链表,如果有任务在这个表上,释放该任务占用的内存空间,并把该任务从任务删除链表中删除。

/****************************************************************

**参数:

pxTaskToDelete是一个指向被删除任务的句柄,这里其实就是等价于任务控制块

**如果这个句柄==NULL

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1