Linux内核数据结构 散列表

这篇具有很好参考价值的文章主要介绍了Linux内核数据结构 散列表。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、散列表数据结构

  • 在Linux内核中,散列表(哈希表)使用非常广泛。本文将对其数据结构和核心函数进行分析。和散列表相关的数据结构有两个:hlist_headhlist_node

    //hash桶的头结点
    struct hlist_head {
    	struct hlist_node *first;//指向每一个hash桶的第一个结点的指针
    };
    //hash桶的普通结点
    struct hlist_node {
    	struct hlist_node *next;//指向下一个结点的指针
    	struct hlist_node **pprev;//指向上一个结点的next指针的地址
    };
    
  • 对应的结构如下
    Linux内核数据结构 散列表,Linux内核数据结构,数据结构,linux,散列表

    • hash_table 为散列表(数组),其中的元素类型为 struct hlist_head 。以 hlist_head 为链表头的链表,其中的节点hash值是相同的(也叫冲突)。first指针指向链表中的节点①,然后节点①的 pprev 指针指向 hlist_head 中 的 first ,节点①的 next 指针指向节点②,以此类推。
    • hash_table 的列表头仅存放一个指针,也就是 first 指针,指向的是对应链表的头结点,没有tail指针,也就是指向链表尾节点的指针,这样的考虑是为了节省空间——尤其在 hash bucket (数组size)很大的情况下可以节省一半的指针空间。
    • pprev是一个二级指针, 它指向前一个节点的next指针的地址 。为什么我们需要这样一个指针呢?它的好处是什么?
      • 哈希表的目的是为了方便快速的查找,所以哈希表中hash桶的数量通常比较大,否则“冲突”的概率会非常大,这样也就失去了哈希表的意义。如何做到既能维护一张大表,又能不使用过多的内存呢?就只能从数据结构上下功夫了。所以对于哈希表的每个hash桶,它的结构体中只存放一个指针,解决了占用空间的问题。现在又出现了另一个问题:数据结构不一致。显然,如果hlist_node采用传统的nextprev指针,对于第一个节点和后面其他节点的处理会不一致。hlist_node巧妙地将pprev指向上一个节点的next指针的地址,由于hlist_headfirst域指向的结点类型和hlist_node指向的下一个结点的结点类型相同,这样就解决了通用性!
      • 下面讲解结点相关操作的时候会有更详细的解释。

2、散列表删除结点

  • 如果要删除hash桶对应链表中的第一个普通结点
    Linux内核数据结构 散列表,Linux内核数据结构,数据结构,linux,散列表对应的程序代码如下:

    static inline void __hlist_del(struct hlist_node *n) 
    { 
    	 struct hlist_node *next = n->next;//获取指向第二个普通结点的指针 
    	 struct hlist_node **pprev = n->pprev;//保留待删除的第一个结点的pprev域(即头结点first域的地址),此时 pprev = &first 
    	 *pprev = next; 
    	 /*
    	 因为pprev = &first,所以*pprev = next,相当于 first = next
    	 即将hash桶的头结点指针指向原来的第二个结点,如上图中的黑线1
    	 */
    	 if (next) //如果第二个结点不为空
    	 next->pprev = pprev;//将第二个结点的pprev域设置为头结点first域的地址,如上图中的黑线2 
    }
    
  • 如果要删除hash桶对应链表中的非第一个结点
    Linux内核数据结构 散列表,Linux内核数据结构,数据结构,linux,散列表对应的程序代码如下:

    static inline void __hlist_del(struct hlist_node *n) 
    { 
    	 struct hlist_node *next = n->next;//获取指向待删除结点的下一个普通结点的指针 
    	 struct hlist_node **pprev = n->pprev;//获取待删除结点的pprev域 
    	 *pprev = next; //修改待删除结点的pprev域,逻辑上使待删除结点的前驱结点指向待删除结点的后继结点,如上图中的黑线1
    	 
    	 if (next) //如果待删除结点的下一个普通结点不为空
    	 next->pprev = pprev;//设置下一个结点的pprev域,如上图中的黑线2,保持hlist的结构 
    }
    

    可以看到删除第一个普通结点和删除非第一个普通结点的代码是一样的。

  • 下面再来看看如果hlist_node中包含两个分别指向前驱结点和后继结点的指针。
    Linux内核数据结构 散列表,Linux内核数据结构,数据结构,linux,散列表很明显删除hash桶对应链表中的非第一个普通结点,只需要如下两行代码:

    n->next->prev = n->prev;
    n->prev->next = n->next;
    

    可是,如果是删除的hash桶对应链表中的第一个普通结点:此时 n->prev->next = n->next 就会出问题,因为hash桶的表头结点没有next域,所以,明显在这种情况下删除hash桶对应链表的第一个普通结点和非第一个普通结点的代码是不一样的。 同理结点插入操作也存在同样的问题。文章来源地址https://www.toymoban.com/news/detail-682624.html

