FreeRTOS学习笔记-01 List及其应用
来源:互联网 发布:数据分析平台 编辑:程序博客网 时间:2024/03/29 23:11
(针对FreeRTOS v9.0.0)
List_t
链表List_t在FreeRTOS数据结构的组织中有很重要的作用,如果想读懂源码,必须要先读懂这个结构。
struct xLIST_ITEM /* 链表的节点类型 */{ TickType_t xItemValue; /* 节点的关键字,排序相关,在插入的时候根据该值判断插入到List的何处 */ struct xLIST_ITEM * pxNext;/* 双向链表 */ struct xLIST_ITEM * pxPrevious;/* 双向链表 */ void * pvOwner;/* 该节点的拥有者,比如任务控制块tcb结构内部只要有这样一个节点成员,就可以用List来组织tcb,通过List中的一个节点可以反向获取对应的tcb */ void * pvContainer;/* 包含该节点的List,可以通过节点来获得所在List */};typedef struct xLIST_ITEM ListItem_t;
struct xMINI_LIST_ITEM{ TickType_t xItemValue; struct xLIST_ITEM * pxNext; struct xLIST_ITEM * pxPrevious;};typedef struct xMINI_LIST_ITEM MiniListItem_t;
/* uxNumberOfItems代表链表中节点数量; pxIndex是一个独立的索引,用来遍历链表; xListEnd是一个哨兵,用来标定链表开头和结尾 */typedef struct xLIST{ UBaseType_t uxNumberOfItems; ListItem_t * pxIndex; MiniListItem_t xListEnd;} List_t;
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner )#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue )#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext )#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )#define listLIST_IS_EMPTY( pxList ) ( ( BaseType_t ) ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) )#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems )
/* (有时我们更关心的是节点的owner,而不是节点本身) * pxIndex会记录当前遍历到的位置,下次调用该函数时,直接从pxIndex指向的位置开始 */#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \{ \List_t * const pxConstList = ( pxList ); \ /* Increment the index to the next item and return the item, ensuring */ \ /* we don't return the marker used at the end of the list. */ \ ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \ { \ ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ } \ ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \}
/* 如果能保证链表是有序插入的,那么有时可以通过获取头部或尾部节点来代替对链表的查找操作,很高效 */#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner )#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( BaseType_t ) ( ( pxListItem )->pvContainer == ( void * ) ( pxList ) ) )#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pvContainer )/* 判断链表是否出事化,参见链表初始化函数 */#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )
/* 初始化链表 */void vListInitialise( List_t * const pxList );/* 初始化链表节点 */void vListInitialiseItem( ListItem_t * const pxItem );/* 插入,链表是按降序排列的 */void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem );/* 插入的位置是pxIndex指向的位置,所以在使用listGET_OWNER_OF_NEXT_ENTRY时,将会最后被遍历到 */void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem );/* 删除节点 */UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove );
/*-----------------------------------------------------------*/
void vListInitialise( List_t * const pxList ){ pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /* portMAX_DELAY是(值域的)最大值,所以排序时哨兵xListEnd节点会一直在尾部,起到哨兵作用, 同时也可以标志链表是否初始化,对照上面的listLIST_IS_INITIALISED()来理解 */ pxList->xListEnd.xItemValue = portMAX_DELAY; /* 目前,链表中只有一个哨兵节点 */ pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd ); pxList->uxNumberOfItems = ( UBaseType_t ) 0U;/* 目前,不计哨兵节点,链表中的有效节点数为0 */}
void vListInitialiseItem( ListItem_t * const pxItem ){ pxItem->pvContainer = NULL;}
/* 将节点插入到链表中pxIndex节点前面,这样可以保证以后遍历操作时会最后一个获得刚刚插入的节点 * 对照listGET_OWNER_OF_NEXT_ENTRY()学习 */void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ){ ListItem_t * const pxIndex = pxList->pxIndex; pxNewListItem->pxNext = pxIndex; pxNewListItem->pxPrevious = pxIndex->pxPrevious; pxIndex->pxPrevious->pxNext = pxNewListItem; pxIndex->pxPrevious = pxNewListItem; /* Remember which list the item is in. */ pxNewListItem->pvContainer = ( void * ) pxList; ( pxList->uxNumberOfItems )++;}
/* 按序插入一个节点 */void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ){ ListItem_t *pxIterator; const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; if( xValueOfInsertion == portMAX_DELAY ) { pxIterator = pxList->xListEnd.pxPrevious; } else { for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) { /* There is nothing to do here, just iterating to the wanted insertion position. */ } } pxNewListItem->pxNext = pxIterator->pxNext; pxNewListItem->pxNext->pxPrevious = pxNewListItem; pxNewListItem->pxPrevious = pxIterator; pxIterator->pxNext = pxNewListItem; pxNewListItem->pvContainer = ( void * ) pxList; ( pxList->uxNumberOfItems )++;}
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ){ List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer; pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; /* Make sure the index is left pointing to a valid item. */ if( pxList->pxIndex == pxItemToRemove ) { pxList->pxIndex = pxItemToRemove->pxPrevious; } pxItemToRemove->pvContainer = NULL; ( pxList->uxNumberOfItems )--; return pxList->uxNumberOfItems;}
List_t相关应用
task.c中定义了以下一些全局变量,用来管理TCB任务控制块。
#define configMAX_PRIORITIES 5 /* 由用户定义,代表优先级的最大值,考虑优化的情况下不要超过32,此处给出是为了易于理解 */static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/* FreeRTOS允许一个优先级上有多个任务(有些RTOS上规定一个优先级上只能有一个任务,比如ucosii,或者汽电领域符合osek/autosar标准的RTOS,这些os只支持抢占式调度就可以了),支持抢占式调度和时间片轮转调度。每个优先级有一个链表,用来组织在该优先级上的就绪任务。 */static List_t xDelayedTaskList1;static List_t xDelayedTaskList2;static List_t * volatile pxDelayedTaskList;static List_t * volatile pxOverflowDelayedTaskList;static List_t xPendingReadyList;
调用 vTaskDelay(xTicksToDelay) 函数可以使当前任务延时等待,该函数会调用 prvAddCurrentTaskToDelayedList( xTicksToDelay…) 将当前任务从就绪队列移到等待队列。如果你仔细看,会发现在上面定义的全局变量中,有两个延时链表。在初始化时, pxDelayedTaskList 指向 xDelayedTaskList1, pxOverflowDelayedTaskList指向 xDelayedTaskList2。为什么需要两个List来组织延时任务呢?请带着这个问题看下面的代码。
prvAddCurrentTaskToDelayedList( xTicksToDelay...){ ... uxListRemove( &( pxCurrentTCB->xStateListItem ) ); /* 从就绪队列中移除当前任务的tcb */ ... /* xConstTickCount记录了当前的tick数值,所以xTimeToWake表示唤醒该任务时的tick值。 * 这样的好处是,对于节点保存的tick值关键字没有写操作,而且是有序插入,这样节省了排序或查找的时间,高效。 * 而且这样的一个加法操作很可能导致溢出:比如tick计数使用16位整数,那么其和超过65535就会溢出了。 * 要将溢出的结果和不溢出的结果分开来组织,这样就有了pxDelayedTaskList和pxOverflowDelayedTaskList两个队列的必要。 * pxDelayedTaskList用来组织延时没有溢出的tcb,当tick到达对应值时就会唤醒对应任务。 * 而pxOverflowDelayedTaskList用来组织延时溢出的tcb,在tick值溢出之前这些任务是绝对不会到达该唤醒的时间点。 * 当tick值溢出之后,会从零开始,在这个临界点上,这两个链表会交换。 * 所以唤醒的任务永远是在pxDelayedTaskList指向的链表中。 */ xTimeToWake = xConstTickCount + xTicksToWait; /* tcb结构中包含ListItem_t类型的xStateListItem元素,便于用链表组织延时任务。而且说明了在组织延时任务的节点中,xItemValue中保存的是下次应该唤醒该任务时的tick值,只要tick值到达该值,这个延时的任务就需要被唤醒 */ listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); if( xTimeToWake < xConstTickCount ) { /* Wake time has overflowed. Place this item in the overflow list. */ vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); } else { /* The wake time has not overflowed, so the current block list is used. */ vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); /* If the task entering the blocked state was placed at the head of the list of blocked tasks then xNextTaskUnblockTime needs to be updated too. */ if( xTimeToWake < xNextTaskUnblockTime ) { xNextTaskUnblockTime = xTimeToWake; } else { mtCOVERAGE_TEST_MARKER(); } } ...}
阅读全文
0 0
- FreeRTOS学习笔记-01 List及其应用
- FreeRTOS系列学习笔记二---FreeRTOS内核详解LIST
- FreeRTOS系列学习笔记三---FreeRTOS内核详解LIST(9.0版本)
- FreeRTOS 学习笔记 1
- FreeRTOS学习笔记
- FreeRTOS学习笔记
- FreeRTOS学习笔记一
- FreeRTOS学习笔记二
- FreeRTOS学习笔记三
- FreeRTOS 学习笔记0920
- FreeRTOS学习笔记0921
- 5.19-Python-语言及其应用-笔记-list
- FreeRTOS学习笔记-1-概述
- FreeRTOS学习及移植笔记之一:开始FreeRTOS之旅
- 离散数学及其应用-学习笔记(1)
- JAVA学习笔记 -- JDBC及其应用
- 【学习笔记----数据结构13-哈夫曼树及其应用】
- 徐飞玉:自然语言理解及其应用学习笔记
- Linux在启动Tmocat时,报权限不够
- Json序列化和反序列化的简单封装
- 被低估的 Babel
- 发票
- 【centos 7】添加路由和永久静态路由
- FreeRTOS学习笔记-01 List及其应用
- 网页Web上调用本地应用程序
- JavaWEB常见问题(一)
- java 修改文件名
- 十一 Java 数组
- 制作发布压缩包和使用压缩包
- C语言函数指针
- 二叉搜索树中的第K大的节点 java实现
- Windows+CPU only+VS2013安装caffe以及配置Python接口