7、linux内核链表
7.1、前述链表数据区域的局限性
(1)之前定义数据区域时直接int data
;我们认为我们的链表中需要存储的是一个int类型的数。但是实际上现实编程中链接中的节点不可能这么简单,而是多种多样的。
(2)一般实际项目中的链表,节点中存储的数据其实是一个结构体,这个结构体中包含若干的成员,这些成员加起来构成了我们的节点数据区域。
7.2、一般性解决思路:数据区封装为一个结构体
(1)因为链表实际解决的问题是多种多样的,所以内部数据区域的结构体构成也是多种多样的。这样也导致了不同程序当中的链表总体构成是多种多样的。导致的问题是:我们无法通过一个泛性的、普遍适用的操作函数来访问所有的链表。这就意味着我们设计一个链表就得写一套链表的操作函数(节点创建、插入、删除、遍历······)
(2)实际上深层次分析会发现:不同的链表虽然这些方法不能通用需要单独写,但是实际上内部的思路和方法是相同的,只是函数的局部地区有不同。(实际上链表操作是相同的,而涉及到数据区域的操作就有不同)
(3)鉴于以上2点:我们的理念就是,能不能有一种办法把所有链表中操作方法里共同的部分提取出来用一套标准方法实现,然后把不同的部分留着让具体链表的实现者自己去处理。
7.3、内核链表的设计思路
(1)内核链表中自己实现了一个纯链表(纯链表就是没有数据区域,只有前后向指针)的封装,以及纯链表的各种操作函数(节点创建、插入、删除、遍历······)。这个纯链表本身自己没有任何用处,它的用法是给我们具体链表作为核心来调用。
7.4、list.h文件简介
(1)内核中核心纯链表的实现在include/linux/list.h文件中
(2)list.h中就是一个纯链表的完整封装,包含节点定义和各种链表操作方法。
#include <linux/list.h>
struct driver_info
{
int data;
};
// driver结构体用来管理内核中的驱动
struct driver
{
char name[20]; // 驱动名称
int id; // 驱动id编号
struct driver_info info; // 驱动信息
struct list_head head; // 内嵌的内核链表成员
};
struct driver2
{
char name[20]; // 驱动名称
int id; // 驱动id编号
struct driver_info info; // 驱动信息
//struct list_head head; // 内嵌的内核链表成员
struct driver *prev;
struct driver *next;
};
// 分析driver结构体,可知:前三个成员都是数据区域成员(就是我们之前简化为int data的东西),第4个成员 是一个struct list_head类型的变量,这就是一个纯链表。
// 本来driver结构体是没有链表的,也无法用链表来管理。但是我们driver内嵌的head成员本身就是一个纯链表,所以driver通过head成员给自己扩展了链表的功能。
// driver通过内嵌的方式扩展链表成员,本身不只是有了一个链表成员,关键是可以通过利用list_head本身事先实现的链表的各种操作方法来操作head。
// 最终效果:我们可以通过遍历head来实现driver的遍历;遍历head的函数在list.h中已经事先写好了,所以我们内核中去遍历driver时就不用重复去写了。
// 通过操作head来操作driver,实质上就是通过操作结构体的某个成员变量来操作整个结构体变量。这里面要借助container_of宏
7.5、内核链表的基本算法和使用简介
7.5.1、内核链表的节点创建、删除、遍历等
7.5.2、内核链表的使用实践
(1)问题:内核链表只有纯链表,没有数据区域,怎么使用?
(2)设计的使用方法是将内核链表作为将来整个数据结构的结构体的一个成员内嵌进去。