我们都知道Linux内核里的双向链表和学校里教给我们的那种数据结构还是些不一样。Linux采用了一种更通用的设计,将链表以及其相关操作函数从数据本身进行剥离,这样我们在使用链表的时候就不用自己去实现诸如节点的插入、删除、遍历等操作了。当然,Linux也是从2.1.x内核开始才对链表进行了这样的统一,和我们目前看到的样子几乎差不多:

点击(此处)折叠或打开

  1. struct list_head {
  2. struct list_head *next, *prev;
  3. };

在2.6.21里这个数据结构定义在include/liinux/list.h头文件里,但是在3.4.1内核里,以及后面要介绍的哈希链表的定义都放在include/linux/types.h头文件里。而本文将以3.4.1内核为例进行介绍,其实对链表来说内核的版本号几乎没什么影响,只要掌握了Linux设计链表的精髓,万变不离其宗。

今天我们首先来聊聊链表。从上述定义代码我们可以看出,Linux内核的链表是双向链表,如果我们要将自己的数据结构以链表的形式进行组织,那么只要在我们自己的数据结构里,增加一个struct list_head{}类型的结构体成员对象就可以了,这样,我们就可以很方便地使用内核提供给我们的一组标准接口来对链表进行各种操作。
   如果我们需要定义一个链表,内核有LIST_HEAD(name)这样的函数供我们使用:

点击(此处)折叠或打开

  1. #define LIST_HEAD(name) \
  2. struct list_head name = LIST_HEAD_INIT(name)

其中#define LIST_HEAD_INIT(name) { &(name), &(name) },其实这样的代码已经太简单不过了。如果我们要定义一个名为student_list的链表,直接LIST_HEAD(student_list)就可以了,展开后等价于下面的代码:

点击(此处)折叠或打开

  1. struct list_head student_list= { &(student_list), &(student_list) };

跟内核通知链类似,如果我们已经有了一个链表对象student_listINIT_LIST_HEAD()接口可以对它初始化。所以,LIST_HEAD(student_list)代码和下面的代码是等价的:

点击(此处)折叠或打开

  1. struct list_head student_list
  2. INIT_LIST_HEAD (&student_list);

假如,我们现在要定义一个学生的结构体,并让其组织成链表的形式,可以这样做:

点击(此处)折叠或打开

  1. #define NAME_MAX_SIZE 32
  2. typedef struct student{
  3. char name[NAME_MAX_SIZE];    /*姓名*/
  4. unsigned char sex;              /*性别:m-男生;f-女生*/
  5. unsigned char age;              /*年龄*/
  6. struct list_head stu_list;  /*所有的学生最终通过这个结构串成链表*/
  7. }Student;

那么在写代码时,如果是通过kmalloc之类的函数动态创建节点,我们就可以用下面代码对链表节点进行初始化:

点击(此处)折叠或打开

  1. Student *stu1;
  2. stu1 = kmalloc(sizeof(*stu1), GFP_KERNEL);
  3. strcpy(stu1->name,”xiaoming”);
  4. stu1->sex = ‘m’;
  5. stu1;
  6. INIT_LIST_HEAD(&stu1-> stu_list); /*和下面的用法注意区别*/

如果是静态定义结构体变量的话就更简单了:

点击(此处)折叠或打开

  1. Student stu2={
  2. .name={“xiaohong”},
  3. .sex=’f’,
  4. ,
  5. .stu_list = LIST_HEAD_INIT(stu2.stu_list); /*和上面的用法注意区别*/
  6. };

有了数据节点,接下来就要对其进行操作了,内核提供了一组常用接口用于对双向链表操作,如下。

还有关于链表的分割list_cut_position(*list,*head,*entry)以及合并list_splice(*list,*head)、list_splice_init
(*list,*head)、list_splice_tail (*list,*head)、list_splice_tail_init
(*list,*head)这几个API用法也都非常简单,对照内核源码的注释很轻松就可以上手了。

通过上面的图我们可以看出来,在内核中当我们提及链表头的时候其实并没有牵扯到我们自己的结构体数据本身,链表头的next所指向的节点才是真正意义上的“链表头节点”,prev所指向的节点叫做“链表尾节点”。注意,不要把链表头和链表的头节点混为一谈。有了这个认识之后,我们就知道如果链表头的next和prev都指向链表头本身的话,那么这个链表其实就是空的,例如list_empty()或者list_empty_careful()所做的事情就是给定一个链表头,判断其是否为空,即是否包含任何有效的数据节点。同样地,如何判断链表是否只有一个节点呢?看看list_is_singular()的实现就豁然开朗了,真的是so easy。

最后,将前面提及的API总结到下表2.1中,方便大家查阅。

