freeRTOS链表学习笔记

本文深入探讨了FreeRTOS操作系统中的链表实现原理及其在任务调度中的应用。详细介绍了链表结构体定义、初始化过程以及插入、删除等核心操作的具体实现方式。

freeRTOS链表学习笔记

链表是RTOS的核心之一,链表的作用主要服务于RTOS的任务之间的调度。任务的唤醒和休眠、任务的调度等都基于链表实现。阅读了freeRTOS链表的源码后,决定梳理一遍链表相关的源码,用于加深对链表的理解。其中对源码进行了一些删减,方便理解理解和阅读。文中的代码重新书写了一遍,可能和源码有些不一样。

1 头文件

头文件其中只涉及3个结构体,其中struct xMiniLIST_ITEM 是struct xLIST_ITEM 的简化版,则实际上只涉及2个结构体。

struct  xLIST_ITEM
{
	TickType_t xItemValue;		/*	项目的值 用于链表的排序	*/	
	struct xLIST_ITEM *pxNext; 	/*	双向链表指针	*/	
	struct xLIST_ITEM *pxPrevious;
	void* pvOwner; 				/* 链表下 所挂载的 内容 例如 TCB结构体。  */	
	void* pvContainer; 			/* 记录 位于哪个 链表 */	

};
typedef struct xLIST 
{
	UBaseType_t uxNumberOfItems;  /*	记录链表中挂在了多少项		*/	
	ListItem_t* pxIndex;		  /*	记录当前链表正在操作那一项  */
	MiniListItem_t xListEnd;	  /*	记录链表头尾指针 方便遍历	*/	
}List_t;

全部源码

#ifndef _LIST_H
#define _LIST_H

#define TickType_t  unsigned int
#define UBaseType_t unsigned int

#define portMAX_DELAY 0xFFFFFFFF

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;

/*
	为了节约内存 简化了 struct xLIST_ITEM 结构体 少了8个字节。
*/
struct  xMiniLIST_ITEM
{
	TickType_t xItemValue;  
	struct xLIST_ITEM *pxNext;
	struct xLIST_ITEM *pxPrevious;
};


typedef	struct xMiniLIST_ITEM MiniListItem_t;

typedef struct xLIST 
{
	UBaseType_t uxNumberOfItems;  /*	记录链表中挂在了多少项		*/	
	ListItem_t* pxIndex;		  /*	记录当前链表正在操作那一项  */
	MiniListItem_t xListEnd;	  /*	记录链表头尾指针 方便遍历	*/	

}List_t;

void xListInitialiseItem(ListItem_t* const pxItem);

void xListInitialise(List_t* const pxList);

void xListInsertEnd(List_t* const pxList, ListItem_t*const pxNewListItem);

void xListInsert(List_t* const pxList, ListItem_t* const pxNewListItem);

void uxListRemove(ListItem_t* const pxItemToRemove);

2 C文件

​ C文件中具体涉及操作也不是很多,主要有初始化、插入和删除操作。其中不太好理解的为xListInsertEndxListInsert ,这里需要结合RTOS的设计思想(实时性),注释中做了说明。

