FreeRTOS相关:linux中的侵入式链表设计
相关博客FreeRTOS解析:List
Linux内核中也有侵入式的链表的设计,在Linux中提供的链表项的定义为
struct list_head
{
struct list_head *next, *prev;
};
使用链表时只需要将其包含进定义的对象中即可
struct node
{
// 一些其它成员定义....
char i;
// 侵入式链表项
struct list_head list_item;
// 一些其它成员定义....
char j;
};
在此它没有定义类似ListItem_t中pxContainer这样的成员变量,其获得包含该链表项的对象地址是通过一段巧妙的宏定义实现的
#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) ); \
})
各输入参数的含义为
-
ptr:结构体实例成员地址。
-
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);
// test_add即为包含list_item_add指向的链表项的对象地址
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()仍然是正确的。
个人认为,FreeRTOS并未借鉴这一设计的一个原因可能是出于系统实时性的考虑,毕竟这样的操作是需要耗费一定时间的,而FreeRTOS内核中需要频繁使用这一功能,这样的设计必定会降低内核效率。另外FreeRTOS的目标平台一般运行速度相对较低,使得内核效率降低更为明显。