为何我编写的Linux内核模块无法模拟按键输入?
问题:Linux内核模块模拟按键无效果的排查与修复
我正在尝试编写一个用于模拟按键输入的Linux内核模块,但该模块无法实现按键功能。模块编译运行均无错误,但没有产生任何按键效果,仅出现以下日志:
[ 1275.986167] input: Unspecified device as /devices/virtual/input/input19
[ 1275.986622] Device Driverz Insert...Done!!!
我的模块代码如下:
#include<linux/kernel.h> #include<linux/module.h> #include<linux/init.h> #include<linux/device.h> #include<linux/timer.h> #include<linux/kdev_t.h> #include<linux/jiffies.h> #include<linux/input.h> #include<asm/io.h> static struct input_dev *key_dev; static int hellopress_init(void) { key_dev = input_allocate_device(); set_bit(EV_KEY,key_dev->evbit); set_bit(KEY_A,key_dev->keybit); set_bit(KEY_B,key_dev->keybit); set_bit(KEY_C,key_dev->keybit); set_bit(KEY_D,key_dev->keybit); set_bit(KEY_E,key_dev->keybit); set_bit(KEY_F,key_dev->keybit); set_bit(KEY_G,key_dev->keybit); set_bit(KEY_H,key_dev->keybit); set_bit(KEY_I,key_dev->keybit); set_bit(KEY_J,key_dev->keybit); set_bit(KEY_K,key_dev->keybit); set_bit(KEY_L,key_dev->keybit); set_bit(KEY_M,key_dev->keybit); set_bit(KEY_N,key_dev->keybit); set_bit(KEY_O,key_dev->keybit); set_bit(KEY_P,key_dev->keybit); set_bit(KEY_Q,key_dev->keybit); set_bit(KEY_R,key_dev->keybit); set_bit(KEY_S,key_dev->keybit); set_bit(KEY_T,key_dev->keybit); set_bit(KEY_U,key_dev->keybit); set_bit(KEY_V,key_dev->keybit); set_bit(KEY_W,key_dev->keybit); set_bit(KEY_X,key_dev->keybit); set_bit(KEY_Y,key_dev->keybit); set_bit(KEY_Z,key_dev->keybit); input_register_device(key_dev); input_report_key(key_dev, KEY_A, 1); input_report_key(key_dev, KEY_A, 0); input_report_key(key_dev, KEY_B, 1); input_report_key(key_dev, KEY_B, 0); input_report_key(key_dev, KEY_C, 1); input_report_key(key_dev, KEY_C, 0); input_report_key(key_dev, KEY_D, 1); input_report_key(key_dev, KEY_D, 0); input_report_key(key_dev, KEY_E, 1); input_report_key(key_dev, KEY_E, 0); input_report_key(key_dev, KEY_F, 1); input_report_key(key_dev, KEY_F, 0); input_report_key(key_dev, KEY_G, 1); input_report_key(key_dev, KEY_G, 0); input_report_key(key_dev, KEY_H, 1); input_report_key(key_dev, KEY_H, 0); input_report_key(key_dev, KEY_I, 1); input_report_key(key_dev, KEY_I, 0); input_report_key(key_dev, KEY_J, 1); input_report_key(key_dev, KEY_J, 0); input_report_key(key_dev, KEY_K, 1); input_report_key(key_dev, KEY_K, 0); input_report_key(key_dev, KEY_L, 1); input_report_key(key_dev, KEY_L, 0); input_report_key(key_dev, KEY_M, 1); input_report_key(key_dev, KEY_M, 0); input_report_key(key_dev, KEY_N, 1); input_report_key(key_dev, KEY_N, 0); input_report_key(key_dev, KEY_O, 1); input_report_key(key_dev, KEY_O, 0); input_report_key(key_dev, KEY_P, 1); input_report_key(key_dev, KEY_P, 0); input_report_key(key_dev, KEY_Q, 1); input_report_key(key_dev, KEY_Q, 0); input_report_key(key_dev, KEY_R, 1); input_report_key(key_dev, KEY_R, 0); input_report_key(key_dev, KEY_S, 1); input_report_key(key_dev, KEY_S, 0); input_report_key(key_dev, KEY_T, 1); input_report_key(key_dev, KEY_T, 0); input_report_key(key_dev, KEY_U, 1); input_report_key(key_dev, KEY_U, 0); input_report_key(key_dev, KEY_V, 1); input_report_key(key_dev, KEY_V, 0); input_report_key(key_dev, KEY_W, 1); input_report_key(key_dev, KEY_W, 0); input_report_key(key_dev, KEY_X, 1); input_report_key(key_dev, KEY_X, 0); input_report_key(key_dev, KEY_Y, 1); input_report_key(key_dev, KEY_Y, 0); input_report_key(key_dev, KEY_Z, 1); input_report_key(key_dev, KEY_Z, 0); input_report_key(key_dev, 30, 1); input_report_key(key_dev, 30, 0); input_report_key(key_dev, 76, 1); input_report_key(key_dev, 76, 0); input_sync(key_dev); printk(KERN_INFO "Device Driverz Insert...Done!!!\n"); return 0; } static void hellopress_exit(void) { input_unregister_device(key_dev); printk(KERN_INFO "Device Driver unloaded...Done!!!\n"); } module_init(hellopress_init); module_exit(hellopress_exit); MODULE_AUTHOR("Vic"); MODULE_VERSION("0.1.2"); MODULE_LICENSE("GPL");
错误原因与修复方案
1. 未设置输入设备的基本标识信息
日志中的"Unspecified device"是核心线索——你没有给input_dev设置name、phys等必要标识字段,部分桌面环境或输入子系统会忽略无标识的虚拟输入设备。
在input_allocate_device()之后添加以下代码:
key_dev->name = "virtual-keyboard-emulator"; key_dev->phys = "virtual-keyboard/input0"; key_dev->id.bustype = BUS_VIRTUAL; key_dev->id.vendor = 0x0001; key_dev->id.product = 0x0001; key_dev->id.version = 0x0100;
2. 按键事件发送时机与方式错误
你在input_register_device()后立刻批量发送所有按键事件,存在两个问题:
- 设备未完成输入子系统的初始化,事件直接被丢弃
- 无延迟的批量发送不符合真实键盘的输入逻辑,输入子系统无法正确识别
必须用定时器模拟真实按键的按下/释放间隔,示例实现如下:
首先定义全局变量:
static struct timer_list key_timer; static int current_key_idx = 0; static const unsigned int target_keys[] = {KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z};
在hellopress_init()中初始化定时器:
// 初始化定时器,绑定回调函数 setup_timer(&key_timer, send_key_event, 0); // 100ms后触发第一个按键事件 mod_timer(&key_timer, jiffies + msecs_to_jiffies(100));
实现定时器回调函数(处理按键按下/释放):
static void send_key_event(unsigned long data) { if (current_key_idx < ARRAY_SIZE(target_keys)) { // 发送按键按下事件 input_report_key(key_dev, target_keys[current_key_idx], 1); input_sync(key_dev); // 标记单次按下事件完成 // 50ms后发送释放事件(模拟真实按键按下时长) mod_timer(&key_timer, jiffies + msecs_to_jiffies(50)); current_key_idx++; } else if (current_key_idx == ARRAY_SIZE(target_keys)) { // 释放最后一个按键 input_report_key(key_dev, target_keys[current_key_idx - 1], 0); input_sync(key_dev); current_key_idx++; // 若需要循环发送,可重置current_key_idx并再次启动定时器 } }
在hellopress_exit()中清理定时器:
del_timer_sync(&key_timer);
3. input_sync()调用不规范
每个完整的按键动作(按下+释放)都需要对应一次input_sync(),告知输入子系统这是一组完整的事件。你之前仅在所有事件末尾调用一次,导致输入子系统无法解析单个按键的动作。
4. 禁止在init函数中阻塞
内核模块的init函数属于初始化路径,不能直接调用msleep()这类阻塞函数,必须用定时器处理延迟逻辑,否则会触发内核警告甚至导致模块加载失败。
完整修复后的核心流程
- 分配
input_dev并设置基本标识信息 - 配置设备支持的事件类型(
EV_KEY)和按键列表 - 注册输入设备到内核输入子系统
- 通过定时器分批发送按键的按下/释放事件,每次事件后调用
input_sync() - 模块卸载时注销设备并清理定时器
内容的提问来源于stack exchange,提问作者JustOneMan
相关产品推荐
相关产品推荐

