在内核编程中哈希链表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)

Linux内核hlist数据结构分析相关推荐

  1. Linux内核网络数据发送(六)——网络设备驱动

    Linux内核网络数据发送(六)--网络设备驱动 1. 前言 2. 驱动回调函数注册 3. `ndo_start_xmit` 发送数据 4. `igb_tx_map` 1. 前言 本文主要介绍设备通过 ...

  2. Linux内核网络数据发送(五)——排队规则

    Linux内核网络数据发送(五)--排队规则 1. 前言 2. `qdisc_run_begin()` and `qdisc_run_end()`:仅设置 qdisc 状态位 3. `__qdisc_ ...

  3. Linux内核网络数据包发送(四)——Linux netdevice 子系统

    Linux内核网络数据包发送(四)--Linux netdevice 子系统 1. 前言 2. `dev_queue_xmit` and `__dev_queue_xmit` 2.1 `netdev_ ...

  4. Linux内核网络数据包发送(三)——IP协议层分析

    Linux内核网络数据包发送(三)--IP协议层分析 1. 前言 2. `ip_send_skb` 3. `ip_local_out` and `__ip_local_out` 3.1 netfilt ...

  5. Linux内核网络数据包发送(二)——UDP协议层分析

    Linux内核网络数据包发送(二)--UDP协议层分析 1. 前言 2. `udp_sendmsg` 2.1 UDP corking 2.2 获取目的 IP 地址和端口 2.3 Socket 发送:b ...

  6. Linux内核网络数据包发送(一)

    Linux内核网络数据包发送(一) 1. 前言 2. 数据包发送宏观视角 3. 协议层注册 4. 通过 socket 发送网络数据 4.1 `sock_sendmsg`, `__sock_sendms ...

  7. Linux内核网络数据包处理流程

    Linux内核网络数据包处理流程 from kernel-4.9: 0. Linux内核网络数据包处理流程 - 网络硬件 网卡工作在物理层和数据链路层,主要由PHY/MAC芯片.Tx/Rx FIFO. ...

  8. linux 内核 发送数据,linux 内核tcp数据发送的实现

    在分析之前先来看下SO_RCVTIMEO和SO_SNDTIMEO套接口吧,前面分析代码时没太注意这两个.这里算是个补充. SO_RCVTIMEO和SO_SNDTIMEO套接口选项可以给套接口的读和写, ...

  9. Linux内核之数据双链表

    导读 Linux 内核中自己实现了双向链表,可以在 include/linux/list.h 找到定义.我们将会首先从双向链表数据结构开始介绍内核里的数据结构.为什么?因为它在内核里使用的很广泛,你只 ...

  10. linux内核安全数据,【漏洞分析】Linux内核XFRM权限提升漏洞分析预警(CVE–2017–16939)...

    0x00 背景介绍 2017年11月24日, OSS社区披露了一个由独立安全研究员Mohamed Ghannam发现的一处存在于Linux 内核Netlink socket子系统(XFRM)的漏洞,漏 ...

最新文章

  1. PCL点云数据 滤波降噪
  2. 如何在面试中有条理的回答用户体验方面的问题
  3. leetcode 492. 构造矩形(Java版,三种解法)
  4. 从零写一个编译器(三):语法分析之几个基础数据结构
  5. ASP.Net中OnBeforeUnLoad事件中调用__doPostBack不起作用?
  6. Matlab中 pdist 函数详解
  7. foreach循环符合就不往下走了_柴油发电机组冷却液循环故障解决方法
  8. Scrapy-Link Extractors(链接提取器)
  9. procreate 笔刷_Procreate新手漫画入门:笔刷,图层,上色
  10. ionic4页面常用判断
  11. C#调用VC DLL 复杂结构 解决方法
  12. Ubuntu 16.04 解压rar问题解决方案
  13. Notepad++插件安装和使用和打开大文件
  14. 每个设计师都应该了解的IOS编年史
  15. js代码实现百度换肤
  16. Nodemailer 使用Gmail发送邮件
  17. Office 2010/2007 简繁体转换按钮不见了?
  18. PHP将商品详情中的尺码表重新进行数据整合并翻译
  19. 微信小程序 定位 获取经纬度城市街道等位置信息
  20. 安卓手游脚本开发!闭关在家37天“吃透”这份345页PDF,已开源

热门文章

  1. [Linked List]Intersection of Two Linked Lists
  2. 【纯干货】4年前想解决的事情,今天才实验成功
  3. arcgis for flex 学习笔记(一)
  4. Dependency, Association, Aggregation Composition的四种区别
  5. 对文件夹添加共享属性!
  6. Android 调整控件位置和大小(以textView为例,并设置字体与背景颜色)
  7. Subversion代码提交中的org.apache.subversion.javahl.ClientException: svn: E200007: Commit failed异常解决...
  8. 转:在Linux中Oracle安装成功后,首次启动使用时,会出现的一些问题总结和解决办法...
  9. c#:浅克隆和深克隆,序列化和反序列化
  10. 基于RTP/RTCP流媒体服务器技术研究