linux 内核链表操作

news2024/12/24 20:52:17

操作系统内核, 如同其他程序, 常常需要维护数据结构的列表. 有时, Linux 内核已经同
时有几个列表实现. 为减少复制代码的数量, 内核开发者已经创建了一个标准环形的, 双
链表; 鼓励需要操作列表的人使用这个设施.
当使用链表接口时, 你应当一直记住列表函数不做加锁. 如果你的驱动可能试图对同一个
列表并发操作, 你有责任实现一个加锁方案. 可选项( 破坏的列表结构, 数据丢失, 内核
崩溃) 肯定是难以诊断的.
为使用列表机制, 你的驱动必须包含文件 <linux/list.h>. 这个文件定义了一个简单的
类型 list_head 结构:
struct list_head { struct list_head *next, *prev; };
真实代码中使用的链表几乎是不变地由几个结构类型组成, 每一个描述一个链表中的入口
项. 为在你的代码中使用 Linux 列表, 你只需要嵌入一个 list_head 在构成这个链表的
结构里面. 假设, 如果你的驱动维护一个列表, 它的声明可能看起来象这样:
struct todo_struct
{
struct list_head list;
int priority; /* driver specific */
/* ... add other driver-specific fields */
};
列表的头常常是一个独立的 list_head 结构. 图链表头数据结构显示了这个简单的
struct list_head 是如何用来维护一个数据结构的列表的.

                                                          链表头数据结构

链表头必须在使用前用 INIT_LIST_HEAD 宏来初始化. 一个"要做的事情"的链表头可能声
明并且初始化用:
struct list_head todo_list;
INIT_LIST_HEAD(&todo_list);
<para>可选地, 链表可在编译时初始化:</para>
LIST_HEAD(todo_list);
几个使用链表的函数定义在 <linux/list.h>:
list_add(struct list_head *new, struct list_head *head);
在紧接着链表 head 后面增加新入口项 -- 正常地在链表的开头. 因此, 它可用来
构建堆栈. 但是, 注意, head 不需要是链表名义上的头; 如果你传递一个
list_head 结构, 它在链表某处的中间, 新的项紧靠在它后面. 因为 Linux 链表
是环形的, 链表的头通常和任何其他的项没有区别.
list_add_tail(struct list_head *new, struct list_head *head);
刚好在给定链表头前面增加一个新入口项 -- 在链表的尾部, 换句话说.
list_add_tail 能够, 因此, 用来构建先入先出队列.
list_del(struct list_head *entry);
list_del_init(struct list_head *entry);
给定的项从队列中去除. 如果入口项可能注册在另外的链表中, 你应当使用
list_del_init, 它重新初始化这个链表指针.
list_move(struct list_head *entry, struct list_head *head);
list_move_tail(struct list_head *entry, struct list_head *head);

给定的入口项从它当前的链表里去除并且增加到 head 的开始. 为安放入口项在新
链表的末尾, 使用 list_move_tail 代替.
list_empty(struct list_head *head);
如果给定链表是空, 返回一个非零值.
list_splice(struct list_head *list, struct list_head *head);
将 list 紧接在 head 之后来连接 2 个链表.
list_head 结构对于实现一个相似结构的链表是好的, 但是调用程序常常感兴趣更大的结
构, 它组成链表作为一个整体. 一个宏定义, list_entry, 映射一个 list_head 结构指
针到一个指向包含它的结构的指针. 它如下被调用:
list_entry(struct list_head *ptr, type_of_struct, field_name);
这里 ptr 是一个指向使用的 struct list_head 的指针, type_of_struct 是包含 ptr
的结构的类型, field_name 是结构中列表成员的名子. 在我们之前的 todo_struct 结构
中, 链表成员称为简单列表. 因此, 我们应当转变一个列表入口项为它的包含结构, 使用
这样一行:
struct todo_struct *todo_ptr = list_entry(listptr, struct todo_struct, list);
list_entry 宏定义使用了一些习惯的东西但是不难用.
链表的遍历是容易的: 只要跟随 prev 和 next 指针. 作为一个例子, 假设我们想保持
todo_struct 项的列表已降序的优先级顺序排列. 一个函数来添加新项应当看来如此:
void todo_add_entry(struct todo_struct *new)
{
struct list_head *ptr;
struct todo_struct *entry;
for (ptr = todo_list.next; ptr != &todo_list; ptr = ptr->next)
{
entry = list_entry(ptr, struct todo_struct, list);
if (entry->priority < new->priority) {
list_add_tail(&new->list, ptr);
return;
}
}
list_add_tail(&new->list, &todo_struct)
}

