【学习日记】【FreeRTOS】链表结构体及函数详解

写在前面

本文主要是对于 FreeRTOS 中链表相关内容的详细解释,代码大部分参考了野火FreeRTOS教程配套源码,作了一小部分修改。

一、结构体定义

主要包含三种结构体:

  1. 普通节点结构体
  2. 结尾节点(mini节点)结构体
  3. 链表结构体

1.普通节点结构体

一个完整的节点,包含五个参数:

  1. 节点的序号,用于确定节点在链表中的位置
  2. 前指针,指向本节点的前一个节点
  3. 后指针,指向本节点的下一个节点
  4. 本节点所属的内核对象,通常是TCB(Task Control Block)是FreeRTOS中用于管理任务的数据结构。
  5. 该节点所属的链表
  • 结构体定义如下:
/* 节点结构体定义 */
struct xLIST_ITEM
{
	TickType_t xItemValue;             /* 辅助值,用于帮助节点做顺序排列 */			
	struct xLIST_ITEM *  pxNext;       /* 指向链表下一个节点 */		
	struct xLIST_ITEM *  pxPrevious;   /* 指向链表前一个节点 */	
	void * pvOwner;					   /* 指向拥有该节点的内核对象,通常是TCB */
	void *  pvContainer;		       /* 指向该节点所在的链表 */
};
typedef struct xLIST_ITEM ListItem_t;  /* 节点数据类型重定义 */

2.结尾节点(mini节点)结构体

Mini节点没有所属内核和所属链表信息,主要被用作双向列表的结尾,只有三种信息:

  1. 节点序号值,标识节点在链表中的位置
  2. 前指针
  3. 后指针
  • 结构体定义如下:
/* mini节点结构体定义,作为双向链表的结尾
   因为双向链表是首尾相连的,头即是尾,尾即是头 */
struct xMINI_LIST_ITEM
{
	TickType_t xItemValue;                      /* 辅助值,用于帮助节点做升序排列 */
	struct xLIST_ITEM *  pxNext;                /* 指向链表下一个节点 */
	struct xLIST_ITEM *  pxPrevious;            /* 指向链表前一个节点 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;  /* 最小节点数据类型重定义 */

3.链表结构体

链表需要三种参数:

  1. 链表节点计数器,统计链表中的节点总数
  2. 链表节点索引指针,指向链表中目前要操作的节点
  3. 链表最后一个节点的指针
  • 结构体定义如下:
/* 链表结构体定义 */
typedef struct xLIST
{
	UBaseType_t uxNumberOfItems;    /* 链表节点计数器 */
	ListItem_t *  pxIndex;			/* 链表节点索引指针 */
	MiniListItem_t xListEnd;		/* 链表最后一个节点 */
} List_t;

二、链表基本函数定义

主要包括下面几种函数:

  1. 节点初始化函数
  2. 空链表初始化函数
  3. 将新节点插入到链表尾部的函数
  4. 将新节点按升序插入链表的函数
  5. 从链表中删除节点的函数

1.节点初始化函数

传入要初始化的节点,将该节点的所属链表参数设置为空即可。

  • 函数定义如下:
//节点初始化
void vListInitialiseItem(ListItem_t * const pxItem)
{
	pxItem->pvContainer = NULL;		//将该节点所在链表初始化为空,表示该节点还没有插入任何链表
}

2. 空链表初始化函数

需要的步骤如下:

  1. 将指针索引指向最后一个节点
  2. 最后一个节点的排序值设置为最大(取决于portmacro中portMAX_DELAY宏定义)
  3. 最后一个节点的前后指针都指向节点自身
  4. 链表节点计数器设置为 0
  • 代码如下:
//空链表初始化
void vListInitialise(List_t * const pxList)
{
	//指针索引指向最后一个节点,pxList->xListEnd是一个值,需要取地址后强制类型转换
	pxList->pxIndex = (ListItem_t *)&pxList->xListEnd;	
	
	//最后一个节点的辅助值设置为最大
	pxList->xListEnd.xItemValue = portMAX_DELAY;		
	
	//最后一个节点的前指针和后指针都指向节点自身,表示链表为空
	pxList->xListEnd.pxNext = (ListItem_t *)&pxList->xListEnd;
	pxList->xListEnd.pxPrevious = (ListItem_t *)&pxList->xListEnd;
	
	//初始化链表节点计数器为0,表示链表为空
	pxList->uxNumberOfItems = (UBaseType_t)0U;
}

3. 将新节点插入到链表尾部的函数

