从键盘设备读取事件并将其存储在文件中会跳过一些输入

Reading events from the keyboard device and storing it in a file skips some input

我正在为一个非常简单的键盘记录器编写代码。它从 /dev/input/event[0..9] 读取输入并使用 read() 系统调用读取键盘完成的输入。然后将此输入重定向以存储在日志文件中。

代码如下

struct input_event ev[64];
int fd, rd, value, i, size = sizeof (struct input_event);
char dev_id[32];
char device[64];
FILE *f = fopen("/home/student/junk/haris/key_log.txt", "a");

FILE* fpp = popen("grep -E 'Handlers|EV=' /proc/bus/input/devices | grep -B1 'EV=120013' | grep -Eo 'event[0-9]+'", "r");
fgets(dev_id, sizeof(dev_id), fpp);
sprintf(device,"/dev/input/%s",dev_id);

for(i=0; device[i] != '\n'; i++)
    ;
device[i] = '[=10=]';

if ((getuid ()) != 0)
    printf ("You are not root! This may not work...n");


if ((fd = open (device, O_RDONLY)) == -1)
    printf ("%s is not a valid device.n", device);

size *= 64;

while (1)
{
    if ((rd = read(fd, ev, size)) < size)
        exit(0); 

    if (ev[1].value == 1 && ev[1].type == 1) // Only read the key press event
    {
        fflush(f);
        fprintf(f, "%s", key_map[(ev[1].code)]);
    }
}

最后一个 while 循环是读取发生的主循环。 一切正常


我面临的问题 是当某些用户键入任何单词时速度太快,程序无法接收所有按键事件。打字太快会漏掉一些字母。

例如,如果我在键盘上快速键入单词 hello haris,则日志文件中的输出将是 hllo hars

我认为发生此错误是因为循环需要时间来迭代。在一个事件可以读取和写入文件之前,下一个事件正在发生并结束。

有什么方法可以优化代码以不错过任何事件。还是有其他原因。


加分题

无论我输入多快,终端和其他设备都能获取所有输入,这是为什么?

有很多方法可以实现你想要的:

1)最简单的使用select+读取+缓存,伪代码:

result = select();
if (result == TIMEOUT)
  flush_input_queue_to_file();
else (result == DATA_READY)
  add_event_to_queue();

所以你需要在你的代码中添加https://en.wikipedia.org/wiki/Circular_buffer 和 select 在阅读之前。 因为你将有一个 I/O 每多个输入事件,一个事件的 intsead = 一个写入文件,这减少了丢失一些事件的可能性。

2) 使用两个线程和带互斥量的队列,一个线程写入文件,它可能会阻塞到 I/O,另一个线程读取输入并将事件放入队列,阅读 pthread 以实现此目的。与写入日志线程相比,您还可以提高读取输入线程的优先级,并使用无锁队列。

问题出在 read()

的大小上
(rd = read(fd, ev, size);

其中,

struct input_event ev[64];

size = sizeof (struct input_event) * 64;

对于 read() 函数来说,这是一个很大的尺寸。

在调试时我发现要获得击键,只有 struct input_event 的单个变量(而不是数组)就足够了。

所以我更改了以下内容,

struct input_event ev;
.
.
size = sizeof (struct input_event);
.
.
(rd = read(fd, ev, size);

它正在读取所有击键,不会遗漏任何一个。