你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【连载】从单片机到操作系统⑤——FreeRTOS列表&列表项的...

[复制链接]
xiaojie0513 发布时间:2018-6-10 22:47

* v) x: C' x3 u
' ^/ h& W  m# K
FreeRTOS列表&列表项的源码解读( n% z: H1 O4 U+ p

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

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

9 }5 Q7 }" e, L

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

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

1    ListItem_t          xStateListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
! y$ J6 p6 c+ @2 x7 r2    ListItem_t          xEventListItem;     /*< Used to reference a task from an event list. */
) h" Y8 d# C. q

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

" w, o0 [# A' k, e" a
FreeRTOS列表&列表项的结构体

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

1struct xLIST_ITEM
( G7 U1 J2 I7 b8 q7 {0 s 2{
+ y! R7 h& @! @% U2 Y" ?, d
3    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /
7 M- R5 N5 [) K  o* Y, Q 4    configLIST_VOLATILE TickType_t xItemValue; / * <正在列出的值。在大多数情况下,这用于按降序对列表进行排序。 * /- }7 z! P2 `( e( O
5    struct xLIST_ITEM * configLIST_VOLATILE pxNext; / * <指向列表中下一个ListItem_t的指针。 * /% _* Z+ G4 u. ]  G& @% n
6    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; / * <指向列表中前一个ListItem_t的指针。 * /  K( D; A4 [9 k$ Q
7    void * pvOwner; / * <指向包含列表项目的对象(通常是TCB)的指针。因此,包含列表项目的对象与列表项目本身之间存在双向链接。 * /  y4 x7 E- M. y6 r* m/ d4 s8 n
8    void * configLIST_VOLATILE pvContainer; / * <指向此列表项目所在列表的指针(如果有)。 * /0 l* f/ X4 r; d. V
9    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /* m% d+ ]0 n: s; ~
10};
. B! ^1 a" t# L; `  P3 O11    typedef struct xLIST_ITEM ListItem_t;
8 u, @, G$ U; S0 d

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

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

     pxNext 指向下一个列表项的指针

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

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

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

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

0 D, _" a+ H2 `: ^" w" P6 n  L! Z

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

1struct xMINI_LIST_ITEM! V0 p; \0 n+ h
2{

2 ^/ N0 B9 g8 [" R. f' [3    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE           /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
* v# Z& @* E# m: F4    configLIST_VOLATILE TickType_t xItemValue;
, ~) w0 R: a# s! _3 A5    struct xLIST_ITEM * configLIST_VOLATILE pxNext;
# L* M! k% E$ {$ a6    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
4 V3 r- h; n( [. H7};
* w$ K0 L4 W# t& K8typedef struct xMINI_LIST_ITEM MiniListItem_t;
- a& Z% m0 Y, R: Q8 H7 X3 X! ]

  再定义了一个列表的结构体,可能看到这里,一些同学已经蒙了,列表与列表项是啥关系啊,按照杰杰的理解,是类似父子关系的,一个列表中,包含多个列表项,就像一个父亲,生了好多孩子,而列表就是父亲,列表项就是孩子。

1typedef struct xLIST
* X/ c& p+ ~3 S) }  K2{
+ B  A; ~% Q6 B, z- M  g/ ^  Z3   listFIRST_LIST_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /
9 k1 S1 H4 M$ X; |$ b! P4   configLIST_VOLATILE UBaseType_t uxNumberOfItems;' S: Y' |" w% U& G
5   ListItem_t * configLIST_VOLATILE pxIndex; / * <用于遍历列表。 指向由listGET_OWNER_OF_NEXT_ENTRY()调用返回的后一个列表项。*/5 e3 G. Z8 g! k* `
6   MiniListItem_t xListEnd; / * <List item包含最大可能的项目值,这意味着它始终在列表的末尾,因此用作标记。*/* w) t7 O. {, f/ {1 B
7   listSECOND_LIST_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /7 t/ X3 c0 S, h) C) o/ `& k
8} List_t;+ x% t0 Y0 b2 o# i/ j1 _
列表的结构体中值得注意的是:

     uxNumberOfItems 是用来记录列表中列表项的数量的,就是记录父亲有多少个儿子,当然女儿也行~。

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

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


; Q, x5 T; P8 o6 m; C

列表的初始化

  函数:

1void vListInitialise( List_t * const pxList ), I5 Y) w! f; G; x) w$ k% p
2{! O6 ]9 |% d9 B3 F2 `2 N
3    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. */
! c) H' j5 v" v- X5 g, @ 4    pxList->xListEnd.xItemValue = portMAX_DELAY;
' T# x) l2 i1 A! r 5    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. */8 w$ L. A$ T2 O' B* i
6    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. */8 g' a$ B! ?( A: A3 S* b+ v5 L
7    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;6 l1 h; _( k- p
8    listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );( P! p8 e1 B$ [& e( r
9    listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );9 G) B) C* l. S
10}
6 R6 L0 c4 L% w9 o( ^. n! W0 G

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

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

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

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

列表初始化完成状态.png

6 r9 {6 ~. D& [4 T3 d9 }
列表项的初始化

函数:

1void vListInitialiseItem( ListItem_t * const pxItem )0 S! J6 `9 r/ M* S9 j/ ~
2
{
1 S+ w4 I  ?1 \; y% e6 P: S3    /* Make sure the list item is not recorded as being on a list. */; M, n1 J- Q# q' L* T
4    pxItem->pvContainer = NULL;
! D. a- H  D; B, u/ g5    /* Write known values into the list item if
! d7 W2 J4 ^- G1 T6    configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */

9 Q( d( F' W3 `6 n1 b* v7    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
- U5 R: j, l" A9 T1 N) s8    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );% u5 _( l7 I0 [8 F0 J
9}
  ?+ K- A& O# r+ E

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

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

1pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
$ y: V6 [* ]' I* \  C/ G# f2 u2pxNewTCB->uxPriority = uxPriority;4 X; A+ B+ z' L2 A' i
3pxNewTCB->uxBasePriority = uxPriority;6 ?6 y7 X" |8 k' ?2 M3 x' {
4pxNewTCB->uxMutexesHeld = 0;1 Y5 ~5 G8 o2 J! D  O( m
5    vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
4 ?  q  m- I& Z% n- l) i$ U2 [  Z. P6    vListInitialiseItem( &( pxNewTCB->xEventListItem ) );( L1 H7 f& h( u3 e, D0 i+ \6 {

  又或者是在定时器相关的初始化中:

1        pxNewTimer->pcTimerName = pcTimerName;2 A* X) x3 C2 l# D7 {8 ~4 @
2        pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;
7 \) V" {/ E+ n% L4 a* E3        pxNewTimer->uxAutoReload = uxAutoReload;9 u0 p/ _! J- p3 ~
4        pxNewTimer->pvTimerID = pvTimerID;
- ?& @+ l5 m- J8 g, r5        pxNewTimer->pxCallbackFunction = pxCallbackFunction;  _, R/ [, I1 m2 z
6        vListInitialiseItem( &( pxNewTimer->xTimerListItem ) );
# c/ ^; L+ _) v8 G3 u列表项的末尾插入

  函数:

1void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
. v0 N" W5 [/ W 2{
" d% Q9 @9 ]! `8 \( y2 P3 V& g 3ListItem_t * const pxIndex = pxList->pxIndex;
/ M, w* I7 t4 p: y4 f' h1 C  ] 4    listTEST_LIST_INTEGRITY( pxList );  I- T2 h) c1 @
5    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );# D  m7 x. t; U$ M
6    listGET_OWNER_OF_NEXT_ENTRY(). */- h/ H/ L7 X3 P. b
7    pxNewListItem->pxNext = pxIndex;    //  1 / F$ Y* F: `9 F4 f. n4 T1 x
8    pxNewListItem->pxPrevious = pxIndex->pxPrevious;    //  2  w$ h9 C- g' ~4 s3 t/ r8 w% b
9    /* Only used during decision coverage testing. */
, l+ s0 ?/ z  [. p10    mtCOVERAGE_TEST_DELAY();: _) T! H3 R( Q- v2 Q1 P/ o
11    pxIndex->pxPrevious->pxNext = pxNewListItem;        //  3
; N+ ]) Z' U0 A% _: j) p' ~% p12    pxIndex->pxPrevious = pxNewListItem;                //  40 `# ]' B$ b! b; r6 m, o
13    /* Remember which list the item is in. */
! @% L7 ]! I/ x- G1 I# g3 I14    pxNewListItem->pvContainer = ( void * ) pxList;
# o" }3 @4 D9 x: M15    ( pxList->uxNumberOfItems )++;
4 h) Q! T7 M0 }" x/ c16}* E6 l5 ~) }/ l7 ?7 |

