01. 概述, O; X( S9 G" U4 K
要想看懂FreeRTOS 源码并学习其原理,有一个东西绝对跑不了,那就是FreeRTOS 的列表和列表项。列表和列表项是FreeRTOS 的一个数据结构,FreeRTOS 大量使用到了列表和列表项,它是FreeRTOS 的基石。要想深入学习并理解FreeRTOS,那么列表和列表项就必须首先掌握,否则后面根本就没法进行。/ ?8 X( ?* V: j g( ?2 C2 r# [
% A5 {' i+ U2 U: G I6 b' g8 i
列表被FreeRTOS调度器使用,用于跟踪任务,处于就绪、挂起、延时的任务,都会被挂接到各自的列表中。用户程序如果有需要,也可以使用列表。
% W' w. D6 X ]+ \- K5 |# g+ a; y: G4 g: o
FreeRTOS列表使用指针指向列表项。一个列表(list)下面可能有很多个列表项(list item),每个列表项都有一个指针指向列表。如下图所示。
: V" B5 }! v2 U) Z
1 T# p( r- U# {, A
' Q+ y8 q- x2 R$ y7 s" S
0 C6 {- C4 u B' I/ L- ~. m02. 列表
% }7 b& e z7 {- z, E; e; M3 g* R列表是FreeRTOS 中的一个数据结构,概念上和链表有点类似,列表被用来跟踪FreeRTOS中的任务。与列表相关的全部东西都在文件list.c 和list.h 中。在list.h 中定义了一个叫List_t 的结构体,如下:
# F; O3 b- }6 {( m
) K3 k6 t! F2 m# G1 ?, G- typedef struct xLIST
+ v! |0 i& A/ E6 y1 W" w3 P - {
* I* E7 @4 C4 h% C8 y5 ^7 e - listFIRST_LIST_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/: K4 G) g; V/ A# z
- configLIST_VOLATILE UBaseType_t uxNumberOfItems;
$ S- f" N* ~# \( }) J# j3 q: q5 e5 |9 q - ListItem_t * configLIST_VOLATILE pxIndex; /*用于遍历列表*/
+ T8 A# v# g3 s! F8 \ - MiniListItem_t xListEnd; /*列表项*/
" V: h- M2 G% } - listSECOND_LIST_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/
, ?0 b w) b2 s3 m3 r- \# Z- I7 T+ \ - }List_t;
复制代码 - s% v, f+ U0 v" s o
/ B' h- p/ P( U% t
* b2 C) L" x4 \$ B D o) s(1) 和(5) 、这两个都是用来检查列表完整性的, 需要将宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为1,开启以后会向这两个地方分别添加一个变量xListIntegrityValue1 和xListIntegrityValue2,在初始化列表的时候会这两个变量中写入一个特殊的值,默认不开启这个功能。
" y; p! y# c9 Z4 ?(2)、uxNumberOfItems 用来记录列表中列表项的数量。
8 Q# L5 e p9 M n' |- I(3)、pxIndex 用来记录当前列表项索引号,用于遍历列表。! e2 X: \6 j D) G
(4)、列表中最后一个列表项,用来表示列表结束,此变量类型为MiniListItem_t,这是一个迷你列表项。
* _) K% }2 E" O8 N. O8 `2 @$ t# q; Q# A3 b* p% O1 V2 J
03. 列表项& G/ Y2 s% H! Q$ ?, Q! ^3 X+ \# N
列表项就是存放在列表中的项目,FreeRTOS 提供了两种列表项:列表项和迷你列表项。这两个都在文件list.h 中有定义。! b0 [ W" G0 y' a" L
6 ^- J d/ T9 M, b5 ?, ~1 s% Z. U Q6 z
xLIST_ITEM列表项
8 p1 T/ W; s0 S- }+ d' K. ~" G C% B2 s# ^+ \7 I! w& N2 `
- struct xLIST_ITEM8 O# Z) G* c; y$ [5 Y
- {
6 \! K! f1 A0 I7 g% A, G8 h - listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/3 u1 X/ W. ?8 Q/ ^; w, a
- configLIST_VOLATILETickType_t xItemValue; /*列表项值*/
& ?! _3 z5 A* J L0 }: t. V - struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*指向列表中下一个列表项*/9 p7 }. b6 y Z' b0 H' g
- struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*指向列表中上一个列表项*/) q% t! ~3 ^% k
- void * pvOwner; /*指向一个任务TCB*/0 \( w. x5 k! H; H
- void * configLIST_VOLATILE pvContainer; /*指向包含该列表项的列表 */
. u9 M3 [ ?9 n) S - listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/
8 i5 u: _; Y9 \$ H) S( R - };
. R9 g) H& X4 l/ T/ a2 K - typedef struct xLIST_ITEM ListItem_t;
复制代码 2 i+ I9 z7 K5 G$ _5 A
2 k6 ]8 y# w% V. j/ n c) g4 P9 ?! Y# _
(1)和(7)、用法和列表一样,用来检查列表项完整性的。以后我们在学习列表项的时候不讨论这个功能!4 t) c) d5 _) y" {, W- u) z* b5 u
(2)、xItemValue 为列表项值。
1 k4 W. M5 t! m# j3 l( R3 @(3)、pxNext 指向下一个列表项。# m H6 x3 ^4 [* M3 p2 c' L
(4)、pxPrevious 指向前一个列表项,和pxNext 配合起来实现类似双向链表的功能。 D; F# Q$ c: d" ]
(5)、pvOwner 记录此链表项归谁拥有,通常是任务控制块。
: L* U g8 z& _; m(6)、pvContainer 用来记录此列表项归哪个列表。注意和pvOwner 的区别,在前面讲解任务控制块TCB_t 的时候说了在TCB_t 中有两个变量xStateListItem 和xEventListItem,这两个变量的类型就是ListItem_t,也就是说这两个成员变量都是列表项。以xStateListItem 为例,当创建一个任务以后xStateListItem 的pvOwner 变量就指向这个任务的任务控制块,表示xSateListItem属于此任务。当任务就绪态以后xStateListItem 的变量pvContainer 就指向就绪列表,表明此列表项在就绪列表中。举个通俗一点的例子:小王在上二年级,他的父亲是老王。如果把小王比作列表项,那么小王的pvOwner 属性值就是老王,小王的pvContainer 属性值就是二年级。+ w8 J. `0 Q* [2 f
, ~4 [4 k' x# i7 l# j4 h) L
xMINI_LIST_ITEM列表项) p, e& F2 ^+ E3 Q x
8 H/ U0 A, E, L7 l0 C5 ~- struct xMINI_LIST_ITEM
2 z: q7 j$ D( h0 i0 |; I% r( x2 i - {
# j5 t; f, ^+ y - listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/
; @8 u# l9 m+ Q- J" b - configLIST_VOLATILE TickType_t xItemValue;
( Z, x( k/ ? D" Y% b4 |2 K. H - struct xLIST_ITEM * configLIST_VOLATILE pxNext;3 k4 ~2 o+ h: I2 W4 {" }" x6 n6 F2 ^
- struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;% R- f3 h2 V$ N p9 Q' W
- };
+ R5 M+ O( }, m: x( \1 R1 j5 F P - typedef struct xMINI_LIST_ITEM MiniListItem_t;
复制代码 6 r2 X8 I) c- j, b$ a
2 r5 Y4 P% I5 D |2 W5 J$ t/ S/ u* G7 I4 S K0 N- M( p
(1)、用于检查迷你列表项的完整性。& b$ c( D. I" x5 |8 L( a. p
* q) a' V$ Q3 @* P! X' W6 a9 n7 N
(2)、xItemValue 记录列表列表项值。; L0 U2 t8 T2 O( o4 b
(3)、pxNext 指向下一个列表项。
A% c8 @- c( u4 F m% Y: a( F(4)、pxPrevious 指向上一个列表项。* m$ S! q5 ]* R+ A
可以看出迷你列表项只是比列表项少了几个成员变量,迷你列表项有的成员变量列表项都有的,没感觉有什么本质区别啊?那为什么要弄个迷你列表项出来呢?那是因为有些情况下我们不需要列表项这么全的功能,可能只需要其中的某几个成员变量,如果此时用列表项的话会造成内存浪费!比如上面列表结构体List_t 中表示最后一个列表项的成员变量xListEnd 就是MiniListItem_t 类型的。/ z. K! U d" K# X! _8 I
i6 h. ^: n, j' R04. 列表相关宏# k0 M! l7 _8 W F' b8 g
- /*
: g& @, p( E% F( X" g3 M - * Access macro to set the owner of a list item. The owner of a list item
+ K+ {/ G! ^$ | T9 r! \- E0 ^( I - * is the object (usually a TCB) that contains the list item.
) V( n- F9 b2 K9 [* X- l* H8 a - *2 d8 t% d) s1 m2 w
- * \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER
- M' G8 Q4 i% n% R) t - * \ingroup LinkedList! v9 i+ k; H* P# F; r- n9 I! s3 u
- */ K$ L, C; u6 g( ~( [. o
- #define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )& a* h1 f8 x+ ?, P# l) S
( v. n, |' E" l N# e# S- /*! ?/ |% v* ~# N: C5 R" ` w" I
- * Access macro to get the owner of a list item. The owner of a list item9 B2 f1 d$ J* M
- * is the object (usually a TCB) that contains the list item.
& A! @& P$ A% H! w; w1 J6 `3 J - *, K# ]) d7 \# [$ H; f: r
- * \page listGET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER$ k A: r+ v% j' d; W
- * \ingroup LinkedList6 v1 l! b0 W) {8 s; O- A
- */' i0 n4 n; O( }
- #define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner )1 Q: J6 f G" F; I
- / T$ ?9 ?8 T4 ?! c a$ G
- /*1 U# E1 p: z, W3 E9 Y9 P: m
- * Access macro to set the value of the list item. In most cases the value is
' `8 i7 `$ B7 q4 R - * used to sort the list in descending order., I3 V7 V. Y: U0 f
- *' T5 c3 s C3 c( L" j
- * \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE
: k! a* z9 R' V( h2 \ - * \ingroup LinkedList
. q, p. J3 T$ B0 `0 X) e - */
% O! [+ y! @( {# T - #define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )' x3 h0 g$ L6 R! u G5 s+ N2 [; X: O
- 2 [ Q/ b! ^2 @2 i5 Y- s* K% `
- /*
5 g) S4 P0 S5 O* z: O - * Access macro to retrieve the value of the list item. The value can
0 _0 J2 H* b) G - * represent anything - for example the priority of a task, or the time at
) U/ ~3 ]/ Z, h - * which a task should be unblocked.
& c* H' D. {5 }( D2 w - *
7 r; }1 {- I, } - * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE
* v# v0 ]/ ]2 b, f: ^$ O - * \ingroup LinkedList) \% P% d" t- U% H" ^
- */
( _: n5 q. A, I1 A$ v - #define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue )
9 }% y1 |2 i! F# ]0 }3 O5 Z, _ p - ) ` I8 i5 e3 p: [
- /*
- M+ [% n8 j: c* T1 o) q - * Access macro to retrieve the value of the list item at the head of a given+ |6 }# b: M: z# l7 o, d
- * list.2 p6 y* g( j# ?
- *
* e) L2 n. |' o - * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE2 L: N* Y% m+ f: J
- * \ingroup LinkedList
: d6 p- s- q- e6 s$ t6 W - */
0 u8 G$ n$ x H5 c - #define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
5 m* [* z+ }! z0 |8 g% F1 k - : O$ n% U, C+ [+ _& D2 i/ `
- /* w7 b' m: L) F* S* w
- * Return the list item at the head of the list.3 w; K8 ?! _) Z8 V- D4 P
- *
9 G8 M0 @' G( H. U4 d, h - * \page listGET_HEAD_ENTRY listGET_HEAD_ENTRY& N/ R# b* ~) {- i0 b2 D2 `. D
- * \ingroup LinkedList
' I( [5 o0 f' x( Q. f( W9 p% } - */
$ B9 Q% ~/ |" ^1 \$ o - #define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )
( ~; u% H& |3 v# v @ - 3 l; ]4 ?9 X+ F! Y
- /*
! n, Y, p }3 A: O0 d. } - * Return the next list item.
& x) ?; G5 z3 q7 V# a3 _ - *& r! X) M: @. i) E+ v; Y
- * \page listGET_NEXT listGET_NEXT% A0 M# ` M" M$ r2 L4 D
- * \ingroup LinkedList5 ~2 z: d% X3 Q- G
- */) _6 T" V4 Y8 a8 C7 t" L0 G j
- #define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext )/ X6 L1 D; r) t; h6 C4 w
# Q/ C0 H6 R! ~1 N) A. c- /*
: j% y8 p. E: J3 r# P, C: | - * Return the list item that marks the end of the list
3 a. _: ^9 S, o7 [: w - *3 B3 ^) @. L1 s' U4 }' b. x1 R
- * \page listGET_END_MARKER listGET_END_MARKER: C; M% N' ]8 s3 Z' h
- * \ingroup LinkedList4 O: V# V: G% S
- */
6 ?( z3 T! A7 o - #define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )
8 q" l: W0 f. e: ^ - # t8 F( ~4 x3 ?% |( B1 n3 M: T
- /*) @5 B: L% u$ ~. r
- * Access macro to determine if a list contains any items. The macro will
3 b9 u @7 p* O0 N" e - * only have the value true if the list is empty.) J0 q' s+ {$ r& T; b. y) D
- *3 N" I1 |4 g. I6 Q: W% J
- * \page listLIST_IS_EMPTY listLIST_IS_EMPTY
& L7 }+ L D$ c' g - * \ingroup LinkedList. N9 Z% I4 o$ S5 o8 X" a9 y! E3 Q4 d
- */
. V* ~% W9 ^- q. c - #define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE )
, h1 J) T# ^+ L- T - 8 _: @% i" M; x! _
- /*" l+ v& M# z3 i' E
- * Access macro to return the number of items in the list.
) ~; P3 {( o5 H( R' s - */
) K4 z1 R( H& w5 A2 D* [) ] - #define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems )& E9 X+ o8 j1 X& C1 p
- 8 d4 H" P" Y7 O0 `# {% t! D
- /*
- P. V1 C7 ]1 ^7 F, }7 ] - * Access function to obtain the owner of the next entry in a list.' ]9 C" w+ I- W, x1 O
- *
1 x H5 L4 T% x. e. g( u - * The list member pxIndex is used to walk through a list. Calling
: o' [ f! K, P - * listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list- z# T v8 p+ Y; a O( |' N* @
- * and returns that entry's pxOwner parameter. Using multiple calls to this
. _7 \1 t1 J1 @6 B% R - * function it is therefore possible to move through every item contained in" g. k3 H$ b4 S0 j" w( k
- * a list. o0 b8 I; [$ y6 F, w5 H
- *3 w! D q1 N+ S1 `5 Y
- * The pxOwner parameter of a list item is a pointer to the object that owns$ k! ~) F" V* p( q) B
- * the list item. In the scheduler this is normally a task control block.
" o3 T; d3 g! _. I8 G3 q - * The pxOwner parameter effectively creates a two way link between the list
7 ^9 f2 j8 \* x, W" m - * item and its owner.
( v- r9 \/ Y5 T. H+ q - * J6 s5 W0 l2 ?; D
- * @param pxTCB pxTCB is set to the address of the owner of the next list item.
6 {3 r$ G! l* F h. ^$ E - * @param pxList The list from which the next item owner is to be returned.0 q/ l) e5 n- ]6 Z3 L
- * r# _+ n8 v5 D4 d' s. {4 l
- * \page listGET_OWNER_OF_NEXT_ENTRY listGET_OWNER_OF_NEXT_ENTRY& }2 b5 H4 S H, C B, t9 t H7 d
- * \ingroup LinkedList
$ i2 G( L1 R2 `* Y - */
5 c% K7 S4 d- p& j3 g, K, U - #define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \2 B& U1 s; d/ b8 ^3 r) V: @& n
- { \/ E& h$ p! p4 A8 H
- List_t * const pxConstList = ( pxList ); \
! ]; j% @9 b4 W- c2 S - /* Increment the index to the next item and return the item, ensuring */ \
# ~& O1 o9 w- K% M4 S" t) u - /* we don't return the marker used at the end of the list. */ \
$ T2 C; K: C9 `! c( u+ @) A5 s' Q0 b - ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
9 M( ?5 \1 I, _" |+ n - if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \5 Y7 F0 R' v0 w& x( y/ `! P
- { \+ _0 z5 \6 |& T
- ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
* u% K7 ]* B* U3 c+ Z7 y - } \
. d* j* a& {) Z% ^ - ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
+ Y9 L4 R$ e- O. \4 D0 F6 I! w6 _ - }
0 l Y) _- @' V
7 V: a3 x: \0 z1 A/ t) ~, }
3 _& j- G% i8 w# S* W, `. @- /*6 D( I: @( V. T# O- A; N
- * Access function to obtain the owner of the first entry in a list. Lists
/ f$ H3 m: x2 N# ?* L. F - * are normally sorted in ascending item value order.0 z2 B$ A" T" T3 C
- *
t2 L! b. w5 E: J( x! t - * This function returns the pxOwner member of the first item in the list./ u6 e, y: @( p# n
- * The pxOwner parameter of a list item is a pointer to the object that owns- a" s& r& q# E
- * the list item. In the scheduler this is normally a task control block.
+ W( S/ V2 M. y7 |0 ] - * The pxOwner parameter effectively creates a two way link between the list2 T. Q2 M, {0 m% H
- * item and its owner.
! r7 W& ~& p0 z1 i3 J Z - *
E. a3 i; g; p" V9 Y( x* {8 N7 Y - * @param pxList The list from which the owner of the head item is to be: `( {+ ?* @9 L2 o, f4 `1 A& x* g
- * returned.' Z6 U: o% R) A
- *. R* g7 q9 D6 o9 Y
- * \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY$ f( m1 Z: ]. S* s
- * \ingroup LinkedList3 z; G" a: g% m9 V4 F
- */
- R2 `* I7 D+ g: D- J - #define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( ( &( ( pxList )->xListEnd ) )->pxNext->pvOwner )
7 [8 j* p+ b; T3 Q2 H0 i - x9 h: \6 u4 a8 Y% l2 B' f
- /*/ X: _6 F" u0 G# f: I
- * Check to see if a list item is within a list. The list item maintains a8 c) j" Q# }( M( Y' L% S* _
- * "container" pointer that points to the list it is in. All this macro does
0 _% \9 S" q: p8 P* V( K - * is check to see if the container and the list match.
6 o- O+ s% _# M' E - *
% s/ U5 p5 \6 [1 a" m3 M. } - * @param pxList The list we want to know if the list item is within.4 h% U9 ]& }' g8 e( @
- * @param pxListItem The list item we want to know if is in the list.
& P$ T- ? w1 g- z$ i - * @return pdTRUE if the list item is in the list, otherwise pdFALSE.
# {2 |/ h% b$ x8 c0 ` - */
! T8 T1 n- p7 n - #define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) )
: P( @+ I7 ^% z# r
5 B( s3 B% `. H1 r3 t2 [1 S+ t- /*
9 H; }/ \8 h- B7 e+ Y - * Return the list a list item is contained within (referenced from).
4 m4 Q$ t' `) x. b8 R - *% q& r/ U1 P# P- l( N
- * @param pxListItem The list item being queried.3 }' x. N" D+ ?
- * @return A pointer to the List_t object that references the pxListItem
3 B9 F4 b) J! [: J J* @ - */
+ u5 ?, H- `5 T, s7 W/ j! m. H - #define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer )
$ q6 c, C; e& |0 C* G; g+ m$ d( D
$ N9 e2 V- ^ p& ^! g/ u- /*
+ Q* W0 L: q! Z B% f" P) l - * This provides a crude means of knowing if a list has been initialised, as
+ p' b* l" A3 M) r: {; i% Y' M - * pxList->xListEnd.xItemValue is set to portMAX_DELAY by the vListInitialise(); r) p8 z6 R0 T
- * function.3 Y) V8 N; ?: b- M3 C2 m1 P* m" X
- */( G+ a5 P5 Y) n; @/ t
- #define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )# v0 ~+ _5 N9 L! I& R5 D
复制代码 ' V% a' Q9 I$ S% A5 q r
05. 列表相关函数; |/ q7 Z# S: W! k& o t2 W- h: W
5.1 初始化列表
- D" G: [- b' L8 ?8 N列表结构体中包含一个列表项成员,主要用于标记列表结束。初始化列表就是把这个列表项插入到列表中。
. C! u* w' o. M ^
+ ]( S R) Z5 @+ g( u1 f) e$ \$ p- void vListInitialise( List_t * const pxList )
2 F) W9 t1 g7 j) _+ M8 _3 w - {
% T5 x( i( t+ q: i8 s6 d - /*列表索引指向列表项*/
2 a- {- x1 \1 T - pxList->pxIndex = ( ListItem_t * )&( pxList->xListEnd ); 8 z, w% c; c j# n
- /* 设置为最大可能值 */0 Y. ^; x- u% `2 b8 @
- pxList->xListEnd.xItemValue =portMAX_DELAY;
, t2 f1 j/ E2 g- v% Y; w) E
% j. }% L2 a( l- /* 列表项xListEnd的pxNext和pxPrevious指针指向了它自己 */
/ j$ d- A3 N+ U+ q6 O. T$ F( ~ - pxList->xListEnd.pxNext = (ListItem_t * ) &( pxList->xListEnd );
# K2 U: q, o$ J$ O) u/ n$ f+ b. K - pxList->xListEnd.pxPrevious= ( ListItem_t * ) &( pxList->xListEnd );
7 \" l: f/ u ?, M+ ]+ G4 [3 k - pxList->uxNumberOfItems = ( UBaseType_t) 0U;: S0 _' _2 K! C5 X5 [
- : }' r& D; W T/ t$ h1 U% [
- /* 设置为已知值,用于检测列表数据是否完整*/
: v5 I2 ]. `- {# o% `' U, n - listSET_LIST_INTEGRITY_CHECK_1_VALUE(pxList );* y* }( g; G! l1 x( j
- listSET_LIST_INTEGRITY_CHECK_2_VALUE(pxList );' N2 H8 {# k- l: I/ R
- }. Z, y4 Y5 f) C) d( M4 l8 N
复制代码
! b% C& J, O2 e& M如果宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则使能列表项数据完整性检查,则宏listSET_LIST_INTEGRITY_CHECK_1_VALUE()和listSET_LIST_INTEGRITY_CHECK_2_VALUE被一个已知值代替,默认为0x5a5a(16位架构)或者0x5a5a5a5a(32位架构)。
; [+ W! @' W i6 @; Y, ]1 X( n; z7 e1 B x& T# W
2 n# N2 ~+ a2 B, I
: X# g' F& n! b8 r' C6 Y5.2 初始化列表项8 ^, v# K7 ~% D, z
列表项的初始比较简单,只要确保列表项不在任何列表中即可。
: b0 L$ t- C! A: v- _- void vListInitialiseItem( ListItem_t * const pxItem )7 t V* Z1 k+ F x# x3 h9 g5 P
- {) ?- j4 z( y+ M& V+ J1 D
- pxItem->pvContainer = NULL;* a) U- a: [! y. l
- 6 N! Z9 @1 v. s
- /*设置为已知值,用于检测列表项数据是否完整*/; D" o6 H+ Y6 s( x
- listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE(pxItem );
& B2 C3 e+ \6 p/ R' X9 i, A' g - listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE(pxItem );
- D3 l# C7 n7 Z) N4 [ - }
复制代码
O& m- c: D& |( q/ [4 M# W如果宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则使能列表项数据完整性检查,则宏listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE会被两个已知的数值代替,默认为0x5a5a(16位架构)或者0x5a5a5a5a(32位架构)。* x1 u/ h* z$ ]! t" y
, E& M, V) ]. g, `0 \' X5.3 列表项插入函数
% T: k3 T. k4 l5 t+ h每个列表项对象都有一个列表项值(xItemValue),通常是一个被跟踪的任务优先级或是一个调度事件的计数器值。调用API函数vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem)可以将pxNewListItem指向的列表项插入到pxList指向的列表中,列表项在列表的位置由pxNewListItem->xItemValue决定,按照升序排列。* s5 J9 p8 T% _; G( C
7 ]3 B+ n# f' G; x7 a7 f- void vListInsert( List_t * const pxList,( A# ^0 j, n; o/ U1 _5 m
- ListItem_t * const pxNewListItem )
/ T$ f2 ]% H9 N% U# C: x+ Y - {
1 ^2 u* ? p# S6 h* \# X - ListItem_t * pxIterator;% o, I# B8 i& E
- const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
1 O% I, D7 m L
; L- J, a8 r9 w Z8 [. p1 `! b- /* 检查列表和列表项数据的完整性,仅当configASSERT()定义时有效。*/
' }/ K$ O# e" M6 _ - /* Only effective when configASSERT() is also defined, these tests may catch, ?7 T! V4 m+ t0 L5 W V
- * the list data structures being overwritten in memory. They will not catch
Y1 J7 E& k: K9 F" H - * data errors caused by incorrect configuration or use of FreeRTOS. */
) A/ ?/ Q+ O1 ?. ^ - listTEST_LIST_INTEGRITY( pxList );& j- k. s5 Y! I r
- listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );, ?, {- v ~2 C
, T C" _* m9 J0 t4 @8 z- /* Insert the new list item into the list, sorted in xItemValue order.. ^) w" K" L& S* x2 T% J U/ n
- *6 ]9 T7 G; `0 s: d' j G T
- * If the list already contains a list item with the same item value then the- h+ N* i2 G4 E ~+ y1 N( e
- * new list item should be placed after it. This ensures that TCBs which are5 I" `: ? d' M
- * stored in ready lists (all of which have the same xItemValue value) get a7 r4 _' }6 H% i6 F( i) B
- * share of the CPU. However, if the xItemValue is the same as the back marker
% [0 {1 G4 }) `( e - * the iteration loop below will not end. Therefore the value is checked
- S6 N4 g) B! @( j4 A8 C - * first, and the algorithm slightly modified if necessary. */1 C% b" ?. s, S1 }, s& q
- /*将新的列表项插入到列表,根据xItemValue的值升序插入列表。*/
7 j+ r3 j5 k$ F5 |5 ?+ F5 Q! y - if( xValueOfInsertion == portMAX_DELAY )
7 O4 g' q: d8 B' {- x - {2 a. u- L6 F2 o! Q) m
- pxIterator = pxList->xListEnd.pxPrevious;
, Z) G/ u! _1 c0 S0 Q( e - }! H, N8 g. E% Y& k- [* V
- else5 W: p6 c$ U5 O7 P5 x j5 ^
- {
" G* c/ K8 K i) w4 D - /* *** NOTE ***********************************************************
. H6 R" i; q- ~& P7 [8 D - * If you find your application is crashing here then likely causes are6 d3 H0 C& }* v% V' I; y) R, b' Z
- * listed below. In addition see <a href="https://www.FreeRTOS.org/FAQHelp.html" target="_blank">https://www.FreeRTOS.org/FAQHelp.html</a> for
F/ v! R# F! Q1 z4 b - * more tips, and ensure configASSERT() is defined! ]: O* Z- X$ ?& ^$ a; c( [& \, q! |" |
- * <a href="https://www.FreeRTOS.org/a00110.html#configASSERT" target="_blank">https://www.FreeRTOS.org/a00110.html#configASSERT</a>: i4 v5 k* A6 a1 W
- *# ~! t. e5 Z: F7 u3 X: z$ u
- * 1) Stack overflow -
7 @' j( `( e. K6 q, H - * see <a href="https://www.FreeRTOS.org/Stacks-and-stack-overflow-checking.html" target="_blank">https://www.FreeRTOS.org/Stacks-and-stack-overflow-checking.html</a>) s' b& g" o8 _2 a1 g* a) o
- * 2) Incorrect interrupt priority assignment, especially on Cortex-M
$ G- ~2 D( F& A3 s, ~# K/ l - * parts where numerically high priority values denote low actual
- A3 M9 w* W; j$ g8 Q R - * interrupt priorities, which can seem counter intuitive. See, P7 o. l* A2 I' o* y
- * and the definition
' z' M0 g3 C8 \# @, [5 g4 \ w- e - * of configMAX_SYSCALL_INTERRUPT_PRIORITY on7 d# }7 p, Q; M" Z5 h5 |( K
- * _* R6 e% r: K( A
- * 3) Calling an API function from within a critical section or when
# I, q( p9 F5 J) j' W* a) M - * the scheduler is suspended, or calling an API function that does
8 {# n2 ^; M+ [, ~ - * not end in "FromISR" from an interrupt.$ b+ W( d0 `8 i2 B$ c, {0 c
- * 4) Using a queue or semaphore before it has been initialised or: v! g/ o+ p) K
- * before the scheduler has been started (are interrupts firing
2 M1 s2 m3 u' X8 C* s- @5 h+ q - * before vTaskStartScheduler() has been called?).
. f7 O& U+ ~" ?5 }" Q5 x! H - **********************************************************************/
6 R3 m! |2 X5 F7 f# {: |2 [. ]
0 H9 b5 B5 m3 C4 ^# [- //xListEnd表示列表的末尾
$ ^$ F- K7 W& ?+ Z6 k, E - for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */
" K; A' f6 }- d% u, _6 ?; @) s - {
8 j8 |! y& K" D9 g" @. Z - /* There is nothing to do here, just iterating to the wanted# ~) i; Z" j" T5 O
- * insertion position. */' P6 ^; n% Z9 ?8 X
- }& P$ i n4 ]9 Z; H* m
- }
% r: F7 h" r6 R" b( x
5 v9 q" d, S- U9 R7 @: u1 A- pxNewListItem->pxNext = pxIterator->pxNext;" u: {4 _+ S7 |0 O% Q7 j! y. J
- pxNewListItem->pxNext->pxPrevious = pxNewListItem;
A# w* ^0 t2 l3 |; ?8 I1 o - pxNewListItem->pxPrevious = pxIterator;
I! C) W( A0 |+ G" L! Z6 @ - pxIterator->pxNext = pxNewListItem;
& ~+ p. R4 [2 W% Z& G- o: s - 5 w! g* K$ E& f$ I( G
- /* Remember which list the item is in. This allows fast removal of the& p7 q4 e! f E5 M: \& u
- * item later. */
$ g9 a4 O. b- q3 l0 | - pxNewListItem->pxContainer = pxList;$ o8 _3 C4 Q! P a; ^
, `3 J* [0 e. w. p+ d3 Y- ( pxList->uxNumberOfItems )++;
: O! |/ Y" ?1 O0 i6 F - }
) @ {; J0 W W
复制代码 ]) p) h; L$ i' A1 T0 g6 v {
根据xItemValue的值将新的列表项插入到列表。如果列表中存在与新列表项xItemValue值相同的列表项,则新插入的列表项位于它之后。如果列表项的xItemValue值等于portMAX_DELAY(列表结束标记,我们在讲列表数据结构时,说到每个列表数据结构体中都有一个列表项成员xListEnd,用于标记列表结束。xListEnd.xItemValue被初始化为一个常数,其值与硬件架构相关,为0xFFFF或者0xFFFFFFFF。这个常数在移植层定义,即宏portMAX_DELAY),则表示到达了列表结束位置。
$ n1 p: g& w( j' N+ ?# I) B1 c2 v+ g( K* ^( L! \4 L8 L
5.4 列表项末尾插入
# P5 x4 [) W- S; ?" aAPI函数vListInsertEnd()是简单的将列表项插入到列表的末端。
" c( @% x; g* k, |
, x. Y- k x0 S- void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )9 O1 T( R8 l% A
- {6 ]! I9 o6 Z4 Z! y, Z; y6 w
- ListItem_t* const pxIndex = pxList->pxIndex;
S* b% t; F8 C9 s& x0 O
6 \- q( {/ W( c8 q- B- /*检查列表和列表项数据的完整性,仅当configASSERT()定义时有效。*/
0 o* }- s6 j( N - listTEST_LIST_INTEGRITY( pxList );
' y: s, J' G& O! [. F# ` - listTEST_LIST_ITEM_INTEGRITY(pxNewListItem );
9 w( k6 ~- O* ^7 b7 z- W
( i; F/ g8 X/ p- /*向列表中插入新的列表项*/' b+ L6 d& Z% {0 L
- pxNewListItem->pxNext = pxIndex;
/ w* w* K0 N: a, h, j* F" Y - pxNewListItem->pxPrevious =pxIndex->pxPrevious;
0 u8 ?/ Y- Z2 O% p: `! d: I. L) l - : {, q! Y( r# g, B1 L, [# [$ D
- mtCOVERAGE_TEST_DELAY();' s& q8 q3 S3 K% j" d, o0 `
- + [) p1 N, N8 J( I/ U
- pxIndex->pxPrevious->pxNext =pxNewListItem;( U4 v$ _) A1 j9 H9 ~& S
- pxIndex->pxPrevious = pxNewListItem;, D0 ?+ L) Q5 r- g
- 0 x# t7 K- t4 J: U$ D
- pxNewListItem->pvContainer = ( void* ) pxList;3 ?5 w- c0 F; F4 b+ \
8 x, O# F) z: G1 O- R+ ^7 ?/ j- O- ( pxList->uxNumberOfItems )++;$ p7 s# w9 u, K1 n
- }
复制代码 ) s3 e1 E. w% ^: G9 g, @5 K' C( u, A
5.5 列表项删除+ I) o8 f* W# Q7 C; I) i" P
列表项的删除只是将指定的列表项从列表中删除,并不会将这个列表项的内存给释放掉。
0 h/ M& G+ N: \: S) z& ]
; {- A3 |1 m, K$ e5 z" B- UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )+ X4 g+ e/ i W% `
- {
& {1 r' v5 `* b - /* The list item knows which list it is in. Obtain the list from the list( D2 j! _( v; M9 W4 P+ I) ~
- * item. */1 O/ g) f7 P# l/ p# M
- List_t * const pxList = pxItemToRemove->pxContainer;- y& \+ Z# x4 q5 z5 o, h
- 8 q" s+ j5 ], a* G' c
- pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
! ~2 H3 B; E& M$ j) D - pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;4 \: e" L" x# N- K; m Y
- 8 h3 u/ B6 a- }9 A3 l
- /* Only used during decision coverage testing. */" _3 Q* M' b" K
- mtCOVERAGE_TEST_DELAY();9 C( P- X7 K1 d
- 3 V- R2 t! x+ T8 Q0 V
- /* Make sure the index is left pointing to a valid item. */8 k1 Q* V1 U( F
- if( pxList->pxIndex == pxItemToRemove )
& a8 X/ y" {' V: R - {, R( U Z" u3 W7 O( B+ Z
- pxList->pxIndex = pxItemToRemove->pxPrevious;) k" k: O( z& [3 v: |
- }
: K+ Q1 |9 V4 K - else
1 P8 l9 n" r- B - {+ F& x: m3 N: P! ]0 O5 h
- mtCOVERAGE_TEST_MARKER();2 `. \- p t( q
- }1 y5 G/ i# l# b$ E2 @- v
9 u0 f ]1 _9 G9 a( x! m- pxItemToRemove->pxContainer = NULL;& T7 U4 o) A+ g6 F7 ?* |: ^8 P2 ~
- ( pxList->uxNumberOfItems )--;
4 l1 g( @7 z7 {$ t; y
, R: E& ]3 v8 `2 ~- return pxList->uxNumberOfItems;2 P. q( U* ?' A( C( [6 Y
- }
% q# ^+ u8 e0 {/ f+ s; G; Z
复制代码
+ B' a& M; Y; |5.6 列表遍历
; F @3 h5 z9 i( t- y* e; Z- #define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
6 G3 x) n0 F" z& u - { \; x, f; W, g5 E6 w# f' U
- List_t * const pxConstList = ( pxList ); \& ]" }3 \2 Q. F3 H5 A8 I
- /* Increment the index to the next item and return the item, ensuring */ \4 M% @+ e6 m, r9 c) L
- /* we don't return the marker used at the end of the list. */ \: f5 p- ?! Z8 i# G9 I/ A$ s3 x
- ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
6 H7 h. d2 Y3 v0 ^" x7 _ - if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \' f5 E1 O+ E, O
- { \/ A3 U( W3 v1 v5 g# Y) S% T, H
- ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
; {' A; u0 C. ~# _ - } \8 f1 y# d3 N* a/ y$ v* l0 B
- ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \- j: Z1 K- z# x5 q2 i/ m8 y$ M
- }
! L% S+ y! {9 w0 z( y, z) ^. g# t
- O, N ]6 M K$ |- u3 X6 x* ^2 m
复制代码 p" O" E1 N3 x8 @
$ Z( I: r5 _3 d% }8 w |