參考自:https://blog.csdn.net/zhanglei4214/article/details/6767288
一、hlist結構簡介
hlist_head 和 hlist_node 是位於linux內核中的數據結構,其設計初衷主要是為了減少Hash表的內存消耗。
struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; };
其內存結構如下:
hlist_head 結構體僅僅有一個first指針.
hlist_node 結構體有兩個指針,next 和 ppre。其中next指針指向下一個hlist_node,如果該節點為最后一個一個節點,那么next指向NULL。
這樣設計的原因在於:通常我們在使用Hash表是為實現快速查找,那么Hash表通常會維護一張相對較大的數組,否則的會帶來較大的沖突,而一張較大的數組必然會帶來較大的內存消耗。我們知道通常一個Hash表中每一個dentry(每一個具體數元素)會使用兩個指針,有其中一個指向尾節點,而我們知道在Hash表中的list通常是非常短(如果過長,說明沖突過多),那么顯然這個指向尾節點的指向我們就不那么的必要,Hash表中的每一個denry只用保存一個指針。這樣的設計顯然會造成鏈表中節點數據結構不一致,如果hlist_node采用的next和pre指針,那么對於第一個節點和其他節點操作必然會使不一致。hlist_node巧妙地將pprev指向上一個節點的next指針的地址,由於hlist_head和hlist_node指向的下一個節點的指針類型相同,這樣就解決了通用性。
二、常用接口
(1)hlist_head 和 hlist_node初始化
#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_HLIST_NODE(struct hlist_node *h) { h->next = NULL; h->pprev = NULL; }
(2)添加節點
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; }
(3)刪除節點
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; }
(4)遍歷節點
#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))