传入的参数:

         pxList:列表项要插入的列表。

         pxNewListItem:要插入的列表项是什么。

         从末尾插入,那就要先知道哪里是头咯,我们在列表中的成员pxIndex就是用来遍历列表项的啊,那它指向的地方就是列表项的头,那么既然FreeRTOS中的列表很像数据结构中的双向链表,那么,我们可以把它看成一个环,是首尾相连的,那么函数中说的末尾,就是列表项头的前一个,很显然其结构图应该是下图这样子的(初始化结束后pxIndex指向了xListEnd):

从末尾插入.png

为什么是这样子的呢,一句句代码来解释:

一开始:

ListItem_t * const pxIndex = pxList->pxIndex;

保存了一开始的索引列表项(xListEnd)的指向。

pxNewListItem->pxNext = pxIndex;         //  1

新列表项的下一个指向为索引列表项,也就是绿色的箭头。

pxNewListItem->pxPrevious = pxIndex->pxPrevious;      //  2

刚开始我们初始化完成的时候pxIndex->pxPrevious的指向为自己xListEnd,那么xNewListItem->pxPrevious的指向为xListEnd。如2紫色的箭头。

pxIndex->pxPrevious->pxNext = pxNewListItem;             //  3

索引列表项(xListEnd)的上一个列表项还是自己,那么自己的下一个列表项指向就是指向了pxNewListItem。