  • 需要的步骤如下:
  1. 拿出指向链表最后一个节点的指针
  2. 将新的节点插入到原来最后节点的前面
  3. 设置新插入节点所属的链表
  4. 链表节点计数器++
  • 代码如下:
//将节点插入到链表的尾部
void vListInsertEnd(List_t * const pxList, ListItem_t * const pxNewListItem)
{
	//拿出指向链表最后节点的指针
	ListItem_t * const pxIndex = pxList->pxIndex;
	
	//将新的节点插入到原来最后节点前面
	pxNewListItem->pxNext = pxIndex;
	pxNewListItem->pxPrevious = pxIndex->pxPrevious;
	pxIndex->pxPrevious->pxNext = pxNewListItem;
	pxIndex->pxPrevious = pxNewListItem;
	
	//设置插入的新节点所在的链表
	pxNewListItem->pvContainer = (void *)pxList;
	
	//链表节点计数器+1
	(pxList->uxNumberOfItems)++;
}

4. 将新节点按升序插入链表的函数

  • 需要的步骤如下:
  1. 定义迭代器(节点指针)用于查找插入的位置
  2. 获取新插入节点的序号
  3. 寻找节点插入的位置
  4. 进行节点插入操作
  5. 修改新插入的节点所在的链表
  6. 链表节点计数器++
  • 代码如下:
//将节点按照升序排列插入链表
void vListInsert(List_t * pxList, ListItem_t * const pxNewListItem)
{
	//1. pxIterator 用于查找插入位置
	ListItem_t * pxIterator;
	
	//2. 获取新插入节点的排序辅助值
	const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
	
	//3. 寻找节点要插入的位置
	if(xValueOfInsertion == portMAX_DELAY){	//如果是最大的节点,插入在链表的最后即可
		pxIterator = pxList->xListEnd.pxPrevious;
	}
	else{
		//for循环寻找插入位置
		//pxIterator = (ListItem_t *)&pxList->xListEnd 环状链表,尾就是头
		for(pxIterator = (ListItem_t *)&pxList->xListEnd; pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext);
	}
	
	//4. 插入新节点
	pxNewListItem->pxNext = pxIterator->pxNext;
	pxNewListItem->pxNext->pxPrevious = pxNewListItem;
	pxNewListItem->pxPrevious = pxIterator;
	pxIterator->pxNext = pxNewListItem;
	
	//5. 修改插入的新节点所在的链表
	pxNewListItem->pvContainer = (void *)pxList;
	
	//6. 链表节点计数器+1
	(pxList->uxNumberOfItems)++;
	
}

5. 从链表中删除节点的函数

  • 需要的步骤如下:
  1. 获取要删除的节点所在的链表
  2. 进行删除操作
  3. 如果链表索引指向要删除的节点,需更新索引使其指向要删除节点的前一个节点,确保索引指向有效
  4. 设置要删除节点的所属链表为空
  5. 链表节点计数器–
  6. 返回删除操作后链表中剩余的节点的数量
  • 代码如下:
//从链表中删除节点
UBaseType_t uxListRemove(ListItem_t * const pxItemToRemove)
{
	//1. 获取要删除的节点所在的列表
	List_t * const pxList = (List_t *)pxItemToRemove->pvContainer;
	
	//2. 将节点从链表中删除
	pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
	pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
	
	//3. 如果要删除的节点是目前链表的索引,就得把索引更新为要删除节点的前一个,确保索引指向有效的节点
	if(pxList->pxIndex == pxItemToRemove){
		pxList->pxIndex = pxItemToRemove->pxPrevious;
	}
	
	//4. 将要删除的节点所属链表设置为空,表示不属于任何链表
	pxItemToRemove->pvContainer = NULL;
	
	//5. 更新链表节点计数器
	(pxList->uxNumberOfItems)--;
	
	//6. 返回链表中剩余的节点数量
	return pxList->uxNumberOfItems;
}

三、宏定义

这些宏定义主要是为了方便链表操作:

/*
************************************************************************
*                                宏定义
************************************************************************
*/
/* 初始化节点的拥有者 */
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner )		( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
/* 获取节点拥有者 */
#define listGET_LIST_ITEM_OWNER( pxListItem )	( ( pxListItem )->pvOwner )

/* 初始化节点排序辅助值 */
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue )	( ( pxListItem )->xItemValue = ( xValue ) )