#include <stdio.h>
#include "list.h"
/*
	初始化一个链表项
*/
void xListInitialiseItem(ListItem_t* const pxItem)
{
	/*
		确保项目 没有 位于任何链表下
	*/
	pxItem->pvContainer = NULL;
}
/*
	初始化一个链表
*/
void xListInitialise(List_t* const pxList)
{
	/*
		链表的偏移量 指向自己 
	*/
	pxList->pxIndex = (ListItem_t* )pxList->xListEnd;
	/*
	此时链表项 数量为0
	*/
	pxList->uxNumberOfItems = 0;
	/*
		链表的链表项的项目值 为最大  这样排序的化 始终位于链表最后。
	*/
	pxList->xListEnd.xItemValue = portMAX_DELAY;
	/*
		初始化时,链表的链表项 的 前后指针 都指向自己的链表项。
	*/
	pxList->xListEnd.pxNext = (ListItem_t*)pxList->xListEnd;
	pxList->xListEnd.pxPrevious = (ListItem_t*)pxList->xListEnd;

}
/*
	在链表的最后插入新的一项。
	这里的最后,不是指的链表的最后一项。而是指链表最后能遍历到的一项。
	
	例如:
		当前链表中有 Item1 Item2 Item3 其中pxIndex指向Item2,此时插入到
	Item2的前面 Item1的后面,这样保证了最新插入的 最后执行。

	场景:
		相同优先的链表中,先插入先执行,后插入的后执行。
	保证了各个任务之间的“公平性”。

*/
void xListInsertEnd(List_t* const pxList, ListItem_t* const pxNewListItem)
{
	/*
		找到 pxList 链表 的偏移量 
	*/
	ListItem_t* pxIndex = pxList->pxIndex;

	/*
		新插入的项 下一项 指向pxIndex,前一项指向 pxIndex 的前一项。
		pxIndex 的前面一项的下一项 指向 新插入的一项。前一项指向新插入的一项。
	*/
	pxNewListItem->pxNext = pxIndex;
	pxNewListItem->pxPrevious = pxIndex->pxPrevious;

	pxIndex->pxPrevious->pxNext = pxNewListItem;
	pxIndex->pxPrevious = pxNewListItem;

	/*
		插入一项后,链表中的项目数量 加 1
	*/
	pxList->uxNumberOfItems++;
	/*
		记录 新插入的一项指向 属于这个链表。
	*/
	pxNewListItem->pvContainer = pxList;

}
/*
	按项目的值 从小到大排序 插入到链表中,如果值相同,则插入到后面。

	场景:
		在延时的链表中,项目值等于项目的 任务 延时的时间,延时时间久的最后执行,
		则放在最后面。
*/
void xListInsert(List_t* const pxList, ListItem_t* const pxNewListItem)
{
	ListItem_t* pxIterator;
	const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

	if (xValueOfInsertion == portMAX_DELAY)
	{
		xValueOfInsertion = pxList->xListEnd.xItemValue;
	}
	else
	{
		/*
			遍历找到 比xValueOfInsertion 大的项目。

			如果项目存在和当前插入的值相等的项目,则需要插入到该项目的后一项。

			pxIterator->pxNext->xItemValue <= xValueOfInsertion
		*/
		for (pxIterator = (ListItem_t*)&pxList->xListEnd;
			pxIterator->pxNext->xItemValue <= xValueOfInsertion;
			pxIterator = pxIterator->pxNext)
		{

		}

	}

	/*
		把pxNewListItem 插入到pxIterator的后面
	*/
	pxNewListItem->pxNext = pxIterator->pxNext;
	pxNewListItem->pxNext->pxPrevious = pxNewListItem;
	pxNewListItem->pxPrevious = pxIterator;
	pxIterator->pxNext = pxNewListItem;
	 
	pxList->uxNumberOfItems++;
	pxNewListItem->pvContainer = (void*)pxList;

}
/*
	从链表中的 某一项 删除 
*/
UBaseType_t int uxListRemove(ListItem_t* const pxItemToRemove)
{
	/*
		确定删除的项目属于 哪个链表。
	*/
	xLIST * const pxList = pxItemToRemove->pvContainer;

	/*
		从链表中移除 pxItemToRemove
	*/
	pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
	pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; 

	/*
		判断当前链表的偏移是否等于目前删除的项目 
		如果等于 则偏移量 移动到上次的偏移的位置。
	*/
	if (pxList->pxIndex == pxItemToRemove)
	{
		pxList->pxIndex = pxItemToRemove->pxPrevious;
	}
	/*
		清除删除项的链表记录信息。
	*/
	pxItemToRemove->pvContainer = NULL;
	
	/*
		链表中的项目减1	
	*/
	pxList->uxNumberOfItems--;

	return pxList->uxNumberOfItems;
}
 
```c
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in.  Obtain the list from the list
 * item. */
    List_t * const pxList = pxItemToRemove->pxContainer;

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

    /* Only used during decision coverage testing. */
    mtCOVERAGE_TEST_DELAY();

    /* Make sure the index is left pointing to a valid item. */
    if( pxList->pxIndex == pxItemToRemove)
	{
		pxList->pxIndex = pxItemToRemove->pxPrevious;
	}
	/*
		清除删除项的链表记录信息。
	*/
	pxItemToRemove->pvContainer = NULL;
	
	/*
		链表中的项目减1	
	*/
	pxList->uxNumberOfItems--;

	return pxList->uxNumberOfItems;
}

### FreeRTOS 源码学习笔记 #### 一、文件结构概述 FreeRTOS 的源代码被精心组织成多个目录,以便于理解和维护。主要的源文件位于 `Source` 目录下,该目录包含了核心调度器和其他重要的模块化组件。 - **tasks.c**: 实现任务管理功能的核心部分,包括创建、删除以及切换上下文等功能[^1]。 - **list.c 和 timers.c**: 提供链表操作函数和支持定时器服务的任务。 - **portable`: 这个子目录内含有针对不同架构实现的具体端口层代码,比如ARM Cortex-M3/M4系列微控制器对应的版本会在此处找到相应的汇编和C语言混合编程接口。 对于 STM32F103C8T6 平台而言,在移植过程中需要特别关注的是如何配置中断优先级以满足实时操作系统的要求,并确保启动文件正确初始化堆栈指针等寄存器。 #### 二、关键概念理解 要深入研究 FreeRTOS 源码,则需掌握以下几个重要知识点: - **任务(Task)**: 用户定义的工作单元;每个应用程序至少有一个默认IDLE Task负责当没有任何其他活动时进入低功耗模式。 - **队列(Queue)**: 用于进程间通信的数据结构之一,支持消息传递机制,允许发送者向接收方传送数据项而不必等待对方准备就绪即可继续执行下去。 - **信号量(Semaphore)**: 控制访问共享资源的一种同步原语,分为二进制型(仅限两个状态)与计数型两种形式来解决竞争条件问题。 通过上述这些基础构建块之间的协作配合实现了高效的多线程并发处理能力。 ```c // 创建一个新的任务并将其加入到就绪列表中去 BaseType_t xTaskCreate( pdTASK_CODE pvTaskCode, // 新建任务入口地址 const char *const pcName, // 名字字符串 uint16_t usStackDepth, // 堆栈大小 void *pvParameters, // 参数传递给新建任务 UBaseType_t uxPriority, // 设置优先级别 TaskHandle_t *pxCreatedTask // 返回句柄指向新建立的对象实例 ); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值