pxIndex->pxPrevious = pxNewListItem;                              //  4

这句就很容易理解啦。如图的4橙色的箭头。

插入完毕的时候标记一下新的列表项插入了哪个列表,并且将uxNumberOfItems进行加一,以表示多了一个列表项。

为什么源码要这样子写呢?因为这只是两个列表项,一个列表含有多个列表项,那么这段代码的通用性就很强了。无论原本列表中有多少个列表项,也无论pxIndex指向哪个列表项!

原子2.png

原子1.png

看看是不是按照源码中那样插入呢?

2 D( s! q+ `5 m
列表项的插入

源码:

1void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
  b& |8 \  C9 w$ ~3 M 2{
+ b) ]/ s" ~! O; Q: q 3ListItem_t *pxIterator;
, y! d3 J: {  ]4 J# x: h' i) n 4const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;/ j% E' u% j5 _
5    listTEST_LIST_INTEGRITY( pxList );
6 e8 O, X$ F& t2 c) _) N 6    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );: t: C& M( _( A1 P1 X
7    if( xValueOfInsertion == portMAX_DELAY )
& m) T! U; I# u/ K% P4 `- } 8    {2 y) k' r% O3 u6 C( \- e
9        pxIterator = pxList->xListEnd.pxPrevious;
+ E) U- D. T- I# Q1 G10    }
! U8 Q2 \8 x& y* ^9 H; U; h/ N11    else2 q/ R' G$ A! e/ U' F. t/ P
12    {; ~$ N9 I" f# f
13        for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM.  This is checked and valid. */
$ N) I9 ]' n  I; K2 P* A4 p14        {
# R* M8 Q. g' l7 K7 k) `- g9 a4 T15            /* There is nothing to do here, just iterating to the wanted( ]$ X& X4 I" F8 `
16            insertion position. */

8 A  x. v. C8 v$ K: D/ i17        }5 n) ?( A1 `, @% T% {8 L
18    }& I0 }3 R% @1 o% ]( T  X6 z, s
19    pxNewListItem->pxNext = pxIterator->pxNext;
) b* e( Z3 E. T; C20    pxNewListItem->pxNext->pxPrevious = pxNewListItem;" ?) G, M6 s/ r7 v/ F9 e
21    pxNewListItem->pxPrevious = pxIterator;
) G5 a; G7 m# f8 R22    pxIterator->pxNext = pxNewListItem;
0 |3 {; [5 l, Q" Z0 m8 ^" f23    /* Remember which list the item is in.  This allows fast removal of the7 @' j) @& B3 @
24    item later. */

4 y* ]- ?* B8 f25    pxNewListItem->pvContainer = ( void * ) pxList;
# T$ D( V& }5 z2 N: J26    ( pxList->uxNumberOfItems )++;
1 H0 x6 A: k0 c2 g. `) E27}! F( ]/ j' b9 v0 A3 m

