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

如何用evdev实现Linux设备动态连接与断开的事件处理

Linux下evdev异步读取输入事件及设备动态管理问题

我需要在Linux系统中使用evdev异步读取输入事件,同时实现设备连接与断开的动态处理。要求系统具备通用性、支持多设备,通过控制线程检测设备状态变化,调度或取消对应设备的处理任务。我编写了四个实现原型,但每个都存在问题:

1. 原型一:设备变更线程无权限添加任务导致崩溃

该方案逻辑直观,但设备变更线程无权限添加新任务,设备连接时程序崩溃。代码如下:

import asyncio
import evdev
import pyudev

# 处理设备变更(连接/断开)的函数
def handle_device_changes(devices, device_tasks):
    current_devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
    added_devices = [device for device in current_devices if device not in devices]
    removed_devices = [device for device in devices if device not in current_devices]
    devices = current_devices
    for device in added_devices:
        print(f"Added device: {device.name}")
        # 为新增设备执行必要操作
        if "GuliKit" in device.name:
            device.grab()
            task = asyncio.create_task(print_events(device))
            device_tasks[device] = task
    for device in removed_devices:
        print(f"Removed device: {device.name}")
        # 为移除设备执行必要操作
        if device in device_tasks:
            task = device_tasks[device]
            task.cancel()
            del device_tasks[device]
    return devices

# 为每个捕获的设备创建的任务
async def print_events(device):
    try:
        async for event in device.async_read_loop():
            print(device.path, evdev.categorize(event), sep=': ')
    except OSError:
        print(f"Device {device.name} disconnected.")
        #handle_device_changes()

# 程序入口
async def main():
    devices = []
    device_tasks = {}
    devices = handle_device_changes(devices, device_tasks)
    queue = asyncio.Queue()

    monitor = pyudev.Monitor.from_netlink(pyudev.Context())
    monitor.filter_by(subsystem='input')
    def log_event(action, device):
        queue.put(handle_device_changes(devices, device_tasks))
    observer = pyudev.MonitorObserver(monitor, log_event)
    observer.start()

    try:
        while True:
            await queue.get()
    except KeyboardInterrupt:
        print("Keyboard interrupt received.")
    finally:
        observer.stop()
        await asyncio.gather(*device_tasks.values(), return_exceptions=True)

if __name__ == '__main__':
    asyncio.run(main(),debug=True)

2. 原型二:存在无法排查的运行错误

重新编写的版本,出现无法定位的运行错误。代码如下:

import evdev
import asyncio
import pyudev

# 处理设备事件的函数
async def handle_event(device):
    async for event in device.async_read_loop():
        # 在此处处理事件
        print("Event:", event)

# 处理设备变更(连接/断开)的函数
def handle_device_changes(devices, device_tasks):
    current_devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
    added_devices = [device for device in current_devices if device not in devices]
    removed_devices = [device for device in devices if device not in current_devices]
    devices = current_devices

    for device in added_devices:
        print(f"Added device: {device.name}")
        # 为新增设备创建事件监听任务
        event_loop = asyncio.get_event_loop()
        event_task = event_loop.create_task(handle_event(device))
        device_tasks[device.path] = event_task
        if "GuliKit" in device.name:
            device.grab()

    for device in removed_devices:
        print(f"Removed device: {device.name}")
        # 取消并清理与移除设备关联的事件监听任务
        event_task = device_tasks.pop(device.path)
        event_task.cancel()

    return devices

async def main():
    devices = []
    device_tasks = {}

    # 初始设置:处理设备变更以建立事件监听
    devices = handle_device_changes(devices, device_tasks)

    # 监听设备变更
    context = pyudev.Context()
    monitor = pyudev.Monitor.from_netlink(context)
    monitor.filter_by(subsystem='input')

    loop = asyncio.get_event_loop()
    while True:
        try:
            async for device_event in loop.run_in_executor(None, monitor.poll):
                # 处理设备变更
                devices = handle_device_changes(devices, device_tasks)
        except KeyboardInterrupt:
            break

if __name__ == '__main__':
    asyncio.run(main())

3. 原型三:设备列表维护正常但无法上报事件

借助ChatGPT生成的实现,能准确维护设备列表,但无法上报设备事件。代码如下:

import evdev
import selectors
import pyudev

# 处理设备事件的函数
def handle_event(device, selector):
    for event in device.read():
        # 在此处处理事件
        print("event")

# 处理设备变更(连接/断开)的函数
def handle_device_changes(devices, device_tasks, selector):
    current_devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
    added_devices = [device for device in current_devices if device not in devices]
    removed_devices = [device for device in devices if device not in current_devices]
    devices = current_devices

    for device in added_devices:
        print(f"Added device: {device.name}")
        # 为新增设备创建事件监听任务
        event_listener = selectors.EVENT_READ | selectors.EVENT_WRITE
        event_task = selector.register(device.fd, event_listener, handle_event)
        device_tasks[device.path] = event_task

    for device in removed_devices:
        print(f"Removed device: {device.name}")
        # 销毁与移除设备关联的事件监听任务
        event_task = device_tasks.pop(device.path)
        selector.unregister(device.fd)

    return devices

def main():
    devices = []
    device_tasks = {}
    selector = selectors.DefaultSelector()

    # 初始设置:处理设备变更以建立事件监听
    devices = handle_device_changes(devices, device_tasks, selector)

    # 监听设备变更
    context = pyudev.Context()
    monitor = pyudev.Monitor.from_netlink(context)
    monitor.filter_by(subsystem='input')

    for device_event in iter(monitor.poll, None):
        # 处理设备变更
        devices = handle_device_changes(devices, device_tasks, selector)

if __name__ == '__main__':
    main()

4. 原型四:使用废弃功能无法正常运行

参考旧帖实现,但使用了已废弃的功能,无法正常运行。代码如下:

import functools
import pyudev

from evdev import InputDevice
from select import select

context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by(subsystem='input')
monitor.start()

fds = {monitor.fileno(): monitor}
finalizers = []

while True:
    r, w, x = select(fds, [], [])

    if monitor.fileno() in r:
        r.remove(monitor.fileno())

        for udev in iter(functools.partial(monitor.poll, 0), None):
            # 我们只关注有设备节点的设备(如/dev/input/eventX)
            if not udev.device_node:
                break

            # 找到目标设备并添加到fds
            for name in (i['NAME'] for i in udev.ancestors if 'NAME' in i):
                # 我使用虚拟输入设备测试——请根据需求调整
                if u'GuliKit' in name:
                    if udev.action == u'add':
                        print('Device added: %s' % udev)
                        print(udev.device_node)
                        fds[dev.fd] = InputDevice(udev.device_node)
                        break
                    if udev.action == u'remove':
                        print('Device removed: %s' % udev)
                        def helper():
                            global fds
                            fds = {monitor.fileno(): monitor}
                        finalizers.append(helper)
                        break

    for fd in r:
        dev = fds[fd]
        for event in dev.read():
            print(event)

    for i in range(len(finalizers)):
        finalizers.pop()()

内容的提问来源于stack exchange,提问作者zane49er

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 12:08:09