# 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上可能利用操作系統。為了進行測試和調試,我們使用小記錄和消毒劑。
觀察者幾乎可以在任何地方運行。唯一的要求是文件系統。
重要的部分是(僅標題)庫和(可選的)CLI程序。
include/wtr/watcher.hpp 。將其包括在您的C ++項目中。將其複製到您的項目中,並將其包括為#include "wtr/watcher.hpp" (或類似)足以以這種語言啟動並運行。在活動中可以找到一些額外的文檔和高級圖書館內部內容和觀看標題。watcher-c構建以其他語言的FFI使用Watcher的構建。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 ,自時期以來納秒中事件的時間。associated (事件,C ++)或associated_path_name (所有其他實現,單個路徑名):(請注意,對於JavaScript,我們使用駱駝盒與該語言的生態系統保持一致。)
watcher類型很特別。
此類型的事件將包括來自觀察者的消息。您可以收到錯誤消息或重要狀態更新。
選擇這種格式以以通用,便攜式格式支持觀察者的異步消息。
最重要的“觀察者”事件中有兩個是初始“現場”事件和最後的“模具”事件。
該消息出現在手錶的基本路徑上。
例如,在/a/path打開觀察者後,您可以從Watcher那裡收到這些消息:
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的實驗。生鏽有好處和缺點。有些事情有點安全,而其他事情絕對不是。例如,在核心上進行一些可變尺寸的不透明類型進行指針數學的必要性並不安全。其他事情更安全,但是這個項目並不能從中受益匪淺。
Rust確實閃耀著可用性和表達。這可能足以使用它。除其他事項外,我們可以使用異步性狀和代數類型來實現良好的好處。
我不確定是否有一種語言可以從定義上“使該項目中的大部分代碼”安全。
這個項目的膽量,適配器,與內核對話。他們必然會使用不安全,不易於警告的系統級界面。
公共API僅大約100行,經過良好的經驗,經過良好的測試且可以驗證。那裡沒有太多發生。
通過用C ABI曝光適配器來創建FFI可能值得。大多數語言都應該能夠吸引這一點。
平台適配器的安全性一定取決於每個平台的界面文檔。與所有系統級接口一樣,只要我們確保正確的前和條件,這些條件是明確的,我們應該沒事。
在特定於平台的實現中, FSEvents API在Darwin上使用,並且在Windows上使用了ReadDirectoryChanges API。我們要做一些額外的工作來選擇Linux上的最佳適配器。當內核版本大於5.9時,使用fanotify適配器,包含過程具有根特性,則允許使用必要的系統調用。儘管有必要的特權和內核版本,但在集裝箱或cgroup內部內部可能會不允許與fanotify相關的系統調用。否則將使用inotify適配器。您可以在此處找到Linux的選擇代碼。
適配器的名稱空間是內聯的。當調用(內部) detail::...::watch()函數時,它將解析為一個(且僅一個)平台 - 規格實現了watch()函數。一個符號是許多平台,其中平台是內聯名稱空間。
當我們帶出與平台無關的適配器warthog時,效率受到了打擊。該適配器用於缺乏更好替代方案的平台,例如(不是達爾文)BSD和Solaris(因為warthog擊敗了kqueue )。
當觀察者沒有比warthog更好的選擇時,它仍然相對有效。作為拇指規則,掃描超過1000千的路徑可能會warthog 。
我會睜開眼睛,以在BSD上獲得更好的內核API。
當觀察者準備將事件發送到回調時,沒有可靠的方式進行交流。
對於幾千條路徑,這可能需要幾毫秒。對於成千上萬的路徑,請考慮等待幾秒鐘。
特定於平台的實現都沒有提供有關從中更改哪些屬性的信息。這使得支持這些事件取決於自己存儲此信息。將路徑的圖表存儲到stat結構,在屬性變化上差異是無關緊要的內存承諾。
所有者和屬性事件不支持,因為我不確定如何支持這些事件。
不包括/proc和/sys在內的特殊文件系統無法通過inotify , fanotify或warthog觀看。未來的工作可能涉及派遣EBPF程序供內核使用。這將使我們能夠在其中一些特殊文件系統上監視modify事件。
使用inotify時,觀察文件的數量受到限制。
對於僅限標題庫和Tiny-Watcher,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中。 wtr::watcher::watch中的命名空間watcher在本慣例上是匿名的。
更深入:函數::detail::wtr::watcher::adapter::watch()在一個(文件中只有一個!)內定義devel/include/detail/wtr/watcher/adapter/*/watch.hpp , *在compile time(根據主機的操作系統)確定 *。
將devel/include中的所有標題合併到include/wtr/watcher.hpp中,並添加了guard 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)