/* 获取节点排序辅助值 */
#define listGET_LIST_ITEM_VALUE( pxListItem )	( ( pxListItem )->xItemValue )

/* 获取链表根节点的节点计数器的值 */
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList )	( ( ( pxList )->xListEnd ).pxNext->xItemValue )

/* 获取链表的入口节点 */
#define listGET_HEAD_ENTRY( pxList )	( ( ( pxList )->xListEnd ).pxNext )

/* 获取链表的第一个节点 */
#define listGET_NEXT( pxListItem )	( ( pxListItem )->pxNext )

/* 获取链表的最后一个节点 */
#define listGET_END_MARKER( pxList )	( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )

/* 判断链表是否为空 */
#define listLIST_IS_EMPTY( pxList )	( ( BaseType_t ) ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) )

/* 获取链表的节点数 */
#define listCURRENT_LIST_LENGTH( pxList )	( ( pxList )->uxNumberOfItems )

/* 获取链表节点的OWNER,即TCB */
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )										\
{																							\
	List_t * const pxConstList = ( pxList );											    \
	/* 节点索引指向链表第一个节点调整节点索引指针,指向下一个节点,
    如果当前链表有N个节点,当第N次调用该函数时,pxInedex则指向第N个节点 */\
	( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;							\
	/* 当前链表为空 */                                                                       \
	if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )	\
	{																						\
		( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;						\
	}																						\
	/* 获取节点的OWNER,即TCB */                                                             \
	( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;											 \
}

#define listGET_OWNER_OF_HEAD_ENTRY( pxList )  ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner )

后记

如果您觉得本文写得不错,可以点个赞激励一下作者!
如果您发现本文的问题,欢迎在评论区或者私信共同探讨!
共勉!

### FreeRTOS TCB 结构体详解 #### 定义与基本概念 任务控制块 (Task Control Block, TCB) 是 FreeRTOS 中用于描述和管理任务的关键数据结构[^3]。该结构体保存了关于每个任务的重要信息,包括但不限于任务状态、优先级、堆栈指针等。 #### 主要成员变量及其功能 TCB 结构体中的主要成员变量如下: - **pxTopOfStack**: 指向当前任务的最高有效堆栈地址。此字段通常位于结构体的第一位置以优化访问效率并满足某些架构的需求[^4]。 - **pxStack**: 指向分配给任务的工作区起始处;即任务使用的私有堆栈区域起点。 - **pcTaskName**: 存储指向字符串的任务名称指针,便于调试时识别不同任务实例。 - **uxBasePriority / uxCurrentPriority**: 分别记录基础优先级和实际运行期间可能调整后的优先级值。 - **pxNextReadyListMember / pxPreviousReadyListMember**: 双向链接列表节点指针,允许内核维护就绪队列和其他调度相关链表。 - **eState**: 表明任务所处的具体生命周期阶段(如正在执行、挂起等待事件触发等)。 - **ulNotifiedValue**: 当启用通知机制时用来传递消息或信号量的一个无符号整数值。 - **ucNotifyState**: 描述接收通知的状态标志位集合。 针对特定平台特性还可能存在其他辅助性的属性项,比如 `pxEndOfStack` 字段就是根据不同硬件平台上栈增长方向来决定放置的位置及用途[^5]。 ```c typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */ { volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the stack. THIS MUST BE THE FIRST MEMBER OF THE STRUCT. */ #if( portUSING_MPU_WRAPPERS == 1 ) xMPU_SETTINGS x MPUSettings; /*< The MPU settings are defined as part of the port layer. */ #endif ListItem_t xStateListItem; /*< The list that the state list item has been added to describes the task state. */ ListItem_t xEventListItem; /*< Used to reference a pending event for this task. */ UBaseType_t uxPriority; /*< The priority of the task. This is actually saved as an index to the array of ready lists. */ StackType_t *pxStack; /*< Points to the start of the stack. */ #ifdef tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE uint8_t ucStaticallyAllocated; #endif char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ } tskTCB; ``` 上述代码展示了简化版的 TCB 结构定义,具体实现细节可能会依据配置选项有所变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__Witheart__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值