Linux下PCIe子系统(二)——PCIe子系统框架详解

Linux下PCIe子系统(二)——PCIe子系统框架详解

1. 概述

PCIe(PCI Express)子系统是Linux内核中负责管理PCI/PCIe设备的核心组件。它提供了一套完整的框架来发现、配置和管理PCI设备,实现了设备的即插即用和热插拔功能。

2. 核心数据结构

PCIe子系统使用以下核心数据结构来组织和管理设备:
在这里插入图片描述

2.1 主要数据结构

结构体描述作用
struct pci_host_bridgeHost Bridge表示PCI域的根管理器,连接CPU和PCI总线
struct pci_busPCI总线表示代表一条PCI总线,可以挂载多个设备
struct pci_devPCI设备表示代表具体的PCI设备实例

2.2 数据结构关系

┌─────────────────────┐
│   pci_host_bridge   │ ← Host Bridge (根管理器)
└──────────┬──────────┘│▼
┌─────────────────────┐
│      pci_bus        │ ← PCI总线 (Bus 0)
│     (Root Bus)      │
└──────────┬──────────┘│▼
┌─────────────────────┐
│      pci_dev        │ ← PCI设备
│   (PCI设备/桥设备)   │
└─────────────────────┘

3. 驱动框架基础

3.1 设备分类

Linux设备驱动将设备分为三大类:

  • 字符设备 - 顺序访问的设备(如串口、键盘)
  • 块设备 - 随机访问的设备(如硬盘、U盘)
  • 网络设备 - 网络通信设备(如网卡)

3.2 字符设备注册流程

PCI设备通常以字符设备的形式加载到内核中,其注册流程包括:

  1. 注册设备号 - 获取主设备号和次设备号
  2. 创建并初始化cdev结构体 - 绑定file_operations
  3. 添加字符设备 - 将设备添加到内核
  4. 创建设备文件 - 在/dev下创建设备节点
  5. 实现file_operations - 实现设备操作函数集

注意: 学习PCIe子系统的目的是为专门的PCI设备开发对应的驱动程序。

4. PCI子系统实现流程

4.1 整体流程概览

内核启动
各子系统初始化
PCI子系统初始化
创建PCI总线结构
设备枚举
扫描PCI设备并创建设备对象
驱动加载
PCI驱动注册并匹配设备

4.2 时间线

启动时间线:
0ms     内核启动开始
2ms     pci_driver_init() - PCI总线类型注册
5ms     pcibios_init() - 架构相关初始化及设备枚举
8ms     pci_subsys_init() - PCI子系统完善
15ms    device_initcall() - 设备驱动匹配
20ms    用户空间启动

5. PCI子系统实现细节

5.1 PCI子系统初始化

5.1.1 pci_driver_init()函数

PCI子系统初始化通过pci_driver_init()函数实现,在内核中创建PCI总线(软件抽象):

static int __init pci_driver_init(void)
{int ret;// 注册PCI总线类型到设备模型ret = bus_register(&pci_bus_type);if(ret)return ret;// 添加DMA调试支持dma_debug_add_bus(&pci_bus_type);return 0;
}
5.1.2 关键功能
  • 总线注册: 调用bus_register(&pci_bus_type)(所有总线都通过此函数注册)
  • 设备模型集成: 将全局pci_bus_type结构体注册到内核设备模型
  • sysfs接口: 在/sys/bus/下创建pci目录,包含devicesdrivers子目录

5.2 设备枚举机制

5.2.1 枚举 vs 设备树

传统设备树方式

  • 在设备树中描述硬件信息
  • 通过compatible属性与驱动的of_match_table匹配
  • 静态配置,启动时解析一次

PCIe枚举方式

  • 动态发现设备
  • 支持即插即用和热插拔
  • 运行时设备管理

枚举定义: 在计算机硬件领域是指系统性地发现、识别和配置硬件设备的过程。

5.2.2 为什么PCIe使用枚举

使用设备树会破坏PCIe设备的核心优势:

  • 即插即用 - 新设备无法自动识别
  • 热插拔 - 运行时插拔设备无法处理
  • 标准化 - 失去PCI协议的标准化优势
5.2.3 PCIe设备枚举详细流程

