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

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

[复制链接]
xiaojie0513 发布时间:2018-6-10 22:47
+ U% L) v1 Y" I. h- M) a3 j5 i, V
5 j; N( p' q' M/ }: d
FreeRTOS列表&列表项的源码解读
6 q9 i: h3 V, p: D

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

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

, f0 o7 `0 v: E. [7 D

     言归正传,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 ). */9 L: o- {8 d' l7 ]8 G# I0 ?$ a3 |; K
2    ListItem_t          xEventListItem;     /*< Used to reference a task from an event list. */
3 U; {* O% u9 b/ V- x3 u

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

5 F( v+ @( j* t9 m
FreeRTOS列表&列表项的结构体

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

1struct xLIST_ITEM
7 @- w) x) N" c% ` 2{

) Y7 d8 v! V0 c% W% t8 r6 R9 Z 3    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /! i1 k# C, |# t% e* B3 C
4    configLIST_VOLATILE TickType_t xItemValue; / * <正在列出的值。在大多数情况下,这用于按降序对列表进行排序。 * /
# Y' d, c/ s1 Q, _- P 5    struct xLIST_ITEM * configLIST_VOLATILE pxNext; / * <指向列表中下一个ListItem_t的指针。 * /
: i8 S) d5 b- A$ @: ` 6    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; / * <指向列表中前一个ListItem_t的指针。 * /; q1 o, I$ v% B7 A8 t
7    void * pvOwner; / * <指向包含列表项目的对象(通常是TCB)的指针。因此,包含列表项目的对象与列表项目本身之间存在双向链接。 * /
) G  y- D  X* o# V7 M 8    void * configLIST_VOLATILE pvContainer; / * <指向此列表项目所在列表的指针(如果有)。 * /
) D3 ]9 j- p1 A& f 9    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /
+ v  _- q- v: {9 ]10};+ o' H4 m1 e. D. b; t% p
11    typedef struct xLIST_ITEM ListItem_t;
: u' v# z) k* K

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

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

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

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

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

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

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


( v3 s1 x* |2 b6 Q

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

1struct xMINI_LIST_ITEM+ O3 E4 F7 @8 ?  p9 k. d
2{

$ v9 O# H6 c( n/ q) E3    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE           /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */2 `0 {& E9 a+ a6 }. r! |
4    configLIST_VOLATILE TickType_t xItemValue;
6 d7 ]1 x% p, c! ]0 S8 w% K5    struct xLIST_ITEM * configLIST_VOLATILE pxNext;
$ I$ y. @& ]& Y/ X  k6    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
, ?9 G9 m/ q% Z& [( q7};
8 s+ e4 G1 _: C8 B# M/ {8typedef struct xMINI_LIST_ITEM MiniListItem_t;
7 V6 V2 G/ p, _8 \

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

1typedef struct xLIST% x) |$ k7 l: P$ m- q8 s
2{6 @% ^  ?2 P, ?# {
3   listFIRST_LIST_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /- L9 U( ^. I6 y" D$ ~9 ?- f
4   configLIST_VOLATILE UBaseType_t uxNumberOfItems;; Z& |4 i1 q% Q, Y5 O9 p! C
5   ListItem_t * configLIST_VOLATILE pxIndex; / * <用于遍历列表。 指向由listGET_OWNER_OF_NEXT_ENTRY()调用返回的后一个列表项。*/% }' R& }; A! H3 v% E
6   MiniListItem_t xListEnd; / * <List item包含最大可能的项目值,这意味着它始终在列表的末尾,因此用作标记。*/3 H3 D; j% v! V' A9 Y
7   listSECOND_LIST_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /
6 N" P* e/ F; D" ^9 ^3 r% F& U8} List_t;$ N# k* N/ |( u  D5 W' T' U/ |
列表的结构体中值得注意的是:

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

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

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