需要注意的是,上述所有链表操作函数的入参都是struct list_head{}的指针类型,这一点需要时刻牢记在心。

未完,待续…

Linux内核【链表】整理笔记(1)的更多相关文章

  1. Linux内核分析课程笔记(一)

    linux内核分析课程笔记(一) 冯诺依曼体系结构 冯诺依曼体系结构实际上就是存储程序计算机. 从两个层面来讲: 从硬件的角度来看,冯诺依曼体系结构逻辑上可以抽象成CPU和内存,通过总线相连.CPU上 ...

  2. C语言 Linux内核链表(企业级链表)

    //Linux内核链表(企业级链表) #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> ...

  3. 深入分析 Linux 内核链表--转

    引用地址:http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/index.html 一. 链表数据结构简介 链表是一种常用的组织有序数据 ...

  4. Linux 内核链表

    一 . Linux内核链表 1 . 内核链表函数 1.INIT_LIST_HEAD:创建链表 2.list_add:在链表头插入节点 3.list_add_tail:在链表尾插入节点 4.list_d ...

  5. linux内核链表分析

    一.常用的链表和内核链表的区别 1.1  常规链表结构        通常链表数据结构至少应包含两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系.按照指针域的组织以及各个节 ...

  6. 深入分析 Linux 内核链表

    转载:http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/   一. 链表数据结构简介 链表是一种常用的组织有序数据的数据结构,它通过指 ...

  7. Linux 内核 链表 的简单模拟(2)

    接上一篇Linux 内核 链表 的简单模拟(1) 第五章:Linux内核链表的遍历 /** * list_for_each - iterate over a list * @pos: the & ...

  8. Linux 内核 链表 的简单模拟(1)

    第零章:扯扯淡 出一个有意思的题目:用一个宏定义FIND求一个结构体struct里某个变量相对struc的编移量,如 struct student { int a; //FIND(struct stu ...

  9. linux内核链表的移植与使用

    一.  Linux内核链表为双向循环链表,和数据结构中所学链表类似,具体不再细讲.由于在内核中所实现的函数十分经典,所以移植出来方便后期应用程序中的使用. /********************* ...

  10. [国嵌攻略][108][Linux内核链表]

    链表简介 链表是一种常见的数据结构,它通过指针将一系列数据节点连接成一条数据链.相对于数组,链表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入 ...

随机推荐

  1. .NET (五)委托第五讲:内置委托Predicate

    // 摘要: // 表示定义一组条件并确定指定对象是否符合这些条件的方法. // // 参数: // obj: // 要按照由此委托表示的方法中定义的条件进行比较的对象. // // 类型参数: // ...

  2. 在spring框架中配置Quartz定时器发生错误: class org.springframework.scheduling.quartz.JobDetailBean has interface org.quartz.JobDetail as sup

    这是由于spring和Quartz的不兼容性造成的.我的spring是4.0.2,但是Quartz是2.2.3的,换了一个1.8版本的Quartz就解决问题了.

  3. 如果客户端禁用cookie,session还能使用吗?

    记得在以前找工作的时候,可多次被问到如果客户端被禁用cookie,session还能使用吗? 今天终于找到了相关的答案:我们来看一下: session是在服务器段保持会话数据的一种方法,对应的cook ...

  4. GIS基本概念

    简介 WKT(Well-known text)是开放地理空间联盟OGC(Open GIS Consortium )制定的一种文本标记语言,用于表示矢量几何对象.空间参照系统及空间参照系统之间的转换. ...

  5. 关于spring AOP的学习

    比较好的帖子http://www.cnblogs.com/xing901022/p/4265544.html

  6. DOM Document

    1.DOM Document对象 定义:每个载入浏览器的 HTML 文档都会成为 Document 对象.Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问. Docume ...

  7. android超快模拟器Ggenymotion的安装和配置

    原文地址:http://xiaochu.blog.51cto.com/1048262/1639613 Genymotion是一款运行速度快,且易于使用的android模拟器,非常适合与android程 ...

  8. redis 扩展 安装 和 memcached 安装

    在Windows下为PHP5.6安装redis扩展和memcached扩展   一.php安装redis扩展   1.使用phpinfo()函数查看PHP的版本信息,这会决定扩展文件版本       ...

  9. C#继承里的【就近原则】

    参考博客:http://www.cnblogs.com/waynechan/p/3570702.html#2883966 参考书籍:<你必须知道的.NET>,作者王涛http://www. ...

  10. oracle 的 nubmer 类型与 C# 的 float double decimal 对应关系

    如果先有oracle 数据的情况下,怎么对应到C#中的类型. 在oralce 中 在dba_tab_columns表中, Data_type表示字段类型:Data_length表示字段类型的长度:Da ...