阶段一:基础设施建设

pci_driver_init() → 注册PCI总线类型
pcibios_init() → 平台特定PCI配置  
pci_subsys_init() → 启动完整枚举流程

在这里插入图片描述

阶段二:Host Bridge注册和Root Bus创建

pci_scan_root_bus_bridge()
├── pci_register_host_bridge()    // 注册Host Bridge
│   ├── 创建Root Bus (Bus 0)
│   ├── 分配地址空间资源
│   │   ├── 内存窗口
│   │   └── I/O端口窗口
│   └── 注册到Linux设备模型
└── pci_scan_child_bus()          // 开始设备扫描

阶段三:递归设备扫描

核心扫描工作由pci_scan_child_bus_extend()完成,采用深度优先递归算法

pci_scan_child_bus_extend(bus, available_buses)
├── 第一阶段:设备扫描
│   └── for (devfn = 0; devfn < 0x100; devfn += 8) {  // 32设备×8功能
│       ├── pci_scan_slot(bus, devfn)
│       ├── pci_scan_single_device()
│       ├── pci_scan_device()      // 读取配置空间
│       └── pci_device_add()       // 添加到系统}
├── 第二阶段:桥设备处理
│   └── for_each_pci_bridge(dev, bus) {
│       ├── pci_add_new_bus()      // 创建子总线
│       ├── 分配下级总线号
│       └── pci_scan_child_bus_extend(child_bus)  // 递归!}
└── 第三阶段:资源分配├── pci_bus_size_bridges()     // 计算桥设备资源需求└── pci_bus_assign_resources() // 分配实际资源
5.2.4 设备发现机制

设备存在检测

// 读取vendor ID判断设备是否存在
pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendor_id);if (vendor_id == 0xffffffff || vendor_id == 0x00000000) {// 设备不存在return NULL;
}

配置空间信息读取

struct pci_dev *dev = pci_alloc_dev(bus);
dev->vendor = vendor_id & 0xffff;
dev->device = (vendor_id >> 16) & 0xffff;// 读取其他重要信息
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);
// 读取BAR寄存器等...
5.2.5 递归扫描示例
扫描Root Bus 0:├── 00:00.0 - Host Bridge├── 00:01.0 - PCIe Root Port (桥设备)│   └── 创建Bus 1 → 递归扫描│       └── 01:00.0 - 显卡├── 00:02.0 - 集成显卡├── 00:1c.0 - PCIe Root Port (桥设备)  │   └── 创建Bus 2 → 递归扫描│       └── 02:00.0 - 网卡└── 00:1f.0 - ISA桥└── 创建Bus 3 → 递归扫描

6. 设备驱动匹配机制

6.1 关键函数

PCIe设备与驱动的匹配依赖两个核心函数:

6.1.1 pci_bus_match()

功能:设备驱动匹配函数

static int pci_bus_match(struct device *dev, struct device_driver *drv)
{struct pci_dev *pci_dev = to_pci_dev(dev);struct pci_driver *pci_drv = to_pci_driver(drv);const struct pci_device_id *found_id;// 遍历驱动的设备ID表进行匹配found_id = pci_match_device(pci_drv, pci_dev);if (found_id)return 1;  // 匹配成功return 0;      // 匹配失败
}

匹配条件

  • Vendor ID: 厂商标识(由PCI-SIG分配)
  • Device ID: 设备标识(厂商自定义)
  • Subsystem Vendor ID: 子系统厂商ID(可选)
  • Subsystem Device ID: 子系统设备ID(可选)
  • Class Code: 设备类别(可选)

特殊值

  • 驱动中设置PCI_ANY_ID可匹配所有对应字段
6.1.2 pci_bus_probe()

功能:设备探测函数

static int pci_device_probe(struct device *dev)
{struct pci_dev *pci_dev = to_pci_dev(dev);struct pci_driver *drv = to_pci_driver(dev->driver);const struct pci_device_id *id;id = pci_match_device(drv, pci_dev);if (!id)return -ENODEV;// 调用驱动的probe函数return drv->probe(pci_dev, id);
}

6.2 匹配流程

