Linux内核hlist数据结构分析

简介:
 在内核编程中哈希链表hlist使用非常多,比方在openvswitch中流表的存储中就使用了(见[1])。 hlist的表头仅有一个指向首节点的指针。而没有指向尾节点的指针,这样在有非常多个buckets的HASH表中存储的表头就能降低一半的空间消耗。
     和hlist相关的数据结构例如以下,桶中存储的 hlist_head 是具有同样hash值的entry构成的链表。每一个entry包括一个 hlist_node 成员,通过它链入到这个哈希链表中。
struct  hlist_head {
       struct  hlist_node *  first ;
};

//next指向下一个节点
// pprev指向前一个节点的next域
struct  hlist_node {
       struct  hlist_node *  next , **  pprev ;
};

结构图为:


因为头结点和其它节点的类型不一致。这样就不能使用普通的prev指针指向前一个节点(否则处理的时候还要讨论是否是第一个节点,没有通用性),这里设计者的巧妙之处就是pprev指针指向前一个节点的next,统一了兴许全部的节点。

一些实用的宏:
//头结点初始化
#define  HLIST_HEAD_INIT { .first = NULL }
//构造一个名为name的头结点
#define  HLIST_HEAD(name)  struct  hlist_head name = {  .first = NULL }

//初始化头指针,链表指针
#define  INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
#define  INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)

1.删除节点
next得到当前节点的下一个节点。pprev是前一个节点的next字段的地址,那么*pprev就指向的是当前这个节点,那么  *pprev=next 就把当前节点更新为下一个节点了。假设n不是最后一个节点,还要设置next->pprev.
static   inline  void  __hlist_del ( struct  hlist_node *n)
{
       struct  hlist_node *next = n->  next ;
       struct  hlist_node **pprev = n->  pprev ;
     *pprev = next;
       if  (next)
          next->  pprev  = pprev;
}

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

