linux内核中hlist_head和hlist_node结构解析
hlist_head和hlist_node用于散列表,分表表示列表头(数组中的一项)和列表头所在双向链表中的某项,两者结构如下:
|
1
2
3
|
struct hlist_head {
struct hlist_node *first;
};
|
|
1
2
3
|
struct hlist_node {
struct hlist_node *next, **pprev;
};
|
在内核中的普通双向链表基本上都是通过list_head实现的:
|
1
2
3
|
struct list_head {
struct list_head *next, *prev;
};
|
list_head很好理解,但是hlist_head和hlist_node为何要这样设计呢?
先看下hlist_head和hlist_node的示意图:

hash_table为散列表(数组),其中的元素类型为struct hlist_head。以hlist_head为链表头的链表,其中的节点hash值是相同的(也叫冲突)。first指针指向链表中的节点①,然后节点①的pprev指针指向hlist_head中的first,节点①的next指针指向节点②。以此类推。
hash_table的列表头仅存放一个指针,也就是first指针,指向的是对应链表的头结点,没有tail指针也就是指向链表尾节点的指针,这样的考虑是为了节省空间——尤其在hash bucket(数组size)很大的情况下可以节省一半的指针空间。
为什么pprev是一个指向指针的指针呢?按照这个设计,我们如果想要得到尾节点,必须遍历整个链表,可如果是一个指向节点的指针,那么头结点现在的pprev便可以直接指向尾节点,也就是list_head的做法。
对于散列表来说,一般发生冲突的情况并不多(除非hash设计出现了问题),所以一个链表中的元素数量比较有限,遍历的劣势基本可以忽略。
在删除链表头结点的时候,pprev这个设计无需判断删除的节点是否为头结点。如果是普通双向链表的设计,那么删除头结点之后,hlist_head中的first指针需要指向新的头结点。通过下面2个函数来加深理解:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//添加节点到链表头
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;//新节点的next指针指向原头结点
if (first)
first->pprev = &n->next;//原头结点的pprev指向新节点的next字段
h->first = n;//first指针指向新的节点(更换了头结点)
n->pprev = &h->first; //此时n是链表的头结点,将它的pprev指向list_head的first字段
}
//删除节点
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next; // pprev指向的是前一个节点的next指针,当该节点是头节点时指向 hlist_head的first,两种情况下不论该节点是一般的节点还是头结点都可以通过这个操作删除掉所需删除的节点。
if (next)
next->pprev = pprev;//使删除节点的后一个节点的pprev指向删除节点的前一个节点的next字段,节点成功删除。
}
|
因本人水平有限,若文章内容存在问题,恳请指出。允许转载,转载请在正文明显处注明原站地址以及原文地址,谢谢!
linux内核中hlist_head和hlist_node结构解析的更多相关文章
- Linux内核中的算法和数据结构
算法和数据结构纷繁复杂,但是对于Linux Kernel开发人员来说重点了解Linux内核中使用到的算法和数据结构很有必要. 在一个国外问答平台stackexchange.com的Theoretica ...
- Linux内核中namespace之PID namespace
前面看了LInux PCI设备初始化,看得有点晕,就转手整理下之前写的笔记,同时休息一下!!~(@^_^@)~ 这片文章是之前写的,其中参考了某些大牛们的博客!! PID框架的设计 一个框架的设计会考 ...
- Linux内核中的GPIO系统之(3):pin controller driver代码分析
一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...
- Linux内核中流量控制
linux内核中提供了流量控制的相关处理功能,相关代码在net/sched目录下:而应用层上的控制是通过iproute2软件包中的tc来实现, tc和sched的关系就好象iptables和netfi ...
- 嵌入式C语言自我修养 01:Linux 内核中的GNU C语言语法扩展
1.1 Linux 内核驱动中的奇怪语法 大家在看一些 GNU 开源软件,或者阅读 Linux 内核.驱动源码时会发现,在 Linux 内核源码中,有大量的 C 程序看起来“怪怪的”.说它是C语言吧, ...
- Linux内核中的GPIO系统之(3):pin controller driver代码分析--devm_kzalloc使用【转】
转自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html 一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道 ...
- 剖析linux内核中的宏---------container_of
#define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) * __mptr = (ptr); ...
- Linux 内核中的 Device Mapper 机制
本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...
- 向linux内核中添加外部中断驱动模块
本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...
随机推荐
- swift中UIImageView的创建
let imageView = UIImageView() let imageView1 = UIImageView(frame: CGRectMake(, , , )) // 创建出来的UIImag ...
- js获取url传值的方法
这篇文章主要介绍了js获取url传值的方法,实例分析了字符串分割与正则分析两种方法,并补充了一个基于正则匹配实现的js获取url的get传值函数,需要的朋友可以参考下 js获取url参数值: inde ...
- jar is specified twice.
Warning:Exception while processing task java.io.IOException: The same input jar [libs\afinal_0.5.1_b ...
- STOP OUR NEGATIVE THOUGHTS
Do you ever feel like you're in over your head and at any moment you're going to burst? You're not a ...
- Linux 学习总结(三)
一. yum 命令 .列出所有可更新的软件清单命令:yum check-update .更新所有软件命令:yum update .仅安装指定的软件命令:yum install <package_ ...
- jenkin 不必要的Execute shell执行失败,导致jenkins都失败的解决
问题:jenkins里配置了多个执行shell,且有后续的执行job任务.但其中一个Execute shell执行失败了导致后续的shell都不执行了 而这个失败的shell并不是一定要执行 解决 ...
- web.config中连接字符串的读写和加密解密
转载:https://www.cnblogs.com/shuai/articles/2248703.html 1.先来看看如何在web.config中写入数据库连接字符串.打开web.config文件 ...
- jumpserver-1.4.0.2
关闭防火墙和selinux IP:192.168.199.115 一. 准备 Python3 和 Python 虚拟环境 yum -y install wget sqlite-devel xz gcc ...
- oracle中的to_number在mysql中的转换
select cast(11 as unsigned int) /*整型*/ select cast(11 as decimal(10,2)) /*浮点型*/ 注:(10,2)代表数字共十位,小数点后 ...
- struct在C和C++中的使用总结
主要理解一下两点: 1.在C和C++中struct的常规使用. 2.在C++中struct和class基本一致,除了在访问控制权限方面,即: 通过struct关键字实现的类,属性,函数默认的访问权限为 ...