linux内核中的散列表

原理

1、概念

根据设定的哈希函数H(key)和处理冲突的方法将一组关键字映射到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“像”作为记录在表中的存储位置,这种表便称为哈希表或散列表,这一映像过程称为哈希造表或散列,所得存储位置称哈希地址或散列地址。

2、哈希函数的构造方法

1)直接定址法

2)数字分析法

3)平方取中法

4)折叠法

5)除留余数法

6)随机数法

实际工作中需视不同的情况采用不同的哈希函数。通常,考虑的因素有:

1)计算哈希函数所需时间(包括硬件指令的因素)

2)关键字的长度

3)哈希表的大小

4)关键字的分布情况

5)记录的查找频率

3、处理冲突的方法

1)开放定址法

2)再哈希法

3)链地址法

4)建立一个公共缓冲区

4、哈希表的查找

在哈希表上进行查找的过程和哈希造表的过程基本一致。给定K值,根据造表时设定的哈希函数求得哈希地址,若表中此位置上没有记录,则查找不成功;否则比较关键字,若和给定值相等,则查找成功;否则根据造表时设定的处理冲突的方法找“下一地址”,直到哈希表中某个位置为“空”或者表中所填记录的关键字等于给定值时为止。

5、装填因子

哈希表的装填因子定义为:

a = 表中填入的记录数 / 哈希表的长度

a标志哈希表的装满程度。直观地看,a越小,发生冲突的可能性就越小;反之,a越大,表中已填入的记录越多,再填记录时,发生冲突的可能性就越大,则查找时,给定值需与之比较的关键字的个数也就越多。


linux内核中的散列表


在处理记录的冲突时,linux内核使用的方法为链地址法,即将所有关键字为同义词的记录存储在同一线性链表中。

下面分析下,内核针对散列链表实现了哪些操作(所有实现均在源文件inlcude/linux/list.h中)。

1、定义

  1. /* 
  2.  * Double linked lists with a single pointer list head. 
  3.  * Mostly useful for hash tables where the two pointer list head is 
  4.  * too wasteful. 
  5.  * You lose the ability to access the tail in O(1). 
  6.  */  
  7. /* 散列表头结点,即散列链表 */  
  8. struct hlist_head {  
  9.     struct hlist_node *first; /* 指向hlist链表的第一个节点 */  
  10. };  
  11.   
  12. /* 散列表节点 */  
  13. struct hlist_node {  
  14.     /* next: 指向下一个节点 
  15.      * pprev: 指向前一个节点的next域,则*pprev就代表前一个节点的下一个节点的地址(即当前节点) 
  16.      *        当前节点: pprev == 前一个节点: &next 
  17.      *        当前节点: *pprev == 前一个节点: next 
  18.      *        当前节点: **pprev == 前一个节点: *next 
  19.      */  
  20.     struct hlist_node *next, **pprev;  
  21. };  
每一个头节点后面可接若干个节点。

2、声明与初始化

  1. /* 散列表头节点的声明与初始化 */  
  2. #define HLIST_HEAD_INIT { .first = NULL }  
  3. #define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL } /* 静态初始化 */  
  4. #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)  /* 动态初始化 */  
  5.   
  6. /* 散列表节点的初始化 
  7.  * 该函数一般用在删除节点之后对节点的操作中 
  8.  */  
  9. static inline void INIT_HLIST_NODE(struct hlist_node *h)  
  10. {  
  11.     h->next = NULL;  
  12.     h->pprev = NULL;  
  13. }  

3、节点是否存在

  1. /* 判断节点h是否在散列表中 
  2.  * 若不在,则返回1 
  3.  * 否则,返回0 
  4.  */  
  5. static inline int hlist_unhashed(const struct hlist_node *h)  
  6. {  
  7.     return !h->pprev;  
  8. }  

4、判空

  1. /* 判断散列链表h是否为空  
  2.  * 为空,则返回1 
  3.  * 否则,返回0 
  4.  */  
  5. static inline int hlist_empty(const struct hlist_head *h)  
  6. {  
  7.     return !h->first;  
  8. }  

