出处:http://blog.csdn.net/hehao9051/article/details/51477127
FreeRTOS内核详解—-LIST(非9.0版本)
从LIST对FreeRTOS的内核进行分析,可能是最容易入手的,也是最明智的。因为他是FreeRTOS内核最基本的一种数据结构,是分析内核最重要部分任务调度的基础。它其实就是一个双向的链表,下面按照链表的基本操作进行讲述,分成如下几个部分:
- LIST项的结构分析
- LIST的结构分析
- LIST的创建
- LIST的初始化
- LIST项的插入
- LIST 项的删除
1. LIST项
struct xLIST_ITEM
{
portTickType xItemValue;
volatile struct xLIST_ITEM * pxNext;
volatile struct xLIST_ITEM * pxPrevious;
void * pvOwner;
void * pvContainer;
};
每个任务都用链表项用于将任务挂接到相应的LIST中,列表通过列表项的pvOwner可以找到相应的任务。有时链表需要对链表项进行排序,例如pxDelayedTaskList
就需要按照延迟的时间长短对任务的链表项进行排序,xItemValue
的作用就是做这个。pxNext
和pxPrevious
的作用就是将链表项串联起来。
2.LIST
typedef struct xLIST
{
volatile unsigned portBASE_TYPE uxNumberOfItems;判断是够为空,此项作用是什么?)
volatile xListItem * pxIndex;
volatile xMiniListItem xListEnd;
} xList;
列表的作用是对列表项进行管理。pxIndex
的作用是对列表中的列表项进行遍寻,例如pxReadyTasksLists[0]
的任务优先级是相同的,如果包含有5个任务需要运行,FreeRTOS这时就会对这些任务进行分时运行,通过pxIndex
来记录当前运行的是哪个任务正在运行,例如任务1运行10ms(pxIndex
指向任务1中的列表项)之后,pxIndex
就指向下个任务2的列表项,任务2再运行10ms,以此下去从而达到让这5个任务都能够得到执行的目的。list.c
中listGET_OWNER_OF_NEXT_ENTRY
的作用就是没调用一次pxIndex
就会指向就会后移一位,直到结尾然后再从头来过(循环表,没有结尾吧?)。
3.LIST的初始化
void vListInitialise( xList *pxList )
{
pxList->pxIndex = ( xListItem * ) &( pxList->xListEnd );
pxList->xListEnd.xItemValue = portMAX_DELAY;
pxList->xListEnd.pxNext = ( xListItem * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( xListItem * ) &( pxList->xListEnd );
pxList->uxNumberOfItems = ( unsigned portBASE_TYPE ) 0U;
}
初始化的部分很简单,初始化后的列表结构如下图
![](https://img-blog.csdn.net/20170807111402961)
4.LIST项的插入
FreeRTOS中根据实际需要提供了两种列表项插入方法:
- vListInsertEnd 插入到列表结尾
- vListInsert 根据链表项xItemValue大小进行升序插入
void vListInsert( xList *pxList, xListItem *pxNewListItem )
{
volatile xListItem *pxIterator;
portTickType xValueOfInsertion;
xValueOfInsertion = pxNewListItem->xItemValue;
if( xValueOfInsertion == portMAX_DELAY )
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
for( pxIterator = ( xListItem * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
{
}
}
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = ( volatile xListItem * ) pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = ( volatile xListItem * ) pxNewListItem;
pxNewListItem->pvContainer = ( void * ) pxList;
( pxList->uxNumberOfItems )++;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
这个插入函数用在需要对列表项进行排序的链表中,例如任务加入到任务延迟列表一般使用此函数,注意插入的列表必须是已经排好序的。下面是一个使用插入函数前后的链表示意图
插入前
![](https://img-blog.csdn.net/20170807111408636)
插入后
![](https://img-blog.csdn.net/20170807111414279)
void vListInsertEnd( xList *pxList, xListItem *pxNewListItem )
{
volatile xListItem * pxIndex;
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;
pxNewListItem->pvContainer = ( void * ) pxList;
( pxList->uxNumberOfItems )++;
}
向列表结尾处插入列表项。这个插入是不进行排序的,仅仅是插入到列表的结尾处,因为是环形的列表,所以也没有什么绝对的结尾,pxIndex
指向的就是结尾,因为如果pxIndex
指向当前列表项,意味着再访问到此列表项需要转一圈才能实现。任务加入到任务就绪列表一般使用此函数。下面一个是使用插入到结尾函数前后的列表示意图
列表项为空情况下的插入
![](https://img-blog.csdn.net/20170807111418642)
列表项不为空情况下的插入
![](https://img-blog.csdn.net/20170807111425506)
4.LIST项的删除
unsigned portBASE_TYPE uxListRemove( xListItem *pxItemToRemove )
{
xList * pxList;
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
pxList = ( xList * ) pxItemToRemove->pvContainer;
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
pxItemToRemove->pvContainer = NULL;
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
列表项的移除主要就是将此列表项的前一个列表项的pxNext
更新为此列表项的前后一个个列表项、此列表项的后一个列表项的pxPrevious
更新为此列表项的前一个列表项,也就是将此链表项的前后列表项连起来。
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
{ \
xList * const pxConstList = ( pxList ); \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
if( ( pxConstList )->pxIndex == ( xListItem * ) &( ( pxConstList )->xListEnd ) ) \
{ \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
} \
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
}
LIST中还有一些宏,这里只讲述一个:是循环遍寻的,一般用在就绪列表中的任务进行顺序执行,没调用一次pxIndex
指向就会后移一位,以此达到相同优先级就绪任务都能够执行的目的。