任务相关数据结构
任务控制块TCB_t
FreeRTOS 的每个任务都有⼀些属性需要存储,FreeRTOS 把这些属性集合到⼀起⽤⼀个结构体来表⽰,这个结构体叫做任务控制块:TCB_t,在使⽤函数 xTaskCreate()创建任务的时候就会⾃动的给每个任务分配⼀个任务控制块。
此结构体在文件 tasks.c 中有定义。类似于 Linux 的 task_struct 结构体,保存进程信息⽤的,每个进程有⼀个。
typedef struct tskTaskControlBlock
{
volatile StackType_t * pxTopOfStack; /* 指向任务栈上最后一个放置的项目的位置。这必须是TCB结构的第一个成员。 */
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSetting; /* MPU设置作为端口层的一部分定义。这必须是TCB结构的第二个成员。 */
#endif
ListItem_t xStateListItem; /* 任务的状态列表项所在的列表,表示任务的状态(就绪、阻塞、挂起)。 */
ListItem_t xEventListItem; /* 用于从事件列表中引用任务。 */
UBaseType_t uxPriority; /* 任务的优先级。0是最低优先级。 */
StackType_t * pxStack; /* 指向栈的起始位置。 */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务创建时赋予的描述性名称,仅用于调试。 */
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
StackType_t * pxEndOfStack; /* 指向栈的最高有效地址。 */
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting; /* 保存关键部分嵌套深度,用于不维护自己计数的端口。 */
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTCBNumber; /* 由系统分配(每创建一个任务,值增加一),分配任务的值都不同,用于调试 */
UBaseType_t uxTaskNumber; /* 由函数 vTaskSetTaskNumber()设置,用于调试 */
#endif
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority; /* 任务最后分配的优先级,用于优先级继承机制。 */
UBaseType_t uxMutexesHeld;/* 记录任务获取的互斥信号量数量 */
#endif
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
TaskHookFunction_t pxTaskTag;/* 用户可自定义任务的钩子函数用于调试 */
#endif
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];/* 保存任务独有的数据 */
#endif
#if ( configGENERATE_RUN_TIME_STATS == 1 )
uint32_t ulRunTimeCounter; /* 存储任务处于运行状态的时间。 */
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
struct _reent xNewLib_reent; /* 为特定任务分配一个Newlib reent结构。 */
#endif
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];/* 任务通知值 */
volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];/* 任务通知状态 */
#endif
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
uint8_t ucStaticallyAllocated; /* 如果任务是静态分配的,则设置为pdTRUE,以确保不会尝试释放内存。 */
#endif
#if ( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;/* 任务被中断延时标志 */
#endif
#if ( configUSE_POSIX_ERRNO == 1 )
int iTaskErrno;/* 用于 POSIX */
#endif
} tskTCB;
/* The old tskTCB name is maintained above then typedefed to the new TCB_t name
* below to enable the use of older kernel aware debuggers. */
typedef tskTCB TCB_t;
tskTCB
的结构体,用于FreeRTOS操作系统中的任务控制块(Task Control Block)。每个任务在系统中都有一个对应的 tskTCB
实例,用于存储任务的各种状态和属性。具体包括:
-
任务栈信息:指向任务栈的起始位置和栈顶位置。
-
任务状态:通过
xStateListItem
和xEventListItem
来记录任务的状态(如就绪、阻塞、挂起)。 -
任务优先级:通过
uxPriority
来记录任务的优先级。 -
任务名称:通过
pcTaskName
来记录任务的名称,主要用于调试。 -
其他配置信息:根据编译选项,可能还包括MPU设置、关键部分嵌套深度、互斥信号量、任务标签、线程局部存储、运行时间统计、任务通知等。
-
任务栈信息:
-
pxTopOfStack
:指向任务栈上最后一个放置的项目的位置。 -
pxStack
:指向栈的起始位置。 -
pxEndOfStack
(可选):指向栈的最高有效地址。
-
-
任务状态:
-
xStateListItem
:任务的状态列表项,用于表示任务的状态(如就绪、阻塞、挂起)。 -
xEventListItem
:用于从事件列表中引用任务。
-
-
任务优先级:
-
uxPriority
:任务的优先级,0是最低优先级。
-
-
任务名称:
-
pcTaskName
:任务创建时赋予的描述性名称,仅用于调试。
-
-
其他配置信息:
-
xMPUSetting
(可选):MPU设置。 -
uxCriticalNesting
(可选):保存关键部分嵌套深度。 -
uxTCBNumber
和uxTaskNumber
(可选):用于调试的任务编号。 -
uxBasePriority
和uxMutexesHeld
(可选):用于优先级继承机制。 -
pxTaskTag
(可选):用户可自定义的任务钩子函数。 -
pvThreadLocalStoragePointers
(可选):保存任务独有的数据。 -
ulRunTimeCounter
(可选):存储任务处于运行状态的时间。 -
xNewLib_reent
(可选):为特定任务分配一个Newlib reent结构。 -
ulNotifiedValue
和ucNotifyState
(可选):任务通知值和状态。 -
ucStaticallyAllocated
(可选):标记任务是否静态分配。 -
ucDelayAborted
(可选):任务被中断延时标志。 -
iTaskErrno
(可选):用于POSIX的错误码。
-
列表 xLIST
列表和列表项是 FreeRTOS 的⼀个数据结构,FreeRTOS ⼤量使⽤到了列表和列表项, 它是 FreeRTOS 的基⽯。
列表被⽤来跟踪 FreeRTOS 中的任务。与列表相关的全部东⻄都在文件 list.c 和 list.h 中。在 list.h 中定义了⼀个叫 List_t 的结构体,如下:
/**
* 定义结构体 xLIST 用于表示一个链表。
*
* 成员变量说明:
* - listFIRST_LIST_INTEGRITY_CHECK_VALUE:
* 当 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为 1 时,此字段将被设置为已知值,用于数据完整性检查。
* - uxNumberOfItems: 表示链表中当前项目的数量。
* - pxIndex: 用于遍历链表的指针,指向通过调用 listGET_OWNER_OF_NEXT_ENTRY() 返回的最后一个项目。
* - xListEnd: 包含最大可能项目值的链表项,这意味着它总是位于链表的末尾,因此用作标记。
* - listSECOND_LIST_INTEGRITY_CHECK_VALUE:
* 当 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为 1 时,此字段将被设置为已知值,用于数据完整性检查。
*/
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< 当 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为 1 时,此字段将被设置为已知值,用于数据完整性检查。 */
volatile UBaseType_t uxNumberOfItems;
ListItem_t * configLIST_VOLATILE pxIndex; /*< 用于遍历链表的指针,指向通过调用 listGET_OWNER_OF_NEXT_ENTRY() 返回的最后一个项目。 */
MiniListItem_t xListEnd; /*< 包含最大可能项目值的链表项,这意味着它总是位于链表的末尾,因此用作标记。 */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< 当 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为 1 时,此字段将被设置为已知值,用于数据完整性检查。 */
} List_t;
1. 在该结构体中, 包含了两个宏,分别为 listFIRST_LIST_INTEGRITY_CHECK_VALUE 和listSECOND_LIST_INTEGRITY_CHECK_VALUE,这两个宏用于存放确定已知常量, FreeRTOS通过检查这两个常量的值,来判断列表的数据在程序运行过程中,是否遭到破坏,类似这样的宏定义在列表项和迷你列表项中也有出现。该功能一般用于调试, 默认是不开启的,因此本教程暂不讨论这个功能。
2. 成员变量 uxNumberOfItems 用于记录列表中列表项的个数(不包含 xListEnd),当往列表中插入列表项时,该值加 1;当从列表中移除列表项时,该值减 1。
3. 成员变量 pxIndex 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项。
4. 成员变量 xListEnd 是一个迷你列表项, 列表中迷你列表项的值一般被设置为最大值,用于将列表中的所有列表项按升序排序时,排在最末尾;同时 xListEnd 也用于挂载其他插入到列表中的列表项。
列表的结构示意图,如下图所示:
列表项ListItem_t
列表项就是存放在列表中的项⽬,FreeRTOS 提供了两种列表项:列表项和迷你列表项。这 两个都在文件 list.h中有定义,先来看⼀下列表项,定义如下:
/**
* 结构体 xLIST_ITEM 用于表示链表中的一个节点。
*
* 成员变量说明:
* - xItemValue: 被列出的值。通常用于按降序对链表进行排序。
* - pxNext: 指向链表中下一个节点的指针。
* - pxPrevious: 指向链表中前一个节点的指针。
* - pvOwner: 指向包含该链表项的对象(通常是任务控制块TCB)的指针。因此,对象和链表项之间存在双向链接。
* - pxContainer: 指向该链表项所属的链表(如果有)的指针。
* - listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE 和 listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE:
* 当 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为 1 时,这些字段将被设置为已知值,用于数据完整性检查。
*/
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
void * pvOwner;
struct xLIST * configLIST_VOLATILE pxContainer;
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
};
/**
* 定义别名 ListItem_t 为结构体 xLIST_ITEM。
*
* 注:lint 工具要求将其定义为两个独立的声明。
*/
typedef struct xLIST_ITEM ListItem_t;ct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
1. 如同列表一样,列表项中也包含了两个用于检测列表项数据完整性的宏定义。
2. 成员变量 xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序。
3. 成员变量 pxNext 和 pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列
表项。
4. 成员变量 pxOwner 用于指向包含列表项的对象(通常是任务控制块),因此,列表项和包含列表项的对象之间存在双向链接。
5. 成员变量 pxContainer 用于指向列表项所在列表。
列表项的结构示意图,如下图所示:
迷你列表项MiniListItem_t
迷你列表项在文件 list.h 中有定义。
/**
* 定义结构体 xMINI_LIST_ITEM 用于表示一个简化版的链表项。
*
* 成员变量说明:
* - listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE:
* 当 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为 1 时,此字段将被设置为已知值,用于数据完整性检查。
* - xItemValue: 被列出的值。通常用于按降序对链表进行排序。
* - pxNext: 指向链表中下一个节点的指针。
* - pxPrevious: 指向链表中前一个节点的指针。
*/
struct xMINI_LIST_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;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
/**
* 定义别名 MiniListItem_t 为结构体 xMINI_LIST_ITEM。
*/
typedef struct xMINI_LIST_ITEM MiniListItem_t;
1. 迷你列表项中也同样包含用于检测列表项数据完整性的宏定义。
2. 成员变量 xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序。
3. 成员变量 pxNext 和 pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项。
4. 迷你列表项相比于列表项,因为只用于标记列表的末尾和挂载其他插入列表中的列表项,
因此不需要成员变量 pxOwner 和 pxContainer,以节省内存开销。
有些情况下我们不需要列表项这么全的功能,可能只需要其中的某⼏个成员变量,如果此时⽤列表项的话会造成内存浪费!比如上⾯列表结构体 List_t 中表⽰最后⼀个列表项的成员变量 xListEnd 就是MiniListItem_t 类型的。
迷你列表项的结构示意图,如下图所示:
不同状态任务定义
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ]; /*< Prioritised ready tasks. */
PRIVILEGED_DATA static List_t xDelayedTaskList1; /*< Delayed tasks. */
PRIVILEGED_DATA static List_t xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. */
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */
PRIVILEGED_DATA static List_t xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */
高优先级的任务可以像中断的抢占一样,抢占低优先级任务的 CPU 使用权;优先级相同的任务则各自轮流运行一段极短的时间(宏观角度),从而产生“同时”运行的错觉。
列表和列表项相关函数
FreeRTOS 中列表和列表项相关的 API 函数如下表所示:
函数 vListInitialise()
此函数用于初始化列表,在定义列表之后,需要先对其进行初始化, 只有初始化后的列表,才能够正常地被使用。列表初始化的过程,其实就是初始化列表中的成员变量。 函数原型如下所示:
void vListInitialise(List_t * const pxList);
void vListInitialise(
List_t * const pxList)
{
/* 初始化时,列表中只有 xListEnd,因此 pxIndex 指向 xListEnd */
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
/* xListEnd 的值初始化为最大值,用于列表项升序排序时,排在最后 */
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* 初始化时,列表中只有 xListEnd,因此上一个和下一个列表项都为 xListEnd 本身 */
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
/*初始化时,列表中的列表项数量为 0(不包含 xListEnd) */
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
/* 初始化用于检测列表数据完整性的校验值 */
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
函数 vListInitialise()初始化后的列表结构示意图, 如下图所示:
函数 vListInitialiseItem()
如同列表一样,在定义列表项之后,也需要先对其进行初始化,只有初始化有的列表项,才能够被正常地使用。列表项初始化的过程,也是初始化列表项中的成员变量。 函数原型如下所示:
void vListInitialiseItem(ListItem_t * const pxItem);
--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
void vListInitialiseItem(
ListItem_t * const pxItem)
{
/* 初始化时,列表项所在列表设为空 */
pxItem->pxContainer = NULL;
/* 初始化用于检测列表项数据完整性的校验值 */
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
这个函数比较简单,只需将列表项所在列表设置为空,以保证列表项不再任何一个列表项中即可。函数 vListInitialiseItem()初始化后的列表项结构示意图, 如下图所示:
函数 vListInsertEnd()
此函数用于将待插入列表的列表项插入到列表 pxIndex 指针指向列表项的前面,是一种无序的插入方法。 函数原型如下所示:
void vListInsertEnd(
List_t * const pxList,
ListItem_t * const pxNewListItem);
--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
void vListInsertEnd(
List_t * const pxList,
ListItem_t * const pxNewListItem)
{
/* 获取列表 pxIndex 指向的列表项 */
ListItem_t * const pxIndex = pxList->pxIndex;
/* 检查参数是否正确 */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* 更新待插入列表项的指针成员变量 */
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* 测试使用,不用理会 */
mtCOVERAGE_TEST_DELAY();
/* 更新列表中原本列表项的指针成员变量 */
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* 更新待插入列表项的所在列表成员变量 */
pxNewListItem->pxContainer = pxList;
/* 更新列表中列表项的数量 */
( pxList->uxNumberOfItems )++;
}
此函数就是将待插入的列表项插入到列表 pxIndex 指向列表项的前面,要注意的时, pxIndex 不一定指向 xListEnd,而是有可能指向列表中任意一个列表项。函数 vListInsertEnd()插入列表项后的列表结构示意图,如下图所示:
函数 vListInsert()
此函数用于将待插入列表的列表项按照列表项值升序排序的顺序,有序地插入到列表中。函数原型如下所示:
void vListInsert(List_t * const pxList,ListItem_t * const pxNewListItem);
void vListInsert(
List_t * const pxList,
ListItem_t * const pxNewListItem)
{
ListItem_t * pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
/* 检查参数是否正确 */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* 如果待插入列表项的值为最大值 */
if( xValueOfInsertion == portMAX_DELAY )
{
/* 插入的位置为列表 xListEnd 前面 */
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
/* 遍历列表中的列表项,找到插入的位置 */
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd );
pxIterator->pxNext->xItemValue <= xValueOfInsertion;
pxIterator = pxIterator->pxNext )
{
}
}
/* 将待插入的列表项插入指定位置 */
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
/* 更新待插入列表项所在列表 */
pxNewListItem->pxContainer = pxList;
/* 更新列表中列表项的数量 */
( pxList->uxNumberOfItems )++;
}
此函数在将待插入列表项插入列表之前,会前遍历列表,找到待插入列表项需要插入的位置。待插入列表项需要插入的位置是依照列表中列表项的值按照升序 排序确定的。函数 vListInsert()插入列表项后的列表结构示意图,如下图所示:
函数 uxListRemove()
此函数用于将列表项从列表项所在列表中移除,函数原型如下所示:
UBaseType_t uxListRemove(ListItem_t * const pxItemToRemove);
UBaseType_t uxListRemove(
ListItem_t * const pxItemToRemove)
{
List_t * const pxList = pxItemToRemove->pxContainer;
/* 从列表中移除列表项 */
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* 测试使用,不用理会 */
mtCOVERAGE_TEST_DELAY();
/* 如果 pxIndex 正指向待移除的列表项 */
if( pxList->pxIndex == pxItemToRemove )
{
/* pxIndex 指向上一个列表项 */
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 将待移除列表项的所在列表指针清空 */
pxItemToRemove->pxContainer = NULL;
/* 更新列表中列表项的数量 */
( pxList->uxNumberOfItems )--;
/* 返回列表项移除后列表中列表项的数量 */
return pxList->uxNumberOfItems;
}
函数 uxListRemove()移除后的列表项,依然于列表有着单向联系,即移除后列表项中用于指向上一个和下一个列表项的指针,依然指向列表中的列表项。 函数 uxListRemove()移除列表项后的列表结构示意图,如下图所示:
标签:FreeRTOS,LIST,列表,任务,用于,pxList,数据结构,INTEGRITY From: https://blog.csdn.net/weixin_52849254/article/details/143262449