linux内核中的哈希散列表

    介绍一下linux内核中的哈希散列表的实现,在linux内核中哈希散列表(hash list)用的非常的多,

并且自己以后在做软件设计的时候,也非常有可能用到。毕竟,哈希散列表在数据的查找过程中非常的方便。

     linux内核对哈希散列表的实现非常的完美,所以非常有必要学习一下。

在哈希散列表的实现过程中,用到的两个非常有用的结构体:

     哈希散列表头结构体 :

                         struct hlist_head

                         {

                            struct hlist_node *first;

                         }

     哈希散列表的节点结构:

                          struct hlist_node

                          {

                            struct hlist_node *next;

                            struct hlist_node **pprev;

                          }

                                        内核中的哈希散列表的组织结构图:

     至于内核中为什么没有将哈希散列表的头节点(struct hlist_head)定义成跟struct hlist_node一样的形式。

     内核中的解释是这样的:应为哈希散列表在内核中使用的非常多,将头节点定义称struct hlist_head {struct hlist_node *first}能节省一半的内存空间。

     而为了实现对哈希散列表中的所有节点都采取同样的操作方式,而不需要将哈希散列表的头节点作为例外来处理。所以对struct hlist_node中定义了struct hlist_node **pprev这样的成员。

     struct hlist_node 结构体中的 pprev成员的作用:用于指向当前struct hlist_node节点的前一个 struct hlist_node结构体的 next成员。

    1.哈希散列表头节点的定义以及初始化:

      #define HLIST_HEAD(name) \

       struct hlist_head name = {.first = NULL}

    2.哈希散列表头的初始化:

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

 

    3.哈希散列表的数据节点的初始化:

      static inline void hlist_node_init(struct hlist_node *node)

      {

          node->next = NULL;

          node->pprev = NULL;

      }

    4.判断一个哈希散列表是否为空:

      int hlist_empty(struct hlist_head *head)

      {

          return !head->first;

      }

    5.判断一个哈希节点是否哈希散列表的哈希链表之中:

      int hlist_unhash( struct hlist_node *node )

      {

          return !node->pprev; // 如果node->pprev为空,则表明 node不在哈希链路之中

      }

 

    6.从哈希链路中删除一个节点:

      void __hlist_del( struct hlist_node *node )

      {

           struct hlist_node *next = node->next;

           struct hlist_node **pperv = node->pperv;

           if(next)

               next->pprev = pperv;

           *pperv = next ; //或 *(node->pprev) = next;          

      }

      在删除哈希链路中的节点元素时,需要考虑删除的元素是否是链路中的末尾元素。

     7.从哈希链路中删除一个节点然后并对其进行初始化:

       void __hlist_node_init( struct hlist_node *node )

       {

           if( !hlist_unhash(node) )

                __hlist_del(node)

           hlist_node_init(node);

       }

     8.在哈希链表头部后面添加一个节点元素:

       void hlist_add_head(struct hlist_head *head, struct hlist_node *new )

       {

            new->next = head->first;

            new->pprev = &(head->first);

            if( !hlist_empty(head) )

               head->first->pprev = &(new->next);

            head->first = new;

       }

       须知:在往哈希链路中头后添加节点是需要考虑的是:要加入的节点是否是第一个节点元素

      9.往哈希链路中的某个节点后面添加一个节点:

        void hlist_add_after(struct hlist_node *node, struct hlist_node *new)

        {

             struct hlist_node *tmp =  node->next;

             new->next = tmp;

             new->pprev = &(node->next);

             node->next = new;

             if(tmp)

               tmp->pprev = &(new->next);

        }

 

        10.往哈希链路中的某个节点前面添加一个节点:

          void hlist_add_before( struct hlist_node *node, struct hlist_node *new )

          {

              struct hlist_node **tmp = node->pprev;

             

               new->next = node;

               new->pperv = node->pprev;

               *tmp = new;

               node->pprev = &(new->next); 

          }

        11.把一个哈希链路从一个头节点移动到另一个头节点上:

          void hlist_move_list(struct hlist_head *old, struct hlist_head *new)

          {

              struct hlist_node *tmp = old->first;

              new->first =  tmp;

              if(tmp)

                tmp->pprev = &(new->first);

              old->first = NULL;   

          }

 

