Linux内核hlist数据结构分析
在内核编程中哈希链表hlist使用非常多,比方在openvswitch中流表的存储中就使用了(见[1])。hlist的表头仅有一个指向首节点的指针。而没有指向尾节点的指针,这样在有非常多个buckets的HASH表中存储的表头就能降低一半的空间消耗。
和hlist相关的数据结构例如以下,桶中存储的 hlist_head 是具有同样hash值的entry构成的链表。每一个entry包括一个 hlist_node 成员,通过它链入到这个哈希链表中。
struct hlist_head {
struct hlist_node
* first;
};
//next指向下一个节点
// pprev指向前一个节点的next域
struct hlist_node {
struct hlist_node
* next, ** pprev;
};
结构图为:
因为头结点和其它节点的类型不一致。这样就不能使用普通的prev指针指向前一个节点(否则处理的时候还要讨论是否是第一个节点,没有通用性),这里设计者的巧妙之处就是pprev指针指向前一个节点的next,统一了兴许全部的节点。
一些实用的宏:
//头结点初始化
#define HLIST_HEAD_INIT { .first
= NULL }
//构造一个名为name的头结点
#define HLIST_HEAD(name) struct hlist_head
name = { .first = NULL }
//初始化头指针,链表指针
#define INIT_HLIST_HEAD(ptr) ((ptr)->first
= NULL)
#define INIT_HLIST_NODE(ptr) ((ptr)->next
= NULL, (ptr)->pprev = NULL)
1.删除节点
next得到当前节点的下一个节点。pprev是前一个节点的next字段的地址,那么*pprev就指向的是当前这个节点,那么 *pprev=next
就把当前节点更新为下一个节点了。假设n不是最后一个节点,还要设置next->pprev.
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;
}
2.插入节点
(1)头插入:让插入的节点成为链表的第一个节点,依次更新对应的指针。示意图例如以下。
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;
}
(2)在已知节点next之前/之后插入,通过自己绘图。非常easy理解清楚。
/* next must be != NULL */
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;
}
static inline void hlist_add_after (struct hlist_node
*n,
struct hlist_node
*next)
{
next-> next =
n-> next;
n-> next =
next;
next-> pprev =
&n-> next;
if(next-> next)
next-> next-> pprev
= &next-> next;
}
3.通过看一个节点h的pprev是否为空。推断其是否在哈希链表中。
static inline int hlist_unhashed (const struct hlist_node
*h)
{
return !h-> pprev;
}
4.哈希链表的遍历(iterate)相关代码
//通过一个字段member的地址 ptr。得到包括它的容器的地址
#define hlist_entry(ptr, type, member)
container_of(ptr,type,member)
//用 pos作为游标来遍历这个链表, prefetch是数据预取
#define hlist_for_each(pos, head)
\
for (pos
= (head)->first; pos && ({ prefetch(pos->next); 1; }); \
pos = pos->next)
#define hlist_for_each_safe(pos,
n, head) \
for (pos
= (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
//通用的哈希链表遍历,当中 pos指向当前节点。 tpos指向的包括hlist_node的当前结构体的指针
#define hlist_for_each_entry(tpos,
pos, head, member) \
for (pos
= (head)->first; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof (*tpos),
member); 1;}); \
pos = pos->next)
/**
* hlist_for_each_entry_continue - iterate over a hlist continuing
after existing point
* @ tpos: the type * to use as a loop counter.
* @ pos: the &struct hlist_node
to use as a loop counter.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_continue(tpos,
pos, member) \
for (pos
= (pos)->next; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof (*tpos),
member); 1;}); \
pos = pos->next)
/**
* hlist_for_each_entry_from - iterate over a hlist continuing from
existing point
* @ tpos: the type * to use as a loop counter.
* @ pos: the &struct hlist_node
to use as a loop counter.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_from(tpos,
pos, member) \
for (;
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof (*tpos),
member); 1;}); \
pos = pos->next)
/**
* 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 counter.
* @ pos: the &struct hlist_node
to use as a loop counter.
* @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)