但是, 作为一个通用的规则, 最好使用一个预定义的宏来创建循环, 它遍历链表. 前一个
循环, 例如, 可这样编码:
void todo_add_entry(struct todo_struct *new)
{
struct list_head *ptr;
struct todo_struct *entry;
list_for_each(ptr, &todo_list)
{
entry = list_entry(ptr, struct todo_struct, list);
if (entry->priority < new->priority) {
list_add_tail(&new->list, ptr);
return;
}
}
list_add_tail(&new->list, &todo_struct)
}
使用提供的宏帮助避免简单的编程错误; 宏的开发者也已做了些努力来保证它们进行正常.
存在几个变体:
list_for_each(struct list_head *cursor, struct list_head *list)
这个宏创建一个 for 循环, 执行一次, cursor 指向链表中的每个连续的入口项.
小心改变列表在遍历它时.
list_for_each_prev(struct list_head *cursor, struct list_head *list)
这个版本后向遍历链表.
list_for_each_safe(struct list_head *cursor, struct list_head *next, struct
list_head *list)
如果你的循环可能删除列表中的项, 使用这个版本. 它简单的存储列表 next 中下
一个项, 在循环的开始, 因此如果 cursor 指向的入口项被删除, 它不会被搞乱.
list_for_each_entry(type *cursor, struct list_head *list, member)
list_for_each_entry_safe(type *cursor, type *next, struct list_head *list,
member)
这些宏定义减轻了对一个包含给定结构类型的列表的处理. 这里, cursor 是一个
指向包含数据类型的指针, member 是包含结构中 list_head 结构的名子. 有了这
些宏, 没有必要安放 list_entry 调用在循环里了.

如果你查看 <linux/list.h> 里面, 你看到有一些附加的声明. hlist 类型是一个有一个
单独的, 单指针列表头类型的双向链表; 它常用作创建哈希表和类型结构. 还有宏用来遍
历 2 种列表类型, 打算作使用 读取-拷贝-更新 机制(在第 5 章的"读取-拷贝-更新"一
节中描述 ). 这些原语在设备驱动中不可能有用; 看头文件如果你愿意知道更多信息关于
它们是如何工作的.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1353213.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小妙招:教你如何查询获取企业工商数据

在行业互联网领域&#xff0c;工商数据的运用场景多种多样。无论是对产业链的上下游企业&#xff0c;还是为企业背景调查、客户获取、财税服务、知识产权服务&#xff0c;乃至于外贸企业&#xff0c;商品批发等业务&#xff0c;TO B服务型企业都离不开工商数据的支持。这些五花…

主动IRS(Active IRS)模型总结

文章目录 A Framework for Transmission Design for Active RIS-Aided Communication with Partial CSIHybrid active and passive IRS从已有passive IRS算法拓展到active IRS算法 active IRS 与 passive IRS模型的区别 A Framework for Transmission Design for Active RIS-Ai…

centos7 ping不通域名

如果ip能ping通&#xff0c;ping不通域名可以试试以下操作&#xff1a; 1.编辑resolv.conf文件 vi /etc/resolv.conf 添加 nameserver 8.8.8.8 2.编辑nsswitch.conf vi /etc/nsswitch.conf 改成下图所示&#xff1a; 3.编辑sysctl.conf vi /etc/sysctl.conf 加上两行内…

爬虫如何获取免费代理IP(二)

89ip代理爬取代码实现 一、代码实现 import requests import time import random from fake_useragent import UserAgent from lxml import etree import os import csv""" 89ip代理爬取 """class IPSipder(object):def __init__(self):self.u…

rime中州韵小狼毫 inputShow lua Translator 输入字符透传翻译器

在 rime中州韵小狼毫 help lua Translator 中我们分享了如何使用 lua 脚本定义一个 translator&#xff0c;并以 五笔・拼音 为例引用了该 translator&#xff0c;并且达到了预期的效果。 今天&#xff0c;我们继续通过 lua 脚本为 rime中州韵/小狼毫 输入法打造一个 translat…

【Linux Shell】1. Shell 简述

文章目录 【 1. Shell 解释器、Shell语言、Shell脚本 】【 2. Shell 环境 】【 3. 一个简单的 Shell 脚本 】3.1 Shell 脚本的编写3.2 Shell 脚本的运行3.2.1 作为可执行程序运行 Shell 脚本3.2.2 作为解释器参数运行 Shell 脚本 【 1. Shell 解释器、Shell语言、Shell脚本 】 …

C++学习(二)

我们是在学习过了C语言&#xff0c;基础上来看这篇文章的&#xff0c;如果你是直接学C&#xff0c;这篇文章不太适合你的&#xff0c;因为这里只讲C基础中与C语言不同之处。 一.main函数区别 在C语言中&#xff0c;我们写main函数是不是可以省略前面的int,但是在C中&#xff…

