【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 :
相关推荐
A小辣椒2 天前
TShark:Wireshark CLI 功能
linux
A小辣椒2 天前
TShark:基础知识
linux
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao3 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334663 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪3 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠4 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush44 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5204 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩4 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言