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

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

[复制链接]
xiaojie0513 发布时间:2018-6-10 22:47
5 r; P, Y6 w! ?' P
8 p6 V) y5 }+ g9 m+ M- M  b+ M
FreeRTOS列表&列表项的源码解读
6 `" h- L; I9 k8 {1 M/ u8 W1 A

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

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


( t. U) `1 o0 `/ _5 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 ). */
0 }- G2 A4 M3 W; f8 O8 [* r2    ListItem_t          xEventListItem;     /*< Used to reference a task from an event list. */" R5 I! q1 O& N9 F6 a' r; D

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


0 ^) Z* Y+ n/ e0 }& R/ i* m& Y) r! VFreeRTOS列表&列表项的结构体

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

1struct xLIST_ITEM
9 r8 V% V$ @# Z( X6 {' V: f( [" }% i 2{

) Z  j9 V7 F) h, o2 u( n0 h 3    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /
! W7 Q- O$ M( o( k 4    configLIST_VOLATILE TickType_t xItemValue; / * <正在列出的值。在大多数情况下,这用于按降序对列表进行排序。 * /
1 x( g$ g, |/ t3 S: f& y: t2 Q" L! o 5    struct xLIST_ITEM * configLIST_VOLATILE pxNext; / * <指向列表中下一个ListItem_t的指针。 * /" V% M# m( a  S' z4 s
6    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; / * <指向列表中前一个ListItem_t的指针。 * /
) B% M3 v  b9 }# s# y' { 7    void * pvOwner; / * <指向包含列表项目的对象(通常是TCB)的指针。因此,包含列表项目的对象与列表项目本身之间存在双向链接。 * /
1 D1 g  M  `. p/ W$ {( K! |7 g 8    void * configLIST_VOLATILE pvContainer; / * <指向此列表项目所在列表的指针(如果有)。 * /& G, j: o  V8 o5 J7 s5 r0 [% i
9    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /4 Z6 r( r) Y" f$ }* o" V
10};4 X8 |; M% \& g" F4 N
11    typedef struct xLIST_ITEM ListItem_t;' j* m1 B4 t) e- L

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

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

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

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

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

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

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


; c: ^! u' I' L  M

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

1struct xMINI_LIST_ITEM5 k+ y  e: W, N6 o
2{
; d) G3 P0 L$ p) m
3    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE           /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */# w, [2 ^) m2 P7 o) H4 _2 `/ U
4    configLIST_VOLATILE TickType_t xItemValue;
$ |3 Y' s0 X5 C: Y5    struct xLIST_ITEM * configLIST_VOLATILE pxNext;+ s  H$ v9 t- B# q% b4 [8 ]0 L
6    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;: ~0 _. ^! q% S1 |, H
7};
' i  c5 y% z8 o6 Q' p* s8typedef struct xMINI_LIST_ITEM MiniListItem_t;0 b6 I/ H7 U  o! _  y: g! s) q

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

1typedef struct xLIST. b& r+ n/ }8 F" A5 e5 _
2{6 S  R& i0 G/ i6 F
3   listFIRST_LIST_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /
3 }" g9 J( Q4 A- O3 b4   configLIST_VOLATILE UBaseType_t uxNumberOfItems;
5 W2 b7 f- b1 \, t+ D5   ListItem_t * configLIST_VOLATILE pxIndex; / * <用于遍历列表。 指向由listGET_OWNER_OF_NEXT_ENTRY()调用返回的后一个列表项。*/- Z: e* y- u/ T/ o6 ?  G" e' t
6   MiniListItem_t xListEnd; / * <List item包含最大可能的项目值,这意味着它始终在列表的末尾,因此用作标记。*/
6 h" l3 k  k% J: A2 n7 d7   listSECOND_LIST_INTEGRITY_CHECK_VALUE / * <如果configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则设置为已知值。* /' {9 x# j0 P$ a( U' A/ |5 C
8} List_t;
; E. d% \& x) j
列表的结构体中值得注意的是:

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

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

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

4 ]# l% s* ]; g  D) ~+ B7 s

列表的初始化

  函数:

1void vListInitialise( List_t * const pxList )/ T0 g5 V0 w* N: S; w& @9 D
2{9 K& K6 J9 j" ~* T6 {. e0 L4 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. */6 a2 Y% H9 w1 j" N. F
4    pxList->xListEnd.xItemValue = portMAX_DELAY;
6 t* ]+ Z1 t& P! b' l 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. */- `+ v+ _: L. h7 m( Y$ L
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. */
/ R( s! a3 u1 x- R) S1 [7 L! Y- I 7    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;$ h  f* M8 M) _* n
8    listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );+ T/ V& r$ c; O2 q- Q' ]8 Q2 F. K
9    listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );7 V3 X  P5 i8 l- I- j9 O
10}
. Y  I0 p  w) ^9 a7 \

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

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

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

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

列表初始化完成状态.png


6 J2 T8 q0 V$ n2 C9 _列表项的初始化