React(2): 使用 html2canvas 生成图片

使用 html2canvas 生成图片 需求 将所需的内容生成图片div 中包括 svg 等 前置准备 "react": "^18.2.0","react-dom": "^18.2.0","html2canvas": "^1.4.1",实现 <div ref{payRef}></div>const pa…

Character Controller Smooth

流畅的角色控制器 Unity的FPS解决方案&#xff01; 它是一种具有非常平滑运动和多种设置的解决方案&#xff1a; - 移动和跳跃 - 坐的能力 - 侧翻角度 - 不平整表面的处理 - 惯性守恒 - 重力 - 与物理物体的碰撞。 - 支持没有家长控制的平台 此解决方案适用于那些需要角色控制器…

Spring Boot 整合 MinIO自建对象存储服务

GitHub 地址&#xff1a;GitHub - minio/minio: The Object Store for AI Data Infrastructure 另外&#xff0c;MinIO 可以用来作为云原生应用的主要存储服务&#xff0c;因为云原生应用往往需要更高的吞吐量和更低的延迟&#xff0c;而这些都是 MinIO 的优势。安装过程跳过。…

(七)独立按键

文章目录 独立按键原理图三行代码法简单概述代码书写键码推算如何使用短按键长按键 状态机法简单概述代码书写键码推算如何使用短按键长按键 现象 独立按键原理图 三行代码法 简单概述 代码书写 u8 Trg 0x00;//短按键 u8 Cont 0x00;//长按键 void BtnThree(void) {u8 reada…

李沐机器学习系列2--- mlp

1 Introduction LP中有一个很强的假设&#xff0c;输入和输出是线性关系&#xff0c;这一般是不符合事实的。 通过几何的方式去对信息进行理解和压缩是比较高效的&#xff0c;MLP可以表示成下面的形式。 1.1 从线性到非线性 X ∈ R n d X \in R^{n \times d} X∈Rnd表示输入…

2023-12-26分割回文串和子集以及子集II

131. 分割回文串 思想&#xff1a;回溯三步骤&#xff01;① 传入参数 ② 回溯结束条件 ③ 单层搜索逻辑&#xff01;抽象成回溯树&#xff0c;树枝上是每次从头部穷举切分出的子串&#xff0c;节点上是待切分的剩余字符串【从头开始每次往后加一】 class Solution:def partiti…

基于混合蛙跳算法优化的Elman神经网络数据预测 - 附代码

基于混合蛙跳算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于混合蛙跳算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于混合蛙跳优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&…

ChatGPT持续火热,OpenAI年收入突破16亿美元

著名科技媒体The Information消息&#xff0c;由于OpenAI的ChatGPT等产品实现强劲增长&#xff0c;其年收入将突破16亿美元。 这主要得益于OpenAI在2023年发布了一系列重磅产品&#xff0c;包括GPT-4、ChatGPT企业版、GPT-4 Turbo、DALLE 3、苹果/安卓应用、自定义GPTs、Assis…

测试人必看,看完必会的fiddler抓包,抓包抓的好........

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

ELF文件信息一览

准备开个专栏&#xff0c;记录《从零开始实现链接器》的学习过程&#xff0c;先占个坑。 之前一直想把自己的学习过程记录在个人博客网站上&#xff0c;但这个要自己维护&#xff0c;上传图片什么的比较麻烦。关键是没有人互动&#xff0c;自己也没有怎么去看&#xff0c;慢慢的…

麒麟云增加计算节点

一、安装基座系统并配置好各项设置 追加的计算节点服务器&#xff0c;安装好系统&#xff0c;把主机名、网络网线&#xff08;网线要和其他网线插的位置一样&#xff09;、hosts这些配置好&#xff0c;在所有节点的/etc/hosts里面添加信息 在控制节点添加/kylincloud/multinod…

解决Redis序列化乱码问题

如果我们使用原生的JDK序列化&#xff0c;那么当我们将数据存储到Redis中就会出现乱码的情况 为了解决这个问题我们需要重写RedisTemplate从而解决序列化乱码问题 首先在Maven中引入相应的依赖 <dependency> <groupId>com.fasterxml.jackson.core</group…

交换机01_以太网

1、交换机工作原理 交换机是数据链路层的设备&#xff0c;数据链路层传输的是数据帧&#xff0c;所以封装的是MAC头部&#xff08;主要有源MAC地址和目的MAC地址&#xff09; 2、数据链路层的功能&#xff1a; 建立逻辑连接&#xff0c;进行物理地址寻址&#xff0c;差错校验…