内核中的定义:

struct hlist_head {
    struct hlist_node *first;
};

struct hlist_node {
    struct hlist_node *next, **pprev;
};
这个数据结构与一般的hash-list数据结构定义有以下的区别:

1) 首先,hash的头节点仅存放一个指针,也就是first指针,指向的是list的头结点,没有tail指针也就是指向list尾节点的指针,这样的考虑是为了节省空间——尤其在hash bucket很大的情况下可以节省一半的指针空间.

2) list的节点有两个指针,但是需要注意的是pprev是指针的指针,它指向的是前一个节点的next指针;其中首元素的pprev指向链表头的fist字段,末元素的next为NULL. (见下图).

现在疑问来了:为什么pprev不是prev也就是一个指针,用于简单的指向list的前一个指针呢?这样即使对于first而言,它可以将prev指针指向list的尾结点.

主要是基于以下几个考虑:
1) hash-list中的list一般元素不多(如果太多了一般是设计出现了问题),即使遍历也不需要太大的代价,同时需要得到尾结点的需求也不多.

2) 如果对于一般节点而言,prev指向的是前一个指针,而对于first也就是hash的第一个元素而言prev指向的是list的尾结点,那么在删除一个 元素的时候还需要判断该节点是不是first节点进行处理.而在hlist提供的删除节点的API中,并没有带上hlist_head这个参数,因此做这 个判断存在难度.

3) 以上两点说明了为什么不使用prev,现在来说明为什么需要的是pprev,也就是一个指向指针的指针来保存前一个节点的next指针--因为这样做即使 在删除的节点是first节点时也可以通过*pprev = next;直接修改指针的指向.来看删除一个节点和修改list头结点的两个API:

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; //此时n是hash的first指针,因此它的pprev指向的是hash的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指针,而当该节点是first节点时指向自己,因此两种情况下不论该节点是一般的节点还是头结点都可以通过这个操作删除掉所需删除的节点
if (next)
next->pprev = pprev;
}

Linux内核 hlist_head/hlist_node结构解析的更多相关文章

  1. 【驱动】网卡驱动·linux内核网络分层结构

    Preface   Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程 ...

  2. [转]linux内核网络分层结构

    Preface   Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程 ...

  3. Linux 网络设备驱动开发(一) —— linux内核网络分层结构

    Preface Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程,又 ...

  4. 基于tiny4412的Linux内核移植 --- aliases节点解析

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  5. Linux内核学习笔记2——Linux内核源码结构

    一 内核组成部分 内核是一个操作系统的核心,主要由五个部分组成:进程调度,内存管理,虚拟文件系统,网络结构,进程间通信. 1.进程调度(SCHED) 控制进程对CPU的访问.当需要选择下一个进程运行时 ...

  6. linux内核源码结构

    一.概述 Linux内核庞大,但是这些文件的结构还是有章可循的,分别位于不同的目录下,各个目录功能相对独立. 二.源码结构表 目录名 描述 arch 体系结构相关的代码,对于每个架构的CPU,arch ...

  7. linux内核之链表操作解析

    本文只是对linux内核中的链表进行分析.内核版本是linux-2.6.32.63.文件在:linux内核/linux-2.6.32.63/include/linux/list.h.本文对list.h ...

  8. 基于tiny4412的Linux内核移植 --- aliases节点解析【转】

    转自:https://www.cnblogs.com/pengdonglin137/p/5252348.html 阅读目录(Content) 作者信息 平台简介 正文 回到顶部(go to top) ...

  9. Linux内核源代码的结构(转)

    源代码所有在目录:/usr/src/linux (大部分linux发行版本中)  init 内核初始化代码  kernel 内核核心部分:进程.定时.程序执行.信号.模块...  mm 内存处理  a ...

随机推荐

  1. JS模块与命名空间的介绍

    起因将代码组织到类中的一个重要原因是让代码更加“模块化”,可以在很多不同的场景中实现代码的重用.但类不是唯一的模块化代码的方式. 一般来讲,模块是一个独立的JS文件.模块文件可以包含一个类定义.一组相 ...

  2. jmeter java请求

    demo下载地址http://yun.baidu.com/share/link?shareid=4277735898&uk=925574576 1.引用jmeter的jar包 到jmeter的 ...

  3. hibernate实体类型映射文件

    1.通过写hibernate映射文件.实体类型转换为数据库中的表 据实体类型而写的. 实体类型User.java package cn.wwh.www.hibernate.dd.property; i ...

  4. WindowsService开发遇到的问题

    1.Windows服务安装 通过cmd运行指令安装Windows服务: 安装方法:运行cmd,以InstallUtil.exe 来运行 Windows服务程序. 如果是Release模式的话,直接在c ...

  5. SVN服务器搭建(1)

    转自:http://www.cnblogs.com/xiaobaihome/archive/2012/03/20/2407610.html SVN服务器搭建和使用(一) Subversion是优秀的版 ...

  6. 使用ServletContext读取properties配置文件

    创建配置文件: 1.在项目的任意地方,右键->New->File->FileName->输入->名称.properties(比如:config.properties) 2 ...

  7. Android手机外置SD卡(TF卡)的获取方法

    Android手机上的外置SD卡,起初的时候,即在Android出世的前几年,那时手机的存储是十分有限的,不像现在到处可见16G.32G和64G的存储,因而那时候的手机有的厂商允许插入外置的SD卡,此 ...

  8. MacVim小试

    Mac OS X使用之——新年第一天弘法寺许愿,MacVim小试 分类: Vi/Vim Mac OS X2013-01-01 22:08 3371人阅读 评论(0) 收藏 举报   目录(?)[+] ...

  9. 如何在数据库中存储IP地址

    最近改一个比较老的web系统,该系统是通过账号或者ip地址(白名单)验证限制访问权限的. 由于运营的时间比较长了,发现进入网站巨卡... 原因就是:之前的数据库(sqlserver)存储ip地址是用的 ...

  10. Day4:T1小技巧(类似于指针操作)T2搜索+小细节

    Day4:其中有很多小技巧get T1 一直没有听到过像这样的小技巧的略专业名词,有点类似于指针操作,之前有碰到过很多这样的题目 每次都是以不同的形式出现,但是感觉思想还是有点接近的吧(就比如某天有一 ...