5

我正在开发一个创建代理虚拟设备(更准确地说是虚拟Xbox 360键盘)的单线程进程小程序;我确实设法使用uinput接口创建它,我正确设置它并且它工作得很好。

为了向这个虚拟设备提供命令,我从另一个真实接口(在本例中为PS3键盘)读取事件,并使用以下标志打开真实设备文件:

fd = open("/dev/input/event22", O_RDONLY); // open the PS3 pad

主循环类似于(减去错误检查):

while(run) {
    input_event ev = {0};
    read(fd, &ev, sizeof(struct input_event));
    // convert from PS3 --> Xbox 360
    convert(ev);
    // write to the new virtual pad
    write(fd_virtual, &ev, sizeof(struct input_event));
}

正如您可以想象的那样,这read(fd, &ev, sizeof(struct input_event));是一个阻塞调用,我希望有一种超时来循环循环并检查其他事件/执行其他代码。

由于这些原因,我正在考虑将该read(fd...调用封装在epoll循环中,这样我也可以有一个超时。

问题是,这样做会有效吗?通过使用epoll_wait,我是否会在当前循环中引入额外的延迟,从而延迟虚拟垫的响应速度?

4

1 回答 1

0

通过使用 epoll_wait,我是否会在当前循环中引入额外的延迟,从而延迟虚拟垫的响应速度?

是的,你肯定会。

这样做会有效吗?

我确定是的,但这在很大程度上取决于您对“高效”的定义。

我们在这里谈论的是人类输入设备。在处理 HID 时,我们最关心的是延迟,它不应该滞后,按键的反应应该是即时的。什么是人类的“即时”?那里有一个很好的讨论,但我最喜欢的一个论点是,在高水平的田径比赛中,在信号发出后不到 100 毫秒内你将被取消参赛资格。

但是这 100 毫秒是对输入信号进行整个处理的时间预算,从按键到游戏中的一些可感知的变化。关于输入滞后的维基百科页面有一些关于该预算通常如何使用的数字。

无论如何,我认为 1 ms 是绝对安全的开销,您可以使用代理添加并且没有人会注意到,假设这是我们最大延迟的目标(如“高效”的定义)。

因此,让我们假设您对当前代码的响应时间感到满意。epoll()添加呼叫时会发生什么变化?基本上,您正在为另一个系统调用添加一些时间,因为现在不是一个系统调用来获得您正在制作的两个值。因此,它可能比您的原始代码慢两倍(让我们暂时忘记不同系统调用的处理时间差异)。但真的有那么糟糕吗?

为了回答这个问题,我们需要对系统调用开销有一些估计。如果我们懒得自己测量,我们可以使用一些20 年前的数字,一些来自关心系统调用的人的数字,一些来自微内核人员(他们总是关心)的 IPC 数字,一些来自 StackOverflow 的随机数或者只是问作为一个安全的假设,丰富并解决了微秒级的问题。

因此,问题归结为在您的毫秒(如 1000 µs)时间预算内增加一些(甚至可以说是 10)微秒是否值得注意。我认为不是。

当您要从“仅添加epoll()”到

循环循环并检查其他事件/执行其他代码。

对于这些循环和检查,您需要注意保持在您的时间预算内。但话又说回来,1 ms 对你来说可能已经足够了。

于 2017-02-03T10:02:27.240 回答