linux kernel hlist_head,hlist_node

纯属个人理解记录,大神请绕道。如有错误,感谢指出。

哈希表

哈希表的作用是为了提高查询的速度:通过将key映射为数组的下标从而实现o(1)复杂度的查找。
数组的大小是有限的,而key的范围一般都比数组大小大,所以必然会出现冲突的情况,即两个不同的key被映射到了数组的同一个位置。
一般的解决办法有两个:

  1. 设置算法进行再映射,直到找到空闲的位置
  2. 数组中不存储单个的元素,而是存储一个链表,映射到相同位置的元素存储在一个链表中。通过合理设置哈希算法,不出现单个链表过长的情况,复杂度依然是o(1)级别。

单向链表

链表又分为单向链表和双向链表。
单向链表:
【【【【【【】】】】】】】】】
单向列表结构简单。
查找操作使用简单的遍历来完成。
在节点a后增加一个节点n的操作如下:

n->next = a->next;
a->next=n;

删除一个节点n的操作比较复杂,必须从头节点(head)进行遍历,找到节点n的前一个节点,假设为a:

temp=head
while(temp->next!=n){temp=temp->next;}
temp->next=n->next;

可以看出,单向链表的缺点就是删除操作复杂度比较高。

双向链表

所以,引入了双向链表。
【【【【【【】】】】】】】】】
增加节点(节点a后增加节点n):

a->next->prev=n;
n->pre=a;

n->next=a->next;
a->next=n;

删除节点n:

n-next->pre=n->pre;
n->pre->next=n->next;

可以看出,由于形成了完全环形的双向链表,无论操作的节点处于何位置,操作都是一样的,这是一个很优良的特性,不用根据不同的情况去判断。

但是双向链表有一个缺点,就是空间占用太大(是单向链表的2倍)。特别是在使用哈希链表的场景,为了追求效率,往往将数组设置得特别大,2倍的空间占用还是很可观的。

hlist_head与hlist_node结构

所以在linuxkernel中使用了hlist_head与hlist_node结构:
include/linux/types.h

struct hlist_head {
    struct hlist_node *first;
};

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

在这里插入图片描述

对于普通节点(非hlist_head后的第一个节点),增加节点(节点a后增加节点n):

n->next=a->next;
a->next=n;
if(n->next)
{
	a->next->prev=n;
}
n->pre=a;

和普通双向链表基本是一样的,只是多了一个判空。

但是,如果删除的是第一个节点的话,情况就不一样了。因为第一个节点的结构都不一样,是hlist_head而不是hlist_node,结构里根本就没有next这个变量。
更关键的是,个人理解似乎根本没法判断出要删除的节点到底是不是第一个节点,所以,即使想进行区分判断,分别处理,都是不可行的。

个人理解,似乎有两个可行的办法:

  1. 将hlist_head中的first名称修改为next,和hlist_node保持一致,进行操作的时候,不管前一个节点是hlist_head还是hlist_node,都统一当做hlist_node处理,并且修改其next变量即可。
    这样做似乎不太优雅。

  2. linux kernel选择了另一种做法:将hlist_node第二个元素定义为hlist_node **ppre。其实结构是完全一样的,只是用了另一种方式表示。
    hlist_node * pre这种写法,表示pre是一个指针。其值指向的地址为hlist_node结构体所在的位置。
    如果pre是hlist_node,hlist_node的第一个元素next,本身还是一个指向hlist_node结构的指针。
    如果pre是hlist_head,hlist_node的第一个元素first,本身还是一个指向hlist_node结构的指针。
    所以说,hlist_node **ppre这种定义方法,就直接将两种情况进行了归一,确实是一种很优雅的实现。

参考:
c中关于结构体,指针,指针的指针的理解
Linux 内核 hlist 详解

hlist_head相关操作

INIT_HLIST_HEAD&INIT_HLIST_NODE

定义:include/linux/list.h

#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)

static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
     h->next = NULL;
     h->pprev = NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值