函数:

1void vListInitialiseItem( ListItem_t * const pxItem )
, [3 X. Y" o& M- _) z2
{- \* j6 Y3 O3 A/ h9 m$ D5 J
3    /* Make sure the list item is not recorded as being on a list. */
; T* B3 h, P1 I  y( e/ ]  X4    pxItem->pvContainer = NULL;
' A' D9 x) {- R$ A  q  y5    /* Write known values into the list item if- r, n4 L- t$ j2 H/ Z+ ^  y- o
6    configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */

% }8 N6 j' \# W: V7    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );9 B- u  M9 [, U. p- l- Z( f
8    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
4 h% o6 R! U4 {6 V9}
# r0 U# v2 j1 A; I

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

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

1pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';( F. {, U; s0 u+ a  V
2pxNewTCB->uxPriority = uxPriority;4 r6 ?7 n* N  \: J" V* A. ?
3pxNewTCB->uxBasePriority = uxPriority;/ ~' ]3 G( P; [& J
4pxNewTCB->uxMutexesHeld = 0;
/ `, I- ?  Q! s3 C- e5    vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
- t( S2 c! h" c  R6    vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
0 E& {; N8 T2 c! e) ]

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

1        pxNewTimer->pcTimerName = pcTimerName;; r% C; ?# ^7 K( S# @+ A1 k
2        pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;3 Z0 t. n4 W6 W* _6 Y
3        pxNewTimer->uxAutoReload = uxAutoReload;
* N: A% ~5 @8 j" t2 I4        pxNewTimer->pvTimerID = pvTimerID;3 W/ `6 x4 W6 @- J- s
5        pxNewTimer->pxCallbackFunction = pxCallbackFunction;
% N* @9 w, O. x: D$ A% d. a6        vListInitialiseItem( &( pxNewTimer->xTimerListItem ) );2 J! F1 m' Z  c( _2 S' ~! x2 g
列表项的末尾插入

  函数:

1void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
  M' q, S$ N/ i3 L, Y 2{3 v5 _0 x8 K2 W3 b& J
3ListItem_t * const pxIndex = pxList->pxIndex;
! y2 A0 t0 t7 i% Z% {: w, k* Y 4    listTEST_LIST_INTEGRITY( pxList );4 Q' ^, [$ i0 M6 p
5    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );! A# s# M- x* R% A/ m
6    listGET_OWNER_OF_NEXT_ENTRY(). */
( F1 G. e* [: X 7    pxNewListItem->pxNext = pxIndex;    //  1
: i6 ~4 t' s; t 8    pxNewListItem->pxPrevious = pxIndex->pxPrevious;    //  22 Q3 m" N: d/ o, I
9    /* Only used during decision coverage testing. */2 h% Y0 p6 I5 o
10    mtCOVERAGE_TEST_DELAY();
7 [9 ]+ F7 x) J/ a$ E  V11    pxIndex->pxPrevious->pxNext = pxNewListItem;        //  3
3 m- v1 w( ~5 u1 A( P5 Z- c" c  F+ A12    pxIndex->pxPrevious = pxNewListItem;                //  4
3 f/ Z8 ~+ V8 L! W- |$ W' r13    /* Remember which list the item is in. */
" Y5 d( P: Q( Q% z% t; w' Q14    pxNewListItem->pvContainer = ( void * ) pxList;) e% h! I( O1 K+ e' ^4 s
15    ( pxList->uxNumberOfItems )++;
! ?7 S) }& c, ^2 k% O2 O! K7 t16}/ P* w& F0 `! i& i5 `, I

传入的参数:

         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

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

) F* P, \; d# _1 U; u5 w; p
列表项的插入

源码:

1void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )$ ?# c: F4 d9 A" P' R) Q# O+ q2 z
2{
! G* r, @7 o& Y6 w+ E% a 3ListItem_t *pxIterator;& k) H5 R: a; C4 g
4const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;3 s' d9 K- P! y/ V1 d9 K; |
5    listTEST_LIST_INTEGRITY( pxList );
: x3 I. V0 v2 T& Q6 k2 ]6 h& ?2 \% e 6    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );+ b+ D' H- ~/ i1 P* b
7    if( xValueOfInsertion == portMAX_DELAY ); W2 ^& K3 G* V" x5 F
8    {; A" o; s/ C" z9 n
9        pxIterator = pxList->xListEnd.pxPrevious;
& v: o' U/ I8 y9 \+ D/ N10    }8 j. w( _) n0 L, T# ^! {! o* i
11    else0 b5 {0 W% _' {: y6 L3 S$ \. O
12    {- U4 Q4 B1 m; g
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. *// y3 V8 Q1 G/ {- W
14        {
( ~+ g4 ?" j3 G' w4 S15            /* There is nothing to do here, just iterating to the wanted
& s) M# @, Z, {5 K) e% f5 n16            insertion position. */
1 I$ Y3 S7 y8 i  P) X3 |
17        }
) G4 x' s# N8 T& y18    }, I" Q5 i2 C. T) |0 t$ |/ z
19    pxNewListItem->pxNext = pxIterator->pxNext;
9 l/ W& U4 _4 g3 B, x20    pxNewListItem->pxNext->pxPrevious = pxNewListItem;; R/ I& _% }/ L4 W# m- W" r1 l( ]/ [
21    pxNewListItem->pxPrevious = pxIterator;6 r, X- R+ B! N( |" Y* D$ Q
22    pxIterator->pxNext = pxNewListItem;3 D* X: D# A- }$ J
23    /* Remember which list the item is in.  This allows fast removal of the
$ R: J. D# l" m- y/ G24    item later. */
' i5 Y& W* j: `+ K! ~
25    pxNewListItem->pvContainer = ( void * ) pxList;
) ]9 k6 L# m' S/ E$ K, `* o26    ( pxList->uxNumberOfItems )++;
$ \1 S: _* y2 b" `9 b$ j27}
9 q1 t+ q, K: F9 h

