內核數據結構--哈希鏈表

Linux內核中,除了有通用了雙向鏈表list,還有通用的哈希鏈表hlist。後者定義與前者有些不同。因爲通常一個哈希表的表頭要佔用很大空間,而如果每個表頭都用一個雙向鏈表來做的話,就顯得太浪費了。只用一個指針可以實現相同的功能,並且可以節省一半的表頭存儲空間。

雙向鏈表定義如下:

struct list_head {
struct list_head *next, *prev;
};

哈希鏈表定義如下:

struct hlist_head {
    struct hlist_node *first;
}
 
struct hlist_node {
    struct hlist_node *next, **pprev;
}

在Linux內核的 hlist_node 結構中不使用通常的 prev 指針,而使用二級指針 pprev,是因爲對每一個哈希表的表項來說,它並不組成循環的鏈表,這樣就不能方便地用統一的形式操作。在雙向鏈表中,表頭和節點是同一個數據結構,直接用 prev 沒有問題。而在 hlist 中,表頭沒有 prev,也沒有 next,只有一個 first。爲了能統一地修改表頭的 first 指針,即表頭的 first 指針必須能夠被引用修改,hlist 就設計了 pprev,從而在表頭插入的操作可以通過一致的“*(node->pprev)”訪問和修改前節點的next(或first)指針。

下面是hlist中常用的幾個宏:

#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 INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)

下面只列出hlist_add_before操作函數,其他hlist鏈表操作函數操作方法類似。這個函數中的參數next不能爲空。它在next前面加入了n節點。函數的實現與list中對應函數類似。

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_add_before(struct hlist_node *n,struct hlist_node *next){
         n->pprev = next->pprev;
         n->next = next;
         next->pprev = &n->next;
          *(n->pprev) = n;
}

#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
pos = pos->next)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章