原文地址:http://blog.csdn.net/ronliu/article/details/6425368
核心中,使用list_head作為鏈表,該資料結構本身即使head又是node。如果將它作為hash list的前端節點,那麼每個前端節點會佔用8個位元組。因此核心中對其最佳化,使用hlist_head作為hash表的前端節點。核心中定義其資料結構為:
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
注意到,這裡的pprev是一個二級指標。為什麼這樣設計呢,有什麼優點?
在中,pprev是指向前一節點next指標的指標。first是hlist_head的欄位,指向第一個節點。
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;
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
/*這裡修改的其實是它的前一節點的next值。無論該節點是不是前端節點*/
<<<<1
*pprev = next;
/*這裡修改它的後節點的pprev值,比較好理解。*/
if (next)
next->pprev = pprev;
}
如果hlist_node中使用的是單級指標,那麼在1處就需要判斷節點是否為前端節點。可以用n->prev是否被賦值,即是否為NULL來區分頭結點和普通節點。在add和delete時都很容易保證前端節點的prev為NULL。
比如這樣判斷:
struct my_hlist_node *next = n->next ;
struct my_hlist_node *prev = n->prev ;
if(n->prev)
n->prev->next = next ;
else
n->prev = NULL ;
if(next)
next->prev = prev ;
為什麼不這樣做?有兩個原因:
1. 代碼不夠簡潔優雅。使用hlist_node,頭結點和普通節點是一致的。
2. pprev有一個作用,在hlist_unhashed()中有體現。
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev; /*是否為NULL,被用來判斷節點是否加入到hash list中。*/
}