# 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 التي تشكل وقت تشغيل Watcher بسيط نسبيًا و 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.)يمكنك مشاهدة نظام ملفات كامل مع هذا المشروع. في جميع الحالات تقريبًا ، نستخدم كمية من الموارد القريبة من الصفر ونستفيد من ذاكرة التخزين المؤقت. نحن نختبر بانتظام أن النفقات العامة لاكتشاف وإرسال حدث إلى المستخدم هو أمر يقل حجمه أقل من عمليات نظام الملفات التي يتم قياسها.
نقوم بتشغيل هذا المشروع من خلال اختبارات الوحدة ضد جميع المطهرات المتاحة. يحاول هذا الرمز أن يكون مؤشر ترابط ، الذاكرة ، الحدود ، النوع وآمن الموارد. ما نفتقر إليه من اللغة ، نحاول التعويض عن الاختبار. للحصول على بعض التعريف العملي للسلامة ، ربما يناسب هذا المشروع.
يعتمد Watcher على مكتبة C ++ القياسية. من أجل الكفاءة ، نستفيد من نظام التشغيل عند الإمكان على Linux و Darwin و Windows. للاختبار وتصحيح الأخطاء ، نستخدم Snitch و Lietizers.
يتم تشغيل المراقب في أي مكان تقريبًا. الشرط الوحيد هو نظام الملفات.
القطع المهمة هي مكتبة (رأس فقط) وبرنامج CLI (اختياري).
include/wtr/watcher.hpp . قم بتضمين هذا لاستخدام Watcher في مشروع C ++ الخاص بك. إن نسخ هذا إلى مشروعك ، بما في ذلك هو #include "wtr/watcher.hpp" (أو ما شابه) يكفي للحصول على هذه اللغة. يمكن العثور على بعض الوثائق الإضافية والداخلية المكتبة عالية المستوى في الحدث ورؤوس مشاهدة.watcher-c . بناء هذا لاستخدام مراقب من C أو من خلال FFI بلغات أخرى.src/wtr/watcher/main.cpp . بناء هذا لاستخدام مراقب من سطر الأوامر. الإخراج هو دفق json شامل.src/wtr/tiny_watcher/main.cpp . برنامج CLI قليلة للغاية ، أكثر قابلة للقراءة الإنسان. المصدر لهذا متطابق تقريبًا مع الاستخدام المثالي لـ C ++.شجرة الدليل في الملاحظات أدناه.
اللبنات الأساسية الأساسية هنا هي:
watch أو الفئة (اعتمادًا على اللغة)event (أو بالمثل ، مرة أخرى اعتمادًا على اللغة) watch تأخذ مسارًا ، وهو شيء يشبه السلسلة ، ورد رد الاتصال ، مع شيء يشبه الوظيفة. على سبيل المثال ، watch مجموعة حرف وإغلاق سيعمل بشكل جيد في C ++.
يمكن العثور على أمثلة لمجموعة متنوعة من اللغات في البداية السريعة. واجهة برمجة التطبيقات متسقة نسبيا عبر اللغات.
سيستمر المراقب لحسن الحظ في المشاهدة حتى تتوقف عن ذلك أو يضرب خطأً غير قابل للاسترداد.
يتم استخدام كائن event لتمرير معلومات حول أحداث نظام الملفات إلى رد الاتصال المعطى (من قبلك) watch .
سيحتوي كائن event على:
path_name ، وهو طريق مطلق للحدث.path_type ، نوع المسار. واحد من:dirfilehard_linksym_linkwatcherothereffect_type ، "ماذا حدث". واحد من:renamemodifycreatedestroyownerothereffect_time ، وقت الحدث في النانو ثانية منذ فترة.associated (حدث ، C ++) أو associated_path_name (جميع التطبيقات الأخرى ، اسم مسار واحد):(لاحظ أنه بالنسبة إلى JavaScript ، نستخدم حالة الجمل ، لتكون متسقة مع النظام الإيكولوجي لهذا اللغة.)
نوع watcher خاص.
ستتضمن الأحداث مع هذا النوع رسائل من المراقب. يمكنك تلقي رسائل الخطأ أو تحديثات الحالة المهمة.
تم اختيار هذا التنسيق لدعم الرسائل غير المتزامنة من المراقب بتنسيق عام محمول.
اثنان من أهم أحداث "مراقب" هما الحدث "المباشر" الأولي وحدث "Die" النهائي.
تظهر الرسالة مسبقًا إلى مسار القاعدة المراقبة.
على سبيل المثال ، بعد فتح مراقب على /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 ++ عندما كتبت هذا لأول مرة. قمت لاحقًا بإعادة كتابة هذا المشروع في الصدأ كتجربة. هناك فوائد وعيوب للصدأ. كانت بعض الأشياء أكثر أمانًا للتعبير ، وأشياء أخرى لم تكن بالتأكيد. إن ضرورة القيام بمؤشر الرياضيات على بعض الأنواع غير المعتمة ذات الحجم المتغير من النواة ، على سبيل المثال ، ليست أكثر أمانًا للتعبير عنها في الصدأ. أشياء أخرى أكثر أمانًا ، لكن هذا المشروع لا يستفيد كثيرًا منها.
الصدأ يضيء حقا في قابلية الاستخدام والتعبير. قد يكون ذلك كافيًا لاستخدامه. من بين أشياء أخرى ، يمكننا العمل مع السمات غير المتزامنة وأنواع الجبرية من أجل الخير العظيم.
لست متأكدًا مما إذا كانت هناك لغة يمكنها "فقط" جعل غالبية الكود في هذا المشروع آمنة بحكم التعريف.
شجاعة هذا المشروع ، المحولات ، تتحدث إلى النواة. لا بد أن تستخدم واجهات غير آمنة ، غير آمنة ، من النوع الغني بالتحذير.
إن واجهة برمجة التطبيقات العامة هي حوالي 100 سطر ، وهي مملوءة جيدًا ، واختبارها بشكل جيد ، ويمكن تنفيذها للإنسان. لا يحدث الكثير هناك.
قد يكون إنشاء FFI عن طريق تعريض المحولات باستخدام C ABI جديرة بالاهتمام. يجب أن تكون معظم اللغات قادرة على ربط ذلك.
تعتمد سلامة محولات المنصة بالضرورة على وثائق كل منصة لواجهاتها. كما هو الحال مع جميع الواجهات على مستوى النظام ، طالما نضمن الشروط الصحيحة قبل وشرطات ، وتلك الشروط محددة جيدًا ، يجب أن نكون على ما يرام.
من بين التطبيقات الخاصة بالمنصة ، يتم استخدام واجهة برمجة تطبيقات FSEvents على Darwin ويستخدم API ReadDirectoryChanges على Windows. هناك بعض الأعمال الإضافية التي نقوم بها لتحديد أفضل محول على Linux. يتم استخدام محول fanotify عندما يكون إصدار kernel أكبر من 5.9 ، والعملية التي تحتوي على خصوصيات الجذر ، ويتم السماح بمكالمات النظام اللازمة. قد يتم عدم السماح لمكالمات النظام المرتبطة بـ fanotify عند داخل حاوية أو CGROUP ، على الرغم من الامتيازات الضرورية وإصدار kernel. يتم استخدام محول inotify على خلاف ذلك. يمكنك العثور على رمز التحديد لـ Linux هنا.
مساحات الأسماء لمحولاتنا مضمونة. watch() يتم detail::...::watch() رمز واحد ، العديد من المنصات ، حيث تكون المنصات مساحات أسماء مضمنة.
تتصرف الكفاءة عندما نخرج warthog ، محولنا المستقلة عن النظام الأساسي. يتم استخدام هذا المحول على المنصات التي تفتقر إلى بدائل أفضل ، مثل (وليس داروين) BSD و Solaris (لأن warthog يتفوق على kqueue ).
لا يزال المراقب فعالًا نسبيًا عندما لا يكون لديه بديل أفضل من warthog . كقاعدة إبهام ، قد يقوم بمسح أكثر من مائة ألف مسار مع warthog قد تلعثم.
سأبقي عيني مفتوحة لتحسين واجهات برمجة تطبيقات kernel على BSD.
لا توجد طريقة موثوقة للتواصل عندما يكون المراقب جاهزًا لإرسال الأحداث إلى رد الاتصال.
لبضعة آلاف المسار ، قد يستغرق هذا بضعة ميلي ثانية. بالنسبة لعشرات الآلاف من المسارات ، فكر في انتظار بضع ثوان.
لا توفر أي من التطبيقات الخاصة بالمنصة معلومات عن السمات التي تم تغييرها منها. هذا يجعل دعم تلك الأحداث يعتمد على تخزين هذه المعلومات بأنفسنا. يعد تخزين خرائط المسارات إلى هياكل stat ، وتنشيطها على تغييرات السمة ، التزامًا غير مهم للذاكرة.
أحداث المالك والسمة غير مدعومة لأنني لست متأكدًا من كيفية دعم هذه الأحداث الفعالة.
لا يمكن مشاهدة أنظمة الملفات الخاصة ، بما في ذلك /proc و /sys ، مع inotify أو fanotify أو warthog . قد يتضمن العمل المستقبلي إرسال برامج EBPF لاستخدام kernel. هذا من شأنه أن يسمح لنا بمراقبة 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 مجهول من خلال هذه الاتفاقية.
المزيد من العمق: الوظيفة ::detail::wtr::watcher::adapter::watch() يتم تعريفه داخل واحد (وآخر واحد!) من الملفات 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)