【Linux 驱动开发】Linux设备驱动框架与驱动模型深度解析

Linux设备驱动框架与驱动模型深度解析

文章目录

  • Linux设备驱动框架与驱动模型深度解析
      1. 引言
      1. Linux设备模型核心组件
      • 2.1 Kobject, Kset 与 Ktype
      • 2.2 总线 (Bus)、设备 (Device)、驱动 (Driver) 模型
        • 2.2.1 总线 (struct bus_type)
        • 2.2.2 设备 (struct device)
        • 2.2.3 驱动 (struct device_driver)
      1. Sysfs 文件系统与驱动的关系
      1. 设备树 (Device Tree) 机制
      • 4.1 核心概念
      • 4.2 驱动与设备树的结合
      • 4.3 传统模型 vs 设备树模型
      1. 常见驱动框架差异
      • 5.1 字符设备 (Character Device)
      • 5.2 块设备 (Block Device)
      • 5.3 网络设备 (Network Device)
      1. 热插拔 (Hotplug) 机制实现
      • 6.1 内核视角
      • 6.2 用户空间视角
      1. Linux 5.x+ 内核驱动模型新特性
      1. 常见问题排查
      1. 参考文献与扩展学习

1. 引言

Linux设备驱动模型(Linux Device Model)是Linux内核中用于抽象和管理硬件设备的统一架构。随着硬件复杂度的增加,传统的驱动开发方式难以应对电源管理、热插拔、复杂的总线拓扑等问题。Linux 2.6内核引入了全新的设备模型,并持续演进至最新的5.x/6.x版本,成为现代Linux驱动开发的核心基石。

本文将从内核源码角度深入剖析Linux驱动框架,涵盖设备模型、总线-设备-驱动架构、sysfs、设备树以及各类驱动框架的差异。

2. Linux设备模型核心组件

Linux设备模型的核心在于构建一个分层的、面向对象的设备树状结构。其底层基础是 kobject 机制。

2.1 Kobject, Kset 与 Ktype

这三个结构体是设备模型的"基类",构成了内核对象的底层骨架。

  • kobject: 它是设备模型中最基本的对象,对应sysfs中的一个目录。它提供了引用计数(kref)、父子层级关系(parent)和对象名称等基本功能。
  • kset : 它是kobject的集合,通常用于将同类型的kobject归类(例如所有的总线都在 bus_kset 下)。kset本身也包含一个kobject,因此它也能在sysfs中体现目录结构。
  • ktype : 定义了kobject的通用操作,特别是属性文件的读写操作(sysfs_ops)和释放操作(release)。

图1: Kobject, Kset, Ktype 类关系图

2.2 总线 (Bus)、设备 (Device)、驱动 (Driver) 模型

这是Linux驱动模型中最著名的"铁三角"。

2.2.1 总线 (struct bus_type)

总线是处理器与设备之间的通道。在软件层面,总线负责管理挂载在它上面的设备和驱动。

  • 核心职责 : 注册设备、注册驱动、匹配 (Match) 设备与驱动。
  • 关键函数 : match()。当有新设备或新驱动注册到总线上时,总线会调用 match 函数来检查两者是否适配。
2.2.2 设备 (struct device)

代表一个具体的物理或虚拟设备。

  • 核心属性 : struct bus_type *bus (所属总线), struct device_driver *driver (绑定的驱动), void *platform_data (平台数据), struct device_node *of_node (设备树节点)。
2.2.3 驱动 (struct device_driver)

代表处理特定设备的软件程序。

  • 核心函数 : probe() (匹配成功后初始化), remove() (卸载), suspend()/resume() (电源管理)。

三者关系运作流程:

图2: 总线匹配机制流程图*

3. Sysfs 文件系统与驱动的关系

