libudev 使用说明书

本文详细介绍了如何使用libudev库进行设备枚举、监控设备插拔、以及获取设备信息的过程,包括创建context、设置过滤器、接收设备事件和读取设备属性。

一、初始化

首先调用 udev_new,创建一个 udev library context。udev library context 采用引用记数机制,创建的 context 默认引用记数为 1,使用 udev_ref 和 udev_unref 增加或减少引用记数,如果引用记数为0,则释放内部资源。

二、枚举设备

使用 udev_enumerate_new 创建一个枚举器,用于扫描系统已接设备。使用 udev_enumerate_ref 和 udev_enumerate_unref 增加或减少引用记数。

使用 udev_enumerate_add_match / nomatch_xxx 系列函数增加枚举的过滤器,过滤关键字以字符表示,如"block"设备。

使用 udev_enumerate_scan_xxx 系列函数扫描 /sys 目录下,所有与过滤器匹配的设备。扫描完成后的数据结构是一个链表,使用 udev_enumerate_get_list_entry 获取链表的首个结点,使用 udev_list_entry_foreach 遍历整个链表。

三、监控设备插拔 udev 的设备插拔基于 netlink 实现。

使用 udev_monitor_new_from_netlink 创建一个新的 monitor,函数的第二个参数是事件源的名称,可选"kernel"或"udev"。基于"kernel"的事件通知要早于"udev",但相关的设备结点未必创建完成,所以一般应用的设计要基于"udev"进行监控。

使用 udev_monitor_filter_add_match_subsystem_devtype 增加一个基于设备类型的 udev 事件过滤器,例如: "block"设备。

使用 udev_monitor_enable_receiving 启动监控过程。监控可以使用 udev_monitor_get_fd 获取一个文件描述符,基于返回的 fd 可以执行 poll 操作,简化程序设计。

插拔事件到达后,可以使用 udev_monitor_receive_device 获取产生事件的设备映射。调用 udev_device_get_action 可以获得一个字符串:"add"或者"remove",以及"change", "online", "offline"等,但后三个未知什么情况下会产生。

四、获取设备信息

使用 udev_list_entry_get_name 可以得到一个设备结点的 sys 路径,基于这个路径使用 udev_device_new_from_syspath 可以创建一个 udev 设备的映射,用于获取设备属性。获取设备属性使用 udev_device_get_properties_list_entry,返回一个存储了设备所有属性信息的链表,使用 udev_list_entry_foreach 遍历链表,使用 udev_list_entry_get_nameudev_list_entry_get_value 获取属性的名称和值。

转载:libudev使用说明书_coroutines的博客-优快云博客_libudev

(SAW:Game Over!)

