linux kernel 中的链表(二)
2016-04-06 21:09
561 查看
linux kernel 中的链表(二)
hlist 的定义
前文提到的linux/list.h,这个文件中实际包含了一个双向链表和一个哈希头,哈希链表的定义如下:
struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; };
hlist_head 是哈希头结点,里面只有一个指向链表结构的指针,表示哈希头下的第一个元素。
hlist_node 中有两个指针,next 指针比较好理解,就是指向下一个链表元素,但是 pprev 则比较难理解一点。
pprev 在使用中,是指向上一个结点的 next 指针(即指向指针的指针变量)。这样做的好处主要是为了保证结构体的通用性。如下所示:
hlist_head1 是一个哈希表数组,为了解决冲突,在数组中都会有一个 first 的指针,从它开始挂载各个冲突到同一个哈希值的元素。那么为了定义这些挂载的哈希元素,首先需要 next 指针做串联,然后需要一个 prev 指向他的前驱节点。那么问题来了,第一个挂载的元素,他的前驱节点是 hlist_head 和其他的节点的头不一致。为了解决这个问题,所有的 hlist_node 的前驱节点修改为
hlist_node **pprev,这样第一个节点也能赋值 pprev = &head->first 。
hlist 的基础操作
类似于普通的链表,hlist 也提供诸如新增,删除等操作,具体如下:/* * 判断类型函数 */ static inline int hlist_unhashed(const struct hlist_node *h) // 判断一个元素是否未在哈希表里 { return !h->pprev; } static inline int hlist_empty(const struct hlist_head *h) // 判断一个哈希桶下是否为空 { return !h->first; } /* * 基础操作 */ static inline void hlist_del_init(struct hlist_node *n); // 从hlist中删除一个节点,并初始化它 static inline void hlist_del(struct hlist_node *n); static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h); static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next); static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *next); static inline void hlist_move_list(struct hlist_head *old, struct hlist_head *new); // 以上是一些顾名思义的操作了,包括删除节点,从头新增,在某个节点前新增,在某个节点后新增,移动一个节点
以其中删除节点为例子进行分析:
/* * 这里的*pprev = next;体现出了 struct 中的设计的优越性 * 一句话屏蔽了对于前驱节点的判断,因为 head 和 node 的下一个节点的指针类型都是 hlist_node */ static inline void __hlist_del(struct hlist_node *n) { struct hlist_node *next = n->next; struct hlist_node **pprev = n->pprev; *pprev = next; //前驱节点的下一个节点修改为后继节点 if (next) next->pprev = pprev; //后继节点的上一个节点修改为前驱节点 } static inline void hlist_del(struct hlist_node *n) { __hlist_del(n); n->next = LIST_POISON1; n->pprev = LIST_POISON2; }
这里有两个宏定义:
poison.h:#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA) poison.h:#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
只是一个访问之后会引起页错误的特殊地址,以下为.h里的原文:
/* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */
常用宏
hlist 中的宏和 list 基本是一样的,也是包括初始化和枚举:初始化:
#define HLIST_HEAD_INIT { .first = NULL } #define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
枚举宏:
#define hlist_entry(ptr, type, member) container_of(ptr,type,member) /** * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @tpos: the type * to use as a loop cursor. * @pos: the &struct hlist_node to use as a loop cursor. * @n: another &struct hlist_node to use as temporary storage * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ for (pos = (head)->first; \ pos && ({ n = pos->next; 1; }) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = n)
这里的 tpos 是指自己的外包 struct 指针,pos 是用来循环的当前 hlist_node 指针,n为下一个链表元素(其实是个临时变量),head 就是 hlist_head 类型的指针了,member 为自己的结构体中对于链表的命名。
下一篇来写一些demo和使用心得,还有把里面比较细节的宏和函数做一些学习和总结吧(container_of 真是个牛逼东西)。
相关文章推荐
- Linux Kernel 4.0 RC5 发布!
- [C/C++]反转链表
- C#实现基于链表的内存记事本实例
- C语言实现带头结点的链表的创建、查找、插入、删除操作
- C++实现简单的学生管理系统
- Linux内核链表实现过程
- C++链表倒序实现方法
- C#通过链表实现队列的方法
- C#实现的简单链表类实例
- 找出链表倒数第n个节点元素的二个方法
- Java数据结构之简单链表的定义与实现方法示例
- C语言单循环链表的表示与实现实例详解
- C++实现的链表类实例
- PHP小教程之实现链表
- PHP中模拟链表和链表的基本操作示例
- C语言双向链表的表示与实现实例详解
- js链表操作(实例讲解)
- C语言实现输出链表中倒数第k个节点
- C++语言实现线性表之链表实例
- STL list链表的用法详细解析