匹配成功
匹配失败
设备/驱动注册
pci_bus_match调用
比对vendor/device信息
pci_bus_probe调用
尝试下一个驱动
pci_device_probe执行
调用驱动probe函数
设备驱动绑定完成
匹配失败

6.3 vendor和device信息

存储位置

  • PCI配置空间: 偏移0x00(Vendor ID)和0x02(Device ID)
  • 硬件固化: 设备制造时写入,无法修改

获取方式

# 使用lspci查看设备信息
lspci -nn
# 输出示例:
# 00:02.0 VGA compatible controller [0300]: Intel Corporation Device [8086:9bc4]
#                                                     ^^^^  ^^^^
#                                                   vendor device

驱动中的使用

static const struct pci_device_id my_pci_ids[] = {{ PCI_DEVICE(0x8086, 0x1234) },  // Intel的某个设备{ PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID) }, // 支持所有设备  { 0, }  // 结束标记
};
MODULE_DEVICE_TABLE(pci, my_pci_ids);

7. PCIe Host设备创建(设备树方式)

7.1 PCIe Host Controller的特殊性

虽然PCIe设备通过枚举发现,但PCIe Host Controller本身需要通过设备树描述:

  • Host Controller: Platform设备,通过设备树描述
  • PCI设备: 通过Host Controller枚举发现

7.2 设备树到Platform设备流程

7.2.1 设备树编译和加载
DTS源文件 → DTC编译器 → DTB二进制文件 → Bootloader加载到内存
7.2.2 DTS文件示例
pcie: pcie@f0000000 {compatible = "samsung,exynos5440-pcie";reg = <0xf0000000 0x1000>, <0xf0001000 0x1000>;interrupts = <0 5 0>, <0 4 0>;ranges = <0x81000000 0 0x60000000 0x60000000 0 0x00010000   /* I/O */0x82000000 0 0x60010000 0x60010000 0 0x0FFF0000>; /* Memory */#address-cells = <3>;#size-cells = <2>;device_type = "pci";
};
7.2.3 内核解析流程
start_kernel()└── setup_arch()└── unflatten_device_tree()              // 展开设备树└── __unflatten_device_tree()└── populate_node()              // 创建device_node结构
7.2.4 Platform设备创建
// 在core_initcall阶段
of_platform_default_populate_init()└── of_platform_default_populate()└── of_platform_bus_create()└── of_platform_device_create_pdata() // 创建platform_device

7.3 时间线对比

启动时间线:
0ms     bootloader加载DTB到内存
2ms     unflatten_device_tree() - 解析成device_node  
5ms     pci_driver_init() - PCI子系统初始化
10ms    of_platform_default_populate() - 创建platform_device
12ms    PCIe Host Controller驱动匹配和probe
15ms    pci_scan_root_bus_bridge() - 开始PCI设备枚举
20ms    PCI设备驱动匹配

8. 枚举过程关键特点

8.1 技术特性

  • 自动发现性: 利用PCI协议的自描述特性,无需外部配置
  • 层次化管理: 通过总线-桥-设备的树状结构组织
  • 资源协调: 统一管理地址空间分配,避免资源冲突
  • 热插拔支持: 为运行时设备插拔提供基础架构

8.2 扫描算法特点

  • 广度优先: 同级设备并行扫描
  • 深度递归: 发现桥设备时递归扫描下级总线
  • 资源预留: 为桥设备预留足够的地址空间
  • 错误处理: 优雅处理配置空间读取失败等异常

8.3 与其他总线对比

特性PCIe设备设备树设备
设备发现硬件自动枚举软件解析设备树
设备信息配置空间自描述设备树人工描述
匹配依据vendor/device IDcompatible字符串
热插拔天然支持通常不支持
标准化完全标准化需要自定义

9. 总结

9.1 PCIe子系统优势

PCIe子系统通过精心设计的层次化架构,实现了从硬件发现到驱动匹配的完整流程:

  1. 自动化设备发现: 通过枚举机制自动发现设备
  2. 标准化管理: 统一的数据结构和接口
  3. 即插即用支持: 支持热插拔和动态配置
  4. 层次化设计: 清晰的Host Bridge → Bus → Device架构
  5. 资源管理: 智能的地址空间分配和管理

