从0开始学FreeRTOS-(列表&列表项)-6

  • 2019 年 10 月 15 日
  • 笔记

FreeRTOS列表&列表项的源码解读

第一次看列表与列表项的时候,感觉很像是链表,虽然我自己的链表也不太会,但是就是感觉很像。

在FreeRTOS中,列表与列表项使用得非常多,是FreeRTOS的一个数据结构,学习过数据结构的同学都知道,数据结构能使我们处理数据更加方便快速,能快速找到数据,在FreeRTOS中,这种列表与列表项更是必不可少的,能让我们的系统跑起来更加流畅迅速。

言归正传,FreeRTOS中使用了大量的列表(List)与列表项(Listitem),在FreeRTOS调度器中,就是用到这些来跟着任务,了解任务的状态,处于挂起、阻塞态、还是就绪态亦或者是运行态。这些信息都会在各自任务的列表中得到。

看任务控制块(tskTaskControlBlock)中的两个列表项:

ListItem_t xStateListItem; /* <任务的状态列表项目引用的列表表示该任务的状态(就绪,已阻止,暂停)。*/  ListItem_t xEventListItem; /* <用于从事件列表中引用任务。*/

一个是状态的列表项,一个是事件列表项。他们在创建任务就会被初始化,列表项的初始化是根据实际需要来初始化的,下面会说。

FreeRTOS列表&列表项的结构体

既然知道列表与列表项的重要性,那么我们来解读FreeRTOS中的list.c与list.h的源码吧。从头文件lsit.h开始,看到定义了一些结构体:

 struct xLIST_ITEM  {      listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /* <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。*/      configLIST_VOLATILE TickType_t xItemValue; /* <正在列出的值。在大多数情况下,这用于按降序对列表进行排序。 */      struct xLIST_ITEM * configLIST_VOLATILE pxNext; /* <指向列表中下一个ListItem_t的指针。 */      struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /* <指向列表中前一个ListItem_t的指针。 */      void * pvOwner; /* <指向包含列表项目的对象(通常是TCB)的指针。因此,包含列表项目的对象与列表项目本身之间存在双向链接。 */      void * configLIST_VOLATILE pvContainer; /* <指向此列表项目所在列表的指针(如果有)。 */      listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /* <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。*/  };  typedef struct xLIST_ITEM ListItem_t; /* 由于某种原因,lint希望将其作为两个单独的定义。 */

列表项结构体的一些注意的地方:

xItemValue 用于列表项的排序,类似1—2—3—4

pxNext 指向下一个列表项的指针
pxPrevious 指向上(前)一个列表项的指针

这两个指针实现了类似双向链表的功能

pvOwner 指向包含列表项目的对象(通常是任务控制块TCB)的指针。因此,包含列表项目的对象与列表项目本身之间存在双向链接。

pvContainer 记录了该列表项属于哪个列表,说白点就是这个儿子是谁生的。。。

        
同时定义了一个MINI的列表项的结构体,MINI列表项是删减版的列表项,因为很多时候不需要完全版的列表项。就不用浪费那么多内存空间了,这或许就是FreeRTOS是轻量级操作系统的原因吧,能省一点是一点。MINI列表项:

struct xMINI_LIST_ITEM  {      listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE           /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */      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 /* <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。*/      configLIST_VOLATILE UBaseType_t uxNumberOfItems;      ListItem_t * configLIST_VOLATILE pxIndex; /* <用于遍历列表。 指向由listGET_OWNER_OF_NEXT_ENTRY()调用返回的后一个列表项。*/      MiniListItem_t xListEnd; /* <List item包含最大可能的项目值,这意味着它始终在列表的末尾,因此用作标记。*/      listSECOND_LIST_INTEGRITY_CHECK_VALUE /* <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。*/  } List_t;

列表的结构体中值得注意的是:
uxNumberOfItems 是用来记录列表中列表项的数量的,就是记录父亲有多少个儿子,当然女儿也行~。

pxIndex 是索引编号,用来遍历列表的,调用宏listGET_OWNER_OF_NEXT_ENTRY()之后索引就会指向返回当前列表项的下一个列表项。

xListEnd 指向的是最后一个列表项,并且这个列表项是MiniListItem属性的,是一个迷你列表项。

列表的初始化

函数:

void vListInitialise( List_t * const pxList )  {       pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );           /*lint The mini list structure is used as the list end to save RAM.  This is checked and valid. */       pxList->xListEnd.xItemValue = portMAX_DELAY;       pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );   /*lint The mini list structure is used as the list end to save RAM.  This is checked and valid. */       pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint The mini list structure is used as the list end to save RAM.  This is checked and valid. */       pxList->uxNumberOfItems = ( UBaseType_t ) 0U;       listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );       listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );  }

将列表的索引指向列表中的xListEnd,也就是末尾的列表项(迷你列表项)

列表项的xItemValue数值为portMAX_DELAY,也就是0xffffffffUL,如果在16位处理器中则为0xffff。

列表项的pxNext与pxPrevious这两个指针都指向自己本身xListEnd。

初始化完成的时候列表项的数目为0个。因为还没添加列表项嘛~。

列表项的初始化

函数:

void vListInitialiseItem( ListItem_t * const pxItem )  {      /* Make sure the list item is not recorded as being on a list. */      pxItem->pvContainer = NULL;      /* Write known values into the list item if      configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */      listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );      listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );  }

只需要让列表项的pvContainer指针指向NULL即可,这样子就使得列表项不属于任何一个列表,因为列表项的初始化是要根据实际的情况来进行初始化的。

例如任务创建时用到的一些列表项初始化:

pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '