【补充】Linux内核链表机制


专题文章:Linux内核链表与Pinctrl数据结构解析

目标: 深入解析Pinctrl子系统中,struct pinctrl如何通过内核链表,来组织和管理其多个struct pinctrl_state

1. 问题背景:一个设备,多种引脚状态

一个复杂的设备,例如一个现代WiFi/蓝牙二合一模块,在不同的工作模式下,对引脚的要求是不同的。我们假设它有四种状态,并在设备树中进行了如下声明:

// 在wifi_bt.dts中
&wifi_bt {...pinctrl-names = "default",      // (A) 高速数据传输状态"idle",         // (B) 低功耗空闲状态"sleep",        // (C) 深度睡眠状态"bt_sco";       // (D) 蓝牙语音通话状态pinctrl-0 = <&wifi_pins_default>;pinctrl-1 = <&wifi_pins_idle>;pinctrl-2 = <&wifi_pins_sleep>;pinctrl-3 = <&bt_sco_pins>;...
};

当内核解析这个设备时,它需要在内存中为wifi_bt设备建立一个数据结构,来清晰地管理这四种引脚状态(A, B, C, D)以及它们对应的具体配置。

2. 核心数据结构:pinctrlpinctrl_state

内核使用两个核心结构体来完成这个管理任务:

  • struct pinctrl (总管):
    • 这是为wifi_bt设备创建的一个总的Pinctrl信息管理器
    • 关键成员:
      struct pinctrl {// (P) ... 其他成员 ...// (Q) 一个链表头,是所有状态的“集合点”struct list_head states; // ... 其他成员 ...
      };
      
  • struct pinctrl_state (具体状态):
    • 每一种引脚状态(如"default"、“idle”)都对应一个这样的结构体实例。
    • 关键成员:
      struct pinctrl_state {// (R) 状态的名字const char *name;// (S) 一个链表头,用于存放此状态下的具体配置指令struct list_head settings; // (T) 一个链表节点,是把自己“挂”到总管链表上的“钩子”struct list_head node;     
      };
      
3. 组织方式:嵌入式链表

Linux内核不把struct pinctrl_state本身直接串起来,而是通过它们内部的node成员(T)来建立连接。struct pinctrl里的states成员(Q)就是这个链表的“头结点”或“锚点”。

可视化数据结构关系:

   (wifi_bt设备的总管: struct pinctrl)
+--------------------------------------------+
| (P) ...                                    |
| (Q) states: [ next ]---------------------->|  (指向A的node)
| ...                                        |
+--------------------------------------------+^| (D的node指向Q,形成闭环)|
+---------------------------------------------------------------------------------------------+
|                                                                                             |
|   (A) pinct-state "default"    (B) pinct-state "idle"     (C) pinct-state "sleep"    (D) pinct-state "bt_sco" |
|  +------------------------+   +------------------------+   +------------------------+   +------------------------+ |
|  | (R) name="default"     |   | (R) name="idle"        |   | (R) name="sleep"       |   | (R) name="bt_sco"      | |
|  | (S) settings: [ ... ]  |   | (S) settings: [ ... ]  |   | (S) settings: [ ... ]  |   | (S) settings: [ ... ]  | |
|  | (T) node: [ next ]---->|-->| (T) node: [ next ]---->|-->| (T) node: [ next ]---->|-->| (T) node: [ next ]-----|
|  +------------------------+   +------------------------+   +------------------------+   +------------------------+ |
|                                                                                             |
+---------------------------------------------------------------------------------------------+

关系解读:

  • pinctrl->states (Q) 是链表的起点和终点,它形成了一个环。
  • 它不直接指向pinctrl_state结构体(A, B, C, D)的开头。
  • 它指向的是下一个元素的node成员(T)。比如,从(Q)出发,可以找到(A)的node成员。从(A)的node成员,可以找到(B)的node成员,以此类推,最后(D)的node成员会指回(Q),形成闭环。
4. 核心操作:如何通过node找到state

这是理解的关键。当内核需要查找名为"idle"的状态时,它会遍历这个由node成员组成的链表。

  • 遍历: 内核代码会从pinctrl->states(Q)开始,沿着next指针逐个访问(A)、(B)、©、(D)的node成员(T)。
  • 获取node指针: 在遍历过程中,比如当它访问到(B)的node成员时,它得到一个指向struct list_head的指针,我们称之为node_ptr
  • 反向计算 (使用list_entry宏):
    • 已知信息:
      1. node_ptrnode成员在内存中的确切地址。
      2. struct pinctrl_statenode成员所在的外部大结构体的类型。
      3. nodenode成员在struct pinctrl_state这个类型定义中的名字。
    • 计算逻辑: 整个结构体的起始地址 = 成员的地址 - 成员在结构体内的偏移量。
    • 内核的实现: list_entry(node_ptr, struct pinctrl_state, node)这个宏会执行上述计算,并返回一个指向struct pinctrl_state的指针。在这个例子中,它会返回(B)这个pinctrl_state结构体的起始地址。
  • 比较与返回: 内核拿到pinctrl_state的指针后,就可以访问它的name成员®,与目标字符串"idle"进行比较。如果匹配,查找成功。
5. 结论
  • pinctrl_state中的node成员(T),是该状态实例能够被链表管理的**“连接件”**。
  • pinctrl中的states成员(Q),是所有状态实例的**“组织者”**和链表的入口。
  • 内核通过遍历由node组成的链表,并利用list_entry宏进行地址反向计算,来高效地查找和访问任何一个具体的pinctrl_state实例。这种“侵入式”设计是Linux内核中一种通用且高效的数据组织模式。

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

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

相关文章

本地部署Dify、Docker重装

需要先安装一个Docker&#xff0c;Docker就像是一个容器&#xff0c;将部署Dify的空间与本地环境隔离&#xff0c;避免因为本地环境的一些问题导致BUG。也确保了环境的统一&#xff0c;不会出现在自己的电脑上能跑但是移植到别人电脑上就跑不通的情况。那么现在就开始先安装Doc…

【每天一个知识点】非参聚类(Nonparametric Clustering)

ChatGPT 说&#xff1a;“非参聚类”&#xff08;Nonparametric Clustering&#xff09;是一类不预先设定聚类数目或数据分布形式的聚类方法。与传统“参数聚类”&#xff08;如高斯混合模型&#xff09;不同&#xff0c;非参聚类在建模过程中不假设数据来自于已知分布数量的某…

人形机器人CMU-ASAP算法理解

一原文在第一阶段&#xff0c;用重定位的人体运动数据在模拟中预训练运动跟踪策略。在第二阶段&#xff0c;在现实世界中部署策略并收集现实世界数据来训练一个增量&#xff08;残差&#xff09;动作模型来补偿动态不匹配。&#xff0c;ASAP 使用集成到模拟器中的增量动作模型对…

next.js刷新页面时二级菜单展开状态判断

在 Next.js 中保持二级菜单刷新后展开状态的解决方案 在 Next.js 应用中&#xff0c;当页面刷新时保持二级菜单的展开状态&#xff0c;可以通过以下几种方法实现&#xff1a; 方法1&#xff1a;使用 URL 参数保存状态&#xff08;推荐&#xff09; import { useRouter } from n…

网络基础DAY13-NAT技术

NAT技术internet接入方式&#xff1a;ADLS技术&#xff1a;能够将不同设备的不同信号通过分离器进行打包之后再internet中传输&#xff0c;到另一端的分离器之后再进行分离。传输到不同的设备中去。常见光纤接入方式internet接入认证方式&#xff1a;PPPoE&#xff1a;先认证再…

HBuilderX中设置 DevEco Studio路径,但是一直提示未安装

前言&#xff1a; HBuilderX中设置 DevEco Studio路径&#xff0c;但是一直提示未安装。 报错信息&#xff1a; 检测到鸿蒙工具链&#xff0c;请在菜单“工具->设置->运行配置”中设置鸿蒙开发者工具路径为 DevEco Studio 的安装路径&#xff0c;请参考 报错原因…

什么是GNN?——聚合、更新与循环

在传统的深度学习中&#xff0c;卷积神经网络&#xff08;CNN&#xff09;擅长处理网格结构数据&#xff08;如图像&#xff09;&#xff0c;循环神经网络&#xff08;RNN&#xff09;擅长处理序列数据&#xff08;如文本&#xff09;。但当数据以图的形式存在时&#xff08;如…

深入解析 Django REST Framework 的 APIView 核心方法

在 Python 3 中&#xff0c;Django 的 APIView 类是 Django REST Framework&#xff08;DRF&#xff09;中用于构建 API 视图的核心基类。它提供了一个灵活的框架来处理 HTTP 请求&#xff0c;并通过一系列方法支持认证、权限检查和请求限制等功能。self.perform_authenticatio…

神经网络——卷积层

目录 卷积层介绍 Conv2d 卷积动画演示 卷积代码演示 综合代码案例 卷积层介绍 卷积层是卷积神经网络&#xff08;CNN&#xff09;的核心组件&#xff0c;它通过卷积运算提取输入数据的特征。 基本原理 卷积层通过卷积核&#xff08;过滤器&#xff09;在输入数据&…

神经网络——线性层

在机器学习中&#xff0c;线性层&#xff08;Linear Layer&#xff09; 是一种基础的神经网络组件&#xff0c;也称为全连接层&#xff08;Fully Connected Layer&#xff09; 或密集层&#xff08;Dense Layer&#xff09;。 其严格的数学定义为&#xff1a;对输入数据执行线…

大模型高效适配:软提示调优 Prompt Tuning

The Power of Scale for Parameter-Efficient Prompt Tuning ruatishi 软提示向量 具体是什么 《The Power of Scale for Parameter-Efficient Prompt Tuning》中增加的部分是“软提示(soft prompts)”,这是一种针对特定下游任务,添加到输入文本中的可调参数序列。它与传统…

https正向代理 GoProxy

背景&#xff1a; 在安全隔离的内网环境中&#xff0c;部署于内网的应用如需调用公网第三方接口&#xff08;如支付、短信&#xff09;&#xff0c;可通过正向代理服务实现访问。 GoProxy 下载&#xff1a; https://github.com/snail007/goproxy/releases 使用文档&#xff…

Java IO流体系详解:字节流、字符流与NIO/BIO对比及文件拷贝实践

一、字节流与字符流&#xff1a;如何选择&#xff1f; 1.1 核心区别特性字节流字符流处理单位字节&#xff08;8位&#xff09;字符&#xff08;16位Unicode&#xff09;适用场景二进制文件&#xff08;图片/视频&#xff09;文本文件&#xff08;TXT/CSV&#xff09;编码处理需…

QT6 源,七章对话框与多窗体(5) 文件对话框 QFileDialog 篇二:源码带注释

&#xff08;13&#xff09;本源代码定义于头文件 qfiledialog . h &#xff1a; #ifndef QFILEDIALOG_H #define QFILEDIALOG_H#include <QtWidgets/qtwidgetsglobal.h> #include <QtCore/qdir.h> #include <QtCore/qstring.h> #include <QtCore/qurl.h…

关于Ajax的学习笔记

Ajax概念&#xff1a;是一门使用了js语言&#xff0c;可以使用于Javaweb&#xff0c;实现前端代码和后端代码连结的的一种异步同步&#xff08;不需要等待服务器相应&#xff0c;就能够发送第二次请求&#xff09;的一种技术&#xff0c;它主要用于网页内容的局部刷新&#xff…

The Missing Semester of Your CS Education 学习笔记以及一些拓展知识(三)

文章目录The Missing Semester of Your CS Education 学习笔记以及一些拓展知识Vim编辑器笔记部分程序员常用的编辑器Vim的模式Vim的普通模式Vim的插入模式Vim的可视模式Vim的替换模式Vim的命令行模式Vim的高级功能文本对象宏寄存器缓冲区标记代码折叠Vim的常用配置Vim的常用插…

PyTorch常用的简单数学运算

一、基础算术运算1. 逐元素运算a torch.tensor([1, 2, 3]) b torch.tensor([4, 5, 6])# 加减乘除 a b # [5, 7, 9] a - b # [-3, -3, -3] a * b # [4, 10, 18] a / b # [0.25, 0.4, 0.5]# 幂运算、平方根 a ** 2 # [1, 4, 9] torch.sqrt(a) # [1.0, 1.414, 1.732]2. 标…

C++ Lambda 表达式详解:从基础到实战

Lambda 表达式是 C11 引入的重要特性&#xff0c;它允许我们在代码中定义匿名函数&#xff0c;极大地简化了代码编写&#xff0c;尤其是在使用 STL 算法和多线程编程时。本文将详细介绍 Lambda 表达式的语法、特性及实际应用场景。什么是 Lambda 表达式&#xff1f;Lambda 表达…

Spring Boot注解详解

文章目录前言1. 核心启动注解SpringBootApplicationEnableAutoConfigurationSpringBootConfiguration2. 组件注解Component及其衍生注解ComponentServiceRepositoryControllerRestController3. 依赖注入注解AutowiredQualifierPrimary4. Web相关注解请求映射注解RequestMapping…

Web开发:ABP框架12——中间件Middleware的创建和使用

一、简介中间件可以用于鉴权、日志&#xff0c;拦截器可以用于指定方法或url的业务逻辑处理&#xff0c;两者分工不同&#xff0c;实现效果相似&#xff0c;先执行中间件&#xff0c;后执行拦截器&#xff0c;再到WebAPI接口。二、示例一个Token验证中间件三、代码1.Startup.cs…