内核数据结构
Linux内核实现了一些通用数据结构,并倡导大家在开发内核时使用。
链表
Linux内核的标准链表采用双向环形链表。
如果需要随机访问数据,一般不使用链表。使用链表存放数据的理想情况是,需要遍历所有数据或者需要动态地加入或删除数据。
Linux内核中的实现
在Linux内核链表中,不是在链表结构中包含数据,而是在数据结构中包含链表节点。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define list_entry(ptr, type, member) container_of(ptr, type, member)
在上例中的用法为
struct fox f={...};
struct fox *f_=container_of(&f.list,struct fox,list);
struct fox *f_=container_of(&f.tail_length,struct fox,tail_length);
struct fox *f_=container_of(&f.weight,struct fox,weight);
struct fox *f_=container_of(&f.weight,struct fox,weight);
声明和初始化
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
Linux 内核通过头指针的next是否指向自己来判断链表是否为空
所有list_head构成一个链表,有一个链表头部head,但是链表头部head没有对应的结构体成员,head->next=第一个链表项,head->prev=最后一个链表项。因此,判断链表是否为空,只需要看整个链表是不是只有一个head字段或者说head->next是不是指向自己。
插入
static inline void __list_add(struct list_head *newhead,struct list_head *prev,struct list_head *next){
newhead->next=prev->next
newhead->prev=next->prev
p
rev->next=newhead
next->prev=newhead
}
用法:
//插入到循环链表头部
static inline void list_add(struct list_head *newhead, struct list_head *head){
__list_add(newhead,head,head->next)
}
//插入到循环链表尾部
static inline void list_add_tail(struct list_head *newhead, struct list_head *head){
__list_add(newhead,head->prev,head)
}
删除
static inline void __list_del(struct list_head *prev,struct list_head *next){
prev->next=next;
next->prev=prev;
}
用法
//删除链表中的一个节点,除头部之外
static inline void list_del(struct list_head *entry){
__list_del(entry->prev,entry->next);
}
其他:从链表中删除一个节点并对其重新初始化list_del_init(struct list_head *entry)
移动和合并链表节点
函数 list_move(struct list_head *list,struct list_head *head)
从一个链表中移除list项,然后将其加入到一个链表的head节点后面;
函数 list_splice(struct list_head *list,struct list_head *head)
将list指向的链表插入到指定链表的head元素之后;
函数 list_splice_init(struct list_head *list,struct list_head *head)
和 list_splice
一样,唯一不同的是list指向的链表要被重新初始化
遍历链表