- PVSM.RU - https://www.pvsm.ru -

Слежение за изменениями в директории: как это делается в разных ОС

Слежение за изменениями в директории: как это делается в разных ОС Я бы хотел посвятить статью обзору API, предоставляемых разными ОС для слежения за изменениями в директории. Статья появилась как результат моей работы над демонами слежения за изменениями для утилиты dklab_realsync (статья на хабре [1], github репозиторий [2]) и своей собственной, которую я пока что не хочу анонсировать.

Windows, ReadDirectoryChangesW

Для операционной системы Windows есть замечательная функция ReadDirectoryChangesW [3], которая возвращает набор изменений для директории, в том числе содержит флаг для работы рекурсивно (bWatchSubtree). Таким образом, реализация слежения за изменениями в директории не представляет особого труда и в том же dklab_realsync реализация [4] занимает 80 строк кода или 3.5 Кб. Интересно, что в Windows эти события поддерживаются даже через SMB!

Тем не менее, существуют определенные подводные камни:

  • конечный размер буфера изменений, после которого очередь событий переполнится и эти события будут потеряны
  • согласно документации к watchdog package [5], событие перемещения посылается раньше, чем изменения становятся видны в ФС
  • размер буфера ограничен в 64 Кб для сетевой ФС

Вывод: Функция ReadDirectoryChangesW позволяет легко узнавать обо всех событиях в файлах, но, очередь событий может переполниться и тогда нужно будет выполнять полное сканирование ФС. Также, возможна доставка событий до того, как они станут актуальны.

Mac OS X, FSEvents

В Mac OS X также есть удобный и простой API для слежения за изменениями в файловой системе под названием FSEvents [6]. С использованием этого API простейшая реализация демона [7] составляет 50 строк кода или 1.8 кб. Очередь не может переполниться (!), но полное сканирование все же может потребоваться, если демон fseventsd «упадет». Стоит отметить, что этот API не предоставляет изменения по файлам, он сообщает только директории, в которых что-то изменилось. Поскольку события никуда не деваются и пишутся в лог (FSEvents service stores events in a persistent, per-volume database [8]), детализация с точностью для директории позволяет сэкономить место на диске.

Вывод: FSEvents API для Mac OS X является самым необычным из всех подобных API. Очередь не переполняется и даже имеется возможность получить события из прошлого. Тем не менее, детализация событий дается с точностью до директории, что означает меньшую эффективность демона для синхронизации файлов.

Linux, inotify

В linux vanilla kernel существует один способ слежения за изменениями в директории — это inotify [9]. Для этого API существует хорошая и подробная документация, но нет поддержки рекурсивного слежения за изменениями! Также, у inotify есть ограничение на максимальное количество объектов, за которыми можно следить. Простейшая реализация [10] демона занимает уже 250 строк кода или 8 кб. Статическая сборка с использованием dietlibc [11] занимает примерно 14 кб. Другим неприятным моментом является то, что приложение должно само поддерживать соответствия между watch descriptor (в нашем случае это всегда директория) и именем. Есть функция inotify_add_watch [12], которой передается путь до отслеживаемой директории, но нет обратной — inotify_get_path, которая бы возвращала этот самый путь по переданному дескриптору. События же содержат только watch descriptor и относительный путь до изменившегося файла внутри директории.

Подводные камни рекурсивного слежения за директорией через inotify:

  • Возможность переполнения очереди (длина очереди задается в /proc/sys/fs/inotify/max_queued_events)
  • Ограничение на максимальное количество объектов слежения (задается в /proc/sys/fs/inotify/max_user_watches)
  • Отсутствие возможности рекурсивного слежения за директорией
  • Необходимость отдельно обрабатывать случай, когда создается директория (например mkdir -p a/b/c). Вы получите событие о том, что создана директория «a», но пока вы навешиваете обработчик на эту директорию, в ней уже могут создать ещё одну директорию и событие об этом вам уже не придет.
  • Теоретическая возможность целочисленного переполнения watch descriptor (wd), так как он задается uint32

FreeBSD, Mac OS X, kqueue

FreeBSD и Mac OS X позволяют отслеживать за изменениями с помощью kqueue, который аналогичен inotify по своим характеристикам и также не имеет возможности рекурсивного слежения за директориями. Также, kqueue принимает в качестве аргументов дескрипторы открытых файлов (директорий), поэтому при использовании этого API ограничения на количество отслеживаемых директорий ещё более строгие.

Итого:

Механизм Переполнение очереди Рекурсивный? Макс. объектов Детализация
ReadDirectoryChangesW Да Да файл
FSEvents Нет Да директория
inotify Да Нет 8192 файл
kqueue Да Нет 1024 файл

Как можно видеть, у всех API существуют свои достоинства и недостатки. Наименее удобными являются механизмы kqueue и inotify, но они же являются самыми эффективными и надежными. Коммерческие ОС предоставляют более удобные механизмы слежения за изменениями, но у них тоже есть свои особенности. Надеюсь, теперь вы имеете больше представления о том, как тяжела участь Dropbox и подобных программ, которым требуется со всем этим уживаться и осуществлять надежную и эффективную синхронизацию данных :).

* Картинка взята с www.alexblogger.com/2008_01_01_archive.html [13]

Автор: youROCK

Источник [14]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/linux/24094

Ссылки в тексте:

[1] статья на хабре: http://habrahabr.ru/post/139348/

[2] github репозиторий: https://github.com/DmitryKoterov/dklab_realsync

[3] ReadDirectoryChangesW: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx

[4] реализация: https://github.com/DmitryKoterov/dklab_realsync/blob/master/src/win32/main.cpp

[5] watchdog package: http://packages.python.org/watchdog/installation.html

[6] FSEvents: https://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html

[7] простейшая реализация демона: https://github.com/DmitryKoterov/dklab_realsync/blob/master/src/darwin/notify.c

[8] FSEvents service stores events in a persistent, per-volume database: https://developer.apple.com/library/mac/#documentation/Darwin/Reference/FSEvents_Ref/Reference/reference.html

[9] inotify: http://linux.die.net/man/7/inotify

[10] Простейшая реализация: https://github.com/YuriyNasretdinov/unrealsync/blob/master/src/linux/notify.c

[11] dietlibc: http://www.fefe.de/dietlibc/

[12] inotify_add_watch: http://linux.die.net/man/2/inotify_add_watch

[13] www.alexblogger.com/2008_01_01_archive.html: http://www.alexblogger.com/2008_01_01_archive.html

[14] Источник: http://habrahabr.ru/post/164775/