Rya*_*lue 7 c linux epoll udev evdev
我的笔记本电脑连接了 2 个键盘(内置键盘和 USB 键盘)。我正在获取这些连接的键盘并libudev使用epoll它们通过接口轮询输入evdev:
// Compile with $(gcc udev.c -ludev)
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <time.h>
#include <libudev.h>
#define BILLION 1000000000L
long timespec_diff(struct timespec *start, struct timespec *end)
{
return (BILLION * (end->tv_sec - start->tv_sec)) +
(end->tv_nsec - start->tv_nsec);
}
bool want_to_run = true;
int
main(int argc, char *argv[])
{
int epoll_fd = epoll_create1(0);
struct udev *udev_obj = udev_new();
struct udev_enumerate *udev_enum = udev_enumerate_new(udev_obj);
udev_enumerate_add_match_subsystem(udev_enum, "input");
udev_enumerate_scan_devices(udev_enum);
struct udev_list_entry *udev_entries = udev_enumerate_get_list_entry(udev_enum);
struct udev_list_entry *udev_entry = NULL;
udev_list_entry_foreach(udev_entry, udev_entries)
{
char const *udev_entry_syspath = udev_list_entry_get_name(udev_entry);
struct udev_device *device = udev_device_new_from_syspath(udev_obj,
udev_entry_syspath);
char const *dev_prop = \
udev_device_get_property_value(device, "ID_INPUT_KEYBOARD");
if (dev_prop != NULL && strcmp(dev_prop, "1") == 0)
{
const char *dev_path = udev_device_get_devnode(device);
if (dev_path != NULL)
{
int dev_fd = open(dev_path, O_RDWR | O_NONBLOCK);
struct epoll_event event = {};
event.events = EPOLLIN;
event.data.fd = dev_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dev_fd, &event);
}
}
udev_device_unref(device);
}
udev_enumerate_unref(udev_enum);
struct timespec prev_timespec = {};
clock_gettime(CLOCK_MONOTONIC_RAW, &prev_timespec);
while (want_to_run)
{
struct epoll_event epoll_events[5] = {0};
int num_epoll_events = epoll_wait(epoll_fd, epoll_events, 5, 0);
for (int epoll_event_i = 0; epoll_event_i < num_epoll_events; ++epoll_event_i)
{
int dev_fd = epoll_events[epoll_event_i].data.fd;
struct input_event dev_events[4] = {0};
int dev_event_bytes_read = read(dev_fd, dev_events, sizeof(dev_events));
int num_dev_events = dev_event_bytes_read / sizeof(dev_events[0]);
for (int dev_event_i = 0; dev_event_i < num_dev_events; ++dev_event_i)
{
int dev_event_type = dev_events[dev_event_i].type;
int dev_event_code = dev_events[dev_event_i].code;
int dev_event_value = dev_events[dev_event_i].value;
bool is_released = (dev_event_type == EV_KEY ? dev_event_value == 0 : false);
bool is_down = (dev_event_type == EV_KEY ? dev_event_value == 1 : false);
bool was_down = (dev_event_type == EV_KEY ? dev_event_value == 2 : false);
bool w = (dev_event_code == KEY_W);
bool a = (dev_event_code == KEY_A);
bool s = (dev_event_code == KEY_S);
bool d = (dev_event_code == KEY_D);
bool q = (dev_event_code == KEY_Q);
bool e = (dev_event_code == KEY_E);
bool up = (dev_event_code == KEY_UP);
bool down = (dev_event_code == KEY_DOWN);
bool left = (dev_event_code == KEY_LEFT);
bool right = (dev_event_code == KEY_RIGHT);
bool escape = (dev_event_code == KEY_ESC);
bool space = (dev_event_code == KEY_SPACE);
bool enter = (dev_event_code == KEY_ENTER);
bool ctrl = (dev_event_code == KEY_LEFTCTRL);
if (q) want_to_run = false;
}
}
struct timespec end_timespec = {};
clock_gettime(CLOCK_MONOTONIC_RAW, &end_timespec);
printf("ns per frame: %lu\n", timespec_diff(&prev_timespec, &end_timespec));
prev_timespec = end_timespec;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
通过在每个键盘上输入按键进行实验,我在以下情况下遇到了一些严重的滞后/停顿(我鼓励您自己编译并尝试):
我用不同的键盘进行了测试,得到了相同的结果。这里发生了什么?
更新
我认为这可能是我的主机环境(Ubuntu 20.04)特有的,因为当我在另一个程序(例如 gnome 终端、firefox 等)的每个键盘上乱按按键时,会发生相同的停顿和延迟。如果我在按键上哀嚎,那就是严重的停顿(CPU 风扇停止运转,一切都停止了)。这只发生在键盘上,似乎将外部鼠标和触控板一起移动不会出现任何问题。
在 Intel VTune 热点分析下运行表明这epoll_wait()是停顿的根源(这并不奇怪)
谢谢@emptyhua。你的回答引导我进行了一条特定的调查。
虽然我不能肯定地说我相信问题的根源在于 GNOME 而不是 Xorg。请参阅GNOME 问题 1 GNOME 问题 2
安装替代的 X 环境(例如 xfce4)sudo apt install xfce4可以消除这种停顿。