# include " wtr/watcher.hpp "
# include < iostream >
# include < string >
using namespace std ;
using namespace wtr ;
// The event type, and every field within it, has
// string conversions and stream operators. All
// kinds of strings -- Narrow, wide and weird ones.
// If we don't want particular formatting, we can
// json-serialize and show the event like this:
// some_stream << event
// Here, we'll apply our own formatting.
auto show (event e) {
cout << to<string>(e. effect_type ) + ' '
+ to<string>(e. path_type ) + ' '
+ to<string>(e. path_name )
+ (e. associated ? " -> " + to<string>(e. associated -> path_name ) : " " )
<< endl;
}
auto main () -> int {
// Watch the current directory asynchronously,
// calling the provided function on each event.
auto watcher = watch ( " . " , show);
// Do some work. (We'll just wait for a newline.)
getchar ();
// The watcher would close itself around here,
// though we can check and close it ourselves.
return watcher. close () ? 0 : 1 ;
} # Sigh
PLATFORM_EXTRAS= $( test " $( uname ) " = Darwin && echo ' -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -framework CoreFoundation -framework CoreServices ' )
# Build
eval c++ -std=c++17 -Iinclude src/wtr/tiny_watcher/main.cpp -o watcher $PLATFORM_EXTRAS
# Run
./watcher #include "wtr/watcher-c.h"
#include <stdio.h>
void callback ( struct wtr_watcher_event event , void * _ctx ) {
printf (
"path name: %s, effect type: %d path type: %d, effect time: %lld, associated path name: %sn" ,
event . path_name ,
event . effect_type ,
event . path_type ,
event . effect_time ,
event . associated_path_name ? event . associated_path_name : ""
);
}
int main () {
void * watcher = wtr_watcher_open ( "." , callback , NULL );
getchar ();
return ! wtr_watcher_close ( watcher );
}pip install wtr-watcher from watcher import Watch
with Watch ( "." , print ):
input ()cargo add wtr-watcher tokio futures use futures :: StreamExt ;
use wtr_watcher :: Watch ;
# [ tokio :: main ( flavor = "current_thread" ) ]
async fn main ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
let show = |e| async move { println ! ( "{e:?}" ) } ;
let events = Watch :: try_new ( "." ) ? ;
events . for_each ( show ) . await ;
Ok ( ( ) )
} import * as watcher from 'watcher' ;
var w = watcher . watch ( '.' , ( event ) => {
console . log ( event ) ;
} ) ;
process . stdin . on ( 'data' , ( ) => {
w . close ( ) ;
process . exit ( ) ;
} ) ;Вывод каждого из вышеупомянутых будет чем -то этим, в зависимости от формата:
modify file /home/e-dant/dev/watcher/.git/refs/heads/next.lock
rename file /home/e-dant/dev/watcher/.git/refs/heads/next.lock -> /home/e-dant/dev/watcher/.git/refs/heads/next
create file /home/e-dant/dev/watcher/.git/HEAD.lock
Наслаждаться!
Наблюдатель за событиями файловой системы, который
Я стараюсь сохранить линии 1579, которые составляют время выполнения наблюдателя относительно простым, а API практично:
auto w = watch(path, [](event ev) { cout << ev; });wtr.watcher ~Наблюдатель может использоваться в качестве библиотеки, программы или обоих . Если вы не хотите что -то создавать с библиотекой, не беспокойтесь. Просто используйте наши, и вы получите себе наблюдатель за файловой системой, который печатает события файловой системы как JSON. Аккуратный. Вот как:
# The main branch is the (latest) release branch.
git clone https://github.com/e-dant/watcher.git && cd watcher
# Via Nix
nix run | grep -oE ' cmake-is-tough '
# With the build script
tool/build --no-build-test --no-run && cd out/this/Release # Build the release version for the host platform.
./wtr.watcher | grep -oE ' needle-in-a-haystack/.+" ' # Use it, pipe it, whatever. (This is an .exe on Windows.)Вы можете посмотреть целую файловую систему с этим проектом. Почти во всех случаях мы используем почти нулевое количество ресурсов и эффективно используем кэш. Мы регулярно проверяем, что накладные расходы обнаружения и отправки события пользователю на порядок меньше, чем измеряемые операции файловой системы.
Мы запускаем этот проект с помощью модульных тестов против всех доступных дезинфицирующих средств. Этот код изо всех сил старается быть потоком, памятью, границами, типом и безопасными для ресурсов. Чего нам не хватает из языка, мы стараемся восполнить с тестированием. Для некоторого практического определения безопасности этот проект, вероятно, подходит.
Наблюдатель зависит от стандартной библиотеки C ++. Для эффективности мы используем ОС, когда это возможно на Linux, Darwin и Windows. Для тестирования и отладки мы используем Snitch и Danitizers.
Наблюдатель управляется практически везде. Единственное требование - файловая система.
Важными частями являются (только заголовок) библиотека и (необязательная) программа CLI.
include/wtr/watcher.hpp . Включите это, чтобы использовать Watcher в вашем проекте C ++. Копирование этого в свой проект, и включение его как #include "wtr/watcher.hpp" (или аналогичный) достаточно, чтобы встать и заработать на этом языке. Некоторые дополнительные документации и внутренние внутренние библиотеки высокого уровня можно найти в заголовках и часах.watcher-c . Создайте это, чтобы использовать наблюдателя из C или через FFI на других языках.src/wtr/watcher/main.cpp . Создайте это, чтобы использовать Watcher из командной строки. Выход - это исчерпывающий поток JSON.src/wtr/tiny_watcher/main.cpp . Очень минимальная, более читаемая человеческая программа CLI. Источник для этого почти идентичен примеру использования для C ++.Дерево каталогов находится в примечаниях ниже.
Два фундаментальных строительных блока здесь:
watch или класс (в зависимости от языка)event (или аналогично назван, снова в зависимости от языка) watch идет по пути, который похож на струнную вещь, и обратный вызов, с функциональной вещью. Например, прохождение watch за массивом персонажей и закрытием будет хорошо работать в C ++.
Примеры для множества языков можно найти в быстром старте. API относительно последовательна между языками.
Наблюдатель будет счастливо продолжать наблюдать, пока вы не остановите это, или это не достигнет невозможности ошибки.
Объект event используется для передачи информации о событиях файловой системы в обратный вызов (вам) для watch .
Объект event будет содержать:
path_name , который является абсолютным путем к событию.path_type , тип пути. Один из:dirfilehard_linksym_linkwatcherothereffect_type , "что случилось". Один из:renamemodifycreatedestroyownerothereffect_time , время события в наносекундах со времен эпохи.associated (событие, C ++) или associated_path_name (все другие реализации, одно имя пути):(Обратите внимание, что для JavaScript мы используем верблюд, чтобы соответствовать экосистеме этого языка.)
Тип watcher особенный.
События с этим типом будут включать сообщения от наблюдателя. Вы можете получить сообщения об ошибках или важные обновления статуса.
Этот формат был выбран для поддержки асинхронных сообщений от наблюдателя в общем, портативном формате.
Два из самых важных событий «наблюдатель» - это начальное событие «живое» и финальное событие «Die».
Сообщение появляется, приготовленное к наблюдаемому базовому пути.
Например, после открытия наблюдателя AT /a/path вы можете получить эти сообщения от наблюдателя:
s/self/live@/a/pathe/self/die@/a/path Сообщения всегда начинаются либо s , что указывает на успешную операцию, w , указывающую на нерадостное предупреждение, либо e , что указывает на фатальную ошибку.
Важно отметить, что закрытие наблюдателя всегда будет создавать ошибку, если
self/live сообщение еще не было отправлено; Или, другими словами, если наблюдатель не полностью начал. В этом случае наблюдатель немедленно закроется после полного открытия и сообщит об ошибке во всех звонках, чтобы закрыть. Последнее событие всегда будет событием destroy от наблюдателя». Вы можете проанализировать это так, для какого -то события ev :
ev.path_type == path_type::watcher && ev.effect_type == effect_type::destroy;Счастливого взлома.
Этот проект пытается облегчить вам работать с событиями файловой системы. Я думаю, что хорошие инструменты просты в использовании. Если этот проект не является эргономичным, подайте проблему.
Вот снимок вывода, сделанного при подготовке этого коммита, прямо перед написанием этого абзаца.
{
"1666393024210001000" : {
"path_name" : " /home/edant/dev/watcher/.git/logs/HEAD " ,
"effect_type" : " modify " ,
"path_type" : " file "
},
"1666393024210026000" : {
"path_name" : " /home/edant/dev/watcher/.git/logs/refs/heads/next " ,
"effect_type" : " modify " ,
"path_type" : " file "
},
"1666393024210032000" : {
"path_name" : " /home/edant/dev/watcher/.git/refs/heads/next.lock " ,
"effect_type" : " create " ,
"path_type" : " other "
}
}Что довольно круто.
Способная программа здесь.
Этот проект доступен через:
tool/build : включает в себя заголовок/библиотека C ++, CLI, тестовые и эталонные целиtool/cross : включает в себя общую библиотеку и заголовок watcher-c , перекрестные считываемые для многих платформwatcher-c (статическая и общая), CLI, тестовые и контрольные целиСмотрите пакет здесь.
nix build # To just build
nix run # Build the default target, then run without arguments
nix run . -- / | jq # Build and run, watch the root directory, pipe it to jq
nix develop # Enter an isolated development shell with everything needed to explore this projectbazel build cli # Build, but don't run, the cli
bazel build hdr # Ditto, for the single-header
bazel run cli # Run the cli program without argumentstool/build
cd out/this/Release
# watches the current directory forever
./wtr.watcher
# watches some path for 10 seconds
./wtr.watcher ' your/favorite/path ' -s 10Это позаботится о некоторых специфических для платформы, создании вариантов релиза, отладки и дезинфицирующих средств, а также провести некоторые тесты.
cmake -S . -B out
cmake --build out --config Release
cd out
# watches the current directory forever
./wtr.watcher
# watches some path for 10 seconds
./wtr.watcher ' your/favorite/path ' -s 10Наблюдатели на всех платформах намеренно игнорируют события модификации, которые изменяют только время Acess в файле или каталоге.
Утилита этих событий была сомнительной.
Это казалось более вредным, чем хорошо. Другие наблюдатели, такие как наблюдатель Microsoft C#, игнорируют их по умолчанию. Некоторые пользовательские приложения полагаются на события модификации, чтобы узнать, когда они сами перезагружают файл.
Лучшие, более полные решения существуют, и эти значения по умолчанию могут снова измениться.
Предоставление способа игнорировать события с идентификатора процесса, сокращение от «этого» процесса и способ указать, какие виды источников событий нас интересуют, являются хорошими кандидатами для более полных решений.
Мне было удобно с C ++, когда я впервые написал это. Позже я переписал этот проект в Rust в качестве эксперимента. Есть преимущества и недостатки ржавчины. Некоторые вещи были немного безопаснее, чтобы выразить, другие вещи определенно не были. Например, необходимость выполнения указателя по математике на некоторых непрозрачных типах размером с ядра не более безопасна для выражения в ржавчине. Другие вещи безопаснее, но этот проект не очень выгодна от них.
Ржавчина действительно сияет в удобстве использования и выражении. Этого может быть достаточно причиной, чтобы использовать его. Среди прочего, мы могли бы работать с асинхронными чертами и алгебраическими типами для отличного блага.
Я не уверен, есть ли язык, который может «просто» сделать большую часть кода в этом проекте безопасным по определению.
Главны этого проекта, адаптеры, разговаривают с ядром. Они обязательно используют небезопасные, небезопасные, богатые предостережения интерфейсы на уровне системы.
Общественный API составляет около 100 строк, хорошо подходит, хорошо проверяется и проверяется человеком. Там не так много.
Создание FFI, разоблачив адаптеры с C abi, может стоить. Большинство языков должны быть в состоянии зацепить это.
Безопасность адаптеров платформы обязательно зависит от документации каждой платформы для своих интерфейсов. Как и во всех интерфейсах на системном уровне, если мы обеспечиваем правильные предварительные условия, и эти условия четко определенные, мы должны быть в порядке.
Среди реализаций, специфичных для платформы, API FSEvents используется на Darwin, а API ReadDirectoryChanges используется в Windows. Есть некоторая дополнительная работа, которую мы делаем, чтобы выбрать лучший адаптер на Linux. Адаптер fanotify используется, когда версия ядра превышает 5,9, в процессе содержания имеет root priveleges, и в противном случае разрешены необходимые системные вызовы. Системные вызовы, связанные с fanotify могут быть запрещены в пределах контейнера или CGROUP, несмотря на необходимые привилегированные и версию ядра. inotify адаптер используется иначе. Вы можете найти код выбора для Linux здесь.
Пространства имен для наших адаптеров встроены. Когда (внутренняя) detail::...::watch() вызывается, она решается к одной (и только одной) реализации платформы функции watch() . Один символ, много платформ, где платформы являются встроенными пространствами имен.
Эффективность получает удар, когда мы выдвигаем warthog , независимый от платформы адаптер. Этот адаптер используется на платформах, в которых отсутствуют лучшие альтернативы, такие как (не Darwin) BSD и Solaris (потому что warthog бьет kqueue ).
Наблюдатель все еще относительно эффективен, когда у него нет альтернативы лучше, чем warthog . Как правила большого пальца, сканирование более ста тысяч путей с warthog может заикаться.
Я буду держать глаза открытыми для лучших API -интерфейсов ядра на BSD.
Нет надежного способа общения, когда наблюдатель готов отправлять события в обратный вызов.
На несколько тысяч путей это может занять несколько миллисекунд. Для десятков тысяч путей рассмотрите возможность ждать несколько секунд.
Ни одна из реализаций, специфичных для платформы, не предоставляет информацию о том, из чего были изменены атрибуты. Это делает поддержку этим событиям в зависимости от хранения этой информации сами. Хранение карт путей к stat структурам, различающих их при изменениях атрибутов, является неизрешенным обязательством памяти.
События владельца и атрибута не поддерживаются, потому что я не уверен, как поддержать эти события эффективно.
Специальные файловые системы, включая /proc и /sys , нельзя наблюдать с inotify , fanotify или warthog . Будущая работа может включать в себя отправку программ EBPF для использования ядра. Это позволило бы нам отслеживать modify событий в некоторых из этих специальных файловых систем.
Количество наблюдаемых файлов ограничено при использовании inotify .
Для библиотеки только для заголовка и крошечного наблюдения C ++ 17 и выше должны быть в порядке.
Мы могли бы когда -нибудь использовать C ++ 20 Coroutines.
$ tool/gen-event/dir &
$ tool/gen-event/file &
$ valgrind --tool=cachegrind wtr.watcher ~ -s 30I refs: 797,368,564
I1 misses: 6,807
LLi misses: 2,799
I1 miss rate: 0.00%
LLi miss rate: 0.00%
D refs: 338,544,669 (224,680,988 rd + 113,863,681 wr)
D1 misses: 35,331 ( 24,823 rd + 10,508 wr)
LLd misses: 11,884 ( 8,121 rd + 3,763 wr)
D1 miss rate: 0.0% ( 0.0% + 0.0% )
LLd miss rate: 0.0% ( 0.0% + 0.0% )
LL refs: 42,138 ( 31,630 rd + 10,508 wr)
LL misses: 14,683 ( 10,920 rd + 3,763 wr)
LL miss rate: 0.0% ( 0.0% + 0.0% ) Пространства имен и символы внимательно следуют каталогам в папке devel/include . Встроенные пространства имен находятся в каталогах с - .
Например, wtr::watch находится внутри файла devel/include/wtr/watcher-/watch.hpp . watcher по пространству имен в wtr::watcher::watch Anonymous от этой конвенции.
Более подробно: функция ::detail::wtr::watcher::adapter::watch() определяется внутри (и только один!) Файлов devel/include/detail/wtr/watcher/adapter/*/watch.hpp , где * определяется во время компиляции (в зависимости от операционной системы хоста).
Все заголовки в devel/include объединяются в include/wtr/watcher.hpp , а керни добавляется керни. Include Guard не меняется с версией релиза. В будущем это могло бы.
watcher
├── src
│ └── wtr
│ ├── watcher
│ │ └── main.cpp
│ └── tiny_watcher
│ └── main.cpp
├── out
├── include
│ └── wtr
│ └── watcher.hpp
└── devel
├── src
│ └── wtr
└── include
├── wtr
│ ├── watcher.hpp
│ └── watcher-
│ ├── watch.hpp
│ └── event.hpp
└── detail
└── wtr
└── watcher
├── semabin.hpp
└── adapter
├── windows
│ └── watch.hpp
├── warthog
│ └── watch.hpp
├── linux
│ ├── watch.hpp
│ ├── sysres.hpp
│ ├── inotify
│ │ └── watch.hpp
│ └── fanotify
│ └── watch.hpp
└── darwin
└── watch.hpp
Вы можете запустить
tool/tree, чтобы просмотреть это дерево локально.
https://github.com/notify-rs/notify :
lines of code : 2799
lines of tests : 475
lines of docs : 1071
implementation languages : rust
interface languages : rust
supported platforms : linux, windows, darwin, bsd
kernel apis : inotify, readdirectorychanges, fsevents, kqueue
non-blocking : yes
dependencies : none
tests : yes
static analysis : yes (borrow checked, memory and concurrency safe language)
https://github.com/e-dant/watcher :
lines of code : 1579
lines of tests : 881
lines of docs : 1977
implementation languages : cpp
interface languages : cpp, shells
supported platforms : linux, darwin, windows, bsd
kernel apis : inotify, fanotify, fsevents, readdirectorychanges
non-blocking : yes
dependencies : none
tests : yes
static analysis : yes
https://github.com/facebook/watchman.git :
lines of code : 37435
lines of tests : unknown
lines of docs : unknown
implementation languages : cpp, c
interface languages : cpp, js, java, python, ruby, rust, shells
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : inotify, fsevents, readdirectorychanges
non-blocking : yes
dependencies : none
tests : yes (many)
static analysis : yes (all available)
https://github.com/p-ranav/fswatch :
lines of code : 245
lines of tests : 19
lines of docs : 114
implementation languages : cpp
interface languages : cpp, shells
supported platforms : linux, darwin, windows, bsd
kernel apis : inotify
non-blocking : maybe
dependencies : none
tests : some
static analysis : none
https://github.com/tywkeene/go-fsevents :
lines of code : 413
lines of tests : 401
lines of docs : 384
implementation languages : go
interface languages : go
supported platforms : linux
kernel apis : inotify
non-blocking : yes
dependencies : yes
tests : yes
static analysis : none (gc language)
https://github.com/radovskyb/watcher :
lines of code : 552
lines of tests : 767
lines of docs : 399
implementation languages : go
interface languages : go
supported platforms : linux, darwin, windows
kernel apis : none
non-blocking : no
dependencies : none
tests : yes
static analysis : none
https://github.com/parcel-bundler/watcher :
lines of code : 2862
lines of tests : 474
lines of docs : 791
implementation languages : cpp
interface languages : js
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : fsevents, inotify, readdirectorychanges
non-blocking : yes
dependencies : none
tests : some (js bindings)
static analysis : none (interpreted language)
https://github.com/atom/watcher :
lines of code : 7789
lines of tests : 1864
lines of docs : 1334
implementation languages : cpp
interface languages : js
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : inotify, fsevents, readdirectorychanges
non-blocking : yes
dependencies : none
tests : some (js bindings)
static analysis : none
https://github.com/paulmillr/chokidar :
lines of code : 1544
lines of tests : 1823
lines of docs : 1377
implementation languages : js
interface languages : js
supported platforms : linux, darwin, windows, bsd
kernel apis : fsevents
non-blocking : maybe
dependencies : yes
tests : yes (many)
static analysis : none (interpreted language)
https://github.com/Axosoft/nsfw :
lines of code : 2536
lines of tests : 1085
lines of docs : 148
implementation languages : cpp
interface languages : js
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : fsevents
non-blocking : maybe
dependencies : yes (many)
tests : yes (js bindings)
static analysis : none
https://github.com/canton7/SyncTrayzor :
lines of code : 17102
lines of tests : 0
lines of docs : 2303
implementation languages : c #
interface languages : c #
supported platforms : windows
kernel apis : unknown
non-blocking : yes
dependencies : unknown
tests : none
static analysis : none (managed language)
https://github.com/g0t4/Rx-FileSystemWatcher :
lines of code : 360
lines of tests : 0
lines of docs : 46
implementation languages : c #
interface languages : c #
supported platforms : windows
kernel apis : unknown
non-blocking : yes
dependencies : unknown
tests : yes
static analysis : none (managed language)