2.插入节点
(1)头插入:让插入的节点成为链表的第一个节点,依次更新对应的指针。示意图例如以下。
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 ;
}
(2)在已知节点next之前/之后插入,通过自己绘图。非常easy理解清楚。
/* next must be != NULL */
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_after ( struct  hlist_node *n,
                          struct  hlist_node *next)
{
     next->  next  = n->  next ;
     n->  next  = next;
     next->  pprev  = &n->  next ;

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

3.通过看一个节点h的pprev是否为空。推断其是否在哈希链表中。

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

4.哈希链表的遍历(iterate)相关代码
//通过一个字段member的地址 ptr。得到包括它的容器的地址
#define  hlist_entry(ptr, type, member) container_of(ptr,type,member)

//用 pos作为游标来遍历这个链表, prefetch是数据预取
#define  hlist_for_each(pos, head) \
       for  (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
          pos = pos->next)

#define  hlist_for_each_safe(pos, n, head) \
       for  (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
          pos = n)

//通用的哈希链表遍历,当中 pos指向当前节点。 tpos指向的包括hlist_node的当前结构体的指针
#define  hlist_for_each_entry(tpos, pos, head, member)               \
       for  (pos = (head)->first;                        \
          pos && ({ prefetch(pos->next); 1;}) &&           \
          ({ tpos = hlist_entry(pos,  typeof  (*tpos), member); 1;}); \
          pos = pos->next)

/**
 * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
 * @ tpos: the type * to use as a loop counter.
 * @ pos:  the &struct hlist_node to use as a loop counter.
 * @member:   the name of the hlist_node within the struct.
 */
#define  hlist_for_each_entry_continue(tpos, pos, member)           \
       for  (pos = (pos)->next;                          \
          pos && ({ prefetch(pos->next); 1;}) &&           \
          ({ tpos = hlist_entry(pos,  typeof  (*tpos), member); 1;}); \
          pos = pos->next)

/**
 * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
 * @ tpos: the type * to use as a loop counter.
 * @ pos:  the &struct hlist_node to use as a loop counter.
 * @member:   the name of the hlist_node within the struct.
 */
#define  hlist_for_each_entry_from(tpos, pos, member)            \
       for  (; pos && ({ prefetch(pos->next); 1;}) &&              \
          ({ tpos = hlist_entry(pos,  typeof  (*tpos), member); 1;}); \
          pos = pos->next)

/**
 * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @ tpos: the type * to use as a loop counter.
 * @ pos:  the &struct hlist_node to use as a loop counter.
 * @n:        another &struct hlist_node to use as temporary storage
 * @head: the head for your list.
 * @member:   the name of the hlist_node within the struct.
 */
#define  hlist_for_each_entry_safe(tpos, pos, n, head, member)        \
       for  (pos = (head)->first;                        \
          pos && ({ n = pos->next; 1; }) &&                     \
          ({ tpos = hlist_entry(pos,  typeof  (*tpos), member); 1;}); \
          pos = n)





本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5394727.html,如需转载请自行联系原作者
相关文章
|
16天前
|
Linux C语言
Linux内核队列queue.h
Linux内核队列queue.h
|
1月前
|
存储 Shell Linux
【Shell 命令集合 系统设置 】Linux 生成并更新内核模块的依赖 depmod命令 使用指南
【Shell 命令集合 系统设置 】Linux 生成并更新内核模块的依赖 depmod命令 使用指南
32 0
|
1月前
|
监控 Shell Linux
【Shell 命令集合 网络通讯 】Linux 分析串口的状态 statserial命令 使用指南
【Shell 命令集合 网络通讯 】Linux 分析串口的状态 statserial命令 使用指南
33 0
|
1月前
|
Shell Linux C语言
【Shell 命令集合 系统设置 】⭐Linux 卸载已加载的内核模块rmmod命令 使用指南
【Shell 命令集合 系统设置 】⭐Linux 卸载已加载的内核模块rmmod命令 使用指南
29 1
|
9天前
|
算法 Linux 调度
深入理解Linux内核的进程调度机制
【4月更文挑战第17天】在多任务操作系统中,进程调度是核心功能之一,它决定了处理机资源的分配。本文旨在剖析Linux操作系统内核的进程调度机制,详细讨论其调度策略、调度算法及实现原理,并探讨了其对系统性能的影响。通过分析CFS(完全公平调度器)和实时调度策略,揭示了Linux如何在保证响应速度与公平性之间取得平衡。文章还将评估最新的调度技术趋势,如容器化和云计算环境下的调度优化。
|
15天前
|
算法 Linux 调度
深度解析:Linux内核的进程调度机制
【4月更文挑战第12天】 在多任务操作系统如Linux中,进程调度机制是系统的核心组成部分之一,它决定了处理器资源如何分配给多个竞争的进程。本文深入探讨了Linux内核中的进程调度策略和相关算法,包括其设计哲学、实现原理及对系统性能的影响。通过分析进程调度器的工作原理,我们能够理解操作系统如何平衡效率、公平性和响应性,进而优化系统表现和用户体验。
20 3
|
22天前
|
负载均衡 算法 Linux
深度解析:Linux内核调度器的演变与优化策略
【4月更文挑战第5天】 在本文中,我们将深入探讨Linux操作系统的核心组成部分——内核调度器。文章将首先回顾Linux内核调度器的发展历程,从早期的简单轮转调度(Round Robin)到现代的完全公平调度器(Completely Fair Scheduler, CFS)。接着,分析当前CFS面临的挑战以及社区提出的各种优化方案,最后提出未来可能的发展趋势和研究方向。通过本文,读者将对Linux调度器的原理、实现及其优化有一个全面的认识。
|
22天前
|
Ubuntu Linux
Linux查看内核版本
在Linux系统中查看内核版本有多种方法:1) 使用`uname -r`命令直接显示版本号;2) 通过`cat /proc/version`查看内核详细信息;3) 利用`dmesg | grep Linux`显示内核版本行;4) 如果支持,使用`lsb_release -a`查看发行版及内核版本。
36 6
|
24天前
|
Prometheus 监控 数据可视化
linux分析方法与技巧
【4月更文挑战第3天】在Linux环境中,进行日志分析和系统性能分析的关键方法包括:使用`cat`, `less`, `tail`查看和过滤日志,`logrotate`管理日志文件,`rsyslog`或`syslog-ng`聚合日志,以及通过`top`, `mpstat`, `pidstat`, `free`, `iostat`, `netstat`, `strace`, `sar`, `dstat`等工具监控CPU、内存、磁盘I/O和网络。对于高级分析,可利用Brendan Gregg的性能工具,以及Grafana、Prometheus等可视化工具。
19 2
linux分析方法与技巧
|
24天前
|
Linux 内存技术
Linux内核读取spi-nor flash sn
Linux内核读取spi-nor flash sn
18 1