FreeRTOS 列表 List 源码解析


链表是 FreeRTOS 的核心数据结构,有关任务调度、延时、阻塞、事件等操作都是通过对链表进行操作进而实现的。本节将详细分析源码文件 list.clist.h 的内容,为后续的任务队列等的实现奠定基础。

一、链表及链表项的定义

FreeRTOS 使用的链表结构是环形的双向链表,而关于链表节点的数据结构都在 list.h 中定义。

1、链表节点数据结构 xList_ITEM

首先来看链表节点数据结构定义:

struct xLIST_ITEM
{
   
   
	// 第一个和最后一个成员值
	// 当 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 被使能的时候会被设定为一个固定值,用来检验一个列表项数据是否完整
    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;                                      // 类似侵入式链表,指向包含该链表项的对象的地址,通常是TCB 
    struct xLIST * configLIST_VOLATILE pxContainer;      // 指向该节点所在的链表
    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE             
};
typedef struct xLIST_ITEM ListItem_t;

其结构如下:

这里如果使用 configLIST_VOLATILE,其会被替换为 volatile 关键字。

#define configLIST_VOLATILE volatile

volatile 关键字是让编译器不对该变量进行优化,所谓的优化可以理解为其在生成汇编时,若多次读取该变量时其可能会将变量值放入寄存器中以加快读取速度,而不是真正的读取,这使得当某个变量会快速变化时,优化后“读取”的值并不是变量真实的值。当使用 volatile 关键字时,其会强迫编译器每次使用该变量时都真正的对它进行一次读取。

在链表项的结构体构造中值得注意的是 pvOwnerpxContainer 这两个成员变量。

  • pvOwner 指向该节点的拥有者,即该节点内嵌在哪个数据结构中,属于哪个数据结构的一个成员。它提供了一种可以快速访问由此链表项代表的对象的方法。
  • pxContainer 用于指向该节点所在的链表,通常指向链表的根节点。它则提供了一种快速访问其所属链表的方法。

这种处理方式大大提高了链表在任务调度等应用中的处理速度,提高系统效率。

侵入式链表


在 Linux 内核中有很多侵入式的链表的设计,比如在 Linux 中提供的链表项的定义为:

struct list_head
{
        struct list_head *next, *prev;
}

使用链表时只需要将其包含进定义的对象中即可:

struct list_head
{
         // 一些其它成员定义....
         struct list_head *next, *prev;
         // 一些其它成员定义....
}

在此它没有定义类似 ListItem_tpxContainer 这样的成员变量,其获得包含该链表项的对象地址是通过下面一段著名的宏定义实现的:

#define offsetof(s,m) (size_t)&(((s *)0)->m
#define container_of(ptr, type, member) \
({ \
         const typeof( ((type *)0)->member ) *__mptr = (ptr); \
         (type *)( (char *)__mptr - offsetof(type,member) ); \
})

使用实例:

struct node test;
struct list_head *list_item_add = &test.list_item;
struct node *test_add = container_of(list_item_add, struct node, list_item);

container_of() 的实现思路简单概括就是:将成员变量地址减去成员变量在结构体类型中的变量便是实例对象的存储地址。以 struct node 结构体为例,其实例 test 在内存中的存储方式如下图左侧所示。下图右侧给出了如何获得成员在结构体存储中的偏移量,当 &test=0x00 时,其成员的地址便是所需要的偏移量。

因此,offsetof() 宏,其所作的事就是获得偏移量。而 container_of() 宏中的:

(type *)( (char *)__mptr - offsetof(type,member) );

便是用成员地址减去偏移量来获得实例的地址。至于 container_of() 宏中的前一句:

const typeof( ((type *)0)->member ) *__mptr = (ptr);

实时上是起到一个类型检验的作用,拓展关键字 typeof 可以获得变量的类型,如果传入的ptr的类型与成员变量类型不符,那么编译器便会抛出警告,便于检查是否出错。注意 typeof 并不是标准 C 中的关键字,如果所用的编译器不支持,可以将第一句删除,将第二句中的__mptr 替换为 ptr,宏 container_of() 仍然是正确的。

2、链表精简节点结构 xMINI_LIST_ITEM

这个结构是专门用来在下面要讲的 xLIST 表示尾节点,相比于刚才讲到的 xList_ITEM 要精简不少。

