You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于修改Linux内核模块加载优先级及udev规则加载模块失败的问题

解决Linux内核模块加载优先级问题:udev规则加载模块失败(退出码1)

我来帮你拆解这个问题:你猜的没错,核心矛盾就是usbhid作为内核原生的HID驱动,会在USB设备枚举阶段就直接绑定键盘接口,而udev规则的触发时机本来就晚于内核的设备绑定流程,所以你靠udev事后insmod模块的思路从根上就有问题——而且你遇到的退出码1,大概率是insmod本身的执行问题(比如路径权限、模块重复加载、udev worker的上下文限制)。

先给你理清楚问题的本质:

  1. 你当前的模块只是个普通的初始化模块,没有任何设备匹配逻辑,就算udev成功加载它,也只是打印一句初始化日志,根本没和键盘设备产生关联;
  2. udev的RUN执行insmod时,usbhid已经绑定了键盘,而且insmod在udev的沙盒环境里很容易因为路径权限、模块已加载(比如你拔插多次)返回错误码1;
  3. 要让你的模块"先执行",不能靠udev事后补救,得让你的模块成为内核USB驱动链的一部分,和usbhid竞争设备绑定权。

下面给你两个可行的解决方案,优先推荐方案一(符合内核驱动的原生逻辑):

方案一:修改模块为USB键盘驱动,和usbhid竞争绑定

把你的模块改成一个真正的USB驱动,让内核在枚举键盘时就尝试加载它,这样就能在usbhid之前触发你的逻辑。修改后的代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#include <linux/hid.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("rcortes-");
MODULE_DESCRIPTION("Load when a Keyboard is Plugged In");
MODULE_VERSION("0.01");

// 匹配USB键盘的接口类/子类/协议,和你udev规则里的条件一致
static const struct usb_device_id kb_id_table[] = {
    { USB_INTERFACE_INFO(USB_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_KEYBOARD) },
    { } // 必须的终止项
};
MODULE_DEVICE_TABLE(usb, kb_id_table);

// 设备绑定回调:内核枚举到匹配的键盘时触发
static int kb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    printk(KERN_INFO "Keyboard has been connected!\n");
    // 返回-ENXIO,告诉内核"我不独占这个设备",让usbhid继续绑定处理
    // 如果返回0,你的模块会独占设备,usbhid就无法识别键盘了
    return -ENXIO;
}

// 设备解绑回调:键盘拔出时触发
static void kb_disconnect(struct usb_interface *intf)
{
    printk(KERN_INFO "Keyboard disconnected!\n");
}

// USB驱动结构体,注册给内核
static struct usb_driver kb_usb_driver = {
    .name = "kb_hotplug",
    .probe = kb_probe,
    .disconnect = kb_disconnect,
    .id_table = kb_id_table,
};

static int __init kb_init(void)
{
    int ret;
    // 注册USB驱动到内核
    ret = usb_register(&kb_usb_driver);
    if (ret) {
        printk(KERN_ERR "Failed to register USB driver: %d\n", ret);
        return ret;
    }
    printk(KERN_INFO "kb_hotplug USB driver registered!\n");
    return 0;
}

static void __exit kb_exit(void)
{
    usb_deregister(&kb_usb_driver);
    printk(KERN_INFO "Module clean up!\n");
}

module_init(kb_init);
module_exit(kb_exit);

后续配置步骤:

  1. 编译修改后的模块,然后把它安装到系统的内核模块目录,比如:
    cp kb_hotplug.ko /lib/modules/$(uname -r)/kernel/drivers/usb/
    
  2. 生成模块依赖:
    depmod -a
    
  3. 删掉你之前的udev规则——现在内核会自动处理模块加载:当你插入键盘时,内核会根据MODULE_DEVICE_TABLE里的匹配规则,自动modprobe你的模块,优先触发kb_probe打印日志,然后让usbhid继续绑定设备。

方案二:修复原udev规则的退出码问题(不推荐,仅作排查)

如果你一定要用udev的方式,先解决退出码1的问题:

  1. 用udevadm test /sys/bus/usb/devices/2-2:1.0(替换成你键盘的实际设备路径)测试规则,看具体的错误日志;
  2. 把udev规则里的insmod换成modprobe——insmod会因为模块已加载直接失败,而modprobe会自动检查:
    ACTION=="add", SUBSYSTEM=="usb", ATTRS{bInterfaceClass}=="03", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="01", RUN+="/usr/sbin/modprobe kb_hotplug"
    
  3. 检查/home/LinuxKernelAssignments的权限——udev的worker进程可能没有访问普通用户home目录的权限,最好把模块放到系统路径里。

但注意:就算修复了udev规则,也做不到让你的模块"先于usbhid执行",因为udev的触发时机本来就在内核绑定设备之后。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 07:59:29