当前位置: 首页 > news >正文

【补充】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.dtcms.com/a/291121.html

相关文章:

  • C语言自定义类型:联合体和枚举
  • CS231n-2017 Lecture4神经网络笔记
  • 【爬虫】05 - 爬虫攻防
  • 车载软件架构 --- 软件开发面临的问题
  • 神经网络——归一化层
  • 从 C# 到 Python:项目实战第五天的飞跃
  • Ubuntu 22 集群部署 Apache Doris 3.0.3 笔记
  • 音视频重回顾及nat内网穿透相关再整理笔记
  • Ubuntu 22.04 安装 Docker (安装包形式)
  • ESP32-S3 小电视学习笔记1:分光棱镜、QMI8658六轴惯导计、1.3英寸LCD屏
  • 4.Java创建对象有几种方式?
  • Spring Cloud——Spring Cloud LoadBalancer
  • 7月21日总结
  • C/C++---emplace和emplace_back
  • 企业IT管理——IT系统灾难恢复计划及实施步骤参考模板
  • rk3588 Android 12 添加framework层服务,HAL库,从硬件驱动层到上层APP,实现led灯控
  • OpenAI开发的一款实验性大型语言模型(LLM),在2025年国际数学奥林匹克竞赛(IMO)中达到了金牌水平
  • 数智管理学(三十七)
  • liunx宝塔面板部署easyswoole项目
  • 常规笔记本和加固笔记本的区别
  • React 中使用immer修改state摆脱“不可变”
  • 打造自己的 Jar 文件分析工具:类名匹配 + 二进制搜索 + 日志输出全搞定
  • 从一开始的网络攻防(六):php反序列化
  • UART串口
  • 什么是内网穿透?本地内网无公网IP如何实现互联网上远程访问?
  • 每日一题7.21
  • 自动化商品监控:利用淘宝API开发实时价格库存采集接口
  • springdoc-openapi-ui的使用教程
  • 嵌入式开发学习———Linux环境下C语言学习(十二)
  • 【Tools】Ubuntu24.04安装详细教程