使用 `libudev` 可以在 Linux 系统中动态发现和识别输入设备(如键盘),而无需手动指定 `/dev/input/eventX`。这使得程序更具可移植性和健壮性。 我们将修改之前的代码,通过 `libudev` 遍历所有输入设备,查找带有“keyboard”标签的设备,并自动选择正确的 event 节点进行监听。 --- ### ✅ 使用 libudev 自动识别键盘设备的 C 代码 ```c #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <linux/input.h> #include <libudev.h> // 键码定义 #define KEY_LEFTCTRL 29 #define KEY_RIGHTCTRL 97 #define KEY_DELETE 111 // xterm 控制相关 #define XTERM_PROCESS_NAME "xterm" #define XTERM_EXEC "/usr/bin/xterm" pid_t xterm_pid = 0; // 检查是否有 xterm 正在运行 int find_xterm_process() { FILE *fp; char cmdline[256]; DIR *dir; struct dirent *entry; dir = opendir("/proc"); if (!dir) return 0; while ((entry = readdir(dir)) != NULL) { if (entry->d_type == DT_DIR && atoi(entry->d_name) > 0) { char path[64]; snprintf(path, sizeof(path), "/proc/%s/comm", entry->d_name); fp = fopen(path, "r"); if (fp) { if (fgets(cmdline, sizeof(cmdline), fp)) { cmdline[strcspn(cmdline, "\n")] = 0; if (strcmp(cmdline, XTERM_PROCESS_NAME) == 0) { closedir(dir); fclose(fp); xterm_pid = atoi(entry->d_name); return 1; } } fclose(fp); } } } closedir(dir); return 0; } void launch_xterm() { pid_t pid = fork(); if (pid == 0) { // 设置 DISPLAY 环境变量(常见为 :0) setenv("DISPLAY", ":0", 1); execl(XTERM_EXEC, XTERM_EXEC, NULL); perror("execl failed"); exit(1); } else if (pid > 0) { printf("Launched xterm with PID: %d\n", pid); xterm_pid = pid; } else { perror("fork"); } } void kill_xterm() { if (xterm_pid > 0) { if (kill(xterm_pid, SIGTERM) == 0) { printf("Terminated xterm process (PID: %d)\n", xterm_pid); } else { printf("Failed to terminate xterm (PID: %d)\n", xterm_pid); } xterm_pid = 0; } } // 使用 libudev 查找第一个可用的键盘设备路径 char* find_keyboard_device(char *buffer, size_t buflen) { struct udev *udev; struct udev_enumerate *enumerate; struct udev_list_entry *devices, *dev_list_entry; struct udev_device *dev; udev = udev_new(); if (!udev) { fprintf(stderr, "Cannot create udev context\n"); return NULL; } // 枚举所有输入设备 enumerate = udev_enumerate_new(udev); udev_enumerate_add_match_subsystem(enumerate, "input"); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(dev_list_entry, devices) { const char *path = udev_list_entry_get_name(dev_list_entry); dev = udev_device_new_from_syspath(udev, path); // 获取父设备中的 name 属性判断是否是键盘 struct udev_device *parent = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"); if (!parent) parent = udev_device_get_parent_with_subsystem_devtype(dev, "hid", NULL); // 支持蓝牙/PS/2 键盘 // 获取设备节点(如 /dev/input/eventX) const char *devnode = udev_device_get_devnode(dev); const char *name = udev_device_get_property_value(dev, "NAME"); if (devnode && name && strstr(name, "keyboard")) { printf("Found keyboard: %s (%s)\n", name, devnode); strncpy(buffer, devnode, buflen - 1); buffer[buflen - 1] = '\0'; udev_device_unref(dev); udev_enumerate_unref(enumerate); udev_unref(udev); return buffer; } udev_device_unref(dev); } udev_enumerate_unref(enumerate); udev_unref(udev); return NULL; } // 主函数:监听键盘事件 void listen_keyboard(const char *dev_path) { int fd = open(dev_path, O_RDONLY); if (fd < 0) { perror("open device"); return; } printf("Listening on %s...\n", dev_path); struct input_event ev; int ctrl_pressed = 0; int del_pressed = 0; while (1) { ssize_t n = read(fd, &ev, sizeof(ev)); if (n != sizeof(ev)) { perror("read event"); continue; } if (ev.type == EV_KEY) { int key_code = ev.code; int key_value = ev.value; // 1=press, 0=release // Ctrl 键检测 if (key_code == KEY_LEFTCTRL || key_code == KEY_RIGHTCTRL) { ctrl_pressed = (key_value == 1); } // Delete 键检测 if (key_code == KEY_DELETE) { del_pressed = (key_value == 1); } // 判断 Ctrl+Del 同时按下 if (ctrl_pressed && del_pressed) { printf("Ctrl+Del detected!\n"); if (find_xterm_process()) { kill_xterm(); } else { launch_xterm(); } sleep(1); // 防止重复触发 } } } close(fd); } int main() { char dev_path[64]; printf("Searching for keyboard device...\n"); if (!find_keyboard_device(dev_path, sizeof(dev_path))) { fprintf(stderr, "No keyboard device found!\n"); fprintf(stderr, "Make sure you have permission to access input devices.\n"); return 1; } listen_keyboard(dev_path); return 0; } ``` --- ### 📦 编译方法(需要安装 libudev-dev) #### Ubuntu/Debian: ```bash sudo apt update sudo apt install libudev-dev gcc ``` #### 编译命令: ```bash gcc -o xterm_toggle_udev xterm_toggle_udev.c -ludev ``` #### 运行(仍需权限读取输入设备): ```bash sudo ./xterm_toggle_udev ``` > 提示:可通过 udev 规则将输入设备权限开放给普通用户,避免每次使用 `sudo`。 --- ### 🔧 如何让非 root 用户也能访问输入设备? 创建一个 udev 规则文件: ```bash sudo nano /etc/udev/rules.d/99-keyboard-access.rules ``` 添加内容(允许 plugdev 组访问): ```text SUBSYSTEM=="input", GROUP="plugdev", MODE="0660" SUBSYSTEM=="input", ATTRS{name}=="*keyboard*", ENV{ID_INPUT_KEYBOARD}="1" ``` 然后把当前用户加入组: ```bash sudo usermod -aG plugdev $USER ``` 重启或重新插拔键盘后即可生效。 --- ### ✅ 总结:libudev 的优势 | 特性 | 说明 | |------|------| | 自动识别 | 不用手动猜测 `/dev/input/eventX` | | 支持热插拔 | 可扩展为设备插入时自动注册 | | 更安全 | 可结合规则实现细粒度权限控制 | | 可扩展 | 可过滤鼠标、触摸板等其他设备 | --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值