浅谈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 无线网络时,我曾迷失在浩瀚的基础代码中,寻找具有介绍性的材料来回答如上面提到的那些高 ...
随机推荐
- Web前端静态页面示例
目录结构: Web25\ |—css\ reset.css.common.css.index.css.login.css.reg.css |—js\ jquery-3.3.1.js.index.js. ...
- Flask 架构 --xunfeng实例研究
文件结构 │ Config.py # 配置文件 │ README.md # 说明文档 │ Run.bat # Windows启动服务 │ Run.py # webserver │ Run.sh # L ...
- 80. Hibernate 5.0命名策略使用naming-strategy 不起作用【从零开始学Spring Boot】
[原创文章,转载请注明出处] 事情的起因:一不小心从1.3.3升级到了1.4.0版本,结果就碰到了各种悲催的事情了,好吧,Hibernate5.0的新特性就是其中一个坑,我们会发现我们配置的namin ...
- [codevs1050]棋盘染色 2
[codevs1050]棋盘染色 2 试题描述 有一个5*N的棋盘,棋盘中的一些格子已经被染成了黑色,你的任务是对最少的格子染色,使得所有的黑色能连成一块. 输入 第一行一个整数N(<=100) ...
- BestCoder Round #47 1003
solution : 就按题解敲了一遍,好久没写这种dp ; ; LL f[MAX][MAX]; ]; scanf( scanf(,b+); ...
- 【HDOJ6330】Visual Cube(模拟)
题意: 思路: import java.util.Scanner; public class Main { public static void main(String args[]) { Scann ...
- ES6__变量的解构赋值
/* 变量的解构赋值 */ /* 基本概念 : 本质上就是一种匹配模式,只要等号两边的模式相同,那么左边的变量就可以被赋予对应的值. 结构赋值主要分为: 1. 数组的解构赋值 2. 对象的结构赋值 3 ...
- MongoDB基本管理命令操作
1. 查看所有数据库: show dbs 或: show databases 注意: 该命令不会显示新创建的空数据库,若想显示需要向空数据库插入一些数据. MongoDB中默认的数据库为test,若果 ...
- 寒武纪camp Day4
补题进度:7/11 A(博弈论) 略 B 待填坑 C(贪心) 题意: 一个序列是good的当且仅当相邻两个数字不相同.给出一个长度为n的数列,每个数字是ai.定义一种操作就是把a中某个元素拿到首位去, ...
- Java面试题,深入理解final关键字
final关键字 final的简介 final可以修饰变量,方法和类,用于表示所修饰的内容一旦赋值之后就不会再被改变,比如String类就是一个final类型的类. final的具体使用场景 fina ...