到了这里,关于Linux内核数据结构 散列表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux驱动】Linux的中断系统 | 中断的重要数据结构

    🐱作者:一只大喵咪1201 🐱专栏:《Linux驱动》 🔥格言: 你只管努力,剩下的交给时间! 如上图所示,本喵使用的 IMX6ULL 也是ARM架构,中断也是异常的一种,CPU在运行的过程中,会被各种异常打断,包括: 未定义指令异常 指令、数据访问异常 SWI(软中断) 快中断 中断 导致

    2024年02月02日
    浏览(30)
  • linux tcp 主要数据结构

    当讨论 tcp 的时候, 我们能想到很多概念: 传输层协议,面向连接,可靠,字节流,状态机,三次握手,四次挥手,端口号,连接队列,mss,rtt,定时器,ack,流控,拥塞控制,重传机制,窗口,慢启动,序列号,保序,发送缓冲区,接收缓冲区,nagle,minshall,autocrok,fast

    2024年02月20日
    浏览(22)
  • 【脚踢数据结构】内核链表

    (꒪ꇴ꒪ ),Hello我是 祐言QAQ 我的博客主页:C/C++语言,Linux基础,ARM开发板,软件配置等领域博主🌍 快上🚘,一起学习,让我们成为一个强大的攻城狮! 送给自己和读者的一句鸡汤🤔: 集中起来的意志可以击穿顽石! 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏

    2024年02月13日
    浏览(29)
  • Python数据结构与算法-数据结构(列表、栈、队列、链表)

    数据结构是指相互之间存在这一种或者多种关系的数据元素的集合和该集合中元素之间的关系组成。 简单来说,数据结构就是设计数据以何种方式组织并存储在计算机中。 比如:列表、集合与字典等都是一种数据结构。 N.Wirth:“程序=数据结构+算法” 数据结构按照其 逻辑结

    2024年02月08日
    浏览(38)
  • 数据结构基础--散列表

    散列表,又叫 哈希表 (Hash Table),是能够通过给定的的值直接访问到具体对应的值的一个数据结构。也就是说,把映射到一个表中的位置来直接访问记录,以加快访问速度。 通常,把这个称为 Key,把对应的记录称为 Value,所以也可以说是通过 Key 访问

    2024年02月04日
    浏览(30)
  • linux 设备驱动之tty_operaions数据结构介绍

    tty_operations 结构包含所有的函数回调, 可以被一个 tty 驱动设置和被 tty 核心调 用. 当前, 所有包含在这个结构中的的函数指针也在 tty_driver 结构中, 但是会很快被 只有一个这个结构的实例来替代. int (*open)(struct tty_struct * tty, struct file * filp); open 函数. void (*close)(struct tty_struct *

    2024年01月19日
    浏览(35)
  • RK3588平台开发系列讲解(内存篇)Linux 伙伴系统数据结构

    平台 内核版本 安卓版本 RK3588 Linux 5.10 Android 12 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢Linux 系统中,用来管理物理内存页面的伙伴系统,以及负责分配比页更小的内存对象的 SLAB 分配器了。 本篇将介绍伙伴系统相关数据结构体。 Linux 也是使用分页机制管理物

    2024年02月05日
    浏览(76)
  • RK3588平台开发系列讲解(进程篇)Linux 进程的数据结构

    平台 内核版本 安卓版本 RK3588 Linux 5.10 Android 12 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 本篇将介绍 Linux 如何表示进程。 Linux 系统下,把运行中的应用程序抽象成一个数据结构 task_struct ,一个应用程序所需要的各种资源,如内存、文件等都包含在 task_struct

    2024年02月07日
    浏览(32)
  • 《LKD3粗读笔记》(6)内核数据结构

    单向链表和双向链表 这里涉及到了对 void 的理解:C高级编程——关于void类型的解释 链表的特点 链表是一种存放和操作可变数量元素(常称为结点)的数据结构 与静态数组不同的是,链表所包含的元素都是动态创建并插入链表的,在编译时不必知道具体创建多少个元

    2023年04月12日
    浏览(27)
  • 数据结构—散列表的查找

    7.4散列表的查找 7.4.1散列表的基本概念 基本思想:记录的存储位置域之间存在对应关系 ​ 对应关系——hash函数 ​ Loc(i)= H(keyi) 如何查找 : 根据散列函数 H(key) = k 查找key=9,则访问H(4)= 18号地址,若内容为18则成功; 若查不到,则返回一个特殊值,如空指针或

    2024年02月12日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包