ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux设备驱动加载失败根因:bus/device/driver匹配机制详解

Linux设备驱动加载失败根因:bus/device/driver匹配机制详解 1. 从“设备没反应”开始一个真实驱动加载失败的现场还原你有没有遇到过这样的场景新焊好的一块基于Xilinx Zynq的开发板上电后串口能出log但接上去的AD7606采集芯片死活不响应或者在ARM64嵌入式系统里插上PCIe SSDlspci -vv能看到设备ID和BAR空间dmesg却安静得像没这回事——既没有probe成功提示也没有任何错误信息。你翻遍设备树、检查compatible字符串、确认reg地址范围、甚至用逻辑分析仪抓了SPI时序一切看起来都对可驱动就是不加载。这不是玄学是Linux内核设备模型里最基础、也最容易被忽略的一环bus、device、driver三者之间的加载顺序与匹配流程。它不像字符设备注册那样有明确的register_chrdev调用点也不像中断处理那样有清晰的request_irq入口而是一套贯穿整个内核启动、模块加载、热插拔事件的隐式状态机。很多开发者卡在这里不是因为不会写.probe()函数而是根本没意识到driver还没等到匹配机会就已经被内核悄悄跳过了。这个机制的核心关键词就三个bus总线、device设备、driver驱动。它们不是并列关系而是存在严格的依赖层级和时序约束。比如一个I2C设备节点只有在I2C总线控制器驱动已加载、总线已注册、且I2C适配器已成功probe之后才具备被枚举和匹配的前提同样一个platform设备能否被某个platform_driver捕获取决于该driver是否已在内核中完成注册且其of_match_table或acpi_match_table能与设备节点的compatible属性精确对齐。我第一次踩进这个坑是在调试一款国产RK3399平台上的USB摄像头模组。设备树里写了usb_host0下挂载usb1compatible ov5640驱动也编译进了内核modprobe ov5640后lsmod能看到模块但/dev/video0始终不出现。dmesg | grep -i ov空空如也。最后发现问题出在usb_host0对应的PHY驱动——phy-rockchip-typec——比usb_host0控制器驱动晚加载了200ms。在这200ms窗口期内USB主机控制器尝试枚举下游设备但PHY未就绪导致枚举超时整个USB设备树被内核标记为“不可用”后续即使PHY加载成功也不会触发二次枚举。这个案例背后就是busUSB host controller与deviceUSB摄像头之间的时间窗口错位。所以理解这套机制不是为了背诵代码路径而是为了建立一种“内核视角”的调试直觉当设备不工作时你要问的不是“我的probe函数写错了吗”而是“此时bus是否readydevice是否已注册driver是否已注册三者状态是否满足匹配触发条件”——这四个问题构成了所有Linux设备驱动加载问题的根因排查地图。2. 内核视角下的三元组bus、device、driver的本质与生命周期要真正搞懂匹配流程必须先剥离“设备”“驱动”这些应用层概念回到内核数据结构的本源。Linux设备模型不是凭空设计的抽象框架而是为了解决硬件资源管理这一物理约束而生的工程方案。它的核心是用软件对象精确映射硬件实体及其连接关系。2.1 bus物理总线的软件镜像与调度中枢struct bus_type不是一条电线而是一个状态机控制器。它定义了三件事如何发现设备match、如何绑定驱动probe、如何解绑驱动remove。以platform_bus_type为例它的match函数是platform_matchprobe函数是platform_drv_probe。但关键在于bus_type本身不主动做任何事它只提供接口真正的动作由内核的设备核心drivers/base/core.c在特定时机调用。bus_register(platform_bus_type)这个调用发生在内核初始化早期postcore_initcall它做了三件关键事在/sys/bus/目录下创建platform子目录初始化bus_type结构体中的kset用于sysfs组织注册一个名为platform_bus的虚拟设备——这是整个platform总线的根节点也是所有platform_device的父设备。这个platform_bus设备的存在是platform总线能工作的前提。它让内核知道“哦这里有一条叫platform的总线它下面可以挂设备”。同理i2c_bus_type注册时会创建/sys/bus/i2c并准备接收来自I2C适配器驱动注册的i2c_adapter实例pci_bus_type则依赖于PCI子系统初始化时扫描到的每个PCI总线段为每个段创建一个struct pci_bus并加入pci_root_buses链表。提示bus_type的probe函数不是驱动的.probe()而是bus自身的probe回调用于处理bus自身状态变化。真正调用驱动.probe()的是bus_rescan_devices()或device_attach()。2.2 device硬件实体的精确建模与状态容器struct device是设备模型的基石但它本身不包含任何驱动逻辑。它只是一个状态容器和关系描述器。一个device实例的创建意味着内核“知道”这个硬件存在并记录了它的位置parent、类型bus、标识name, of_node、电源状态power等元信息。设备的注册有两种典型路径静态注册在内核启动时由总线控制器驱动如dw_mmc、xhci_hcd在probe成功后为每个检测到的子设备调用device_register()。例如USB主机控制器probe后会为每个枚举到的USB设备创建struct usb_device再将其封装为struct device并注册。动态注册通过设备树Device Tree或ACPI表在内核解析阶段由of_platform_populate()或acpi_bus_scan()函数为每个匹配的节点创建platform_device并注册。这就是为什么你在设备树里加了一个i2c0 { ov56403c {...}; };内核启动时就会自动创建一个device实例名字是ov5640.0数字是index由of_alias_get_id()生成。device的关键字段bus指向其所属总线driver指针在匹配前为NULL匹配成功后才指向具体的struct device_driver。init_name和of_node是匹配的依据——前者用于legacy name匹配后者用于DT/ACPI匹配。2.3 driver功能实现的封装与能力声明struct device_driver是驱动程序的“身份证”。它不包含设备操作的具体代码那些在.probe()、.remove()里而是声明自己能服务哪些设备。这种声明通过两个核心字段完成struct bus_type *bus声明自己属于哪条总线。platform_driver的bus必须是platform_bus_typei2c_driver的bus必须是i2c_bus_type。这是硬性约束注册时内核会校验。const struct of_device_id *of_match_table或const struct acpi_device_id *acpi_match_table声明自己支持的设备兼容性列表。这是匹配的“钥匙”。driver_register()的执行是将这个“身份证”提交给对应总线的“户籍科”。内核会将其加入bus-p-drivers_klist链表并立即触发一次bus_rescan_devices(bus)——即遍历该总线上所有已注册但尚未绑定驱动的device逐个调用bus-match()函数进行匹配。注意driver_register()本身不保证立即probe。它只是“报名”后续的匹配和probe由内核异步调度。这也是为什么modprobe xxx后dmesg里probe日志可能延迟几毫秒才出现。2.4 三者的生命周期耦合一个不能少的闭环这三者不是独立存在的它们的生命周期被内核严格耦合bus必须先于device和driver注册否则device_register()会因找不到bus而失败driver_register()也会因bus未注册而返回-EINVAL。device和driver的注册顺序无强制要求你可以先注册device如DT静态注册再加载driver模块也可以先加载driver模块再热插拔设备如USB。内核会自动处理两种情况。匹配是双向触发的driver注册时触发一次全量匹配device注册时也会调用bus_add_device()进而触发device_attach()对该device进行单次匹配。这个闭环的健壮性体现在device_attach()函数里。它会遍历bus上所有已注册的driver对每个driver调用bus-match()。如果匹配成功则调用driver-probe()。如果probe失败返回非0值内核会将该device标记为driver_bound false并继续尝试下一个driver——这就是为什么一个设备节点可以同时匹配多个driver如compatible vendor,chip, generic,chip内核会按顺序尝试直到第一个probe成功的driver为止。3. 匹配流程的四步拆解从注册到probe的完整链路匹配不是一蹴而就的魔法而是一个由内核设备核心drivers/base/dd.c精确控制的四步状态机。每一步都有明确的触发条件、执行主体和失败回退机制。理解这四步你就掌握了所有驱动加载问题的诊断钥匙。3.1 第一步bus注册与初始化——总线就绪的信号bus_register()是整个流程的起点。以platform_bus_type为例其注册发生在drivers/base/platform.c的platform_bus_init()函数中该函数被postcore_initcall(platform_bus_init)调用在内核初始化的postcore阶段执行早于大部分驱动。这个调用的实质是向内核设备模型注册一个“总线类型模板”。它创建了/sys/bus/platform目录并初始化了platform_bus_type结构体的kset、subsys等字段。更重要的是它调用了device_register(platform_bus)注册了一个名为platform的虚拟设备。这个设备没有parent是platform总线的根。验证bus是否就绪最直接的方法是检查sysfsls /sys/bus/ # 输出应包含 platform, i2c, pci, usb 等 ls /sys/bus/platform/devices/ # 此时应为空因为还没有platform设备被注册如果某条总线如i2c没有出现在/sys/bus/下说明其bus_register()调用失败或未执行。常见原因包括总线驱动未编译进内核CONFIG_I2Cy/m未设置、总线驱动模块加载失败modprobe i2c-core报错、或总线驱动的initcall函数被跳过如CONFIG_I2Cm但模块未加载。3.2 第二步device注册——硬件存在的宣告device的注册是内核“感知”到硬件的第一步。对于基于设备树的系统这个步骤发生在of_platform_populate()函数中。该函数遍历设备树中所有compatible字段匹配simple-bus或simple-mfd的节点并为其子节点创建platform_device。以一个典型的I2C设备为例i2c0 { status okay; clock-frequency 400000; ov5640: camera3c { compatible ovti,ov5640; reg 0x3c; clocks cru CLK_CIF_OUT; clock-names mclk; }; };当内核解析到i2c0节点时of_i2c_register_devices()会被调用它会为ov5640节点创建一个struct i2c_client并将其作为struct device注册到i2c_bus_type上。注册后的device在sysfs中表现为ls /sys/bus/i2c/devices/ # 输出0-003c (格式为 busnum-devaddr)关键点在于device注册时其driver指针为NULL且state为KOBJ_ADD。这意味着它已“登记在册”但尚未“分配工作”。3.3 第三步driver注册——能力声明的提交driver注册是驱动程序向内核“投递简历”。以ov5640的I2C驱动为例其核心结构如下static const struct of_device_id ov5640_of_match[] { { .compatible ovti,ov5640 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ov5640_of_match); static struct i2c_driver ov5640_i2c_driver { .driver { .name ov5640, .of_match_table ov5640_of_match, }, .probe ov5640_probe, .remove ov5640_remove, }; module_i2c_driver(ov5640_i2c_driver);module_i2c_driver()宏展开后会在模块加载时调用i2c_register_driver()最终进入driver_register()。该函数执行以下关键操作将ov5640_i2c_driver加入i2c_bus_type.p-drivers_klist调用bus_rescan_devices(i2c_bus_type)触发匹配流程。此时内核会遍历/sys/bus/i2c/devices/下的所有device对每个device调用i2c_bus_type.match()即i2c_device_match()。该函数的逻辑很简单如果device是i2c_client且其of_node的compatible属性与driver的of_match_table中任一项匹配则返回true。3.4 第四步match与probe——从匹配到功能激活当i2c_device_match()返回true时内核执行driver_probe_device()这是整个流程的临门一脚。它做了三件事锁定device和driver防止并发修改调用driver的.probe()函数传入client-dev作为参数更新device状态将dev-driver指向该driver设置dev-state为KOBJ_BOUND。probe()函数的成功执行标志着驱动正式接管设备。它通常会申请并映射设备的内存区域devm_ioremap_resource()请求中断devm_request_threaded_irq()初始化硬件寄存器i2c_smbus_write_byte_data()创建sysfs属性文件device_create_file()注册字符设备cdev_add()。如果probe()返回非0值如-ENODEV、-EIO内核会回滚将dev-driver置为NULLdev-state恢复为KOBJ_ADD并打印probe failed日志。此时该device仍留在bus上等待下一个driver的匹配尝试。实操心得probe()失败后dmesg里通常只有一行ov5640: probe failed with error -X但根本原因往往藏在更早的日志里。比如-EIO可能是I2C通信失败需检查i2cdetect -y 0是否能看到设备地址-ENODEV可能是设备树reg地址写错导致i2c_client的addr字段为0。4. 常见失配场景的深度诊断从dmesg到源码级排查理论再完美不如一次真实的故障复现。下面我将带你走一遍四个最典型的“设备不加载”场景每一步都展示dmesg、sysfs、debugfs的实操命令并指出问题根源和修复方法。这些不是教科书案例而是我在RK3399、Xilinx Zynq MPSoC、Intel Atom平台上反复验证过的真问题。4.1 场景一设备树compatible字符串完全匹配失败现象设备树里写了compatible vendor,chip驱动里of_match_table也写了{ .compatible vendor,chip }但dmesg里完全没有驱动probe日志ls /sys/bus/platform/devices/能看到设备节点lsmod | grep chip能看到驱动已加载。诊断链路首先确认device是否存在# 查看platform总线上的所有设备 ls /sys/bus/platform/devices/ | grep chip # 输出chip.0检查device的of_node属性cat /sys/bus/platform/devices/chip.0/of_node/compatible # 如果输出为空说明设备树节点未被正确解析 # 如果输出是vendor,chip\0generic,chip注意\0分隔说明匹配字符串正确检查driver的of_match_table是否被正确加载# 查看driver的sysfs属性 ls /sys/bus/platform/drivers/chip/ # 如果不存在说明driver未注册成功 # 如果存在查看其of_match_table cat /sys/bus/platform/drivers/chip/modalias # 输出应为of:Nvendor,chipTNULL表示匹配规则已加载根因定位cat /sys/bus/platform/devices/chip.0/of_node/compatible输出为空。这说明设备树节点的compatible属性在解析时被忽略了。常见原因设备树节点的status disabled需改为okaycompatible字符串末尾有多余空格如vendor,chip 导致内核strcmp失败设备树编译时使用了错误的dtc版本compatible属性未被正确编码。修复修正设备树重新编译dtb重启。验证cat /sys/bus/platform/devices/chip.0/of_node/compatible输出正确。4.2 场景二bus未就绪导致device注册失败现象dmesg里有chip: Failed to register device: -ENODEVls /sys/bus/platform/devices/看不到chip.0但驱动模块能正常加载modprobe chip无报错。诊断链路检查bus状态ls /sys/bus/platform/ # 如果报错cannot access /sys/bus/platform/: No such file or directory说明platform bus未注册追踪内核启动日志dmesg | grep -i platform bus # 应看到platform bus: registered # 如果没有说明platform_bus_init()未执行检查内核配置zcat /proc/config.gz | grep CONFIG_BASE_PLATFORM # 必须为y或m根因定位dmesg里没有platform bus: registered且/sys/bus/platform/不存在。这通常发生在极度裁剪的内核中CONFIG_PLATFO被设为n。但更隐蔽的情况是总线驱动如dw_mmc的probe函数里调用了platform_device_register()但此时platform_bus_type尚未注册因为platform_bus_init()是postcore_initcall而某些总线驱动是fs_initcall导致注册失败。修复确保CONFIG_PLATFOy或调整总线驱动的initcall级别使其晚于platform_bus_init()。4.3 场景三driver注册早于device但匹配后probe失败现象dmesg里有chip: probe failed with error -EIOls /sys/bus/platform/devices/chip.0/driver为空说明未绑定ls /sys/bus/platform/drivers/chip/存在。诊断链路确认匹配发生过dmesg | grep -i chip.*match # 应看到chip.0: matched with driver chip深入probe失败原因# 启用driver的详细日志 echo 8 /proc/sys/kernel/printk modprobe -r chip modprobe chip dmesg | tail -20检查硬件资源# 查看device的resource cat /sys/bus/platform/devices/chip.0/resource # 检查irq是否被其他设备占用 cat /proc/interrupts | grep chip根因定位dmesg显示chip: failed to request irq 123: IRQ is busy。说明设备树里指定的interrupts GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH但IRQ 123已被另一个驱动如gpio-keys占用。修复修改设备树为chip节点分配一个空闲的IRQ号或在gpio-keys驱动中释放该IRQ。4.4 场景四热插拔设备无法触发匹配现象USB摄像头插入后dmesg有usb 1-1: new high-speed USB device但/dev/video0不出现lsmod | grep uvc显示uvcvideo已加载。诊断链路确认USB设备被识别lsusb -v -s 1:1 | grep -A5 Interface Descriptor # 查看bInterfaceClass, bInterfaceSubClass检查UVC驱动是否支持该设备# UVC驱动的modalias cat /sys/bus/usb/drivers/uvcvideo/modalias # 输出类似usb:v1234p5678d*dc*dsc*dp*ic0Eisc01ip00in* # 其中ic0Eisc01ip00对应UVC Class强制触发匹配# 手动绑定 echo 1-1 /sys/bus/usb/drivers/uvcvideo/bind # 如果报错Device or resource busy说明已有其他driver绑定根因定位lsusb -v显示该摄像头的bInterfaceClass0xFFVendor Specific而非标准的0x0EVideo。UVC驱动只匹配0x0E因此被usb-storage或其他通用驱动抢占。修复修改UVC驱动的id_table添加对该Vendor ID的支持或在usb-storage驱动中排除该设备ID。5. 工具链实战用sysfs、debugfs和tracepoint构建自己的诊断仪表盘纸上得来终觉浅绝知此事要躬行。光看原理不够你必须掌握一套能在现场快速定位问题的工具链。这套工具不依赖外部调试器全部基于内核自带的sysfs、debugfs和ftrace是我过去十年在客户现场救火的标准配置。5.1 sysfs设备模型的实时快照sysfs是内核设备模型的“对外窗口”每个device、driver、bus都在/sys/下有对应目录。它是诊断的第一站。查看所有bus及其状态# 列出所有bus ls /sys/bus/ # 查看platform bus的driver列表 ls /sys/bus/platform/drivers/ # 查看platform bus的device列表 ls /sys/bus/platform/devices/深入一个device# 查看device的parent上级总线或设备 readlink /sys/bus/platform/devices/chip.0/parent # 查看device的driver如果已绑定 readlink /sys/bus/platform/devices/chip.0/driver # 查看device的resource内存、IRQ cat /sys/bus/platform/devices/chip.0/resource # 查看device的uevent触发匹配的事件 cat /sys/bus/platform/devices/chip.0/uevent深入一个driver# 查看driver的modalias匹配规则 cat /sys/bus/platform/drivers/chip/modalias # 查看driver绑定的device数量 ls /sys/bus/platform/drivers/chip/ | wc -l实操技巧readlink比ls -l更可靠因为它直接输出符号链接的目标不受权限影响。uevent文件的内容就是内核向用户空间发送的事件add表示设备添加bind表示驱动绑定。5.2 debugfs内核内部状态的显微镜debugfs提供了更底层的视图需要内核配置CONFIG_DEBUG_FSy。它能让你看到bus、device、driver在内核数据结构中的真实链接关系。启用debugfsmount -t debugfs none /sys/kernel/debug查看bus的内部状态# 查看platform bus的driver链表 cat /sys/kernel/debug/devices_deferred # 查看所有deferred device因driver未就绪而延迟probe的设备 cat /sys/kernel/debug/devices_deferred # 查看bus的klist cat /sys/kernel/debug/klist/platform_bus_type_drivers查看device的deferred状态# 如果device因driver未加载而deferred会出现在此 cat /sys/kernel/debug/devices_deferred # 输出格式platform:chip.0 (bus:device_name)实操心得devices_deferred是诊断“driver加载了但device不probe”的黄金线索。如果这里列出了你的device说明driver注册时它还没注册内核已将其放入defer队列等待driver注册完成后的自动重试。此时只需modprobe你的driver内核会自动触发重试。5.3 ftrace匹配流程的全程录像ftrace是内核的“黑匣子”能记录任意函数的调用栈。对于匹配流程我们重点关注driver_probe_device、bus_match、platform_match等函数。启用ftrace跟踪# 设置tracer echo function_graph /sys/kernel/debug/tracing/current_tracer # 过滤目标函数 echo driver_probe_device /sys/kernel/debug/tracing/set_ftrace_filter echo bus_match /sys/kernel/debug/tracing/set_ftrace_filter # 开启跟踪 echo 1 /sys/kernel/debug/tracing/tracing_on # 触发事件如加载driver modprobe chip # 关闭跟踪 echo 0 /sys/kernel/debug/tracing/tracing_on # 查看结果 cat /sys/kernel/debug/tracing/trace解读trace输出# tracer: function_graph # # CPU: 0 # TASK: swapper/0 PID: 0 COMMAND: swapper/0 # 0) 1.234567 | driver_probe_device() { 0) 1.234589 | bus_match() { 0) 1.234612 | platform_match() { 0) 1.234634 | platform_match: comparing chip.0 with chip 0) 1.234656 | } 0) 1.234678 | } 0) 1.234690 | chip_probe() { 0) 1.234712 | chip_probe: start 0) 1.234734 | chip_probe: failed to get clk 0) 1.234756 | } 0) 1.234778 | }这段trace清晰地展示了driver_probe_device()被调用 →bus_match()被调用 →platform_match()执行并返回true →chip_probe()被调用 →chip_probe()内部失败。每一行的时间戳帮你精确定位失败发生在probe的哪个子步骤。实操技巧ftrace的function_graph模式比function模式更易读因为它用缩进表示函数调用深度。结合set_ftrace_filter精准过滤避免海量无关日志淹没关键信息。6. 进阶实践自定义bus与driver的匹配逻辑理解了标准流程下一步就是定制化。在实际项目中你经常会遇到标准bus无法满足需求的情况比如需要基于设备的物理位置如PCB上的槽位编号而非compatible字符串来匹配驱动或者需要在匹配前执行一段安全校验如读取设备EEPROM的校验码。6.1 定义一个全新的bus_type创建自定义bus核心是实现match、probe、remove三个回调函数。以一个名为slot_bus的总线为例它根据设备的slot_id属性匹配driver// slot_bus.h struct slot_device { struct device dev; int slot_id; }; struct slot_driver { struct device_driver driver; int (*probe)(struct slot_device *dev); int (*remove)(struct slot_device *dev); }; #define to_slot_driver(drv) container_of((drv), struct slot_driver, driver) #define to_slot_device(dev) container_of((dev), struct slot_device, dev) // slot_bus.c static int slot_bus_match(struct device *dev, struct device_driver *drv) { struct slot_device *sdev to_slot_device(dev); struct slot_driver *sdrv to_slot_driver(drv); // 匹配逻辑driver的slot_id_mask与device的slot_id按位与 if ((sdrv-slot_id_mask sdev-slot_id) sdrv-slot_id_mask) return 1; return 0; } static int slot_bus_probe(struct device *dev) { struct slot_device *sdev to_slot_device(dev); struct slot_driver *sdrv to_slot_driver(dev-driver); return sdrv-probe(sdev); } static int slot_bus_remove(struct device *dev) { struct slot_device *sdev to_slot_device(dev); struct slot_driver *sdrv to_slot_driver(dev-driver); return sdrv-remove(sdev); } struct bus_type slot_bus_type { .name slot, .match slot_bus_match, .probe slot_bus_probe, .remove slot_bus_remove, }; // 初始化 static int __init slot_bus_init(void) { return bus_register(slot_bus_type); } postcore_initcall(slot_bus_init);6.2 实现一个slot_device和slot_driver// my_slot_dev.c static struct slot_device my_slot_dev { .dev { .init_name my_slot_dev, .bus slot_bus_type, }, .slot_id 0x01, // 槽位1 }; static int my_slot_probe(struct slot_device *dev) { pr_info(my_slot_dev: probe on slot %d\n, dev-slot_id); return 0; } static struct slot_driver my_slot_drv { .driver { .name my_slot_drv, .bus slot_bus_type, }, .slot_id_mask 0x01, // 只匹配slot_id为0x01的设备 .probe my_slot_probe, }; static int __init my_slot_init(void) { int ret; ret device_register(my_slot_dev.dev); if (ret) return ret; return driver_register(my_slot_drv.driver); } module_init(my_slot_init);6.3 匹配流程的定制化优势这种自定义bus的优势在于解耦硬件细节slot_id可以来自设备树的reg属性、PCIe的Function Number、或一个GPIO读取的编码开关driver无需关心具体来源只关注slot_id值。支持复杂策略slot_id_mask允许一个driver匹配多个slot如0x03匹配slot 1和2或实现主备切换driver A匹配0x01driver B匹配0x02两者互斥。安全校验前置slot_bus_match()中可以加入EEPROM读取和CRC校验校验失败直接返回0阻止不安全的driver加载。实操心得自定义bus不是银弹它增加了内核复杂度。只有当标准busplatform、i2c、pci无法满足业务逻辑时才考虑。上线前务必在debugfs中验证/sys/kernel/debug/klist/slot_bus_type_*的链表状态确保device和driver正确挂载。7. 性能与可靠性考量匹配流程在高并发与热插拔场景下的表现在工业控制、车载系统等对实时性和可靠性要求极高的场景中匹配流程的性能和鲁棒性至关重要。一个probe()函数耗时200ms在普通桌面系统中无感但在一个需要10ms周期响应的PLC系统中就是灾难。7.1 probe函数的实时性约束Linux内核的设备probe默认在system_workqueue中执行这是一个全局的、优先级为DEFAULT_WQ_PRIORITY的workqueue。这意味着多个device的probe会串行化执行如果某个probe阻塞如等待一个慢速I2C EEPROM读取
返回列表