9 h- W+ S! }; |6 E4 `% F* k

列表的初始化

  函数:

1void vListInitialise( List_t * const pxList )8 p7 `! w% I* m/ }) k8 X
2{' i3 P+ g8 n8 |# M3 U
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. */' Y& p) L9 v: c; O3 X1 J7 X* C8 a
4    pxList->xListEnd.xItemValue = portMAX_DELAY;$ ?- [4 a9 ~9 A3 o* t( ?3 `; B
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. */
" t' w1 {2 N1 g  ?2 H& E7 e# y 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. */
% o1 z  ^8 r* D/ K2 m1 d 7    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;5 L( q: C4 G8 ]; k2 d! n' Z, t
8    listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
- ^( o, c4 b2 N* a 9    listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
2 B6 D/ M' z2 U3 I10}3 W) }5 Y! H8 b

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

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

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

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

列表初始化完成状态.png


2 \/ G5 O7 }" @4 [# F, r/ X列表项的初始化

函数:

1void vListInitialiseItem( ListItem_t * const pxItem ). o! R3 W3 }2 @, L5 a6 p
2
{3 p. g) H& z& \+ f( `0 e
3    /* Make sure the list item is not recorded as being on a list. */9 k  z. z' v( N2 ~
4    pxItem->pvContainer = NULL;
+ \# W# f7 X- H9 q; R3 I5    /* Write known values into the list item if- d; s8 k9 P( `4 U! i3 B
6    configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */

- l/ g; {: l: T* q1 j7    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );0 S% d/ T0 D. |3 y- }) I( s
8    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );( H, x* l5 m& }
9}
+ K0 |7 G! |4 P& R7 [

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

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

1pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
; |& m" L) M- d3 \9 p2pxNewTCB->uxPriority = uxPriority;
: O% _$ P' o/ y% @) x: K/ H3pxNewTCB->uxBasePriority = uxPriority;! ~4 \8 A0 B( s. b( b$ D. n
4pxNewTCB->uxMutexesHeld = 0;- H4 l8 L8 X. q# Y4 Y  o8 g/ X
5    vListInitialiseItem( &( pxNewTCB->xStateListItem ) );9 u9 {" E5 ?5 D& w- M- m+ A
6    vListInitialiseItem( &( pxNewTCB->xEventListItem ) );% s& J& p% S; E; L

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

1        pxNewTimer->pcTimerName = pcTimerName;0 I/ j3 _0 u+ k
2        pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;
( B# F. j! M6 R, t' u' s8 A, Y3        pxNewTimer->uxAutoReload = uxAutoReload;3 G+ D8 K  U& C5 |
4        pxNewTimer->pvTimerID = pvTimerID;
7 M6 [( Z9 r* L# N- G; d3 @' Y5        pxNewTimer->pxCallbackFunction = pxCallbackFunction;' F# d* I1 s/ w6 k) J: U: ~$ e6 J
6        vListInitialiseItem( &( pxNewTimer->xTimerListItem ) );
  H( W( I* S. S- @: I' j6 v列表项的末尾插入

  函数:

1void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )2 N5 i+ J/ s* x
2{
/ x8 l9 M4 u& k$ u 3ListItem_t * const pxIndex = pxList->pxIndex;
3 w9 n; S: a0 X' V9 `3 l: f 4    listTEST_LIST_INTEGRITY( pxList );- o) V( D7 u3 R& b/ l" F' ^$ G0 C
5    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );1 ~3 c. Y; B1 E4 y7 Q# G
6    listGET_OWNER_OF_NEXT_ENTRY(). */
4 k) H) P, N' j$ w 7    pxNewListItem->pxNext = pxIndex;    //  1 8 V8 C6 N/ Z& N
8    pxNewListItem->pxPrevious = pxIndex->pxPrevious;    //  2, _2 M+ y7 A/ S4 C% \; t
9    /* Only used during decision coverage testing. */
; D0 @, D8 b+ x- E: T( M10    mtCOVERAGE_TEST_DELAY();
% {; h" U. V. U* D' X11    pxIndex->pxPrevious->pxNext = pxNewListItem;        //  3 " L# `/ U7 G9 }& _* n" Q( R$ [! q) d
12    pxIndex->pxPrevious = pxNewListItem;                //  4
1 z7 b7 Q6 e0 [6 b2 \/ x$ O13    /* Remember which list the item is in. */% _2 m0 M) L4 d) H- N
14    pxNewListItem->pvContainer = ( void * ) pxList;
5 N! m) R9 E+ t5 W$ f15    ( pxList->uxNumberOfItems )++;
, q/ K6 d: u  t; P6 y( \. S16}+ u* ~' O9 a2 X- m/ `) O

传入的参数:

         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

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

) j1 h5 E; ^8 i3 P( y" L
列表项的插入

源码:

1void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
. B; r. l) K4 V 2{4 u8 M" M( o; N* u, v
3ListItem_t *pxIterator;0 D! d. J7 \  `' d/ R/ _5 |
4const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
5 g, q1 W3 U8 M 5    listTEST_LIST_INTEGRITY( pxList );4 A& R! P1 b* ?( L
6    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
* c0 K( m9 P9 z9 P4 T 7    if( xValueOfInsertion == portMAX_DELAY )  X0 M  I' b5 P  G, w
8    {1 {8 T$ y0 M' n) c9 s# P# [: [
9        pxIterator = pxList->xListEnd.pxPrevious;
: I! }% |) e0 h% O* c6 _0 C10    }
. ^% m: Y: [) Y# ^& Y/ t11    else
# F0 {) @6 |( J; @" w- m  f$ [12    {' g+ E+ {: d* W4 z4 u7 e
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. */2 x5 G* i: I  H8 d3 K3 ?
14        {. v" D$ W/ s5 o2 q
15            /* There is nothing to do here, just iterating to the wanted) T. j7 z  @, v4 L1 G1 t/ o" @
16            insertion position. */

! e- V- S: n" K% c4 x8 v17        }& ^: h# m! j* R% G" o$ e( w( G0 K. s5 [/ x
18    }
5 n0 g9 W& s  p19    pxNewListItem->pxNext = pxIterator->pxNext;
1 h" g% i: j3 a  Q20    pxNewListItem->pxNext->pxPrevious = pxNewListItem;) p. |& K& C7 M# j6 P/ G* |8 V
21    pxNewListItem->pxPrevious = pxIterator;
+ G; ?, T& t, X1 l6 U$ T! H9 K+ [& O7 _22    pxIterator->pxNext = pxNewListItem;
/ d, b# F  O- l. P+ y1 t/ w23    /* Remember which list the item is in.  This allows fast removal of the% b3 _/ f) Y' t; o7 ~
24    item later. */

2 r1 v9 y1 ^' Q: v  q3 m25    pxNewListItem->pvContainer = ( void * ) pxList;/ F1 ^/ r9 q3 g& Y% F* s6 e% v
26    ( pxList->uxNumberOfItems )++;& g2 W) r* h* q. K
27}
, c" Q6 t, N8 F; U) |

传入的参数:

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

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

& W2 z) O$ i8 j( j' Y5 f) |

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的代码通用性很强,逻辑思维也很强。

! c9 @+ U% y5 W; ~

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

过程分析:

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

pxNewListItem->pxNext = pxIterator->pxNext;

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

pxNewListItem->pxNext->pxPrevious = pxNewListItem;

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

  pxIterator 的 pxNext 指向了新列表项: m, |) W% F+ t+ x# D# O8 H+ O

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

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

) `( ^6 E2 f5 ]9 \; \: H$ W

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

  假设插入的列表项的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 )++;
+ ]2 P$ d5 h( V* e! U4 R9 f删除列表项

    源码:

1UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
9 Z9 z9 u3 E: m4 s# p* w 2{
6 R5 |' L( Y0 F7 N3 S$ A 3/* The list item knows which list it is in.  Obtain the list from the list0 K$ s8 Q9 {% V1 N$ G6 `
4item. */
  ?- y  w: F# d: ^0 N
5List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;/ E6 J- K7 W; G6 z7 m5 Q3 m
6    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
/ |& _; H* g! x7 x, G: ?$ q 7    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;. }5 m: {& t, O6 }
8    /* Only used during decision coverage testing. */
  L2 m7 t, d0 F/ f; t3 S/ @2 s 9    mtCOVERAGE_TEST_DELAY();
5 u* A! z/ y; w10    /* Make sure the index is left pointing to a valid item. */
( Q  \. ~/ ?+ H% p11    if( pxList->pxIndex == pxItemToRemove )- y/ ?" {' G! |- Z2 A) F( g4 h
12    {
0 t  L+ c* ]2 W* n1 q13        pxList->pxIndex = pxItemToRemove->pxPrevious;6 b& w0 o$ [9 {
14    }
; [) [/ s" l. V+ P( K! V15    else6 y8 d7 N( d* B. O5 v- \/ g2 B( w( R2 u
16    {
6 W0 h, F* \' Z1 h$ o9 S17        mtCOVERAGE_TEST_MARKER();
. M5 S3 s- Z' l" ?! ~; H& S% s18    }
0 \! ^. p- N$ a) ?8 b4 q19    pxItemToRemove->pvContainer = NULL;) n. G* L" F, M8 j$ d- A7 f
20    ( pxList->uxNumberOfItems )--;
4 Y' ?" l3 J$ ], @% O% `& r21    return pxList->uxNumberOfItems;
, l3 {% r. H# N2 |4 P' Z/ i22}
2 l# K/ Q- ^* z) A

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

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

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

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

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

: l/ v( B! ^9 \$ E* [

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

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

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

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


7 m7 f. e( W$ d( P% ?* j5 c- {

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

9 R* w- n% ]. Y7 i
最后

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

1define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )                                        \
" V; a+ }" P0 p+ _& }6 y" e8 e1 b6 x  ` 2{                                                                                            \& ?8 A- K$ v' X6 @% ]7 q5 w; |; B
3List_t * const pxConstList = ( pxList );                                                    \9 b+ F; q9 q6 T7 C
4    /* Increment the index to the next item and return the item, ensuring */                \6 T: W2 b; E+ T- q$ t
5    /* we don't return the marker used at the end of the list.  */                          \$ d4 \9 W; C6 U; y3 u
6    ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                            \$ h/ b9 l. H# V& w
7    if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )  \
3 D% q4 G4 n/ ~+ s5 t 8    {                                                                                       \
* G) d6 P& m+ y$ W3 x 9        ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                        \5 `0 F$ X' \: c- y  [
10    }                                                                                       \1 c6 j0 V% F" k" b5 d, I
11    ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;                                          \
5 h; P" T2 P* t( X, A6 M0 T, N, d6 @12}5 d+ I$ q. [$ H6 a: F1 l

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

+ D, W, G3 k6 X3 _& j. C

2 j% @. k- X3 y

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

【连载】从单片机到操作系统⑤——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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版