5、删除节点

  1. static inline void __hlist_del(struct hlist_node *n)  
  2. {  
  3.     struct hlist_node *next = n->next;  
  4.     struct hlist_node **pprev = n->pprev;  
  5.     *pprev = next; /* 将节点n的前一个节点的next域设置为n的下一个节点的地址 */  
  6.     if (next)  
  7.         /* 将节点n的下一个节点的pprev域设置为节点n的前一个节点的next域的地址 */  
  8.         next->pprev = pprev;   
  9. }  
  10.   
  11. /* 将节点n从散列表中删除,再次访问该节点将会产生页错误 */  
  12. static inline void hlist_del(struct hlist_node *n)  
  13. {  
  14.     __hlist_del(n);  
  15.     n->next = LIST_POISON1;  
  16.     n->pprev = LIST_POISON2;  
  17. }  
  18.   
  19. /* 将节点n从散列表中删除,然后将其初始化为空节点 */  
  20. static inline void hlist_del_init(struct hlist_node *n)  
  21. {  
  22.     if (!hlist_unhashed(n)) { /* 判断节点是否存在散列链表中 */  
  23.         __hlist_del(n);  
  24.         INIT_HLIST_NODE(n);  
  25.     }  
  26. }  

6、插入节点

  1. /* 将节点n插入到头节点h之后*/  
  2. static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)  
  3. {  
  4.     struct hlist_node *first = h->first;  
  5.     n->next = first;  
  6.     if (first)  
  7.         first->pprev = &n->next;  
  8.     h->first = n;  
  9.     n->pprev = &h->first;  
  10. }  
  11.   
  12. /* 将节点n插入到节点next之前,next在散列表中 */  
  13. /* next must be != NULL */  
  14. static inline void hlist_add_before(struct hlist_node *n,  
  15.                     struct hlist_node *next)  
  16. {  
  17.     n->pprev = next->pprev;  
  18.     n->next = next;  
  19.     next->pprev = &n->next;  
  20.     *(n->pprev) = n;  
  21. }  
  22.   
  23. /* 将节点next插入到节点n之后,n在散列表中 */  
  24. static inline void hlist_add_after(struct hlist_node *n,  
  25.                     struct hlist_node *next)  
  26. {  
  27.     next->next = n->next;  
  28.     n->next = next;  
  29.     next->pprev = &n->next;  
  30.   
  31.     if(next->next)  
  32.         next->next->pprev  = &next->next;  
  33. }  

7、移动散列链表

  1. /* 
  2.  * Move a list from one list head to another. Fixup the pprev 
  3.  * reference of the first entry if it exists. 
  4.  */  
  5. /* 将一个散列链表的头节点用new节点代替,将以前的头节点old删除 */  
  6. static inline void hlist_move_list(struct hlist_head *old,  
  7.                    struct hlist_head *new)  
  8. {  
  9.     new->first = old->first;  
  10.     if (new->first)  
  11.         new->first->pprev = &new->first;  
  12.     old->first = NULL;  
  13. }  

8、遍历

1)一个重要的宏

  1. /* 通过成员指针获得整个结构体的指针  
  2.  *  
  3.  * ptr: 指向该数据结构中hlist_head/hlist_node成员的指针 
  4.  * type: 该数据结构的类型 
  5.  * member: 该数据结构类型定义中hlist_head/hlist_node成员的变量名 
  6.  */  
  7. #define hlist_entry(ptr, type, member) container_of(ptr,type,member)  

