漫谈Linux内核哈希表(2)

    对照前面介绍过的内核通知链、链表,本章我们将要介绍的哈希表的初始化和定义也是如出一辙的:

点击(此处)折叠或打开

  1. 定义并初始化一个名为name的哈希链表表头
  2. #define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }

  3. 初始化一个已经定义好的哈希链表,其中ptr指向哈希表头的地址
  4. #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
   
   其中,HLIST_HEAD_INIT
一般这么用:

点击(此处)折叠或打开

  1. struct hlist_head myhlist;
  2. HLIST_HEAD_INIT(&myhlist);

    对于哈希表中的每一个hlist_node节点,通常情况下都要调用初始化函数INIT_HLIST_NODE()来初始化:

点击(此处)折叠或打开

  1. static inline void INIT_HLIST_NODE(struct hlist_node *h)
  2. {
  3.     h->next = NULL;
  4.     h->pprev = NULL;
  5. }

    一个给定的哈希节点,判断它是否已经被插入到某条哈希链表里hlist_unhashed()

点击(此处)折叠或打开

  1. static inline int hlist_unhashed(const struct hlist_node *h)
  2. {
  3.     return !h->pprev;
  4. }
    这里我们可以看到, hlist_node 里的 pprev 完成了这个功能,即如果一个 hlist_node pprev NULL ,则说明该节点目前并未加入任何哈希链表。

   
下面这个接口就没啥好说的,用于判断是一个给定哈希表是否为空(即不包含任何哈希节点)。注意,该接口入参为 hlist_head 类型而非 hlist_node 类型:

点击(此处)折叠或打开

  1. static inline int hlist_empty(const struct hlist_head *h)
  2. {
  3.     return !h->first;
  4. }
    
   剩下的其他接口,也都非常简单,这里不再一一赘述。下面我们看几个宏定义:

点击(此处)折叠或打开

  1. #define hlist_entry(ptr, type, member) container_of(ptr,type,member)
  2. 该宏和前面介绍过的list_entry()的实现、作用完全一样
  3. #define list_entry(ptr, type, member)  container_of(ptr,type,member)
    
   对照list的学习过程,可想而知,下面这几组结构,其作用也就不言而喻了:
哈希表 链表
hlist_for_each(pos, head)
list_for_each(pos, head)
hlist_for_each_safe(pos, n, head)
list_for_each_safe(pos, n, head)
hlist_for_each_entry(tpos, pos, head, member)
list_for_each_entry(pos, head, member)
hlist_for_each_entry_safe(tpos, pos, n, head, member)
list_for_each_entry_safe(pos, n, head, member)

    区别在于最后两个宏的入参上有些小区别。由于哈希链表,表头和表节点是不同的数据结构,所以才会有这个差异。还是对照着list_for_each_*的学习过程:

点击(此处)折叠或打开

  1. hlist_for_each_entry(tpos, pos, head, member)
    其中tpos,是hlist_node所属宿主结构体类型的指针,pos是hlist_node类型的指针,tpos和pos都充当的游标的作用。例如:

点击(此处)折叠或打开

  1. typedef struct student
  2. {
  3.     char m_name[MAX_STRING_LEN];
  4.     char m_sex;
  5.     int m_age;
  6.     struct list_head m_list; /*把我们的学生对象组织成双向链表,就靠该节点了*/
  7.     struct hlist_node m_hlist; /*把我们的学生对象组织成哈希链表,就靠该节点了*/
  8. }Student;

  9. HLIST_HEAD(myhlist);
  10. Student *st;
  11. struct hlist_node *i;
  12. hlist_for_each_entry(st, i&myhlist, m_hlist)
  13. {
  14.     //To do something here…
  15.     //通常情况,开发者在这里仅需要关注、使用st变量就可以,不需要关心i
  16. }
    
   同样地,在使用hlist_for_each_entry_safe(tpos, pos, n, head, member)时,tpos也是宿主结构体类型的一个指针变量,当游标使用,n是一个hlist_node类型的另一个指针,这个指针指向pos所在元素的下一个元素,它由hlist_for_each_entry_safe()本身进行维护,开发者不用修改它

点击(此处)折叠或打开

  1. HLIST_HEAD(myhlist);
  2. Student *st;
  3. struct hlist_node *i,*j

你可能感兴趣的:(Linux)