news 2026/6/15 3:04:59

Linux 下“发现设备”的机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 下“发现设备”的机制

Linux 下“发现设备”的机制主要分几大类。你可以先记住一个总原则:

谁发现设备,谁创建对应的 device 对象;你就要写对应的 driver 框架去匹配它。

也就是说,发现机制不同,最终驱动框架就不同。


1. 第一类:总线协议自己能枚举设备

这类设备本身有标准枚举机制,Linux 可以主动扫描/枚举出来。

典型包括:

发现机制典型设备Linux 设备对象驱动框架
PCI / PCIe 枚举PCIe FPGA、网卡、NVMe、GPUstruct pci_devstruct pci_driver
USB 枚举U盘、USB摄像头、USB串口struct usb_device/ USB interfacestruct usb_driver
SCSI/SATA/SAS 扫描硬盘、光驱、存储设备SCSI device / block deviceSCSI / libata / block 框架
NVMe 枚举NVMe SSDPCI device + NVMe controllerPCI + NVMe 框架

比如 PCIe 设备,主机上电后 Root Complex 会枚举 PCIe 总线,读取设备配置空间,拿到 Vendor ID、Device ID、BAR、中断能力等信息。Linux PCI core 会据此创建struct pci_dev,然后拿它去匹配struct pci_driver。Linux PCI 驱动文档也说明,PCI 驱动通过注册pci_driver,由 PCI 子系统在匹配设备时调用驱动的probe()。(Linux内核文档)

所以你的 PCIe FPGA 板卡属于这一类:

PCIe 枚举 ↓ Linux 创建 struct pci_dev ↓ 匹配 struct pci_driver ↓ 调用 pci_driver.probe()

因此驱动框架就是:

structpci_driver

而不是platform_driveri2c_driver或者单纯字符设备驱动。


2. 第二类:Firmware 描述设备

有些设备总线本身不能自动枚举,Linux 需要通过固件描述知道它们存在。

主要有两种:

Device Tree ACPI

2.1 Device Tree 发现设备

常见于 ARM、Zynq、嵌入式 Linux。

比如设备树里写:

my_dma@43c00000 { compatible = "mycompany,my-dma"; reg = <0x43c00000 0x10000>; interrupts = <0 61 4>; };

这段意思是:

这里有一个设备 寄存器基地址是 0x43c00000 大小是 0x10000 中断号是 61 匹配字符串是 "mycompany,my-dma"

Linux 启动时解析 Device Tree,创建对应的platform_device,然后用compatible字符串去匹配platform_driver。Linux 的 Device Tree 文档说明,Device Tree 是 Linux 描述硬件布局的重要方式;platform 驱动文档也说明,driver core 会在 device 和 driver 匹配后调用probe()。(Linux内核文档)

所以这种路径是:

Device Tree ↓ Linux 创建 platform_device ↓ 匹配 platform_driver ↓ 调用 platform_driver.probe()

对应驱动框架是:

structplatform_driver

2.2 ACPI 发现设备

常见于 x86 PC、服务器、笔记本。

ACPI 表由 BIOS/UEFI 提供,里面描述了平台设备、电源管理、中断、I2C/SPI/UART 等设备信息。Linux ACPI 文档说明,ACPI 可以枚举 platform、SPI、I2C 以及某些 UART 后面的设备,并创建对应的物理设备对象。(Linux内核文档)

路径类似:

BIOS/UEFI ACPI table ↓ Linux ACPI core 解析 ↓ 创建 platform_device / i2c_client / spi_device 等 ↓ 匹配对应 driver

所以 ACPI 不一定只创建platform_device,它也可能创建 I2C/SPI 子设备。


3. 第三类:父设备驱动创建子设备

这是非常重要的一类。

有些设备不是一开始就被系统直接看到,而是:

先发现一个父设备 父设备驱动 probe 后 再创建它下面的子设备

典型场景:

父设备子设备说明
PCIe 板卡板卡上的 I2C 芯片PCIe 驱动起来后,注册内部 I2C adapter,再创建 i2c_client
USB 设备多个 USB interfaceUSB core 把一个物理设备拆成多个 interface 给不同驱动
MFD 芯片GPIO / RTC / regulator / codec一个芯片内部有多个功能块
MDIO 控制器Ethernet PHYMAC 驱动注册 MDIO bus,再扫描 PHY
SCSI HBA磁盘 LUNHBA 先被发现,再扫描下面的磁盘
FPGA Manager / Overlay动态加载后的逻辑外设FPGA 配置后再生成新设备

比如一个 PCIe FPGA 板卡上,FPGA 内部实现了一个 I2C 控制器。流程可能是:

PCIe 枚举发现 FPGA ↓ pci_driver.probe() ↓ 驱动映射 BAR ↓ 发现 FPGA 内部有 I2C controller ↓ 注册 i2c_adapter ↓ I2C core 再创建 i2c_client ↓ 匹配 i2c_driver

所以这里有两层发现:

第一层:PCIe 发现 FPGA 板卡 第二层:FPGA 驱动创建内部子设备

这也是为什么复杂设备里会出现“一个物理设备对应多个 Linux 驱动”的情况。


4. 第四类:I2C / SPI 这类“不能自发现”的总线

I2C 和 SPI 很特殊。

它们不像 PCIe、USB 那样可以标准枚举。大多数 I2C/SPI 从设备不会主动告诉 Linux:

我是谁 我在哪里 我的型号是什么

所以 Linux 需要通过其他方式知道:

哪个总线上 哪个地址/片选 挂了什么芯片

4.1 I2C 设备发现方式

I2C 设备通常有几种实例化方式:

方式说明
Device Tree嵌入式最常见
ACPIx86/笔记本/服务器常见
board info老式板级代码
父驱动显式创建比如 PCIe/USB 设备内部挂 I2C 芯片
sysfs 手动创建调试用,例如new_device

Linux I2C 文档明确提到,I2C 设备可以显式实例化,例如填充struct i2c_board_info并调用i2c_new_client_device()。(Linux内核文档)

I2C 的典型路径是:

Device Tree / ACPI / board info / 父驱动创建 ↓ Linux 创建 struct i2c_client ↓ 匹配 struct i2c_driver ↓ 调用 i2c_driver.probe()

所以 I2C 驱动框架是:

structi2c_driver

但注意:

I2C 设备不是靠 I2C 总线自己扫描出来的,通常是被描述出来或者被父驱动创建出来的。


4.2 SPI 设备发现方式

SPI 也类似。

SPI 设备通常由:

Device Tree ACPI board info 父驱动创建

来描述。

路径是:

固件/板级信息/父驱动 ↓ 创建 struct spi_device ↓ 匹配 struct spi_driver ↓ 调用 spi_driver.probe()

SPI 也不能像 PCIe 那样自动读出 Vendor ID / Device ID。


5. 第五类:platform_device 静态注册

这是老式或者简单嵌入式系统里常见的方式。

设备不是通过 Device Tree,也不是通过 ACPI,而是内核板级代码里直接注册:

platform_device_register(&my_device);

或者注册一组:

platform_add_devices(...);

然后驱动用:

platform_driver_register(&my_driver);

匹配方式通常是:

device name driver name

或者通过of_match_table/acpi_match_table

这类方式现在在很多新项目里被 Device Tree / ACPI 替代了,但内核里仍然存在。

路径是:

板级代码注册 platform_device ↓ platform bus ↓ 匹配 platform_driver ↓ 调用 probe()

6. 第六类:热插拔 / 重新扫描

有些设备不是开机时就存在,而是运行中插入或重新扫描出来。

典型包括:

机制例子
PCIe Hotplug插入 PCIe 热插拔设备
USB Hotplug插入 U盘、USB串口
Thunderbolt Hotplug外接扩展坞
PCI rescan手动触发 PCIe 总线重新扫描
SCSI scan手动扫描新磁盘
Device Tree overlay运行时加载新的 DT overlay
FPGA reconfigurationFPGA 重新配置后出现新逻辑外设

例如 PCIe 可以通过 rescan 重新发现设备:

echo 1 > /sys/bus/pci/rescan

但本质仍然是:

PCI core 重新扫描 ↓ 发现新 pci_dev ↓ 匹配 pci_driver

也就是说,热插拔只是“发现时机”不同,不改变驱动框架。


7. 第七类:用户手动创建设备

有些设备 Linux 不会自动知道,可以由用户手动告诉内核。

比如 I2C 调试时,可以手动创建一个设备:

echotmp102 0x48>/sys/bus/i2c/devices/i2c-1/new_device

这会告诉 I2C core:

在 i2c-1 总线上,0x48 地址有一个 tmp102 设备

然后 Linux 创建i2c_client,再匹配i2c_driver

这种方式常用于:

调试 验证 没有设备树/ACPI 描述的临时场景

不太适合作为正式产品的主要发现方式。


8. 第八类:虚拟设备 / 软件总线

还有一些设备不是传统物理硬件,而是由虚拟化或软件框架创建。

典型包括:

机制设备
virtio虚拟网卡、虚拟磁盘、虚拟 console
vhost虚拟化后端设备
rpmsg异构多核通信设备
remoteproc远程处理器设备
auxiliary bus一个大设备拆出的辅助子功能
MFD多功能芯片拆出的子设备

这类设备的发现路径通常是:

父框架/虚拟化层/远程处理器框架创建 device ↓ 挂到对应 bus ↓ 匹配对应 driver

9. 重要区分:udev不是硬件发现机制

很多人会把udev和设备发现混在一起。

严格说:

内核负责发现硬件并创建 device udev 负责根据内核事件创建设备节点、设置权限、加载规则

例如:

PCIe 设备被 PCI core 发现 ↓ pci_driver probe 成功 ↓ 驱动注册字符设备 ↓ 内核产生 uevent ↓ udev 创建 /dev/mydma

所以:

udev不是发现 PCIe 设备的人,它只是用户空间里的设备节点管理器。


10. 也要区分:字符设备不是硬件发现机制

比如你写:

register_chrdev();misc_register();cdev_add();

这只是创建用户态访问入口:

/dev/xxx

它不是发现硬件。

对于 PCIe FPGA DMA,正确结构是:

PCIe 发现机制 ↓ pci_driver 绑定硬件 ↓ probe 里初始化 BAR / DMA / IRQ ↓ 注册字符设备 ↓ 用户通过 /dev/mydma 访问

所以不能说:

我写字符设备驱动,所以不用 pci_driver

更准确是:

底层用 pci_driver 发现和绑定 PCIe 设备 上层用字符设备给用户态提供接口

11. 总结表:Linux 常见设备发现机制

发现机制谁发现设备创建什么对象驱动框架例子
PCI/PCIe 枚举PCI corepci_devpci_driverFPGA PCIe 板卡、网卡、NVMe
USB 枚举USB coreusb_device/ interfaceusb_driverU盘、USB摄像头
Device TreeOF/DT coreplatform_device/i2c_client/spi_deviceplatform/I2C/SPIARM SoC 外设
ACPIACPI coreplatform/I2C/SPI/UART 等设备对应 driverx86 平台设备
board info板级代码platform/I2C/SPI device对应 driver老式嵌入式
父驱动创建parent driver子 device子系统 driverMFD、MDIO、FPGA 内部 I2C
总线重新扫描bus core新 device对应 driverPCI rescan、SCSI scan
用户手动创建sysfs/configfs对应 device对应 driverI2Cnew_device
虚拟化/软件总线virtio/rpmsg/auxiliary 等虚拟 device对应 drivervirtio-net、rpmsg

12. 对你最重要的判断方法

你以后看到一个硬件,先不要问“我要写什么模板”,而是按这个顺序判断:

1. lspci 能不能看到? 能 → PCIe 设备 → pci_driver 2. lsusb 能不能看到? 能 → USB 设备 → usb_driver 3. 设备树里有没有 compatible/reg/interrupts? 有 → 多数是 platform_driver,也可能是 i2c_driver/spi_driver 4. 是 I2C 地址设备吗? 是 → i2c_driver,但设备通常要由 DT/ACPI/父驱动/手动创建 5. 是 SPI 片选设备吗? 是 → spi_driver,但设备通常要由 DT/ACPI/父驱动创建 6. 是 SoC 内部寄存器外设吗? 是 → platform_driver 7. 是 PCIe 设备内部又挂了子功能吗? 是 → 先 pci_driver,再由父驱动创建子设备 8. 只是想给用户态 /dev 接口? 那是字符设备接口,不是硬件发现路径

13. 一句话记忆

能自报家门的总线,比如 PCIe/USB,靠总线枚举发现;不能自报家门的设备,比如 platform/I2C/SPI,靠 Device Tree、ACPI、板级信息或父驱动创建;用户态的/dev只是访问入口,不负责发现硬件。

对你的 PCIe FPGA 来说,最关键就是:

发现机制:PCIe 枚举 内核对象:struct pci_dev 驱动框架:struct pci_driver 匹配依据:Vendor ID / Device ID 资源来源:BAR / IRQ / DMA capability 用户接口:再额外注册字符设备
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 3:00:50

2026年|论文降AI率、降AIGC怎么搞?10款学姐亲测免费工具高效搞定

哈喽各位被论文逼到脱发的学弟学妹&#xff01;是不是刚熬过查重大战&#xff0c;转头就被AIGC检测率搞心态崩了&#xff1f;我太懂这种崩溃了&#xff01;去年我熬了三天拼出来的论文&#xff0c;一查AI率快70%&#xff0c;当场差点把电脑掀了&#xff0c;感觉学位证都在跟我挥…

作者头像 李华