2)从头节点开始遍历

  1. /* 从头节点开始遍历散列链表  
  2.  * 遍历时,不可删除pos节点 
  3.  *  
  4.  * pos: 辅助指针(struct hlist_node *类型),用于链表遍历 
  5.  * head: 散列链表头节点(struct hlist_head *类型) 
  6.  */  
  7. #define hlist_for_each(pos, head) \  
  8.     for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \  
  9.          pos = pos->next)  
  10.   
  11. /* 从头节点开始遍历散列链表  
  12.  * 遍历时,会暂存pos节点的下一个节点,可以删除pos节点 
  13.  *  
  14.  * pos: 辅助指针(struct hlist_node *类型),用于链表遍历 
  15.  * n: 与pos同类的指针,用于暂存pos节点的下一个节点 
  16.  * head: 散列链表头节点(struct hlist_head *类型) 
  17.  */  
  18. #define hlist_for_each_safe(pos, n, head) \  
  19.     /* pos && ({ n = pos->next; 1; }): 
  20.      * 若pos为真,则会执行后一条复合语句,取得pos节点的下一个节点 
  21.      * 该复合语句永远为真 
  22.      */  
  23.     for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \  
  24.          pos = n)  
  1. /** 
  2.  * hlist_for_each_entry - iterate over list of given type 
  3.  * @tpos:   the type * to use as a loop cursor. 
  4.  * @pos:    the &struct hlist_node to use as a loop cursor. 
  5.  * @head:   the head for your list. 
  6.  * @member: the name of the hlist_node within the struct. 
  7.  */  
  8. /* 从链表头开始遍历散列链表  
  9.  * 
  10.  * tpos: 包含struct hlist_node型变量的数据类型指针,一般为结构体 
  11.  * pos: 辅助指针,struct hlist_node *类型 
  12.  * head: 散列链表头节点 
  13.  * member: 包含在tpos数据类型中的struct hlist_node成员名称 
  14.  */  
  15. #define hlist_for_each_entry(tpos, pos, head, member)            \  
  16.     for (pos = (head)->first;                     \  
  17.          pos && ({ prefetch(pos->next); 1;}) &&           \  
  18.         ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \  
  19.          pos = pos->next)  
  20.          
  21. /** 
  22.  * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry 
  23.  * @tpos:   the type * to use as a loop cursor. 
  24.  * @pos:    the &struct hlist_node to use as a loop cursor. 
  25.  * @n:      another &struct hlist_node to use as temporary storage 
  26.  * @head:   the head for your list. 
  27.  * @member: the name of the hlist_node within the struct. 
  28.  */  
  29. /* 从链表头开始遍历散列链表  
  30.  * 遍历时,会暂存pos节点的下一个节点 
  31.  * 
  32.  * tpos: 包含struct hlist_node型变量的数据类型指针,一般为结构体 
  33.  * pos: 辅助指针,struct hlist_node *类型 
  34.  * n: 辅助指针,struct hlist_node *类型,暂存pos节点的下一个节点 
  35.  * head: 散列链表头节点 
  36.  * member: 包含在tpos数据类型中的struct hlist_node成员名称 
  37.  */  
  38. #define hlist_for_each_entry_safe(tpos, pos, n, head, member)        \  
  39.     for (pos = (head)->first;                     \  
  40.          pos && ({ n = pos->next; 1; }) &&                \  
  41.         ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \  
  42.          pos = n)  
3)从当前节点的下一个节点开始遍历散列链表
  1. /** 
  2.  * hlist_for_each_entry_continue - iterate over a hlist continuing after current point 
  3.  * @tpos:   the type * to use as a loop cursor. 
  4.  * @pos:    the &struct hlist_node to use as a loop cursor. 
  5.  * @member: the name of the hlist_node within the struct. 
  6.  */  
  7. /* 从当前节点的下一个节点开始遍历散列链表 
  8.  *  
  9.  * tpos: 包含struct hlist_node型变量的数据类型指针,一般为结构体 
  10.  * pos: struct hlist_node *类型,从pos节点的下一个节点开始遍历 
  11.  * member: 包含在tpos数据类型中的struct hlist_node成员名称 
  12.  */  
  13. #define hlist_for_each_entry_continue(tpos, pos, member)         \  
  14.     for (pos = (pos)->next;                       \  
  15.          pos && ({ prefetch(pos->next); 1;}) &&           \  
  16.         ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \  
  17.          pos = pos->next)  
4)从当前节点开始遍历散列链表
  1. /** 
  2.  * hlist_for_each_entry_from - iterate over a hlist continuing from current point 
  3.  * @tpos:   the type * to use as a loop cursor. 
  4.  * @pos:    the &struct hlist_node to use as a loop cursor. 
  5.  * @member: the name of the hlist_node within the struct. 
  6.  */  
  7. /* 从当前节点开始遍历散列链表 
  8.  *  
  9.  * tpos: 包含struct hlist_node型变量的数据类型指针,一般为结构体 
  10.  * pos: struct hlist_node *类型,从pos节点开始遍历 
  11.  * member: 包含在tpos数据类型中的struct hlist_node成员名称 
  12.  */  
  13. #define hlist_for_each_entry_from(tpos, pos, member)             \  
  14.     for (; pos && ({ prefetch(pos->next); 1;}) &&             \  
  15.         ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \  
  16.          pos = pos->next)  



内核版本:2.6.32

参考:

1、《数据结构(C语言版)[严蔚敏]》

2、Linux内核哈希表分析与应用


阅读更多
换一批

没有更多推荐了,返回首页