struct xMINI_LIST_ITEM
{
   
   
    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE;           // 校验值
    configLIST_VOLATILE TickType_t xItemValue;			 // 辅助值,用于帮助节点做升序排列
    struct xLIST_ITEM * configLIST_VOLATILE pxNext;      // 指向下一个链表项
    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;  // 指向前一个链表项
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;

3、链表根节点结构 xLIST

typedef struct xLIST
{
   
   
    listFIRST_LIST_INTEGRITY_CHECK_VALUE       // 校验值
    volatile UBaseType_t uxNumberOfItems;      // 记录该链表里有多少成员(根节点除外)
    ListItem_t * configLIST_VOLATILE pxIndex;  // 链表节点索引指针
    MiniListItem_t xListEnd;                   // 链表尾部(实际也是链表的第一个节点),为节省内存使用Mini 链表项
    listSECOND_LIST_INTEGRITY_CHECK_VALUE      // 校验值
} List_t;

结构图如下:

概要:   DevCon 实用工具是一种命令行实用工具,可以替代设备管理器。使用 DevCon,您可以启用禁用、重新启动、更新、删除查询单个设备或一组设备。DevCon 提供与开发人员有关但无法在设备管理器中看到的信息。   您可以将 DevCon 用于 Windows 2000 、Windows XPWindows vista。不能将 Devcon 用于 Microsoft Windows 95、Windows 98、或 Windows Millennium Edition。   下载:http://download.microsoft.com/download/1/1/f/11f7dd10-272d-4cd2-896f-9ce67f3e0240/devcon.exe 用法及参数说明:   devcon.exe [-r] [-m:\\] [...]   -r 如果指定它,在命令完成后若需要则重新启动计算机。    是目标计算机的名称。    是将要执行的命令(如下所示)。   ... 是命令需要的一个或多个参数。   要获取关于某一特定命令的帮助,请键入:devcon.exe help   classfilter 允许修改类别筛选程序。   classes 列出所有设备安装类别。   disable 禁用与指定的硬件或实例 ID 匹配的设备。   driverfiles 列出针对设备安装的驱动程序文件。   drivernodes 列出设备的所有驱动程序节点。   enable 启用与指定的硬件或 实例 ID 匹配的设备。   find 查找与指定的硬件或 实例 ID 匹配的设备。   findall 查找设备,包括那些未显示的设备。   help 显示此信息。   hwids 列出设备的硬件 ID。   install 手动安装设备。   listclass 列出某一安装类别的所有设备。   reboot 重新启动本地计算机。   remove 删除与特定的硬件或 实例 ID 匹配的设备。   rescan 扫描以发现新的硬件。   resources 列出设备的硬件资源。   restart 重新启动与特定的硬件或 实例 ID 匹配的设备。   stack 列出预期的设备驱动程序堆栈。   status 列出设备的运行状态。   update 手动更新设备。   UpdateNI 手动更新设备,无用户提示   SetHwID 添加、删除更改根枚举设备的硬件 ID 的顺序。 示例:   devcon -m:\\test find pci\* 列出计算机 test 上的所有已知 PCI 设备。(通过使用 -m,您可以指定一个目标计算机。您必须使用“进程间通信”(IPC) 访问此计算机。)   devcon -r install Windows directory\Inf\Netloop.inf *MSLOOP 安装一个新的 Microsoft 环回适配器实例。这将创建一个新的根枚举设备节点,使用此节点您可以安装“虚拟设备”,如环回适配器。如果需要重新启动计算机,此命令还将以安静模式重启计算机。   devcon classes 列出所有已知的安装类别。输出结果包含短的未本地化的名称(例如,“USB”)描述性名称(例如,“通用串行总线控制器”)。 禁用启用网卡的步骤:   1.用devcon hwids PCI*命令得到所有以PCI开头的设备。这时会列出很多设备,那么哪个才是网卡对应的呢?   2.打开设备管理器,展开网络适配器,找到网卡的名称,然后记住到刚才得到的列表中找对应的Name,然后你会在下面看到好几个ID,随便挑一个就行   3.用devcon disable "PCI\VEN_11AB&DEV_4380&SUBSYS_301B17AA&REV_10"禁用网卡(启用的话讲disable换成enable就行了)   4.其实用PCI开头得到的几组设备中一般第一个就是网卡设备 sysdzw 16:01 2010-11-16
评论 25
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值