Sysfs 是一个基于内存的虚拟文件系统,挂载在 /sys 下。它将内核中的设备模型层次结构导出到用户空间,提供了一种可视化的、可交互的视图。

  • 映射关系 :
    • kobject -> sysfs 目录
    • attribute -> sysfs 文件
  • 作用 :
    • 展示拓扑 : /sys/devices/ 展示了全局设备层级。
    • 控制接口 : 驱动可以通过 DEVICE_ATTR 创建属性文件,用户可以通过 cat 读取状态或 echo 写入控制指令。
    • 热插拔事件 : 用户空间的 udev/mdev 通过监听内核发送的 uevent(基于 sysfs 路径)来动态创建 /dev 节点。

典型目录结构:

bash 复制代码
/sys/bus/       # 系统中所有的总线 (pci, i2c, usb, platform...)
/sys/class/     # 按功能分类的设备 (net, input, block, tty...)
/sys/devices/   # 所有设备的真实物理层次视图
/sys/drivers/   # 已加载的驱动程序

4. 设备树 (Device Tree) 机制

在ARM架构引入设备树之前,内核代码中充斥着大量的 arch/arm/mach-xxx 板级信息文件(Hard code)。设备树机制将硬件描述从内核源码中分离出来。

4.1 核心概念

  • DTS (Device Tree Source): 文本格式的硬件描述文件。
  • DTB (Device Tree Blob): 编译后的二进制文件,由Bootloader传递给内核。
  • Device Node : 设备树中的节点,被内核解析为 struct device_node

4.2 驱动与设备树的结合

Linux内核启动时会解析DTB,将节点转换为 platform_device(对于平台总线设备)。

  • 匹配方式 : of_match_table
    驱动程序中定义一个 of_device_id 数组,其中 compatible 属性必须与 DTS 节点中的 compatible 字符串一致。
