kernel list
list
linux内核中的list数据结构的用法和我们平时学习到的list是优点不同的,例如:
//平时学习遇到的
struct fox {
unsigned int fid;
unsigned int age;
struct fox *next, *prev;
};
//kernel list:
struct fox {//这里将 fox称为 list的owner
unsigned int fid;
unsigned int age;
struct list_head list;
};
struct list_head {
struct list_head *next, *prev;
};
这样做的好处是: struct list_head可以在多种自定义结构体中实现代码复用. 以下分析的均为 kernel list.
kernel list 如何找到owner对象的地址
在 linux/include/linux/list.h 中, 发现全部操作都是针对 list_head的, 但是我们知道了 list_head的地址, 怎么找到其对应的 owner的地址呢?因为平时使用时候都是用的owner的地址,而不是里边的list的地址!!
list_head作为 fox的一个成员, 当我们知道了 list_head的地址, 可以通过宏 list_entry 来获取它父结构的地址:
#define list_entry(ptr, type, member) container_of(ptr, type, member)
#define container_of(ptr, type, member) ({ \
const typeof( ((type*)0)->member ) *__mptr=(ptr);
(type*)( (char*)__mptr - offsetof(type, member));})
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
宏 offsetof:
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
/*
分解一下后半句: (size_t)&((TYPE*)0)->MEMBER, 按符号优先级进行拆分:
1. (TYPE*)0: 把0(地址,即 NULL)强转成TYPE*, 你把0换成NULL也是可以的.
2. ((TYPE*)0)->MEMBER: 在这个临时构建出的地址结构中(以NULL地址为首地址构建的), 找到MEMBER成员
3. 对找到的这个MEMBER成员取地址: &((TYPE*)0)->MEMBER
4. 把这个地址转成 size_t类型: (size_t)&((TYPE*)0)->MEMBER
可以简单理解为:在0地址的地方构建了一个临时的TYPE对象, 并通过返回MEMBER成员的地址来确定该MEMBER成员的地址距离0(TYPE对象的起始地址)的距离, 即Offset.
*/
宏 container_of:
#define container_of(ptr, type, member) ({ \
const typeof( ((type*)0)->member ) *__mptr=(ptr);
(type*)( (char*)__mptr - offsetof(type, member));})
/*
首先理解一下全貌, 可以看到ptr被转换成一个临时对象 __mptr, 并最终使用 __mptr来计算当前member距离type对象起始地址的offset, 即地址。 如果ptr是一个普通的ptr, 而不是表达式, 比如: ptr++, ++ptr等, 那么宏 container_of将改写为:
*/
#define container_of(ptr, type, member) ( (type*)( (char*)ptr - offsetof(type,member)) )
/*
ptr之所以要转换成char*, 因为地址是以字节为单位的, 而char的长度就是一个字节.
__mptr是结构体type中的list_head的地址, offsetof求的是member在type结构体中的偏移量, 那么 __mptr减去offsetof得到的就是 member的地址了!!
*/
list interface
创建链表
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
/**
* INIT_LIST_HEAD - Initialize a list_head structure
* @list: list_head structure to be initialized.
*
* Initializes the list_head to point to itself. If it is a list header,
* the result is an empty list.
*/
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
可以通过 LIST_HEAD(mylist)进行初始化一个链表, mylist的prev 和 next指针都指向自己。 这只是一个空链表而已, 并且不具有有效字段. 我们可以这样初始化一个含有有效字段的链表:
struct fox f = {
.id = 1,
.age = 2,
.list_head = LIST_HEAD_INIT(f.list_head)
};
这样就行了。
具体内核list代码:https://github.com/torvalds/linux/blob/master/include/linux/list.h 具体多余的分析:https://blog.csdn.net/wanshilun/article/details/79747710