9.2 关键技术点

  • 递归扫描算法: 深度优先遍历整个PCI设备树
  • 配置空间标准: 标准化的设备信息获取方式
  • 双重机制: Host Controller用设备树,PCI设备用枚举
  • 设备驱动分离: 清晰的设备发现和驱动匹配分离

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

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

相关文章

[特殊字符] LLM(大型语言模型):智能时代的语言引擎与通用推理基座

本文由「大千AI助手」原创发布&#xff0c;专注用真话讲AI&#xff0c;回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我&#xff0c;一起撕掉过度包装&#xff0c;学习真实的AI技术&#xff01; 从千亿参数到人类认知的AI革命 &#x1f50d; 一、核心定义与核心特征…

18-C#改变形参内容

C#改变形参内容 1.ref 参数 int A100; add1(ref A) public int add1 (ref int x) {x x 10;return x; }2.out 参数 int A100; int B200; int Z; add3(A,B, out Z) public int add3 (int x&#xff0c;int y&#xff0c;int z) {z x y;return z; }

恒盾C#混淆加密大师最新版本1.4.0更新 - 增强各类加密效果, 提升兼容性, 使.NET加密更简单

C#/.NET作为托管语言, 其编译生成的EXE/DLL极易被反编译工具还原源码。据统计&#xff0c;大量的商业软件曾遭遇过代码逆向风险&#xff0c;导致核心算法泄露、授权被跳过. 因此对于C#语言开发的.NET程序来说, 在发布前进行混淆和加密非常有必要. 恒盾C#混淆加密大师作为一款.N…

数学建模:非线性规划:二次规划问题

一、定义如果规划模型的目标函数是决策向量的二次函数&#xff0c;约束条件都是线性的&#xff0c;那么这个模型称为二次规划&#xff08;QP&#xff09;模型。二次规划模型的一般形式为二、性质凸性判定准则二次规划问题的凸性完全由Hessian矩阵H决定&#xff1a;​​严格凸QP…

4. 那在详细说一下 http 2.0 的特点

总结 二进制协议&#xff1a;文本通信改为二进制帧通信&#xff0c;数据可以划分为更小的帧&#xff0c;便于高效解析和传输。多路复用&#xff1a;废除 pipeline 管道&#xff0c;避免了“队头阻塞”问题。允许同一个 TCP 连接同时发送多个请求和协议&#xff0c;提高网络资源…

Qt中遍历QMap的多种方法及性能分析

Qt中遍历QMap的多种方法及性能分析遍历QMap的方法**1、使用迭代器&#xff08;STL风格&#xff09;****2、使用Java风格迭代器****3、使用C11范围循环****4、使用键值分离遍历**性能分析使用建议遍历QMap的方法 1、使用迭代器&#xff08;STL风格&#xff09; QMap<QStrin…

Unity3D物理引擎性能优化策略

前言 在Unity3D中优化物理引擎性能&#xff0c;尤其是处理3D碰撞器与2D碰撞器的映射问题&#xff0c;需要结合系统特性和最佳实践。以下是关键策略和实现方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;希望大家可以点击进来一起交流一下开发经验呀…

集群与集群应用

负载均衡与高可用综合实验一、集群是什么&#xff1f;是有一组独立的计算机系统构成的一个松耦合的多处理系统&#xff0c;作为一个整体向用户提供一组网络资源&#xff0c;这些单个的计算机就是集群的节点。二、集群类型Load Balance cluster&#xff08;负载均衡集群&#xf…

jmm,`as - if - serial` 与 `happens - before` 原则

在Java并发编程中&#xff0c;as - if - serial 与 happens - before 原则是确保程序在多线程环境下正确执行的重要规则&#xff0c;下面为你详细讲解&#xff1a; as - if - serial原则 定义&#xff1a;as - if - serial 原则是指&#xff0c;不管编译器和处理器如何优化&…

主流大模型Agent框架 AutoGPT详解

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》&#xff08;跟我一起学人工智能&#xff09;【陈敬雷编著】【清华大学出版社】 GPT多模态大模型与AI Agent智能体书籍本章配套视频课程【陈敬雷】 文…

kotlin学习,val使用get()的问题

