Skip to content

Linux 内核驱动机制总结:模块、注册与加载流程

本文档总结了关于 Linux 驱动程序的存在形式、注册时机、硬件发现机制以及文件存储结构的对话要点。

1. 核心概念辨析:驱动 vs 模块

驱动(Driver)和模块(Module)是两个维度不同的概念,但在 Linux 中常被混用。

概念定义关系
驱动 (Driver)功能层面。指控制特定硬件的代码逻辑(如“怎么读写网卡寄存器”)。驱动代码向内核子系统(Bus)注册 driver 结构体。
模块 (Module)部署层面。指可动态加载/卸载的二进制容器(.ko 文件)。模块是驱动的容器。一个模块可以包含一个或多个驱动,也可以不包含驱动(如文件系统)。

存在的两种形式

  1. 内建 (Built-in):编译进内核镜像 (vmlinux)。启动时自动初始化,常用于核心硬件(CPU、根文件系统、SATA 控制器)。
  2. 可加载模块 (Loadable):编译为独立的 .ko 文件。按需加载,常用于外设(USB、显卡、声卡)。

2. 驱动的注册与硬件发现 (Discovery & Probe)

Linux 驱动模型遵循 总线 (Bus) - 设备 (Device) - 驱动 (Driver) 的分离设计。

A. 什么时候注册?

  • 内建驱动:在内核启动极早期 (start_kernel -> do_initcalls) 按优先级顺序执行 init 函数注册。
  • 模块驱动:在用户态执行 insmodmodprobe 加载模块时,执行 module_init 进行注册。

B. 内核如何发现硬件?

内核通过 枚举 (Enumeration) 过程发现硬件,生成 struct device 对象。

  • 智能总线 (PCI, USB):具有自举能力。总线控制器扫描物理插槽,硬件上报 Vendor/Device ID。内核主动探测
  • 静态总线 (Platform, I2C, SPI):硬件无法自述。依赖固件提供的信息:
    • x86/服务器:UEFI 提供的 ACPI 表
    • 嵌入式/ARM设备树 (Device Tree / .dtb)

C. UEFI 的角色澄清

  • 误区:UEFI 负责注册驱动(如 perf 计数器)。
  • 真相:UEFI 仅负责硬件初始化(Power on)和提供硬件信息表(ACPI)。驱动的注册完全由 Linux 内核代码完成。UEFI 可通过锁死寄存器来阻止内核使用某些硬件。

D. 匹配过程 (Match & Probe)

当总线上同时存在“设备对象”和“驱动对象”时,触发匹配:

  1. Match:总线比对设备的 ID 和驱动支持的 ID 表。
  2. Probe:匹配成功后,调用驱动的 probe() 函数。此时驱动正式接管硬件(申请中断、映射 IO 内存等)。

3. 模块文件存储与 udev 自动加载机制

文件存储路径

所有模块文件存放在:

text
/lib/modules/$(uname -r)/

结构通常与内核源码目录一致(如 /lib/modules/.../kernel/drivers/net/)。

udev 查找流程

当硬件插入时,内核发送 uevent,udev 根据硬件 ID 自动加载模块,无需遍历文件系统。

  1. 索引文件depmod 命令会在模块根目录下生成索引。
    • modules.alias:硬件 ID (Modalias) -> 模块名 (Module Name) 的映射。
    • modules.dep:模块名 -> 具体 .ko 文件路径及依赖关系的映射。
  2. 查找逻辑
    • 硬件上报 ID (如 pci:v00008086...)。
    • modules.alias 找到对应的模块名(如 e1000e)。
    • modules.dep 找到 .ko 文件路径。
    • 执行 modprobe 加载。

4. 常用调试命令速查

目的命令
查看已加载模块lsmod
查看模块详细信息modinfo <模块名> (可看 ko 路径、作者、参数)
查看设备对应的驱动lspci -kethtool -i <接口名>
查看驱动注册日志`dmesg
查找硬件ID对应关系grep "<硬件ID>" /lib/modules/$(uname -r)/modules.alias
查看总线上的绑定情况ls -l /sys/bus/pci/drivers/