传入的参数:

     pxList:列表项要插入的列表。

     pxNewListItem:要插入的列表项是什么。

  E( V  L, C% Y& K

pxList决定了插入哪个列表,pxNewListItem中的xItemValue值决定了列表项插入列表的位置。

ListItem_t *pxIterator;  2const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

  定义一个辅助的列表项pxIterator,用来迭代找出插入新列表项的位置,并且保存获取要插入的列表项pxNewListItem的xItemValue。

  如果打开了列表项完整性检查,就要用户实现configASSERT(),源码中有说明。

  既然是要插入列表项,那么肯定是要知道列表项的位置了,如果新插入列表项的xItemValue是最大的话(portMAX_DELAY),就直接插入列表项的末尾。否则就需要比较列表中各个列表项的xItemValue的大小来进行排列。然后得出新列表项插入的位置。

for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )

  上面源码就是实现比较的过程。

与上面的从列表项末尾插入的源码一样,FreeRTOS的代码通用性很强,逻辑思维也很强。

6 T3 n, n' k: H8 `% ?" Q$ U5 s

         如果列表中列表项的数量为0,那么插入的列表项就是在初始化列表项的后面。如下图所示:

过程分析:

新列表项的pxNext指向pxIterator->pxNext,也就是指向了xListEnd(pxIterator)。

pxNewListItem->pxNext = pxIterator->pxNext;

    而xListEnd(pxIterator)的pxPrevious指向则为pxNewListItem。

pxNewListItem->pxNext->pxPrevious = pxNewListItem;

     新列表项的(pxPrevious)指针指向xListEnd(pxIterator)

  pxIterator 的 pxNext 指向了新列表项
9 H5 l3 _4 ?7 A/ ?! q1 y

pxNewListItem->pxPrevious = pxIterator;2pxIterator->pxNext = pxNewListItem;

与从末尾插入列表项其实是一样的,前提是当前列表中列表项的数目为0。


% l0 F8 d6 U9 |" ?0 T5 R

     假如列表项中已经有了元素呢,过程又是不一样的了。原来的列表是下图这样子的:

  假设插入的列表项的xItemValue是2,而原有的列表项的xItemValue值是3,那么,按照源码,我们插入的列表项是在中间了。而pxIterator则是①号列表项。

插入后的效果:

列表项插入.png

分析一下插入的过程:

      新的列表项的pxNext指向的是pxIterator->pxNext,也就是③号列表项。因为一开始pxIterator->pxNext=指向的就是③号列表项!!

pxNewListItem->pxNext = pxIterator->pxNext;

     而pxNewListItem->pxNext 即③号列表项的指向上一个列表项指针(pxPrevious)的则指向新插入的列表项,也就是②号列表项了。

pxNewListItem->pxNext->pxPrevious = pxNewListItem;

     新插入列表项的指向上一个列表项的指针pxNewListItem->pxPrevious指向了辅助列表项pxIterator。很显然要连接起来嘛!

pxNewListItem->pxPrevious = pxIterator;     

     同理,pxIterator列表项的指向下一个列表项的指针则指向新插入的列表项了pxNewListItem。

pxIterator->pxNext = pxNewListItem;

而其他没改变指向的地方不需改动。(图中的两条直线做的连接线是不需要改动的)

当插入完成的时候,记录一下新插入的列表项属于哪个列表。并且让该列表下的列表项数目加一。