疑问&#xff1a;定义val怎么还能使用get()代码示例&#xff1a;private val nametype:Intget()Business.carInfo?.let{carSc(it)}?:LType.AS回答&#xff1a;Kotlin 允许为属性定义自定义 getter&#xff0c;每次访问属性时会执行该方法疑问&#xff1a;这里引出另一个不解&…

解决el-select数据类型相同但是显示数字的问题

这个不是我写的&#xff0c;只是遇到的bug&#xff0c;写法问题&#xff0c;忽略了值的绑定的问题源代码bug&#xff1a;<el-selectv-model"schemeInfo.horizon"placeholder"请选择起报月份"clearablefilterable><el-option v-for"(option,i…

熟练掌握RabbitMQ和Kafka的使用及相关应用场景。异步通知与解耦,流量削峰,配合本地消息表实现事务的最终一致性并解决消息可靠、顺序消费和错误重试等问题

RabbitMQstock.#.nyse &#xff0c;#匹配多个字符&#xff0c;*匹配一个字符。 Confirm Callback 到达exchange的回调。 Return Callback 到达queue失败的回调。 Kafka Kafka生产端分区器&#xff1a; 1.直接指定partition 指定0,1。 2.设置hashkey&#xff0c;计算key的hash值…

飞算科技:以原创技术赋能数字转型

在数字科技迅猛发展的浪潮中&#xff0c;飞算数智科技&#xff08;深圳&#xff09;有限公司&#xff08;简称 “飞算科技”&#xff09;作为一家自主创新型的数字科技公司&#xff0c;同时也是国家级高新技术企业&#xff0c;正以扎实的技术实力和丰富的实践经验&#xff0c;在…

基于 Rust 的Actix Web 框架的应用与优化实例

基于 Rust 的Actix Web 框架的应用与优化实例 Actix Web 框架概述 Actix Web 是一个基于 Rust 的高性能、轻量级 Web 框架,构建于 Actix 异步运行时之上。它支持异步编程模型,适合构建高并发、低延迟的 Web 服务和 API。 核心特性 异步支持:基于 async/await 语法,充分利…

springMVC01-特点、创建项目、@RequestMapping、获取参数请求,三种域对象

一、简介 SpringMVC 就是 Spring 框架中的 MVC 模块&#xff0c;用于构建 Web 应用中的“控制层”。 SpringMVC 是 Spring 提供的一个基于 Servlet 的 Web MVC 框架模块&#xff0c;是 Spring 整个体系中的“Web 层核心”。 SpringMVC 是 Spring 的一部分&#xff0c;Spring…

Java基础,反射破坏封装性 - 单例模式的崩塌

目录一、容易出现问题的小李代码小李的单例设计看似完美&#xff0c;实则存在三个致命问题&#xff1a;1、反射攻击的天然漏洞2、序列化的隐患3、性能瓶颈二、隔壁老王的优化方案三、为什么这样优化&#xff1f;四、小结周五下午&#xff0c;代码审查会议上&#xff0c;小李自信…

Neo4j 综合练习作业

Neo4j 综合练习作业 作业说明 这个作业涵盖了 Neo4j 的多个重要知识点&#xff0c;包括节点和关系的创建、查询、更新、删除以及高级查询功能。请使用 Cypher 语句完成以下所有题目。 数据准备 首先执行以下语句创建示例数据&#xff1a; ACTED_IN: 表示出演关系 DIRECTED: 表示…

基于PA算法的FTL引导

一、抽象绑定关系 1. 什么是 AF Block,什么是 NF Block,为什么要将多个 NF Block 绑定为一个 AF Block AF Block(Allocation Flash Block) 和 NF Block(NAND Flash Block) 是在 NAND Flash 存储架构中用于管理数据的基本单位。 AF Block 定义:AF Block 是一组多个 NF…

快速入门Java中的IO操作

以下是 Java 中常用的 IO 知识点总结&#xff1a; 1. 流的分类 按数据流向&#xff1a;输入流&#xff08;读取数据&#xff09;和输出流&#xff08;写入数据&#xff09;。按数据类型&#xff1a;字节流&#xff08;处理二进制数据&#xff0c;以字节为单位&#xff09;和字符…