LINUX中哈希表的原理与应用

1. 哈希表的定义

linux-5.11.6\include/linux/types.h    //linux中关于哈希表结构体的定义
struct hlist_head {
	struct hlist_node *first;
};

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

整个哈希表结构如下图所示,其中ppre是个二级指针,它指向前一个节点的第一个指针变量,例如node1的ppre指向mylist的first指针,node2的ppre指向node1的next指针。

 2. 哈希表的声明和初始化、节点增加、遍历等

 (1)哈希表的声明和初始化
linux-5.11.6\include\linux\list.h
//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)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
	h->next = NULL;
	h->pprev = NULL;
}

(2)在哈希表中增加节点
在内核代码list.h中增加节点的函数为:
//把一个哈希链表的节点插入到哈希链表的头节点的后边
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_behind(struct hlist_node *n,
				    struct hlist_node *prev)
static inline void hlist_add_fake(struct hlist_node *n)

(3)遍历哈希表
linux-5.11.6\include\linux\list.h
#define hlist_for_each(pos, head) \
	for (pos = (head)->first; pos ; pos = pos->next)

头插法增加节点参考下图:

        hlist_add_before作用是把一个节点插入到一个哈希链表的节点的前边,首先把将要插入的节点的pprev成员变量指向next的前边的节点,要插入的节点的next指向下一个节点,然后next节点的pprev就要指向已经插入的节点的next节点的地址,已经插入的节点的pprev指向的前一个节点的值就要变成已经插入节点的地址

static inline void hlist_add_before(struct hlist_node *n,
					struct hlist_node *next)
{
	n->pprev = next->pprev;
	n->next = next;
	next->pprev = &n->next;
	*(n->pprev) = n;
}

内核驱动代码如下:

// hashlist.c
// Created by linux on 2020/9/25.
//
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/list.h>

#define N 10
//数字链表
struct numlist{
    struct hlist_head hlistHead;
};
//数字链表节点
struct numnode{
    int num;
    struct hlist_node hlistNode;
};
struct  numlist nhead;
struct  numnode nnode;

static int __init hlist_init(void){
    //init head node
    struct hlist_node *pos;
    struct numnode *listnode;
    int i;
    struct numnode *p;

    printk("hashlist is starting...\n");
    //初始化头节点
    INIT_HLIST_HEAD(&nhead.hlistHead);

    for ( i = 0; i < N; ++i) {
        listnode = (struct numnode *)kmalloc(sizeof(struct numnode),GFP_KERNEL);
        listnode->num = i + 1;

        //头插法,添加节点在头节点之前
        hlist_add_head(&(listnode->hlistNode), &nhead.hlistHead);
        printk("Node %d has added to the hash list...\n",i+1);
    }
    //遍历链表
    i = 1;
    hlist_for_each(pos, &nhead.hlistHead){	//移动pos的值
        //使用contain_of, 根据元素hlistNode的值得到用户结构指针
        p = hlist_entry(pos, struct numnode, hlistNode);
        printk("Node %d data:%d\n",i,p->num);
        i++;
    }
    return 0;
}

static void __exit hashlist_exit(void){
    struct hlist_node *pos,*n;
    struct numnode *p;
    int i;

    i = 1;
    //遍历数字链表,根据链表头hlistHead遍历
    hlist_for_each_safe(pos,n,&nhead.hlistHead){
        hlist_del(pos);		//删除哈希节点
        //取得删除节点的数据域值
        p = hlist_entry(pos,struct numnode,hlistNode);
        kfree(p);
        printk("Node %d has removed from the hashlist ...\n",i++);
    }
    printk("hash list is exiting...\n");
}

module_init(hlist_init);
module_exit(hashlist_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("linux");


Makefile

obj-m := hashlist.o
CURRENT_PATH := $(shell pwd)
LINUX_KERNEL := $(shell uname -r)
LINUX_KERNEL_PATH :=/usr/src/linux-headers-$(LINUX_KERNEL)

all:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

执行结果:

本文来自于:LINUX中哈希表的原理与应用

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值