一、链表
1.常用的链表结构
特点:每个链表的节点就是一个数据结构
struct common_fox {
unsigned long tail_length; //尾巴长度
unsigned long weight; //重量
bool is_fantastic; //这只动物奇妙吗?
struct common_fox *next;
struct common_fox *prev;
};
2.linux内核的链表结构
特点:链表的节点只是数据结构的一部分
优点:1.常用的带数据域的链表降低了链表的通用性,不容易扩展。linux内核定义的链表结构不带数据域,只需要两个指针完成链表的操作。将链表节点加入数据结构,具备非常高的扩展性,通用性。
2.每个fox节点都包含一个list_head指针,于是可以从任何一个节点起遍历链表。
struct list_head {
struct list_head *next;
struct list_head *prev;
};
struct linux_fox {
unsigned long tail_length;
unsigned long weight;
bool is_fantastic;
struct list_head list;
};
那么问题来了,其实一个指向链表结构的指针通常是无用的,我们需要的是一个指向包含list_head结构体的指针。以linux_fox为例,我们执行链表操作使用的是list_head类型的数据,但是信息都包含在linux_fox中,需要一种途径由list_head获得linux_fox。
使用宏container_of()我们可以很方便的从链表指针找到副结构中包含的任何变量。这是应为在c语言中,一个给定的结构体的变量偏移地址在编译时就被ABI固定下来了。
#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) );})
3.linux内核链表的操作函数
1)初始化头结点
通过宏定义#define LIST_HEAD(name)来实现头结点的初始化,linux内核中头结点一般只当做头结点不初始化数据。
LIST_HEAD(dog)等同于struct list_head dog; foo.next = &dog; dog.prev = &dog;
struct list_head {
struct list_head *next, *prev; //双向链表
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
2)向链表中添加一个节点
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
if (!__list_add_valid(new, prev, next)) //判断合法
return;
next->prev = new;
new->next = next;
new->prev = prev;
WRITE_ONCE(prev->next, new);
} 这个函数在prev和next间插入一个节点new。
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next); //添加元素
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head); //在尾部添加元素
}
3)从链表中删除一个节点
当执行删除操作时,被删节点的prev和next指针被指向一个固定的位置。其中,宏定义LIST_POISON1和LIST_POISON2定义在poison.h头文件中,是内核空间的两个地址。
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}