Linux内核【链表】整理笔记(1)
我们都知道Linux内核里的双向链表和学校里教给我们的那种数据结构还是些不一样。Linux采用了一种更通用的设计,将链表以及其相关操作函数从数据本身进行剥离,这样我们在使用链表的时候就不用自己去实现诸如节点的插入、删除、遍历等操作了。当然,Linux也是从2.1.x内核开始才对链表进行了这样的统一,和我们目前看到的样子几乎差不多:
点击(此处)折叠或打开
- struct list_head {
- struct list_head *next, *prev;
- };
在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)这样的函数供我们使用:
点击(此处)折叠或打开
- #define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
其中#define LIST_HEAD_INIT(name) { &(name), &(name) },其实这样的代码已经太简单不过了。如果我们要定义一个名为student_list的链表,直接LIST_HEAD(student_list)就可以了,展开后等价于下面的代码:
点击(此处)折叠或打开
- struct list_head student_list= { &(student_list), &(student_list) };
跟内核通知链类似,如果我们已经有了一个链表对象student_list,INIT_LIST_HEAD()接口可以对它初始化。所以,LIST_HEAD(student_list)代码和下面的代码是等价的:
点击(此处)折叠或打开
- struct list_head student_list;
- INIT_LIST_HEAD (&student_list);
假如,我们现在要定义一个学生的结构体,并让其组织成链表的形式,可以这样做:
点击(此处)折叠或打开
- #define NAME_MAX_SIZE 32
- typedef struct student{
- char name[NAME_MAX_SIZE]; /*姓名*/
- unsigned char sex; /*性别:m-男生;f-女生*/
- unsigned char age; /*年龄*/
- struct list_head stu_list; /*所有的学生最终通过这个结构串成链表*/
- }Student;
那么在写代码时,如果是通过kmalloc之类的函数动态创建节点,我们就可以用下面代码对链表节点进行初始化:
点击(此处)折叠或打开
- Student *stu1;
- stu1 = kmalloc(sizeof(*stu1), GFP_KERNEL);
- strcpy(stu1->name,”xiaoming”);
- stu1->sex = ‘m’;
- stu1;
- INIT_LIST_HEAD(&stu1-> stu_list); /*和下面的用法注意区别*/
如果是静态定义结构体变量的话就更简单了:
点击(此处)折叠或打开
- Student stu2={
- .name={“xiaohong”},
- .sex=’f’,
- ,
- .stu_list = LIST_HEAD_INIT(stu2.stu_list); /*和上面的用法注意区别*/
- };
有了数据节点,接下来就要对其进行操作了,内核提供了一组常用接口用于对双向链表操作,如下。
还有关于链表的分割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)的更多相关文章
- Linux内核分析课程笔记(一)
linux内核分析课程笔记(一) 冯诺依曼体系结构 冯诺依曼体系结构实际上就是存储程序计算机. 从两个层面来讲: 从硬件的角度来看,冯诺依曼体系结构逻辑上可以抽象成CPU和内存,通过总线相连.CPU上 ...
- C语言 Linux内核链表(企业级链表)
//Linux内核链表(企业级链表) #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> ...
- 深入分析 Linux 内核链表--转
引用地址:http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/index.html 一. 链表数据结构简介 链表是一种常用的组织有序数据 ...
- Linux 内核链表
一 . Linux内核链表 1 . 内核链表函数 1.INIT_LIST_HEAD:创建链表 2.list_add:在链表头插入节点 3.list_add_tail:在链表尾插入节点 4.list_d ...
- linux内核链表分析
一.常用的链表和内核链表的区别 1.1 常规链表结构 通常链表数据结构至少应包含两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系.按照指针域的组织以及各个节 ...
- 深入分析 Linux 内核链表
转载:http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/ 一. 链表数据结构简介 链表是一种常用的组织有序数据的数据结构,它通过指 ...
- Linux 内核 链表 的简单模拟(2)
接上一篇Linux 内核 链表 的简单模拟(1) 第五章:Linux内核链表的遍历 /** * list_for_each - iterate over a list * @pos: the & ...
- Linux 内核 链表 的简单模拟(1)
第零章:扯扯淡 出一个有意思的题目:用一个宏定义FIND求一个结构体struct里某个变量相对struc的编移量,如 struct student { int a; //FIND(struct stu ...
- linux内核链表的移植与使用
一. Linux内核链表为双向循环链表,和数据结构中所学链表类似,具体不再细讲.由于在内核中所实现的函数十分经典,所以移植出来方便后期应用程序中的使用. /********************* ...
- [国嵌攻略][108][Linux内核链表]
链表简介 链表是一种常见的数据结构,它通过指针将一系列数据节点连接成一条数据链.相对于数组,链表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入 ...
随机推荐
- NHibernate系列文章二十七:NHibernate Mapping之Fluent Mapping基础(附程序下载)
摘要 从这一节起,介绍NHibernate Mapping的内容.前面文章都是使用的NHibernate XML Mapping.NHibernate XML Mapping是NHibernate最早 ...
- 答:SQLServer DBA 三十问之五:有关视图索引
5. 视图上我们能建索引吗,如果能建的话,会啥好处和坏处:视图上建索引和表上建索引有啥区别: ----------------------------------------------------- ...
- 通过Fiddler修改包
1.修改Request 命令行键入: bpu URL 优缺点:对单个URL打断点.不影响其他接口. 用途:可以修改客户端对服务器发送的请求,通过修改成不同的参数,验证服务器响应是否正确.通常用于模拟客 ...
- PHP memory_get_usage()管理内存
PHP memory_get_usage()管理内存 我们在实际编码中,要想实现对内存的查看和操作,许多程序员们第一个想到的就是PHP memory_get_usage()这个PHP脚本内存函数. 下 ...
- 通过类名获取spring里的Bean
import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactor ...
- springboot 使用c3p0数据库连接池的方法
使用springboot开发时,默认使用内置的tomcat数据库连接池,经常碰到这种情况:运行时间一长,数据库连接中断了.所以使用c3p0连接池吧. 引入的maven依赖: <dependenc ...
- GetStoredProcCommand和GetSqlStringCommand的区别
原文:http://www.ithao123.cn/content-4004602.html http://hi.baidu.com/847270942/blog/item/c224dd557ff74 ...
- MongoDB是一个介于关系数据库和非关系数据库之间的产品
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.他支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类型.M ...
- Host基本概念
TSO TSO是Time Sharing Option的缩写,是MVS的命令输入处理器. TSO是在操作系统的管理下,用来支持ISPF菜单式会话系统和资源管理设备( ...
- 36、重新复习html和css之二
(1)由于公司是意大利的网段, (2)而且公司的电脑是保密的, (3)文件发不出去, (4)U盘插不进去. (5)而且我们组的项目整体上已经开发完毕,客户暂时没有什么大的需求, 所以如果我不把这些技术 ...