pxNewListItem->pvContainer = ( void * ) pxList;2         ( pxList->uxNumberOfItems )++;
  P6 ]( s  H  D4 B删除列表项

    源码:

1UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )! O" U: L) j0 b0 [3 r8 }
2{
) l  ?6 v9 v+ G+ B. U( j: w 3/* The list item knows which list it is in.  Obtain the list from the list
6 z- F. x, H, E9 a9 u  S3 O' L 4item. */

2 p6 z$ q- R% X 5List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;! f7 I& l; Y& h3 W# Q
6    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
2 D7 n8 ]3 }' J) L# q9 F 7    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;0 ^* |  i4 o" t: U
8    /* Only used during decision coverage testing. */5 j" T1 Q" T( Y* W7 A; H9 c6 G
9    mtCOVERAGE_TEST_DELAY();
7 r( w: Z6 V6 H4 N! {10    /* Make sure the index is left pointing to a valid item. */* F% t8 m; ]. X
11    if( pxList->pxIndex == pxItemToRemove )
3 a# X5 _6 K- C0 w+ p12    {
' j: ?. V' n; t7 k4 w# e& N- p13        pxList->pxIndex = pxItemToRemove->pxPrevious;. B, r9 w4 S8 L/ }
14    }
% ]% d: W$ n. |1 Z0 L# Y: C15    else
; ^& v1 i( \. H* c) K16    {4 S) |& w3 |: J: S4 T# D# ^
17        mtCOVERAGE_TEST_MARKER();( R2 @( q; a2 \" }( Z6 y( ^
18    }
5 s) a. G% k+ _( g5 k  H19    pxItemToRemove->pvContainer = NULL;- M) t) r9 k) x1 U# ]  H4 M/ B0 u
20    ( pxList->uxNumberOfItems )--;
# p2 J; i( z8 [6 a; {% x21    return pxList->uxNumberOfItems;1 U" K+ A3 O& \* m  [, x8 u
22}. I: T# b: S' X3 I4 g

  其实删除是很简单的,不用想都知道,要删除列表项,那肯定要知道该列表项是属于哪个列表吧,pvContainer就是记录列表项是属于哪个列表的。

  删除就是把列表中的列表项从列表中去掉,其本质其实就是把他们的连接关系删除掉,然后让删除的列表项的前后两个列表连接起来就行了,假如是只有一个列表项,那么删除之后,列表就回到了初始化的状态了。

pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;2 pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

这两句代码就实现了将删除列表项的前后两个列表项连接起来。

按照上面的讲解可以理解这两句简单的代码啦。

& Y! R- f% O$ ]) K8 b' k3 q6 }7 x7 @

  假如删除的列表项是当前索引的列表项,那么在删除之后,列表中的pxIndex就要指向删除列表项的上一个列表项了。

if( pxList->pxIndex == pxItemToRemove )2  {3      pxList->pxIndex = pxItemToRemove->pxPrevious;4  }

  当然还要把当前删除的列表项的pvContainer指向NULL,让它不属于任何一个列表,因为,删除的本质是删除的仅仅是列表项的连接关系,其内存是没有释放掉的,假如是动态内存分配的话。

  并且要把当前列表中列表项的数目返回一下。

- V1 a, R2 @9 s/ J; J; }

至此,列表的源码基本讲解完毕。

# y' z# {! z" ^! e4 n5 F( E1 F
最后

大家还可以了解一下遍历列表的宏,它在list.h文件中:

1define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )                                        \
) n- U2 q; V+ Q 2{                                                                                            \2 w' f% j8 H, g1 [* D# M0 \
3List_t * const pxConstList = ( pxList );                                                    \: `% o4 l4 x3 |  e
4    /* Increment the index to the next item and return the item, ensuring */                \
: p2 X: y3 c$ n6 \; K* j8 B! x 5    /* we don't return the marker used at the end of the list.  */                          \
* q* h0 e7 U# D$ j' e) e 6    ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                            \
4 o" r0 w9 t; C 7    if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )  \
& Z  q, x# _2 W2 ?# M 8    {                                                                                       \
$ g4 c: x, [) K# C! ~7 L% c3 F 9        ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                        \0 x% n/ Z+ q' A
10    }                                                                                       \
( o- l. g1 G! E11    ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;                                          \
+ c3 v: |2 m' o12}7 Y. @% b4 c" \  h+ u

  这是一个宏,用于列表的遍历,返回的是列表中列表项的pxOwner成员,每次调用这个宏(函数)的时候,其pxIndex索引会指向当前返回列表项的下一个列表项。


9 N! N! t) N8 S1 [2 ^' ~7 b0 A, L; u1 t- ^) U

本文为杰杰原创,转载请说明出处

【连载】从单片机到操作系统⑤——FreeRTOS列表&列表项的源码解读
收藏 3 评论3 发布时间:2018-6-10 22:47

举报

3个回答
xiaojie0513 回答时间:2018-6-10 23:15:18
这排版看起来好难看啊。。。。。。。。。。破总!!!!
xiaojie0513 回答时间:2018-6-10 23:16:38
@ zero99
Jdden 回答时间:2018-8-25 10:02:03
感谢分享!

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版