内核数据结构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))
版权声明:本文为博主原创文章,未经博主允许不得转载。