# 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
즐기다!
파일 시스템 이벤트 감시자
나는 감시자 의 런타임을 비교적 단순하고 API 실용적으로 유지하는 1579 라인을 유지하려고 노력합니다.
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.)이 프로젝트로 전체 파일 시스템을 볼 수 있습니다. 거의 모든 경우에, 우리는 거의 0의 리소스를 사용하고 캐시를 효율적으로 사용합니다. 우리는 정기적으로 이벤트를 탐지하고 사용자에게 보내는 오버 헤드가 측정되는 파일 시스템 작업보다 적은 순서임을 정기적으로 테스트합니다.
우리는 이용 가능한 모든 Sanitiziers에 대한 단위 테스트를 통해이 프로젝트를 실행합니다. 이 코드는 스레드, 메모리, 경계, 유형 및 리소스 안전이되기 어려워집니다. 우리가 언어에서 부족한 것은 테스트를 위해 보충하려고 노력합니다. 안전에 대한 실질적인 정의를 위해이 프로젝트는 아마도 적합 할 것입니다.
감시자는 C ++ 표준 라이브러리에 의존합니다. 효율성을 위해 Linux, Darwin 및 Windows에서 가능한 경우 OS를 활용합니다. 테스트 및 디버깅을 위해 Snitch 및 Sanitizers를 사용합니다.
감시자는 거의 어디서나 달릴 수 있습니다. 유일한 요구 사항은 파일 시스템입니다.
중요한 부분은 (헤더 전용) 라이브러리와 (선택 사항) CLI 프로그램입니다.
include/wtr/watcher.hpp . C ++ 프로젝트에서 감시자를 사용하도록 포함하십시오. 이것을 프로젝트에 복사하고 #include "wtr/watcher.hpp" (또는 이와 유사한)를 포함 하여이 언어로 일어나기에 충분합니다. 일부 추가 문서 및 고급 라이브러리 내부는 이벤트 및 시계 헤더에서 찾을 수 있습니다.watcher-c . C에서 Watcher를 사용하거나 다른 언어로 FFI를 통해이를 구축하십시오.src/wtr/watcher/main.cpp . 명령 줄에서 감시자를 사용하려면 이것을 빌드하십시오. 출력은 철저한 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 , 이벤트 시간은 epoch 이후 나노 초입니다.associated (이벤트, C ++) 또는 associated_path_name (기타 모든 구현, 단일 경로 이름) :(JavaScript의 경우, 우리는 Camel-Case를 사용하여 해당 언어의 생태계와 일치합니다.)
watcher 유형은 특별합니다.
이 유형의 이벤트에는 감시자의 메시지가 포함됩니다. 오류 메시지 또는 중요한 상태 업데이트를받을 수 있습니다.
이 형식은 일반적이고 휴대용 형식으로 감시자의 비동기 메시지를 지원하도록 선택되었습니다.
가장 중요한 "Watcher"이벤트는 초기 "라이브"이벤트와 최종 "다이"이벤트입니다.
메시지가 시청 된 기본 경로로 선정 된 것으로 보입니다.
예를 들어, Watcher 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# Watcher와 같은 다른 감시자들은 기본적으로 무시합니다. 일부 사용자 응용 프로그램은 수정 이벤트에 의존하여 파일을 다시로드 할시기를 알 수 있습니다.
더 나은 솔루션이 존재하며 이러한 불이행이 다시 변경 될 수 있습니다.
프로세스 ID에서 이벤트를 무시할 수있는 방법, "이"프로세스의 속기, 그리고 우리가 관심있는 어떤 종류의 이벤트 소스를 지정하는 방법은보다 완전한 솔루션을위한 좋은 후보자입니다.
나는 이것을 처음 썼을 때 C ++에 익숙했습니다. 나중에 실험으로 Rust 로이 프로젝트를 다시 작성했습니다. 녹에 대한 이점과 단점이 있습니다. 어떤 것들은 표현하기가 조금 더 안전했고 다른 것들은 분명히 그렇지 않았습니다. 예를 들어, 커널에서 다양한 크기의 불투명 한 유형에서 포인터 수학을 수행해야 할 필요성은 녹에서 표현하는 것이 더 안전하지 않습니다. 다른 것들은 더 안전하지만이 프로젝트는 그들로부터 많은 도움이되지 않습니다.
녹은 실제로 사용성과 표현으로 빛납니다. 그것은 그것을 사용해야 할 이유가 충분할 수 있습니다. 무엇보다도, 우리는 비동기 특성과 대수 유형으로 작업 할 수 있습니다.
이 프로젝트의 코드의 대부분을 정의상 안전하게 만들 수있는 언어가 있는지 확실하지 않습니다.
이 프로젝트의 장, 어댑터는 커널과 대화합니다. 그들은 안전하지 않은, 불쾌한, 경고가 풍부한 시스템 수준 인터페이스를 사용하도록되었습니다.
공개 API는 약 100 라인에 불과하며, 잘 정리되고 잘 테스트되어 있으며 인간이 검증 할 수 있습니다. 그곳에는 많이 일어나지 않습니다.
C ABI로 어댑터를 노출시켜 FFI를 만드는 것은 가치가있을 수 있습니다. 대부분의 언어는 그것에 연결할 수 있어야합니다.
플랫폼 어댑터의 안전은 반드시 인터페이스에 대한 각 플랫폼의 문서에 따라 다릅니다. 모든 시스템 수준의 인터페이스와 마찬가지로 올바른 사전 및 포스트 컨디셔닝을 보장하고 이러한 조건이 잘 정의되어있는 한 괜찮을 것입니다.
플랫폼 별 구현 중에서 FSEvents API는 Darwin에서 사용되며 ReadDirectoryChanges API는 Windows에서 사용됩니다. Linux에서 최고의 어댑터를 선택하기 위해 우리가하는 추가 작업이 있습니다. fanotify 어댑터는 커널 버전이 5.9보다 클 때 사용되며, 포함 된 프로세스에는 루트 명판이 있으며 필요한 시스템 호출이 허용됩니다. 필요한 특권 및 커널 버전에도 불구하고 fanotify 와 관련된 시스템 호출은 컨테이너 나 CGroup 내부에있을 때 허용되지 않을 수 있습니다. inotify 어댑터는 그렇지 않으면 사용됩니다. Linux의 선택 코드를 여기에서 찾을 수 있습니다.
어댑터의 네임 스페이스는 인라인입니다. (내부) detail::...::watch() 함수가 호출되면 watch() 함수의 하나 (그리고 하나의) 플랫폼 specifc 구현으로 해결됩니다. 플랫폼이 인라인 네임 스페이스 인 많은 플랫폼, 많은 플랫폼.
플랫폼 독립 어댑터 인 warthog 꺼낼 때 효율성은 타격을받습니다. 이 어댑터는 BSD 및 Solaris ( warthog kqueue 이기기 때문에)와 같은 더 나은 대안이없는 플랫폼에 사용됩니다.
Watcher는 warthog 보다 더 나은 대안이 없을 때 여전히 비교적 효율적입니다. 엄지 규칙으로서, warthog 로 10 만 개 이상의 경로를 스캔하면 말더듬이 될 수 있습니다.
BSD에서 더 나은 커널 API를 위해 눈을 뜨게 될 것입니다.
감시자가 콜백에 이벤트를 보낼 준비가되었을 때 신뢰할 수있는 방법은 없습니다.
수천 개의 경로의 경우 몇 밀리 초가 걸릴 수 있습니다. 수만 경로의 경우 몇 초 동안 기다리는 것을 고려하십시오.
플랫폼 별 구현 중 어느 것도 어떤 속성이 변경되었는지에 대한 정보를 제공하지 않습니다. 이를 통해 이러한 이벤트는이 정보를 직접 저장하는 데 의존합니다. stat 구조에 경로를 저장하여 속성 변경에 따라 차단하는 것은 중요하지 않은 메모리 약속입니다.
소유자 및 속성 이벤트는 해당 이벤트를 효율적으로 지원하는 방법을 잘 모르기 때문에 지원되지 않습니다.
/proc 및 /sys 를 포함한 특수 파일 시스템은 inotify , fanotify 또는 warthog 로 볼 수 없습니다. 향후 작업에는 커널을 사용할 수있는 EBPF 프로그램을 파견하는 것이 포함될 수 있습니다. 이를 통해 특별한 파일 시스템 중 일부에서 이벤트 modify 모니터링 할 수 있습니다.
inotify 사용하면 시청 파일의 수가 제한됩니다.
헤더 전용 라이브러리와 작은 시청자의 경우 C ++ 17 이상은 괜찮습니다.
언젠가 C ++ 20 코 루틴을 사용할 수 있습니다.
$ 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 내부에 있습니다. wtr::watcher::watch 의 네임 스페이스 watcher 이 컨벤션의 익명입니다.
더 깊이 : 기능 ::detail::wtr::watcher::adapter::watch() 는 파일의 1 개 (그리고 하나!)가 devel/include/detail/wtr/watcher/adapter/*/watch.hpp ( * 의 운영 체제에 따라)에 결정됩니다.
devel/include 의 모든 헤더는 include/wtr/watcher.hpp 로 합병되며 포함 가드가 상단에 추가됩니다. 포함 가드는 릴리스 버전으로 변경되지 않습니다. 앞으로는 아마도 가능합니다.
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)