c 复制代码
static const struct of_device_id my_driver_dt_ids[] = {
    { .compatible = "vendor,my-device-v1", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_driver_dt_ids);

static struct platform_driver my_driver = {
    .driver = {
        .name = "my-driver",
        .of_match_table = my_driver_dt_ids, // 关联匹配表
    },
    .probe = my_probe,
    // ...
};

4.3 传统模型 vs 设备树模型

特性 传统模型 (Legacy) 设备树模型 (Device Tree)
硬件描述 C代码 (struct platform_device) .dts 文本文件
修改硬件 需重新编译内核 只需重新编译DTS
耦合度 代码与硬件强耦合 代码与硬件解耦
参数传递 平台数据 (pdata) 设备树属性 (Properties)

5. 常见驱动框架差异

Linux内核为不同类型的设备提供了不同的子系统框架。

5.1 字符设备 (Character Device)

  • 特点: 按字节流访问,无缓存,不支持随机访问(通常)。
  • 核心结构 : struct cdev
  • API : register_chrdev_region, cdev_add, file_operations (open, read, write, ioctl)。
  • 应用: 串口、传感器、LED、GPIO、RTC。

5.2 块设备 (Block Device)

  • 特点: 以块(扇区)为单位访问,有复杂的缓存和I/O调度算法,支持随机访问。
  • 核心结构 : struct gendisk, struct request_queue, struct bio
  • API : alloc_disk, blk_mq_init_queue (多队列), submit_bio
  • 应用: 硬盘、SSD、SD卡、Flash存储。

5.3 网络设备 (Network Device)

  • 特点 : 面向数据包,不对应 /dev 下的设备节点,而是通过 ifconfig (net_device) 管理。它是异步的。
  • 核心结构 : struct net_device, struct sk_buff (套接字缓冲区)。
  • API : register_netdev, netif_start_queue, ndo_start_xmit (发包函数)。
  • 应用: 以太网卡、WiFi、CAN总线。

架构对比图:

图3: 三大设备驱动框架架构对比*

6. 热插拔 (Hotplug) 机制实现

热插拔是指在系统运行时添加或移除设备。

6.1 内核视角

当硬件被插入(如USB设备),总线控制器检测到电气变化,触发中断。内核枚举设备,创建 device 对象,并注册到驱动模型。

此时,内核对象核心(kobject core)会调用 kobject_uevent() 发送一个uevent消息(包含环境变量 ACTION=add, DEVPATH=..., SUBSYSTEM=...)。

6.2 用户空间视角

  1. 内核通过 netlink socket 向用户空间广播 uevent。
  2. 用户空间守护进程(udevd 或 embedded Linux 中的 mdev)监听该 socket。
  3. udevd 解析规则文件(/etc/udev/rules.d/),根据匹配规则执行动作:
    • 加载驱动模块 (modprobe)。
    • 创建 /dev/ 下的设备节点 (mknod)。
    • 设置权限或创建符号链接。

7. Linux 5.x+ 内核驱动模型新特性

随着内核演进,驱动模型也在不断优化:

  1. Driver Core 的清理 : 引入了 device_add_groups() 等辅助函数,简化 sysfs 属性组的创建。
  2. fw_devlink (Firmware Device Links) :
    • 在 5.x 内核中,引入了更智能的设备依赖管理。内核会自动解析设备树中的 phandle 引用(如 clocks, regulators, iommus),并在消费者(Consumer)和提供者(Supplier)之间建立 device_link
    • 效果 : 即使驱动加载顺序混乱,内核也能通过 EPROBE_DEFER 机制和设备链接保证正确的探测顺序,无需驱动开发者手动处理复杂的依赖。
  3. Google Android GKI (Generic Kernel Image) 影响: 推动了模块化和接口标准化,使得驱动更容易作为独立模块维护。
  4. 辅助总线 (Auxiliary Bus) : 引入 auxiliary_bus,用于解决复杂的、多功能的驱动场景,允许一个主驱动创建多个辅助设备供其他子驱动绑定,解决了 mfd (Multifunction Device) 框架在某些场景下的局限性。

8. 常见问题排查

在驱动开发中,常见问题及排查思路:

  1. 驱动未加载 / Probe 未执行:

    • 检查 dmesg 日志。
    • 确认 DTS 中的 compatible 字符串与驱动是否完全一致(包括空格)。
    • 确认 status = "okay"
    • 检查 config 是否开启了该驱动编译。
    • 查看 /sys/bus/platform/devices/ 下是否有该设备节点(确认设备是否注册)。
    • 查看 /sys/bus/platform/drivers/ 下是否有该驱动(确认驱动是否注册)。
  2. Probe 延迟 (Deferred Probe):

    • 日志显示 deferring probe
    • 原因:依赖的资源(如时钟、电源、GPIO控制器)尚未就绪。
    • 对策:这是正常机制,内核稍后会重试。如果一直失败,检查依赖的驱动是否加载成功。
    • 调试:cat /sys/kernel/debug/devices_deferred 查看等待列表。
  3. Sysfs 节点无法创建:

    • 检查 DEVICE_ATTR 权限设置。
    • 检查父对象是否已初始化。

9. 参考文献与扩展学习

  1. Source Code : Linux Kernel Source drivers/base/ (核心代码), Documentation/driver-api/
  2. Books :
    • Linux Device Drivers, 3rd Edition (LDD3) - 经典但稍显陈旧,需结合新内核阅读。
    • Essential Linux Device Drivers.
    • Linux Kernel Development (Robert Love).
  3. Links :
相关推荐
福尔摩斯张1 小时前
C语言文件操作详解(一):文件的打开与关闭(详细)
java·linux·运维·服务器·c语言·数据结构·算法
豐儀麟阁贵1 小时前
9.4字符串操作
java·linux·服务器·开发语言
minji...1 小时前
Linux 进程控制(二) (进程等待wait/waitpid)
linux·运维·服务器·数据结构
知南x1 小时前
【正点原子STM32MP157 启动篇】(3) STM32MP1 二进制头部信息+Linux 系统启动过程
linux·stm32·嵌入式硬件·stm32mp157
IT 乔峰1 小时前
Apache工作原理详细说明
linux·apache
fufu03111 小时前
Linux环境下的C语言编程(三十六)
linux·c语言·开发语言·数据结构·算法
小嘟嘟131 小时前
第2章 Shell 变量与参数传递:3 种定义方式 + 避坑指南
linux·运维·shell
looking_for__2 小时前
【Linux】进程概念
linux
Eric.Lee20212 小时前
ubuntu系统在bashrc文件中对conda进行启用设置
linux·运维·python·ubuntu·conda