对哈希散列表中的元素进行遍历的方法:

      对每个struct hlist_node{}结构体进行遍历:

        //pos用于存放每个struct hlist_node结构体的地址;

        // head : 哈希散列表的头节点;

      1.#define hlist_for_each(pos, head)  \

          for(pos = (head)->first ;   pos != NULL ;  pos = pos->next)

 

      2.#define hlist_for_each_safe(pos, tmp, head) \

          for( pos = (head)->first,  tmp = pos->next; \

                pos != NULL; \

              pos = tmp, tmp = pos->next)

     对哈希散列表中的每个元素进行遍历:

          // ptr :是struct hlist_node 结构体的地址;

          // member : 是struct hlist_node 结构体嵌入到其他结构体中的成员名;

          // type   : 是strcut hlist_node 结构体嵌入的结构体的类型;

      1. #define hlist_entry(ptr, member, type)  \

                  container_of(ptr, type, member)

         hlist_entry()用于求出struct hlist_node结构体所嵌入的结构体的地址;

        

          // pos : 用于存放struct hlist_node结构体嵌入的结构体的地址;

          // node : 用于存放哈希链路中的下一个struct hlist_node 结构体的地址;

          // head : 哈希散列表的头节点;

          // member: struct hlist_node 在大的结构体中的成员名;

      2. #define hlist_for_each_entry(pos, node, head, member) \

          for((node) = head->first; \

              (node) != NULL  && pos = hlist_entry(node, member, typeof(*pos)); \

              (node) = (node)->next )

       

        // 从 struct hlist_node *node 节点之后开始继续遍历;

      3.#define hlist_for_each_entry_continue(pos, node, member) \

         for((node) = (node)->next ; \

            (node) != NULL &&  pos = hlist_entry(node, member, typeof(*pos) ) ; \

            (node) = (node)->next  )

         //从 struct hlist_node *node 开始继续遍历;

      4. #define hlist_for_each_entry_from(pos, node, member) \

          for( ;  \

             (node) != NULL  && pos = hlist_entry(node, member, typeof(*pos) ); \

             (node) = (node)->next )

      

        // hlist_for_each_entry_safe()函数用于在遍历链路的同时,删除某个元素。

        // pos :用于存放链路中元素的地址;

        // node :用于存放链路中的struct hlist_node 结构体的地址;

        // tmp  :用于缓存链路中下一个struct hlist_node 结构体的地址;

        // head : 哈希散列表的链表头节点; struct hlist_head;

        // member : struct hlist_node 结构体在大的结构体中成员名;

      5. #define hlist_for_each_entry_safe(pos, node, tmp, head, member)

            for( (node) = (head)->first ; \

                 (node) != NULL  && (tmp) = (node)->next  &&

                         pos = hlist_entry(node, member, typeof(*pos) ); \

                 (node) = (tmp) )

   以上这些对哈希散列表的操作函数,是我根据内核中对哈希散列表的各种操作写的,基本的做法都源于 /include/linux/list.h文件。

时间: 2024-08-02 06:36:26

linux内核中的哈希散列表的相关文章

Linux内核中的哈希表

Author:tiger-john Time:2012-12-20mail:[email protected]Blog:http://blog.csdn.net/tigerjb/article/details/8450995 转载请注明出处. 前言: 1.基本概念: 散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表.

KSM剖析——Linux 内核中的内存去耦合

