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内核链表]
链表简介 链表是一种常见的数据结构,它通过指针将一系列数据节点连接成一条数据链.相对于数组,链表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入 ...
随机推荐
- JAVA学习笔记(33-53)
33:java中的多维数组,以二位为例: 创建方法:int[][] a = new int[2][3]; 建立一个5*5的数组. 或者下面的建立方法也可以: int[][] c = { {1, 2, ...
- 广东地区电信官方DNS服务器
以下是广东地区电信官方DNS服务器,简单记录,以备后用! 主解析服务器: 202.96.128.143 202.96.128.68 202.105.80.210 缓存服务器(亦可作DNS解析之用) c ...
- 《SSM框架搭建》一.构建maven web项目
一.创建maven工程File-New-other-Maven Project 二.设置项目工作空间,取消默认勾选,手动设置 三.选择internal,输入web,选择maven.archetypes ...
- 编程模式之观察者模式(Observer)
观察者模式由四个角色组成:抽象主题角色,抽象观察者角色,具体主题角色,抽象观察者角色,具体观察者角色. 抽象主题角色(Subject):把所有的观察者角色的引用保存在一个集合中,可以有任意数量的观察者 ...
- mysql存储过程详细教程
记录mysql存储过程中的关键语法:DELIMITER // 声明语句结束符,用于区分;CREATE PROCEDURE demo_in_parameter(IN p_in int) 声明存储过程 ...
- 用fxc.exe编译shader文件(*.fx, *.hlsl)的设置
原文出自:http://msdn.microsoft.com/en-us/library/windows/desktop/bb509709(v=vs.85).aspx#Profiles 拿DX11 S ...
- javaSwing
一.使用java Swing写个登陆界面,感受一下布局管理器的特性和熟悉一下控件的使用 package com.swing; import java.awt.BorderLayout; import ...
- 循序渐进Python3(十一) --0-- web之html
HTML: HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写,他是一种制作万维网页面标准语言(标记). 相当于定义统一的一套规则,大家都来遵守他,这样就可以 ...
- jquery实现动画
animate() 方法 语法: $(selector).animate({params},speed,callback); 必需的 params 参数定义形成动画的 CSS 属性. 可选的 spee ...
- linux学习笔记--NFS
NFS分为客户端,服务端.客户端要访问服务端,要写从RPC服务获得端口. 整个流程分为: 1,服务端rpc服务先启动2,nfs服务把端口上报给rpc服务3,rpc客户端通过服务端的rpcbind拿到访 ...