linux中的hlist分析应用

在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;
};

 可以看出,其都为双向链表。

双头(next,prev)的双链表对于Hash表来说“过于浪费”,因而另行设计了一套Hash表专用的hlist数据结构——单指针表头双循环链表,hlist的表头仅有一个指向首节点的指针,而没有指向尾节点的指针,这样在可能是海量的Hash表中存储的表头就能减少一半的空间消耗。

最终的链表形态如下所示

ps:1)此时与可以看到,最后一个节点的next域为空,不是循环双链表了。

     2)该图有问题,第一个节点的pprev域一个指向hlist_head->first的地址。

由于设计了这种结构,所以其双向链表的prev域设计也做了相应的改变,在如下文章内核数据结构:hlist_head解释了为何要这样做的原因。

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 ;

pprev有一个作用,在hlist_unhashed()中有体现。

static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev; /*是否为NULL,被用来判断节点是否加入到hash list中。*/
}


下面是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)

常用接口

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_del(struct hlist_node *entry);
原文地址:https://www.cnblogs.com/westfly/p/hlist_linux.html