IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Linux内核数据结构之双向循环链表 list_head -> 正文阅读

[数据结构与算法]Linux内核数据结构之双向循环链表 list_head

目录

概述

内核中 list_head 的实现

1.list_head 结构体

2.list_head 实现的操作

3.list_entry 宏

list_head 的使用

结语


概述

Linux 内核中双向循环链表采用将抽象结构和数据分离的特殊方式实现,即将抽象链表 list_head 嵌入到储存具体数据的数据结构中,通过 list_entry 宏(下文有讲解)将链表结构与数据连接起来。


内核中 list_head 的实现

list.h 文件实现了list_head 的所有功能,可在 .../include/linux 中找到(博主用的2.6版本内核)

1.list_head 结构体

struct list_head {
	struct list_head *next, *prev;
};

list_head 的结构非常简单, 只储存其前后节点,这符合其抽象的特征。

2.list_head 实现的操作

list.h 内部实现的对链表的操作都是对 list_head 链表自身的,包括初始化、插入、删除、移动等操作,但并不实现对其所嵌入数据结构的操作,篇幅原因这里仅简单例举初始化和插入操作.

初始化

//使用举例 list_head *head = LIST_HEAD_INIT(head)
#define LIST_HEAD_INIT(name) { &(name), &(name) }

//使用举例 list_head *head;
//         LIST_Head(head);
#define LIST_HEAD(name) \
	struct list_head name = LIST_HEAD_INIT(name)

//使用举例 list_head *head;
//         INIT_LIST_HEAD(head);
static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

需要注意的是因为是双向循环链表因此初始化后的头节点的 prev 和 next 均指向 head 自己

插入

//在两个已知节点间插入
static inline void __list_add(struct list_head *new,
			      struct list_head *prev,
			      struct list_head *next)
{
	next->prev = new;
	new->next = next;
	new->prev = prev;
	prev->next = new;
}

//在 head 节点后添加新节点
static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}

//在 head 节点前添加新节点 即插入到尾部
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

list.h 中实现了三种插入操作,其中后两种操作便于实现队列。

list.h 中其他函数也并不复杂,请读者自行研究,下面我们来介绍一个重要的宏 list_entry?

3.list_entry 宏

list_head 结构和储存数据的结构联系起来的关键就是 list_entry 宏,这个宏一般是提供给与其绑定的数据结构使用的,首先我们看其定义:

#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

可以看到 list_entry 宏重新封装了另一个宏 container_of ,这个宏在内核中经常用到,定义如下:

#define container_of(ptr, type, member) ({	    \
	const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
	(type *)( (char *)__mptr - offsetof(type,member) );})

这个宏比较复杂,让我们来层层拆开来分析,首先我们看三个参数,ptr实例化结构体 member 指针type结构体的类型名称member 为被嵌入在结构体中的 某成员的名称

下面让宏的内容更整齐一些

{

????????const typeof( ((type *)0)->member ) *__mptr = (ptr);

????????(type *)( (char *)__mptr - offsetof(type, member) );

}

第一行巧妙的将0强制转换为 type?类型指针并指向其 member 成员然后再利用 typeof 获取此member 的类型即 typeof( ((type *)0)->member )? ,然后将 ptr 赋给此类型的临时指针 __mptr ,前置 const 防止 ptr 指向的内容被修改。【1.宏的参数中并未提供 member 的类型 2.宏 typeof 用于获取类型名称】

第二行 offsetof 用于获取 member 成员在数据结构中内存偏移,用 __mptr 指针减去偏移量即可获取 member 所嵌入数据结构的地址,即储存数据的结构的地址,之后将这个地址强转为 type 类型指针即可,宏返回的便是 type 类型的指针。【offsetof 由编译器提供用于计算元素相对于结构体头部的偏移】

下面的图将有助于理解:

?

用 list_node 的地址减去 data_size 即可取得 实例化的data_struct 的地址,注意图中所标识的地址是相对于结构体内部的。【注意这里的 data_size 为 指针 data 的da'xiao

使用如下:

struct data_struct {
    void *data;
    list_head list_node;
};

struct data_struct *temp = init();
list_entry(&temp->list_node, struct data_struct, list_node);

list_head 的使用

上面 data_struct 的例子便是使用 list_head 时数据的组织结构,list.h 中封装了对链表的很多操作使用时直接使用这些即可形成链表,而除 data 数据需要另行处理。

//此处援引上文中用到的 data_struct

struct data_struct *head = init();

//插入
struct data_struct *new_1 = init();
list_add(&new->list_node, &head->list_node);

//删除
struct data_struct *new_2 = init();
list_del(&new1->list_node);//仅仅删除抽象链表中的元素,并未清除任何内存

上面删除操作便很好的体现了数据和抽象链表的分离,当我们需要真正删除某一元素时需要自己定义删除函数来实现操作。

void list_del_m(struct data_struct *node)
{
    list_del(&node->list_node);
    free(node->data);
    node->data = NULL;
    free(node);
    node = NULL;
}

//援引上面代码
list_del_m(new_1);

结语

限于篇幅原因本文先写到这里,对于 list_head 的更多使用可以参考内核中 task 结构体及其相关功能,可以在 .../include/linux/sched.h 找到 task_struct 定义。

最后,非常感谢您能读到这里,有不妥之处敬请指正。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-01-01 14:11:59  更:2022-01-01 14:12:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 17:32:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码