网站前期规划报告,百度搜索平台,网站开发实用技术第二版答案,住建局特种作业证FreeRTOS内核调度使用了大量的列表#xff08;list#xff09;和列表项#xff08;listitem#xff09;数据结构。它的源码中涉及到很多列表的操作#xff0c;对于FreeRTOS来说#xff0c;列表就是它最基础的一部分#xff0c;列表被用作FreeRTOS调度器使用#xff0c;…FreeRTOS内核调度使用了大量的列表list和列表项listitem数据结构。它的源码中涉及到很多列表的操作对于FreeRTOS来说列表就是它最基础的一部分列表被用作FreeRTOS调度器使用用于跟踪任务处于就绪挂起延时的任务都会被挂接到各自的列表中用户程序如果有需要也可以使用列表其中就连FreeRTOS的任务调度其实核心也涉及到列表。 FreeRTOS列表使用指针指向列表项。一个列表list下面可能有很多个列表项list item每个列表项都有一个指针指向列表。如图所示 列表和列表项
列表项有两种形式全功能版的列表项xLIST_ITEM和迷你版的列表项xMINI_LIST_ITEM。我们来看一下它们具体的定义先看全功能版。
struct xLIST_ITEM
{listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/configLIST_VOLATILE TickType_t xItemValue; /*列表项值*/struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*指向列表中下一个列表项*/struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*指向列表中上一个列表项*/void * pvOwner; /*指向一个任务TCB*/void * configLIST_VOLATILE pvContainer; /*指向包含该列表项的列表 */listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/
};
typedef struct xLIST_ITEM ListItem_t; 宏listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE用于检查列表项数据是否完整在projdefs.h中如果将宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1则使能列表项数据完整性检查则宏listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE会被两个已知的数值代替。 xItemValue是列表项值通常是一个被跟踪的任务优先级或是一个调度事件的计数器值。如果任务因为等待从队列取数据而进入阻塞状态则任务的事件列表项的列表项值保存任务优先级有关信息状态列表项的列表项值保存阻塞时间有关的信息。这个变量被configLIST_VOLATILE修饰configLIST_VOLATILE被映射成C语言关键字volatile表明这个变量是“易变的”告诉编译器不得对这个变量进行代码优化因为列表项的成员可能会在中断服务程序中被更新。 pxNext和pxPrevious是列表项类型指针用来指向列表中下一个和上一个列表项通过这两个指针列表项之间可以形成类似双向链表结构。 指针pvOwner通常指向一个任务TCB。 指针pvContainer指向包含该列表项的列表。 迷你版的列表项xMINI_LIST_ITEM是全功能版列表项xLIST_ITEM的一个子集定义如下所示
struct xMINI_LIST_ITEM
{listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/configLIST_VOLATILE TickType_t xItemValue;struct xLIST_ITEM * configLIST_VOLATILE pxNext;struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t; 既然有了全功能版的列表项为什么还要声明迷你版的列表项呢这是因为列表结构体需要一个列表项成员但又不需要列表项中的所有字段所以才有了迷你版列表项。迷你列表项起到的主要作用就是标识列表的末尾所以它的值也设置成了最大值列表结构体定义为
typedef struct xLIST
{listFIRST_LIST_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/configLIST_VOLATILE UBaseType_t uxNumberOfItems;ListItem_t * configLIST_VOLATILE pxIndex; /*用于遍历列表*/MiniListItem_t xListEnd; /*列表项*/listSECOND_LIST_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/
}List_t; 和列表项定义相同宏listFIRST_LIST_INTEGRITY_CHECK_VALUE和listSECOND_LIST_INTEGRITY_CHECK_VALUE用于检查列表项数据是否完整在projdefs.h中如果将宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1则使能列表项数据完整性检查则宏listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE会被两个已知的数值代替。 uxNumberOfItems表示该列表中挂接的列表项数目0表示列表为空。 列表项类型指针用于遍历列表列表初始化后这个指针指向xListEnd。通过宏listGET_OWNER_OF_NEXT_ENTRY()来获取列表中的下一个列表项。 列表项xListEnd用于标记列表结束。xListEnd.xItemValue被初始化为一个常数其值与硬件架构相关为0xFFFF16位架构或者0xFFFFFFFF32位架构。
关于列表的一些操作
初始化列表
列表结构体中包含一个列表项成员主要用于标记列表结束。初始化列表就是把这个列表项插入到列表中。
void vListInitialise( List_t * const pxList )
{/*列表索引指向列表项*/pxList-pxIndex ( ListItem_t * )( pxList-xListEnd ); /* 设置为最大可能值 */pxList-xListEnd.xItemValue portMAX_DELAY;/* 列表项xListEnd的pxNext和pxPrevious指针指向了它自己 */pxList-xListEnd.pxNext (ListItem_t * ) ( pxList-xListEnd );pxList-xListEnd.pxPrevious ( ListItem_t * ) ( pxList-xListEnd );pxList-uxNumberOfItems ( UBaseType_t) 0U;/* 设置为已知值用于检测列表数据是否完整*/listSET_LIST_INTEGRITY_CHECK_1_VALUE(pxList );listSET_LIST_INTEGRITY_CHECK_2_VALUE(pxList );
}
如果宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1则使能列表项数据完整性检查则宏listSET_LIST_INTEGRITY_CHECK_1_VALUE()和listSET_LIST_INTEGRITY_CHECK_2_VALUE被一个已知值代替默认为0x5a5a16位架构或者0x5a5a5a5a32位架构。 假设禁止列表数据完整性检查初始化后的列表如图1-2所示uxNumberOfItems被初始化为0xListEnd.xItemValue初始化为0xffffffffpxIndex、xListEnd.pxNext和xListEnd.pxPrevious初始化为指向列表项xListEnd。 初始化列表项
列表项的初始化很简答 只需要声明该列表项不属于任何一个列表就可以了。
void vListInitialiseItem( ListItem_t * const pxItem )
{pxItem-pvContainer NULL;/*设置为已知值用于检测列表项数据是否完整*/listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE(pxItem );listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE(pxItem );
}
如果宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1则使能列表项数据完整性检查则宏listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE会被两个已知的数值代替默认为0x5a5a16位架构或者0x5a5a5a5a32位架构。 假设禁止列表项数据完整性检查初始化后的列表项如图1-3所示。仅是将指针pvContainer设置为空指针该指针用于指向包含该列表项的列表这里设置为NULL表示这个列表项不属于任何列表。 列表插入列表项 每个列表项对象都有一个列表项值xItemValue通常是一个被跟踪的任务优先级或是一个调度事件的计数器值。调用API函数vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem)可以将pxNewListItem指向的列表项插入到pxList指向的列表中列表项在列表的位置由pxNewListItem-xItemValue决定按照升序排列。
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion pxNewListItem-xItemValue;/* 检查列表和列表项数据的完整性仅当configASSERT()定义时有效。*/listTEST_LIST_INTEGRITY( pxList );listTEST_LIST_ITEM_INTEGRITY(pxNewListItem );/*将新的列表项插入到列表根据xItemValue的值升序插入列表。*/if( xValueOfInsertion portMAX_DELAY){pxIterator pxList-xListEnd.pxPrevious;}else{for( pxIterator (ListItem_t * ) ( pxList-xListEnd );pxIterator-pxNext-xItemValue xValueOfInsertion; pxIterator pxIterator-pxNext ){/* 这里为空 */}}pxNewListItem-pxNext pxIterator-pxNext;pxNewListItem-pxNext-pxPrevious pxNewListItem;pxNewListItem-pxPrevious pxIterator;pxIterator-pxNext pxNewListItem;pxNewListItem-pvContainer ( void* ) pxList;( pxList-uxNumberOfItems );
} 列表项末尾插入
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t* const pxIndex pxList-pxIndex;/*检查列表和列表项数据的完整性仅当configASSERT()定义时有效。*/listTEST_LIST_INTEGRITY( pxList );listTEST_LIST_ITEM_INTEGRITY(pxNewListItem );/*向列表中插入新的列表项*/pxNewListItem-pxNext pxIndex;pxNewListItem-pxPrevious pxIndex-pxPrevious;mtCOVERAGE_TEST_DELAY();pxIndex-pxPrevious-pxNext pxNewListItem;pxIndex-pxPrevious pxNewListItem;pxNewListItem-pvContainer ( void* ) pxList;( pxList-uxNumberOfItems );
}
Tips: 这个函数是最容易造成误解的一个函数字面理解在我开始学的时候我以为就是插入到最后一个迷你列表项的前面所谓末尾插入肯定是最后一项嘛阅读源码之后其实不然因为列表中有一项成员
ListItem_t * configLIST_VOLATILE pxIndex;
该成员主要作用就是用来遍历列表的。阅读源码发现它是插入在pxIndex所指的列表项的前面。这里体现了FreeRTOS的哲学理念“公平”如果pxIndex指向的是当前的索引的列表项表示正在使用这时比如顺序是1-2-3现在pxIndex指向2要插入4这时你4肯定是要最后遍历访问的意味着就是访问顺序应该是2-3-1-4所以它要插入在2前面我的方法是记住一个口诀末尾插入就是插入pxIndex所指列表项的前一项的后面可能听着有点别扭不就是所指列表项的前面嘛 细细体会有公平的哲学思想
重点
重点一开始学习的时候一直不明白这个pxIndex有什么用因为我在有关列表的list.c中的API函数中根本没发现有改变它的代码以为它一直是初始化的值就是一直指向迷你列表项其实不然作为一名程序员一切从源码中得到答案。
搜索代码之后发现中间对pxIndex赋值的地方只有listGET_OWNER_OF_NEXT_ENTRY这个接口list.h中的一个有参宏 每调用一次listGET_OWNER_OF_NEXT_ENTRY列表的pxIndex会指向下一个列表项。 而调用listGET_OWNER_OF_NEXT_ENTRY主要是 FreeRTOS的列表和列表项采用了一种统一的列表管理不像我以前学的数据结构中的链表操作一样其中的节点都是具体的结构体的内容所以是针对具体的一类结构体比如struct people这样导致的后果就是所有有关链表操作的内容都是针对这类结构体如果稍微改成struct dog这样就需要全部重写链表的所有操作了。FreeRTOS采用一种方法写出了通用的链表操作让我不得不感叹这代码确实是写的好