传入的参数:

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

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


- f4 o% @. U7 i( o, }. `. s

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

- I9 b; ]7 x( ~) q+ _

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

过程分析:

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

pxNewListItem->pxNext = pxIterator->pxNext;

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

pxNewListItem->pxNext->pxPrevious = pxNewListItem;

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

  pxIterator 的 pxNext 指向了新列表项
; F9 K- f3 _# _/ z

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

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


! G& ^5 J% M1 g! d& }* O

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

  假设插入的列表项的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 )++;
9 U; l5 n/ A% O7 z' \删除列表项

    源码:

1UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )( O/ f; {" c3 Q6 q# L: x
2{& j( `# Z; h8 a" b- G
3/* The list item knows which list it is in.  Obtain the list from the list1 i  b9 r+ R6 m- W0 w
4item. */

! W" ?0 g  a. R% t 5List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;+ A3 h! q4 j# N& z, l
6    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;# _9 n" V7 S' K6 D7 G, h: E
7    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;6 J( G5 [- d3 p# x! Q
8    /* Only used during decision coverage testing. */
) \/ F; ]$ j1 \- O7 _  M) Y 9    mtCOVERAGE_TEST_DELAY();  C! i+ ]; n3 V, j/ n9 _
10    /* Make sure the index is left pointing to a valid item. */
8 P. U$ x0 L$ ?# h6 w5 p  e11    if( pxList->pxIndex == pxItemToRemove )4 @4 r7 M6 V5 S
12    {
7 s3 ~! s4 X( a5 ^# }13        pxList->pxIndex = pxItemToRemove->pxPrevious;
3 o5 R& L/ i) ~3 N: B. _14    }. R& L. H$ e! P9 C
15    else2 p$ a% Q1 {4 r; U* W
16    {4 t& |* T* ~$ |  I8 T. c( j* d# u$ O
17        mtCOVERAGE_TEST_MARKER();' O$ W9 _' @2 L4 l
18    }3 d3 p3 J3 ~  [0 u% N# N( I
19    pxItemToRemove->pvContainer = NULL;
1 v& w" S, H' I& v5 U: K+ m0 h20    ( pxList->uxNumberOfItems )--;
/ E: d6 X. V/ X8 l  Z6 H/ f1 p7 T+ V21    return pxList->uxNumberOfItems;6 g% Y% s5 W; M' }
22}
# j; t8 B/ F1 Z: C7 F' N: J- l

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

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

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

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

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

$ w8 |% g4 @( y4 `( b& F3 R

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

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

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

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

+ p$ Y$ Z3 K' K( {

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


. r% D3 m% N% @: K( X* m* a最后

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

1define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )                                        \
6 J% Y' \, Q; P% N. [6 c7 ` 2{                                                                                            \% {& Y) N/ q& K5 B! t4 v
3List_t * const pxConstList = ( pxList );                                                    \
9 J+ ^: d* x. f4 ?) P( u3 {6 } 4    /* Increment the index to the next item and return the item, ensuring */                \5 E0 j" k) j8 P" O
5    /* we don't return the marker used at the end of the list.  */                          \
: T3 t3 Q# D5 Y1 u. c& u7 |8 k 6    ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                            \. Z# O3 n$ h% O7 y* p
7    if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )  \
+ R9 c0 J! z5 k' e6 n 8    {                                                                                       \5 _( k  v- x. l! c' B; x6 @
9        ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                        \
- c% a4 ~* _  [/ M  a10    }                                                                                       \
. g* A$ S: t, a1 b11    ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;                                          \7 ~  \7 \9 c* w4 B/ s; l/ _+ N' B
12}% F3 [% v1 ^% Y* o3 x  S8 R  h/ x$ i

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

: J; K9 s; m* o, U6 I" |7 t0 p: {
( X9 z' S2 K  T1 }8 @' S1 H1 g

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

【连载】从单片机到操作系统⑤——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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版