浅谈Linux Kernel 中循环链表的实现

前阵子在弄缓存的时候,我们需要将qemu对于磁盘镜像文件写请求串成一个链表,最终将这个链表里面的写请求全部刷回到镜像文件里面,那么我们便需要一个强健,可靠的链表的接口,于是我们仿照Linux 2.4.0的内核,来造了这么一个链表的轮子。今天抽抽空来记录一下。
链表,估计学过数据结构这门课程的人都对其印象深刻,因为老师们都喜欢将它放在比较靠前的地方讲,很多人都认为链表是一种非常easy的数据结构。因为链表的逻辑非常地简单,每个节点就是分成指针和数据,一头一尾地通过指针将每个节点串起来,没有树形(二叉树,B-树,红黑树,基树等)的数据结构那样复杂的前驱和后继的结构,以及复杂的结构伸展变化的操作。并且链表的各种操作的复杂度也都非常容易估算出来。
可是,要实现一个非常可靠,通用,强健的链表数据结构却是很有难度的。
一般地,我们都会对一个循环链表的节点做出如下的定义:
struct node {
int val;
struct node *prev;
struct node *next;
};
代码很简单,构造完一个链表之后会有如下的效果:

这只是普通链表的实现,当我们需要创建另一种数据结构组成的链表的时候,如果还是按照上述的方法来创建一个链表,那么我们就不得不重新定义一个链表的节点
来存储这种数据结构,如果某个应用里面有一万种数据结构,那我们岂不要定义一万种链表的数据结构,及其查询,删除,修改的应用接口?
我们可以尝试着将链表单独地抽离出来,对其进行解耦,这样我们便可以将他当作一套通用的应用接口来使用了。
先来定义一下链表节点的访问入口:
typedef struct cir_list_head {
struct cir_list_head *prev, *next;
}gc_list;
现在我们为特定的存储数据来定义一个链表的节点:
typedef struct test_list{
gc_list list;
int val;
} test_list;
注意,我们在这里就将链表节点的访问接口打包插入到了链表节点里面,仔细体会,我们最终所构建的效果如下:

接下来我们来看看如何通过gc_list * 指针来访问这些节点里面的数据:
我们希望能够通过结构体test_list里面的list(gc_list)来获取数据val,也就是说在同一个结构体下,通过其中一个结构体成员来获取另一个结构体成员的信息,
这个时候,我们就需要用到一些奇技淫巧:
#define OFFSETOF(type, member) (size_t)&(((type *)0)->member)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *))->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - OFFSETOF(type,member) );})