简介: 作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通过合并内存页面来增加并发虚拟机的数量.本文探索 KSM 背后的理念(比如存储去耦合).KSM 的实现.以及如何管理 KSM. 服务器虚拟化 虚拟化技术从上世纪 60 年代开始出现,经由 IBM® System/360® 大型机得以流行.50 年过后,虚拟化技术取得了跨越式发展,使得多个操作系统和应用

linux内核中的hlist_head、list_head、hlist_node结构体

在linux内核中经常会看到这几个结构体: struct list_head; struct hlist_head; struct hlist_node; 在linux内核源代码中对这三个结构体的定义如下: struct list_head { struct list_head *prev; struct list_head *next; } struct hlist_node { struct hlist_node **prev; struct hlist_node *next; } stru

Linux内核中的软中断、tasklet和工作队列详解

[TOC] 本文基于Linux2.6.32内核版本. 引言 软中断.tasklet和工作队列并不是Linux内核中一直存在的机制,而是由更早版本的内核中的"下半部"(bottom half)演变而来.下半部的机制实际上包括五种,但2.6版本的内核中,下半部和任务队列的函数都消失了,只剩下了前三者. 介绍这三种下半部实现之前,有必要说一下上半部与下半部的区别. 上半部指的是中断处理程序,下半部则指的是一些虽然与中断有相关性但是可以延后执行的任务.举个例子:在网络传输中,网卡接收到数据包这

Linux内核中namespace之PID namespace

前面看了LInux PCI设备初始化,看得有点晕,就转手整理下之前写的笔记,同时休息一下!!~(@^_^@)~ 这片文章是之前写的,其中参考了某些大牛们的博客!! PID框架的设计 一个框架的设计会考虑很多因素,相信分析过Linux内核的读者来说会发现,内核的大量数据结构被哈希表和链表链接起来,最最主要的目的就是在于查找.可想而知一个好的框架,应该要考虑到检索速度,还有考虑功能的划分.那么在PID框架中,需要考虑以下几个因素. 如何通过task_struct快速找到对应的pid 如何通过pid快

大话Linux内核中锁机制之RCU、大内核锁

大话Linux内核中锁机制之RCU.大内核锁 在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linux内核中锁机制>系列博文进行了总结,并提出关于目前Linux内核中提供的锁机制的一些基本使用观点. 十.RCU机制 本节将讨论另一种重要锁机制:RCU锁机制.首先我们从概念上理解下什么叫RCU,其中读(Read):读者不需要获得任何锁就可访问RCU保护的临界

Linux内核中的软中断、tasklet和工作队列具体解释

[TOC] 本文基于Linux2.6.32内核版本号. 引言 软中断.tasklet和工作队列并非Linux内核中一直存在的机制,而是由更早版本号的内核中的"下半部"(bottom half)演变而来. 下半部的机制实际上包含五种,但2.6版本号的内核中.下半部和任务队列的函数都消失了,仅仅剩下了前三者. 介绍这三种下半部实现之前.有必要说一下上半部与下半部的差别. 上半部指的是中断处理程序,下半部则指的是一些尽管与中断有相关性可是能够延后运行的任务. 举个样例:在网络传输中.网卡接收

linux内核中socket的创建过程源码分析(总结性质)

http://www.jianshu.com/p/5d82a685b5b6 在漫长地分析完socket的创建源码后,发现一片浆糊,所以特此总结,我的博客中同时有另外一篇详细的源码分析,内核版本为3.9,建议在阅读本文后若还有兴趣再去看另外一篇博文.绝对不要单独看另外一篇. 一:调用链: 二:数据结构 一一看一下每个数据结构的意义: 1) socket, sock, inet_sock, tcp_sock的关系创建完sk变量后,回到inet_create函数中: 这里是根据sk变量得到inet_s

Linux内核中的GPIO系统之(3):pin controller driver代码分析--devm_kzalloc使用【转】

转自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html 一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datasheet会把pin controller的内容放入GPIO controller的章节中),主要功能包括: (1)pin multiplexing.基于ARM core