linux内核数据结构---hash表

关于linux内核链表请参考“linux内核数据结构---链表”。

链表虽然是最常见的数据结构,但实际使用中,由于链表的检索能力较差,更多的是作为队列和栈结构使用,如果需要查询,比如通过pid查找进程,通过描述符查找inode,就需要用到检索更快的数据结构——Hash表
先来看Hash节点的定义:

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

其中 hlist_head 为头结点,与链表不同的是还需要一个节点的概念 hlist_node,他们之间的关系如图:


左侧是定长数组,每个节点是 hlist_head ,其中first指向 hlist_node 节点,hlist_node又组成一列链表,hlist_node的链表结构跟 list_head不同之处在于 hlist_node 的 pprev 指向了 前一个节点的指针地址。
Hash表使用起来也非常简单,首先通过hash函数计算目标值,得到一个索引,在hlist_head数组中找到相应位置,再插入链表。Hash的链表Linux也提供了多种操作方式:

#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h);
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_move_list(struct hlist_head *old,
		struct hlist_head *new);

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

INIT_HLIST_HEAD —— 初始化链表;
hlist_add_head —— 在链表头插入节点;
hlist_add_before —— 在一个节点之前插入;
hlist_add_after —— 在一个节点之后插入;
hlist_entry —— 同list_entry;
hlist_for_each —— 相关的一系列函数进行链表的遍历,与普通双向链表操作相同。
除了Hash表中链表的操作,还有一个重要的元素是Hash函数,Hash函数的好坏直接影响Hash表的性能,Hash函数一般来讲跟具体要实现的业务相关,include/linux/jhash.h下实现了几个不同用途的Hash函数,另外,Linux还给出一个简单的Hash函数:

static inline u32 hash_32(u32 val, unsigned int bits)
{
        /* On some cpus multiply is faster, on others gcc will do shifts */
        u32 hash = val * GOLDEN_RATIO_PRIME_32;

        /* High bits are more random, so use them. */
        return hash >> (32 - bits);
}

这个函数把32位整数Hash成bits位的整数,另外还有 hash_64 等等Hash函数。
最后再来通过一个简单的例子看一下Hash表的使用:

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/hash.h>

struct simple_hash
{
    int data;
    struct hlist_node node;
};

struct hlist_head * phash = NULL;

static int __init initialization(void)
{
    int i,k;
    struct hlist_head * phead;
    struct hlist_node * pnode;
    struct simple_hash * p;

    printk(KERN_INFO " init simple start\n");

    phash = (struct hlist_head*)kmalloc(sizeof(struct hlist_head) * 0xFF, GFP_KERNEL);
    for (i = 0; i < 0xFF; i++) {
        INIT_HLIST_HEAD(&phash[i]);
    }
    for (i = 0; i < 10; i++) {
        p = (struct simple_hash*)kmalloc(sizeof(struct simple_hash), GFP_KERNEL);
        k = i * 13;

        p->data = k;
        INIT_HLIST_NODE(&p->node);

        printk(KERN_INFO "insert %d\n", k);
        phead = &phash[hash_32(k, 8)];
        hlist_add_head(&p->node, phead);
    }
    k = 3 * 13;
    phead = &phash[hash_32(k, 8)];
    printk(KERN_INFO "search %d\n", k);
    hlist_for_each_entry(p, pnode, phead, node) {
        if (p->data == k) {
            printk(KERN_INFO " find it\n");
        }
    }

    printk(KERN_INFO "init simple end\n");
    return 0;
}

static void __exit cleanup(void)
{
    int i;
    struct hlist_head * phead = NULL;
    struct simple_hash * p = NULL;
    printk(KERN_INFO "cleanup simple\n");

    if (phash == NULL) {
        return;
    }

    for (i = 0; i < 0xFF; i++) {
        phead = &phash[i];
        while (!hlist_empty(phead)) {
            p = hlist_entry(phead->first, struct simple_hash, node);
            printk(KERN_INFO "delete %d", p->data);
            hlist_del(&p->node);
            kfree(p);
        }
    }
    kfree(phead);
}

module_init(initialization);
module_exit(cleanup);

MODULE_AUTHOR("cppbreak cppbreak@gmail.com");
MODULE_DESCRIPTION("A simple linux kernel module");
MODULE_VERSION("V0.1");
MODULE_LICENSE("Dual BSD/GPL");
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值