盗用一幅图描述上面的这几行代码的意思,不用太多的文字的解释~~,通过上面的这个接口我们便可以随意地操控循环链表里面的各种数据啦~,这种封装方法
比较好的地方便是你再也不用为每一种链表里面的数据特意去重写一套API,注意这段代码和编译器平台相关(主要都在GCC平台下),并不具备100%的可移植性。
相关链接:
造的一个循环链表的轮子:https://github.com/jusonalien/VM-DB/blob/master/list.h
测试使用代码:https://github.com/jusonalien/VM-DB/blob/master/TestList.c
关于container_of宏的详尽的解释: http://radek.io/2012/11/10/magical-container_of-macro/
浅谈Linux Kernel 中循环链表的实现的更多相关文章
- (转)浅谈 Linux 系统中的 SNMP Trap
原文:https://www.ibm.com/developerworks/cn/linux/l-cn-snmp/index.html 简介 本文讲解 SNMP Trap,在介绍 Trap 概念之前, ...
- 浅谈Linux系统中如何查看进程
进程是一个其中运行着一个或多个线程的地址空间和这些线程所需要的系统资源.一般来说,Linux系统会在进程之间共享程序代码和系统函数库,所以在任何时刻内存中都只有代码的一份拷贝. 1,ps命令 作用:p ...
- 浅谈Linux系统中如何查看进程 ——ps,pstree,top,w,全解
进程是一个其中运行着一个或多个线程的地址空间和这些线程所需要的系统资源.一般来说,Linux系统会在进程之间共享程序代码和系统函数库,所以在任何时刻内存中都只有代码的一份拷贝. 1,ps命令 作用:p ...
- 浅谈linux系统中pdf文件的默认打开方式
atril.gimp和evince,三者均可以打开application/pdf格式文件.gimp为一款图像处理软件:atril为mate环境下常用的文档查看器:evince为gnome环境下常用的文 ...
- 浅谈Linux中的信号处理机制(二)
首先谢谢 @小尧弟 这位朋友对我昨天夜里写的一篇<浅谈Linux中的信号处理机制(一)>的指正,之前的题目我用的“浅析”一词,给人一种要剖析内核的感觉.本人自知功力不够,尚且不能对着Lin ...
- Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理
Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理 转自:https://www.jianshu.com/p/2b71ea919d49 本系列文章首发于我的个人博 ...
- 浅谈linux中shell变量$#,$@,$0,$1,$2,$?的含义解释
浅谈linux中shell变量$#,$@,$0,$1,$2,$?的含义解释 下面小编就为大家带来一篇浅谈linux中shell变量$#,$@,$0,$1,$2的含义解释.小编觉得挺不错的,现在就分享给 ...
- 浅谈 Linux 内核无线子系统
浅谈 Linux 内核无线子系统 本文目录 1. 全局概览 2. 模块间接口 3. 数据路径与管理路径 4. 数据包是如何被发送? 5. 谈谈管理路径 6. 数据包又是如何被接收? 7. 总结一下 L ...
- (转)浅谈 Linux 内核无线子系统
前言 Linux 内核是如何实现无线网络接口呢?数据包是通过怎样的方式被发送和接收呢? 刚开始工作接触 Linux 无线网络时,我曾迷失在浩瀚的基础代码中,寻找具有介绍性的材料来回答如上面提到的那些高 ...
随机推荐
- 大数据学习——mapreduce共同好友
数据 commonfriends.txt A:B,C,D,F,E,O B:A,C,E,K C:F,A,D,I D:A,E,F,L E:B,C,D,M,L F:A,B,C,D,E,O,M G:A,C,D ...
- loadrunner 场景设计-手工场景设计
概述 通过选择需要运行的脚本,分配运行脚本的负载生成器,在脚本中分配Vuser来建立手工场景 手工场景就是自行设置虚拟用户的变化,主页是通过设计用户的添加和减少过程,来模拟真实的用户请求模型,完成负载 ...
- [luoguP2962] [USACO09NOV]灯Lights(高斯消元 + dfs)
传送门 先进行高斯消元 因为要求最少的开关次数,那么: 对于关键元,我们可以通过带入消元求出, 对于自由元,我们暴力枚举,进行dfs,因为只有开关两种状态,0或1 #include <cmath ...
- To_Date函数用法
spl> select * from emp where dates between to_date('2007-06-12 10:00:0 ...
- BZOJ1986: [USACO2004 Dec] Dividing the Path 划区灌溉
L<=1000000的土地上用长度在2*A~2*B的线段覆盖所有点,且给定n<=1000个区间,每个区间上只允许有一条线段,求最少多少线段,无解-1. f[i]表示填前i个土地最少线段,f ...
- 跨多种环境部署 Gearman -改善应用程序性能和降低服务器负载
您可能想要将工作扩散到一个大型机器群体中,或者想要在不同语言和环境之间共享功能,那么开放源码的 Gearman 服务可以让您轻松地将工作分布到网络中的其他机器.本文将介绍 Gearman 的一些典型应 ...
- List排列组合
/** * 步骤::每次递归时,把原始数据和满足条件的工作空间复制一份,所有的操作均在复制文件中进行,目的就是保证不破坏原始数据, * 从而可以让一轮递归结束后可以正常进行下一轮. * 其次,把数据的 ...
- Java获取Linux系统cpu使用率
原文:http://www.open-open.com/code/view/1426152165201 import java.io.BufferedReader; import java.io.Fi ...
- Oracle: 通过命令行下载安装文件
1. 导出oracle cookies 参考:https://blog.pythian.com/how-to-download-oracle-software-using-wget-or-curl/ ...
- org.hibernate.AnnotationException: No identifier specified for entity:
使用hibernate的e-r映射pojo类的时候遇到org.hibernate.AnnotationException: No identifier specified for entity的异常, ...