list.h 解析
内核数据结构list.h 解析
首先这个数据结构不同于我们我们设计的链表结构。
其实链表这种结构的各种操作就是改变链表的指针域而已,所以可以发现内核的链表结构在设计的时候其实只有指针域。
我的内核版本: 4.0.8-200.fc21.x86_6 此篇博客所有的源码都来自这个版本的内核。
节点的定义在./include/types.h 中
struct list_head { struct list_head *next,*prev; };其实就是:
struct list_head{ struct list_head *next; struct list_head *prev; };可以看到链表的核心其实就是指针域的改变。
下面是两个头结点的初始化宏。
#define LIST_HEAD_INIT(name) { &(name),&(name) } #define LIST_HEAD(name)\ struct list_head name = LIST_HEAD_INIT(name)这两句是源代码的头两个宏定义,作用是初始化一个链表节点的链接
例如申请一个变量head作为头结点
LIST_HEAD(head) 等价于struct list_head head = {&(head),&(head)};
就是对头节点的赋值了
同样有一个函数可以初始化
static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; }在已知的两个节点中插入一个节点,此处是已经知道这两个节点的情况:
static inline void __list_add(strcut list_head *new,strcut list_head *prev, strcut list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; }有了这个添加一个节点的函数,我们基本可以添加元素到任意位置了
例如添加一个节点进头结点之后
static inline void list_add_tail(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); }如果删除一个节点,其实本质上是两个节点之间的操作
static inline void list_del(struct list_head *prev,struct list_head *next) { next->prev = prev; prev->next = next; }如果需要删除一个具体存在的节点
static inline void __list_del_entry(struct list_head *entry) { __list_del(entry->prev,entry->next); }删除一个节点并且给相应的节点处理
static inline void list_del(struct list_head *entry) { __list_del(entry->prev,entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; }使用一个新的节点替换一个旧的节点
static inline void list_replace(struct list_head *old, struct list_head *new) { new->next = old->next; new->next->prev = new; new->prev = old->prev; new->prev->next = new; }
使用一个新的节点替换一个旧的节点并且将旧的节点置空
static inline void list_replace_init(struct list_head *old, struct list_head *new) { list_replace(old,new); INIT_LIST_HEAD(old); }
删除一个结点从链表中,并且重新初始化
static inline void list_del_init(struct list_head *entry) { __list_del_entry(entry); INIT_LIST_HEAD(entry); }
删除一个节点并且在这个位置上增加一个其它的节点
static inline void list_move(struct list_head *list, struct list_head *head) { __list_del_entry(list); list_add(list,head); }//删除一个节点的前一个节点,并且插入一个新节点
static inline void list_move_tail(struct list_head *list struct list_head *head) { __list_del_entry(list); list_add_tail(list,head); }/测试一个节点是不是其头节点
static inline int list_is_last(const struct list_head *list, const struct list_head *head) { return list->next == head; }
测试一个链表是不是空的
static inline int list_empty(const struct list_head *head) { return head->next == head; }
测试一个链表是否非空
static inline int list_empty_careful(const struct list_head *head) { struct list_head *next = head->next; return (next == head) && (next == head->next); }
反转一个节点
static inline void list_roate_left(struct list_head *head) { struct list_head *first; if(!list_empty(head)){ first = head->next; list_move_tail(first,head); } }
测试一个链表是不是仅仅只有一个节点
static inline int list_is_singular(const struct list_head *head) { return !list_empty(head) && (head->next == head->prev); }
链表分割函数一共有三个参数,list :新的链表头结点;head:裁剪剩下的链表
entry:包含在链表head内并且是开始裁剪的结尾
static inline void __list_cut_position(struct list_head *list, struct list_head *head,struct list_head *entry) { struct list_head *new_first = entry->next; list->next = head->next; list->next->prev = list; list->prev = entry; entry->next = list; head->next = new_first; new_first->prev = head; }
一个分割链表的实际操作
static inline void list_cut_position(struct list_head *list, struct list_head *head,struct list_head *entry) { if(list_empty(head)) return; if(list_is_singular(head) && (head->next != entry && head != entry)) return ; if(entry == head) INIT_LIST_HEAD(list); else __list_cut_position(list,head,entry); }合并操作,这里主要看下核心步骤,就是将list领头的这个链表插入到
prev 和 next 之间不包含list 节点
static inline void __list_splice(const struct listg_head *list, struct list_head *prev, struct list_head *next) struct list_head *first = list->next; struct list_head *last = list->prev; first->prev = prev; prev->next = first; last->next = next; next->prev = last; }
static inline void list_splice(const struct list_head *list, struct list_head *head) { if(!list_empty(list)){ __list_splice(list,head,head->next); } }@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@前@@@方@@@高@@@能@@@@@@@@@@@@@@@@@@@@@@@@@@@
链表的宏遍历再说这个之前,需要看几个必须的宏,不然,难以理解,
这是通过一个已经知道的指针地址,得到这个结构体的首地址,这样做封装性更好,适用范围更广。
#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)) ; })
offsetof 的代码还在下边
#define offsetof(type, member) ((unsigned int )&((type *)0)->member)
一般人看到这样的C代码肯定是一头雾水难以理解这是为什么,甚至根本看不明白是什么意思,毕竟是内核代码,看不懂也是正常的。首先第一个宏有三个参数 list_head * 指针,最终它会是这个链表节点的首地址,就可以通过访问它的其它成员变量了。 ptr 传入时的类型,也就是list_head * 这个结构体中 list_head 的变量名<其次是一个运算符 typeof 这是 C 语言的扩展。可以得到一个结构体的类型。((type *)0)->member ) * __mptr = (ptr);这一句,就是声明一个type 的member 类型的指针 __mptr 并且赋值ptr.
#define offsetof(type, member) ((unsigned int )&((type *)0)->member)
这个宏可以得到member 相对于结构体首地址的相对地址。
(type *)((char *)__mptr - offsetof(type, member)) ; })这一句,就可以得到这个结构体的首地址了,就是地址相减,用member的地址减去它相对于首地址的偏移量就是这个结构体的首地址。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
这里ptr是一个链表的头结点,这个宏就是取得这个链表第一个元素的所指结构体的首地址
#define list_first_entry(ptr,type,member) \ list_entry((ptr)->next,type,member)
判断一个链表是否为空,且取首地址。
#define list_first_entry_or_null(ptr,type,member) \ (!list_empty(ptr) ? list_first_entry(ptr,type,member):null)
也可以根据变量的next与prev来确定结构体的地址
#define list_prev_entry(pos,member) \ list_entry((pos)->member.prev,typeof(*(pos)),member) #define list_next_entry(pos,member) \ list_entry((pos)->member.prev,typeof(*(pos)),member)
链表的遍历
#define list_for_each(pos,head)\ for(pos = (head)->next; pos != (head);pos = pos->next)
遍历一遍链表,使用entry方法
#define list_for_each_entry(pos,head,member) \ for(pos = list_first_entry(head,typeof(*pos),member)\ &pos->member != (head) \ pos = list_next_entry(pos,member))
pos 表示结构体变量,head 是头结点,member 是list_head 在结构体当中的名字,这个函数就是如果Pos 为非空,那么pos就是其本身,否则在链表头强制扩充一个虚指针,为了list_for_each_entry_continue()做准备
#define list_prepare_entry(pos,head,member) \ ((pos) ? :list_entry(head,typeof(*pos),member))
可以指定链表的任意一个节点开始遍历
#define list_for_each_entry_continue(pos,head,member) \ for(pos = list_next_entry(pos,member); \ &pos->member != (head); \ pos = list_next_entry(pos,member))同上从前边遍历
#define list_for_each_continue_reverse(pos,head,member) \ for(pos = list_prev_entry(pos,member);\ &pos->member != (head); \ pos = list_prev_entry(pos,member)) #define list_safe_reset_next(pos,n,member) \ n = list_next_entry(pos,member)
内核哈希链表:
这个链表和我们平时常见的链表有很大的不同,哈希表的结构设计和普通的链表有很大的区别
首先哈希表的头结点和其它节点有很大的不同。
struct hlist_head{ struct hlist_node *first; }; struct hlist_node{ struct hlist_node *next,**prev; };
这个链表首先使用的是hlist_head 节点做头结点,然后使用hlist_node 做其它节点,最后一个节点指向NULL
其中**prev 指向的是前一个节点的next.就是指向自己。
如果画成图就是这个样子的。
初始化以及判断是否为空 #define HLIST_HEAD_INIT{ .first = NULL } #define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) static inline void INIT_LIST_NODE(struct hlist_node *h) { h->next = NULL; h->pprev = NULL; } static inline int hlist_unhashed(const struct hlist_node *n) { return !h->pprev; } static inline int hlist_empty(const struct hlist_head *h) { return !h->first; }
删除一个结点,看起来代码不多但是挺复杂的 static inline void __hlist_del(struct hlist_node *n) { struct hlist_node *next = n->next; //首先删除N节点,申请一个next 指向n的下一个节点; struct hlist_node **prev = n->pprev;//申请一个二重指针开始指向n, *pprev = next; //n前面的next指向了n后面的元素 if(next) //如果next非空,后一个元素赋值pprev next->pprev = pprev; }
添加节点一共有三个函数 第一个向链表头结点后边增加节点 static inline void hlist_add_head(struct hlist_node *n,struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; if(first) first->pprev = &n->next; //注意符号优先级,二级指针 h->first = n; n->pprev = &h->first; } //next must be != NULL,在next前边添加一个节点n static inline void hlist_add_before(struct hlist_node *n,struct hlist_node *next) { n->pprev = next->pprev; n->next = next; next->pprev = &n->next; *(n->pprev) = n; } //next must be != NULL ,在next后边添加一个节点n static inline void hlist_add_behind(struct hlist_node *n,struct hlist_node *prev) { n->next = prev->next; prev->next = n; n->pprev = &prev->next; if(n->next) n->next->pprev = &n->next; }
下面是hlist的宏遍历 #define hlist_entry(ptr,type,member) \ container_of(ptr,type,member) #define hlist_for_each(pos,head) \ for(pos = (head)->first;pos;pos= pos->next) //这是一个hlist的宏的安全遍历,其中添加了变量n,防止在遍历过程中 //发生断链的情况 #define hlist_for_each_safe(pos,n.head) \ for(pos = (head)->first;pos && ({n = pos->next;1;});\ pos = n) \
可能有人对pos && ({n = pos->next;1;})
这一句也不理解,这个for循环还是在判断pos这个东西是不是为空值于后边的部分,其实就是一个赋值操作,但是它必须返回1,
所以就写成<pre name="code" class="cpp" style="font-size:18px;"> ({n = pos->next;1;})
这个样子了,这个部分始终返回1.
#define hlist_entry_safe(ptr,type,member) \ ({typeof(ptr)___ptr = (ptr); \ ___ptr?hlist_entry(___ptr,type,member):NULL; \ })
遍历整个链表 #define hlist_for_each_entry(pos,head,member) \ for(pos = hlist_entry_safe((head)->first),typeof(*(pos)),member); \ pos;\ pos = hlist_entry_safe((pos)->member.next,typeof(*(pos)),member) #define hlist_for_each_entry_continue(pos, member)pos\ for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member);\ pos;for\ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) #define hlist_for_each_entry_from(pos, member)hlist_for_each_entry_from\ for (; pos;typeof\ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) #define hlist_for_each_entry_safe(pos, n, head, member) \ for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\ pos && ({ n = pos->member.next; 1; });pos\ pos = hlist_entry_safe(n, typeof(*pos), member))
版权声明:本文为博主原创文章,未经博主允许不得转载。