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 v5.0版本的代码为例子分析下它的任务管理方面的实现。时间关系可能没有太多时间写的很详细了。
1.链表管理
freertos里面的任务管理,queue,semaphore管理等都借助于双向链表,它定义了个通用的数据结构
struct xLIST_ITEM
{
portTickType xItemValue; //链表节点的数据项,通常用在任务延时,表示 //一个任务延时的节拍数
volatile struct xLIST_ITEM * pxNext; //通过这两个成员变量将所有节点
volatile struct xLIST_ITEM * pxPrevious;//链接成双向链表
void * pvOwner; //指向该item的所有者,通常是任务控制块
void * pvContainer; //指向此链表结点所在的链表
};
这个数据结构定义了一个通用的链表节点;下面的数据结构定义了一个双向链表
typedef struct xLIST
{
volatile unsigned portBASE_TYPE uxNumberOfItems;//表示该链表中节点的数目
volatile xListItem * pxIndex;//用于遍历链表,指向上次访问的节点
volatile xMiniListItem xListEnd;//链表尾结点
} xList;
而下面这个数据结构用在xList中,只是为了标记一个链表的尾,是一个marker
struct xMINI_LIST_ITEM
{
portTickType xItemValue;
volatile struct xLIST_ITEM *pxNext;
volatile struct xLIST_ITEM *pxPrevious;
};
typedef struct xMINI_LIST_ITEM xMiniListItem;
对于链表的操作也定义了一系列的函数和宏,在list.c文件中。如初始化个链表,吧一个节点插入链表等。
初始化链表:
void vListInitialise( xList *pxList )
{
/* The list structure contains a list item which is used to mark the
end of the list. To initialise the list the list end is inserted
as the only list entry. */
pxList->pxIndex = ( xListItem * ) &( pxList->xListEnd );
/* The list end value is the highest possible value in the list to
ensure it remains at the end of the list. */
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* The list end next and previous pointers point to itself so we know
when the list is empty. */
pxList->xListEnd.pxNext = ( xListItem * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( xListItem * ) &( pxList->xListEnd );
pxList->uxNumberOfItems = 0;
}
把一个节点插入到链表尾部:
void vListInsertEnd( xList *pxList, xListItem *pxNewListItem )
{
volatile xListItem * pxIndex;
/* Insert a new list item into pxList, but rather than sort the list,
makes the new list item the last item to be removed by a call to
pvListGetOwnerOfNextEntry. This means it has to be the item pointed to by
the pxIndex member. */
pxIndex = pxList->pxIndex;
pxNewListItem->pxNext = pxIndex->pxNext;
pxNewListItem->pxPrevious = pxList->pxIndex;
pxIndex->pxNext->pxPrevious = ( volatile xListItem * ) pxNewListItem;
pxIndex->pxNext = ( volatile xListItem * ) pxNewListItem;
pxList->pxIndex = ( volatile xListItem * ) pxNewListItem;
/* Remember which list the item is in. */
pxNewListItem->pvContainer = ( void * ) pxList;
( pxList->uxNumberOfItems )++;
}
这些就不多说了。
2.任务控制块
typedef struct tskTaskControlBlock
{
volatile portSTACK_TYPE *pxTopOfStack;//指向堆栈顶
xListItem xGenericListItem; //通过它将任务连入就绪链表或者延时链表或者挂起链表中
xListItem xEventListItem;//通过它把任务连入事件等待链表
unsigned portBASE_TYPE uxPriority;//优先级
portSTACK_TYPE *pxStack; //指向堆栈起始位置
signed portCHAR pcTaskName[ configMAX_TASK_NAME_LEN ];
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
unsigned portBASE_TYPE uxCriticalNesting;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
unsigned portBASE_TYPE uxTCBNumber;//用于trace,debug时候提供方便
#endif
#if ( configUSE_MUTEXES == 1 )
unsigned portBASE_TYPE uxBasePriority;//当用mutex发生优先级反转时用
#endif
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
pdTASK_HOOK_CODE pxTaskTag;
#endif
} tskTCB;
其中uxBasePriority用于解决优先级反转,freertos采用优先级继承的办法解决这个问题,在继承时,将任务原先的优先级保存在这个成员中,将来再从这里恢复任务的优先级。 |