Доброго времени суток.
Господа, объясните пожалуйста специфику работы с inotify. Признаюсь, задал похожий вопрос в разделе Unix, но пока безрезультатно.
Итак суть вопроса. Несмотря на то, что man гласит:
Each successful read(2) returns a buffer containing one or more of the following structures:
struct inotify_event {
int wd; /* Watch descriptor */
uint32_t mask; /* Mask of events */
uint32_t cookie; /* Unique cookie associating related
events (for rename(2)) */
uint32_t len; /* Size of name field */
char name[]; /* Optional null-terminated name */
};
mask contains bits that describe the event that occurred (see below).
мой код упорно получает по одной записи на каждое событие, что весьма странно, так как не соответствует информации в официальном man. Да и весьма неудобно это. Тоесть вместо одной структуры inotify_event с объединением всех событий (даже повторяющихся, что весьма удобно) я получаю длинный список где каждое событие представлено отдельно.
Вопрос: это нормальное поведение inotify? Или можно/нужно настраивать inotify?
Так выглядит, что для обеспечения наблюдения за изменениями файлов в заданном каталоге надо еще дополнительно реализовывать надстройку над inotify, которая бы группировала одинаковые события, чтобы многократно не вызывать обработчик для заданного события.
Здравствуйте, real_sba, Вы писали:
_>Господа, объясните пожалуйста специфику работы с inotify. Признаюсь, задал похожий вопрос в разделе Unix, но пока безрезультатно. _>Итак суть вопроса. Несмотря на то, что man гласит: _>
_> Each successful read(2) returns a buffer containing one or more of the following structures:
...
_>
_>мой код упорно получает по одной записи на каждое событие, что весьма странно, так как не соответствует информации в официальном man. Да и весьма неудобно это. Тоесть вместо одной структуры inotify_event с объединением всех событий (даже повторяющихся, что весьма удобно) я получаю длинный список где каждое событие представлено отдельно. _>Вопрос: это нормальное поведение inotify? Или можно/нужно настраивать inotify?
У меня этот код за раз читает несколько событий. Для теста я удаляю несколько папок в отслеживаемой папке командой rm -r /tmp/test/*. И мне в одном read приходят сразу несколько событий.
Может у тебя события слишком медленно происходят, так что read успевает вызваться несколько раз? Попробуй паузу по-больше поставить.
Здравствуйте, Mazay, Вы писали:
M>У меня этот код за раз читает несколько событий. Для теста я удаляю несколько папок в отслеживаемой папке командой rm -r /tmp/test/*. И мне в одном read приходят сразу несколько событий. M>Может у тебя события слишком медленно происходят, так что read успевает вызваться несколько раз? Попробуй паузу по-больше поставить.
Я имею ввиду немного другое. Данный код и куча других примеров которые нагуглил и попробовал работают именно так как написано выше.
Не имеет значения как часто происходят события. Код выше расчитан чтобы вычитывать сколько угодно большой read, все сначала быстро складывается в буфер послче чего буфер обрабатывается. Вопрос совсем в другом. Меня крайне удивляет следующее. Допустим я наблюдаю некоторое возможно длительное время за каталогом и тут внезапно решил сделать read. Вместо того чтобы получить по одному inotify_event для каждого каким либо образом измененного файла с битовой маской всех событий (согласно как написано в мане)
mask contains bits that describe the event that occurred (see below).
я получаю каждый event отдельно. И это однозначно не связано с тем что события происходят медленно. Все они получены именно в одном единственном read. Но почемуто даже для одного и того же файла каждое событие отдельно.
Пример:
1. Добавляем наблюдение для каталога /home/sba/projects/temp
2. Открываем и сохраняем в kate файл example.txt
3. Ждем 10 сек и только после этого делаем один read
Как видно маска вовсе не contains bits а contains bit.
Для себя хочу понять, действительно ли это нормальное поведение inotify.
И отсюда вопрос, для определения устаревания файлов, таких как удаление, изменение (нужно для кешера) придется поверх inotify сооружать еще контейнер который бы группировал однотипные события в одну записть, чтобы не вызывать лишний раз обработчик события. На примере предоставленом выше пользователь только один раз сохранил файл, значит внутренности поменялись только один раз, и по логике надо только один раз вызвать обработчик который удалит файл из кеша, но inotify уперто подсказывает что IN_MODIFY аж 2 раза. Ладно это неприциппиально, привяжемся только к IN_CLOSE_WRITE который действительно показывается только 1 раз. Но если б надо было цеплять обработчик на любой другой event, то получилась бы каша из ненужный вызовов.
Здравствуйте, real_sba, Вы писали:
_>Здравствуйте, Mazay, Вы писали:
Вопрос совсем в другом. Меня крайне удивляет следующее. Допустим я наблюдаю некоторое возможно длительное время за каталогом и тут внезапно решил сделать read. Вместо того чтобы получить по одному inotify_event для каждого каким либо образом измененного файла с битовой маской всех событий (согласно как написано в мане) _>
_>mask contains bits that describe the event that occurred (see below).
_>
_>я получаю каждый event отдельно. И это однозначно не связано с тем что события происходят медленно. Все они получены именно в одном единственном read. Но почемуто даже для одного и того же файла каждое событие отдельно.
_>Как видно маска вовсе не contains bits а contains bit.
Прошу обратить внимание: event стоит в единственном числе. Маска описывает ровно одно событие (точнее, серию идентичных). Упомянутые биты (которые во множественном числе), включают IN_IGNORED и компанию.
_>Для себя хочу понять, действительно ли это нормальное поведение inotify.
Да.
_>И отсюда вопрос, для определения устаревания файлов, таких как удаление, изменение (нужно для кешера) придется поверх inotify сооружать еще контейнер который бы группировал однотипные события в одну записть, чтобы не вызывать лишний раз обработчик события. На примере предоставленом выше пользователь только один раз сохранил файл, значит внутренности поменялись только один раз, и по логике надо только один раз вызвать обработчик который удалит файл из кеша, но inotify уперто подсказывает что IN_MODIFY аж 2 раза. Ладно это неприциппиально, привяжемся только к IN_CLOSE_WRITE который действительно показывается только 1 раз. Но если б надо было цеплять обработчик на любой другой event, то получилась бы каша из ненужный вызовов.
Это особенности редактора. Он зачем-то переоткрывает файл несколько раз. Протестируй на чем-нибудь более простом, например (watch вполне аналогичен твоему коду):
$ ./watch junk &
[1] 21595
$ echo aaa > junk/foo
foo : 0002 modify
foo : 0020 open
foo : 0002 modify
foo : 0008 close write
$ echo aaa > junk/bar
bar : 0100 create
bar : 0020 open
bar : 0002 modify
bar : 0008 close write
$ echo aaa >> junk/bar
bar : 0020 open
bar : 0002 modify
bar : 0008 close write
$
Здравствуйте, Mazay , Вы писали:
M>>Может у тебя события слишком медленно происходят, так что read успевает вызваться несколько раз? Попробуй паузу по-больше поставить.
У меня наблюдается именно эта проблема, не могу понять как поставить паузу. Подскажите как это сделать?
char buf[4096];
ssize_t len = sizeof(buf);
while (len == sizeof(buf)) { //<----------- посмотри сюда
len = read(m_Inotify, buf, sizeof(buf));
if (len <= 0) {
break;
}
m_ReadBuff += std::string(buf, len); //<---- А это что? У тебя читаются не строки, а структуры inotify_event
}
}
У тебя твой цикл всегда будет выполняться ровно один раз, потому что read может вернуть размер твоего буфера только случайно.
Тебе надо вычитать структуру в буфер размером sizeof(struct inotify_event), затем в буфер, размером не менее inotify_event::len прочитать inotify_event::name, и только потом добавлять этот name в m_ReadBuff, если я правильно понял, что m_ReadBuff — строка.
И ещё нужно учитывать, что у тебя необязательно сразу прочитается столько, сколько ты попросил. Может быть и меньше. В этом случае нужно продолжать чтение с учётом уже прочитанного. Но и в случае ошибки нельзя сразу выходить. В случае если read(...) == -1 нужно проверять errno на EINTR и на EAGAIN (см. man 2 read) и повторять чтение, если поймана одна из них.