Linux 内核数据结构解析--哈希链表

一、Hash表的基本定义

1.1 Hash的概念

散列表(Hash table,也叫哈希表),是一种数据结构,可以用于存储Key-Value键值对。也就是说,通过Key来映射到具体的Value。通常用于查找。将Key映射到Value的函数叫做Hash函数,而存储Key-Value的表叫做Hash表。Hasn表常用数组来存储。

1.2 常用的Hash函数

1.3 常用的处理碰撞的方法

如果说存储空间是无线的,那只要定义一个与需要存储的键值对数量等同的数组即可,这样每一个元素都有对应的存储空间,但实际上使用hash表时,数组通常远小于元素数量。这样不同的key值通过hash函数后可能映射到相同的位置,这样就必须有处理碰撞的方法。

   (1)直接定址法
 (2)数字分析法
 (3)平方取中法
 (4)折叠法
 (5)随机数法
 (6)除留余数法

(7)拉链法

hash表与数据结构的相关内容这里不多描述,可以参考相关的数据结构书籍。

linux内核的hash表用的是拉链法

二、Linux内核中的Hash表

2. 1 定义

数据结构定义于:include/linux/types.h

接口定义于:inxlude/linux/list.h

struct hlist_head {
	struct hlist_node *first;
};

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

(1)hlist_head是哈希链表的表头,会定义一个数组作为哈希表,用于存放hlist_head。hlist_head也就是每个链表的头结点。hlist_head只有一个first成员,指向hlist链表的第一个节点,也就是说链表是双向链表而不是双向循环链表。hlist_head只有一个first成员的原因是,为了节省空间。因为hash数组存放的是hlist_head,去掉一个成员可以节省一半的空间,这样可以存放更多的hlist_head,减少碰撞的几率,加快查表的速度。

(2)hlist_node是哈希链表的节点,哈希到同一个表头的节点会链接到对应的hlist_head后面。hlist_node有两个成员,分别是next和pprev。next指向下一个hist_node节点,倘若改结点是链表的最后一个节点,next则指向NULL。pprev是一个二级指针,它指向前一个节点的next指针。 为了能统一地修改表头的first指针,即表头的first指针必须修改指向新插入的结点,hlist就设计了pprev。list结点的pprev不再 是指向前一个结点的指针,而是指向前一个节点(可能是表头)中的next(对于表头则是first)指针,从而在表头插入的操作中可以通过一致的 node->pprev访问和修改前结点的next(或first)指针。还解决了数据结构不一致,hlist_node巧妙的将pprev指向上一个节点的next指针的地址,由于hlist_head和hlist_node指向的下一个节点的指针类型相同,就解决了通用性。如下图

2.2 接口

2.2.1 链表头hlist_head的初始化:

主要是对成员初始化为NULL

#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)

 2.2.2 节点的初始化

static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
	h->next = NULL;
	h->pprev = NULL;
}

static inline void hlist_del_init(struct hlist_node *n)
{
	if (!hlist_unhashed(n)) {
		__hlist_del(n);
		INIT_HLIST_NODE(n);
	}
}

2.2.3 判断函数

判断一个节点是否在hash表中。

判断hash链表是否空

static inline int hlist_unhashed(const struct hlist_node *h)
{
	return !h->pprev;
}

static inline int hlist_empty(const struct hlist_head *h)
{
	return !h->first;
}

static inline bool hlist_fake(struct hlist_node *h)
{
	return h->pprev == &h->next;
}

2.2.4 操作函数

static inline void hlist_del(struct hlist_node *n)
{
	__hlist_del(n);
	n->next = LIST_POISON1;
	n->pprev = LIST_POISON2;
}

static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
	struct hlist_node *first = h->first;
	n->next = first;
	if (first)
		first->pprev = &n->next;
	h->first = n;
	n->pprev = &h->first;
}

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

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

	if (n->next)
		n->next->pprev  = &n->next;
}

/* after that we'll appear to be on some hlist and hlist_del will work */
static inline void hlist_add_fake(struct hlist_node *n)
{
	n->pprev = &n->next;
}

static inline void hlist_move_list(struct hlist_head *old,
				   struct hlist_head *new)
{
	new->first = old->first;
	if (new->first)
		new->first->pprev = &new->first;
	old->first = NULL;
}

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值