inotify(7)

الاسم
inotify - مراقبة أحداث نظام الملفات

الوصف
توفر واجهة برمجة التطبيقات inotify آلية لمراقبة أحداث نظام الملفات. يمكن استخدام inotify لمراقبة ملفات فردية، أو لمراقبة أدلة. عند مراقبة دليل، يُرجع inotify أحداثًا للدليل نفسه، وللملفات داخل الدليل.

تُستخدم استدعاءات النظام التالية مع واجهة برمجة التطبيقات هذه:

•   ينشئ inotify_init(2) مثيل inotify ويُعيد واصف ملف يشير إلى مثيل inotify. الإصدار الأحدث inotify_init1(2) مشابه لـ inotify_init(2)، لكنه يحتوي على وسيط flags يوفر الوصول إلى بعض الوظائف الإضافية.

•   يتلاعب inotify_add_watch(2) بقائمة المراقبة المرتبطة بمثيل inotify. يحدد كل عنصر ("مراقبة") في قائمة المراقبة اسم مسار ملف أو دليل، بالإضافة إلى مجموعة من الأحداث التي يجب على النواة مراقبتها للملف المشار إليه بواسطة اسم المسار ذلك. إما أن ينشئ inotify_add_watch(2) عنصر مراقبة جديد، أو يعدل مراقبة موجودة. لكل مراقبة "واصف مراقبة" فريد، وهو عدد صحيح يُعاد بواسطة inotify_add_watch(2) عند إنشاء المراقبة.

•   عند حدوث أحداث للملفات والأدلة المراقبة، تُتاح تلك الأحداث للتطبيق كبيانات منظمة يمكن قراءتها من واصف ملف inotify باستخدام read(2) (انظر أدناه).

•   يزيل inotify_rm_watch(2) عنصرًا من قائمة مراقبة inotify.

•   عند إغلاق جميع واصفات الملفات التي تشير إلى مثيل inotify (باستخدام close(2))، يُحرر الكائن الأساسي وموارده لإعادة الاستخدام بواسطة النواة؛ تُحرر جميع المراقبات المرتبطة آليًا.

مع البرمجة الدقيقة، يمكن للتطبيق استخدام inotify لمراقبة وتخزين حالة مجموعة من كائنات نظام الملفات بكفاءة. ومع ذلك، يجب على التطبيقات القوية أن تأخذ في الاعتبار حقيقة أن الأخطاء في منطق المراقبة أو حالات السباق من النوع الموصوف أدناه قد تترك الخبيئة غير متسقة مع حالة نظام الملفات. من الحكمة على الأرجح إجراء بعض التحقق من الاتساق، وإعادة بناء الخبيئة عند اكتشاف حالات عدم الاتساق.

  قراءة الأحداث من واصف ملف inotify
لتحديد الأحداث التي حدثت، يقرأ التطبيق read(2) من واصف ملف inotify. إذا لم تحدث أي أحداث حتى الآن، فبافتراض واصف ملف محظور، سيحظر read(2) حتى يحدث حدث واحد على الأقل (ما لم يُقاطع بإشارة، وفي هذه الحالة يفشل الاستدعاء مع الخطأ EINTR؛ انظر signal(7)).

يُعيد كل read(2) ناجح مخزنًا مؤقتًا يحتوي على بنية واحدة أو أكثر مما يلي:

    struct inotify_event {
        int      wd;       /* Watch descriptor */
        uint32_t mask;     /* Mask describing event */
        uint32_t cookie;   /* Unique cookie associating related
                              events (for rename(2)) */
        uint32_t len;      /* Size of name field */
        char     name[];   /* Optional null-terminated name */
    };

يُحدد wd المراقبة التي يحدث لها هذا الحدث. وهو أحد واصفات المراقبة التي أعيدت بواسطة استدعاء سابق لـ inotify_add_watch(2).

يحتوي mask على بتات تصف الحدث الذي حدث (انظر أدناه).

cookie هو عدد صحيح فريد يربط الأحداث ذات الصلة. حاليًا، يُستخدم هذا فقط لأحداث إعادة التسمية، ويسمح للتطبيق بربط الزوج الناتج من أحداث IN_MOVED_FROM و IN_MOVED_TO. بالنسبة لجميع أنواع الأحداث الأخرى، يُضبط cookie على 0.

حقل name موجود فقط عند إعادة حدث لملف داخل دليل مراقب؛ يُحدد اسم الملف داخل الدليل المراقب. اسم الملف هذا منتهي بصفر، وقد يتضمن بايتات صفرية إضافية ('\0') لمحاذاة القراءات اللاحقة إلى حد عنوان مناسب.

يحسب حقل len جميع البايتات في name، بما في ذلك البايتات الصفرية؛ وبالتالي فإن حجم كل بنية inotify_event هو sizeof(struct inotify_event)+size.

يعتمد السلوك عندما يكون المخزن المؤقت المُعطى لـ read(2) صغيرًا جدًا لإعادة معلومات حول الحدث التالي على إصدار النواة: قبل Linux 2.6.21، يُعيد read(2) 0؛ منذ Linux 2.6.21، يفشل read(2) مع الخطأ EINVAL. تحديد مخزن مؤقت بحجم

    sizeof(struct inotify_event) + NAME_MAX + 1

سيكون كافيًا لقراءة حدث واحد على الأقل.

  أحداث inotify
وسيط mask لـ inotify_add_watch(2) وحقل mask لبنية inotify_event المُعاد عند قراءة read(2) لواصف ملف inotify هما قناعا بتات يُحددان أحداث inotify. يمكن تحديد البتات التالية في mask عند استدعاء inotify_add_watch(2) وقد تُعاد في حقل mask المُعاد بواسطة read(2):

    IN_ACCESS (+)
        تم الوصول إلى الملف (مثل read(2)، execve(2)).

    IN_ATTRIB (*)
        تغيرت البيانات الوصفية—على سبيل المثال، الأذونات (مثل chmod(2))، الطوابع الزمنية (مثل utimensat(2))، السمات الموسعة (setxattr(2))، عدد الروابط (منذ Linux 2.6.25؛ مثل لهدف link(2) و unlink(2))، ومعرف المستخدم/المجموعة (مثل chown(2)).

    IN_CLOSE_WRITE (+)
        أغلق الملف المفتوح للكتابة.

    IN_CLOSE_NOWRITE (*)
        أغلق الملف أو الدليل غير المفتوح للكتابة.

    IN_CREATE (+)
        أنشئ ملف/دليل في الدليل المراقب (مثل open(2) O_CREAT، mkdir(2)، link(2)، symlink(2)، bind(2) على مقبس نطاق UNIX).

    IN_DELETE (+)
        حذف ملف/دليل من الدليل المراقب.

    IN_DELETE_SELF
        حذف الملف/الدليل المراقب نفسه. (يحدث هذا الحدث أيضًا إذا نقل كائن إلى نظام ملفات آخر، لأن mv(1) ينسخ الملف فعليًا إلى نظام الملفات الآخر ثم يحذفه من نظام الملفات الأصلي.) بالإضافة إلى ذلك، سيولد حدث IN_IGNORED لاحقًا لوصف المراقبة.

    IN_MODIFY (+)
        عدل الملف (مثل write(2)، truncate(2)).

    IN_MOVE_SELF
        نقل الملف/الدليل المراقب نفسه.

    IN_MOVED_FROM (+)
        يولد للدليل الذي يحتوي على اسم الملف القديم عند إعادة تسمية ملف.

    IN_MOVED_TO (+)
        يولد للدليل الذي يحتوي على اسم الملف الجديد عند إعادة تسمية ملف.

    IN_OPEN (*)
        فتح الملف أو الدليل.

مراقبة Inotify قائمة على inode: عند مراقبة ملف (ولكن ليس عند مراقبة الدليل الذي يحتوي على ملف)، يمكن توليد حدث لنشاط على أي رابط للملف (في نفس الدليل أو دليل مختلف).

عند مراقبة دليل:

•   الأحداث المميزة أعلاه بعلامة النجمة (*) يمكن أن تحدث لكل من الدليل نفسه وللكائنات داخل الدليل؛ و

•   الأحداث المميزة بعلامة الجمع (+) تحدث فقط للكائنات داخل الدليل (وليس للدليل نفسه).

ملاحظة: عند مراقبة دليل، لا تُنشأ أحداث للملفات داخل الدليل عندما تُنفذ الأحداث عبر اسم مسار (أي رابط) يقع خارج الدليل المراقب.

عند إنشاء أحداث للكائنات داخل دليل مراقب، يحدد حقل name في بنية inotify_event المعادة اسم الملف داخل الدليل.

عُرفت الكلية IN_ALL_EVENTS كقناع بت لجميع الأحداث أعلاه. يمكن استخدام هذه الكلية كوسيط mask عند استدعاء inotify_add_watch(2).

عُرفت كليتان إضافيتان للتسهيل:

    IN_MOVE
        تساوي IN_MOVED_FROM | IN_MOVED_TO.

    IN_CLOSE
        تساوي IN_CLOSE_WRITE | IN_CLOSE_NOWRITE.

يمكن تحديد البتات الإضافية التالية في mask عند استدعاء inotify_add_watch(2):

    IN_DONT_FOLLOW (منذ Linux 2.6.15)
        لا تفك إسناد pathname إذا كان رابطًا رمزيًا.

    IN_EXCL_UNLINK (منذ Linux 2.6.36)
        مبدئيًا، عند مراقبة أحداث على أبناء دليل، تُنشأ أحداث للأبناء حتى بعد فصلهم عن الدليل. قد ينتج عن هذا أعداد كبيرة من الأحداث غير المهمة لبعض التطبيقات (مثلًا، عند مراقبة /tmp، حيث تنشئ تطبيقات عديدة ملفات مؤقتة تُفصل أسماؤها فورًا). تغيير تحديد IN_EXCL_UNLINK السلوك المبدئي، بحيث لا تُنشأ أحداث للأبناء بعد فصلهم عن الدليل المراقب.

    IN_MASK_ADD
        إذا وُجدت نسخة مراقبة مسبقًا لكائن نظام الملفات المطابق لـ pathname، أضف (OR) الأحداث في mask إلى قناع المراقبة (بدل استبدال القناع)؛ ينتج الخطأ EINVAL إذا حُدد IN_MASK_CREATE أيضًا.

    IN_ONESHOT
        راقب كائن نظام الملفات المطابق لـ pathname لحدث واحد، ثم أزله من قائمة المراقبة.

    IN_ONLYDIR (منذ Linux 2.6.15)
        راقب pathname فقط إذا كان دليلًا؛ ينتج الخطأ ENOTDIR إذا لم يكن pathname دليلًا. يوفر استخدام هذه العلامة للتطبيق طريقة خالية من السباق لضمان أن الكائن المراقب هو دليل.

    IN_MASK_CREATE (منذ Linux 4.18)
        راقب pathname فقط إذا لم يكن لديه مراقبة مرتبطة به بالفعل؛ ينتج الخطأ EEXIST إذا كان pathname مراقبًا بالفعل.

        يوفر استخدام هذه العلامة للتطبيق طريقة لضمان أن المراقبات الجديدة لا تعدل الموجودة. هذا مفيد لأن مسارات متعددة قد تشير إلى نفس inode، وقد تؤدي الاستدعاءات المتعددة لـ inotify_add_watch(2) بدون هذه العلامة إلى إتلاف أقنعة المراقبة الموجودة.

قد تُضبط البتات التالية في حقل mask المعاد بواسطة read(2):

    IN_IGNORED
        أزيلت المراقبة صراحة (inotify_rm_watch(2)) أو آليًا (حُذف الملف، أو فُصل نظام الملفات). انظر أيضًا BUGS.

    IN_ISDIR
        موضوع هذا الحدث هو دليل.

    IN_Q_OVERFLOW
        فاضت قائمة انتظار الأحداث (wd يساوي -1 لهذا الحدث).

    IN_UNMOUNT
        نظام الملفات الذي يحتوي على الكائن المراقب فُصل. بالإضافة إلى ذلك، يُولد حدث IN_IGNORED لاحقًا لواصف المراقبة.

  أمثلة
افترض أن تطبيقًا يراقب الدليل dir والملف dir/myfile لجميع الأحداث. الأمثلة أدناه تُظهر بعض الأحداث التي ستُولد لهذين الكائنين.

    fd = open("dir/myfile", O_RDWR);
        يُولد أحداث IN_OPEN لكل من dir و dir/myfile.

    read(fd, buf, count);
        يُولد أحداث IN_ACCESS لكل من dir و dir/myfile.

    write(fd, buf, count);
        يُولد أحداث IN_MODIFY لكل من dir و dir/myfile.

    fchmod(fd, mode);
        يُولد أحداث IN_ATTRIB لكل من dir و dir/myfile.

    close(fd);
        يُولد أحداث IN_CLOSE_WRITE لكل من dir و dir/myfile.

افترض أن تطبيقًا يراقب الدليلين dir1 و dir2، والملف dir1/myfile. الأمثلة التالية تُظهر بعض الأحداث التي قد تُولد.

    link("dir1/myfile", "dir2/new");
        يُولد حدث IN_ATTRIB لـ myfile وحدث IN_CREATE لـ dir2.

    rename("dir1/myfile", "dir2/myfile");
        يُولد حدث IN_MOVED_FROM لـ dir1، وحدث IN_MOVED_TO لـ dir2، وحدث IN_MOVE_SELF لـ myfile. أحداث IN_MOVED_FROM و IN_MOVED_TO سيكون لها نفس قيمة cookie.

افترض أن dir1/xx و dir2/yy هما (الروابط الوحيدة) لنفس الملف، وتطبيق يراقب dir1 و dir2 و dir1/xx و dir2/yy. تنفيذ الاستدعاءات التالية بالترتيب المعطى أدناه سيُولد الأحداث التالية:

    unlink("dir2/yy");
        يُولد حدث IN_ATTRIB لـ xx (لأن عدد روابطه يتغير) وحدث IN_DELETE لـ dir2.

    unlink("dir1/xx");
        يُولّد أحداث IN_ATTRIB وIN_DELETE_SELF وIN_IGNORED لـ xx، وحدث IN_DELETE لـ dir1.

افترض أن تطبيقًا يراقب الدليل dir والدليل (الفارغ) dir/subdir. الأمثلة التالية تُظهر بعض الأحداث التي قد تُولّد.

    mkdir("dir/new", mode);
        يُولّد حدث IN_CREATE | IN_ISDIR لـ dir.

    rmdir("dir/subdir");
        يُولّد أحداث IN_DELETE_SELF وIN_IGNORED لـ subdir، وحدث IN_DELETE | IN_ISDIR لـ dir.

  واجهات /proc
الواجهات التالية يمكن استخدامها لتحديد كمية ذاكرة النواة التي يستهلكها inotify:

/proc/sys/fs/inotify/max_queued_events
    القيمة في هذا الملف تُستخدم عندما يستدعي تطبيق inotify_init(2) لتعيين حد أعلى لعدد الأحداث التي يمكن وضعها في طابور لنفس نسخة inotify. الأحداث التي تتجاوز هذا الحد تُسقط، لكن حدث IN_Q_OVERFLOW يُولّد دائمًا.

/proc/sys/fs/inotify/max_user_instances
    هذا يُحدد حدًا أعلى لعدد نسخ inotify التي يمكن إنشاؤها لكل معرف مستخدم حقيقي.

/proc/sys/fs/inotify/max_user_watches
    هذا يُحدد حدًا أعلى لعدد المراقبات التي يمكن إنشاؤها لكل معرف مستخدم حقيقي.

المعايير
لينكس.

التاريخ
Inotify دُمج في Linux 2.6.13. واجهات المكتبة المطلوبة أُضيفت في glibc 2.4. (IN_DONT_FOLLOW وIN_MASK_ADD وIN_ONLYDIR أُضيفت في glibc 2.5.)

ملاحظات
مُعرّفات ملفات inotify يمكن مراقبتها باستخدام select(2) وpoll(2) وepoll(7). عندما يكون حدث متاحًا، يُشير مُعرّف الملف إلى أنه قابل للقراءة.

منذ Linux 2.6.25، إعلام الإدخال/الإخراج المُوجّه بالإشارة متاح لمُعرّفات ملفات inotify؛ انظر مناقشة F_SETFL (لتعيين علامة O_ASYNC) وF_SETOWN وF_SETSIG في fcntl(2). بنية siginfo_t (الموصوفة في sigaction(2)) التي تُمرّر إلى معالج الإشارة تحتوي على الحقول التالية المُعيّنة: si_fd يُعيّن إلى رقم مُعرّف ملف inotify؛ si_signo يُعيّن إلى رقم الإشارة؛ si_code يُعيّن إلى POLL_IN؛ وPOLLIN يُعيّن في si_band.

إذا كانت أحداث inotify الناتجة المتتالية على مُعرّف ملف inotify متطابقة (نفس wd وmask وcookie وname)، فإنها تُدمج في حدث واحد إذا لم يُقرأ الحدث الأقدم بعد (لكن انظر BUGS). هذا يُقلل كمية ذاكرة النواة المطلوبة لطابور الأحداث، لكنه يعني أيضًا أن التطبيق لا يمكنه استخدام inotify لعد أحداث الملفات بشكل موثوق.

الأحداث المُعادة بالقراءة من مُعرّف ملف inotify تُشكّل طابورًا مُرتّبًا. وبالتالي، على سبيل المثال، مضمون أنه عند إعادة التسمية من دليل إلى آخر، ستُنتج الأحداث بالترتيب الصحيح على مُعرّف ملف inotify.

مجموعة مُعرّفات المراقبة التي تُراقب عبر مُعرّف ملف inotify يمكن عرضها عبر الإدخال لمُعرّف ملف inotify في دليل العملية /proc/pid/fdinfo. انظر proc(5) لمزيد من التفاصيل. FIONREAD ioctl(2) يُعيد عدد البايتات المتاحة للقراءة من مُعرّف ملف inotify.

  القيود والتحذيرات
واجهة برمجة inotify لا تُوفّر معلومات عن المستخدم أو العملية التي أثارت حدث inotify. على وجه الخصوص، لا توجد طريقة سهلة لعملية تُراقب الأحداث عبر inotify لتمييز الأحداث التي تُثيرها بنفسها عن تلك التي تُثار بواسطة عمليات أخرى.

Inotify يُبلغ فقط عن الأحداث التي يُثيرها برنامج في مساحة المستخدم عبر واجهة برمجة نظام الملفات. نتيجة لذلك، لا يلتقط الأحداث البعيدة التي تحدث على أنظمة ملفات الشبكة. (يجب على التطبيقات العودة إلى استقصاء نظام الملفات لالتقاط مثل هذه الأحداث.) علاوة على ذلك، أنظمة ملفات زائفة متنوعة مثل /proc و/sys و/dev/pts غير قابلة للمراقبة باستخدام inotify.

واجهة برمجة inotify لا تُبلغ عن وصولات وتعديلات الملفات التي قد تحدث بسبب mmap(2) وmsync(2) وmunmap(2).

واجهة برمجة inotify تُحدد الملفات المتأثرة باسم الملف. ومع ذلك، بحلول الوقت الذي يعالج فيه تطبيق حدث inotify، قد يكون اسم الملف قد حُذف أو أُعيدت تسميته بالفعل.

واجهة برمجة inotify تُحدد الأحداث عبر مُعرّفات المراقبة. من مسؤولية التطبيق تخزين تعيين (إذا كان مطلوبًا) بين مُعرّفات المراقبة وأسماء المسارات. كن على علم بأن إعادة تسمية الدلائل قد تؤثر على أسماء مسارات مخزنة متعددة.

مراقبة الأدلة باستخدام inotify ليست متكررة: لمراقبة الأدلة الفرعية ضمن دليل، يجب إنشاء مراقبات إضافية. قد يستغرق هذا وقتًا كبيرًا لأشجار الأدلة الكبيرة.

إذا كنت تراقب شجرة دليل كاملة، وتم إنشاء دليل فرعي جديد في تلك الشجرة أو تمت إعادة تسمية دليل موجود إلى تلك الشجرة، فاعلم أنه بحلول الوقت الذي تنشئ فيه مراقبة للدليل الفرعي الجديد، قد تكون الملفات (والأدلة الفرعية) الجديدة موجودة بالفعل داخل الدليل الفرعي. لذلك، قد ترغب في مسح محتويات الدليل الفرعي فورًا بعد إضافة المراقبة (وإذا رغبت، أضف مراقبات بشكل متكرر لأي أدلة فرعية يحتويها).

لاحظ أن قائمة انتظار الأحداث قد تفيض. في هذه الحالة، تُفقد الأحداث. يجب على التطبيقات القوية التعامل مع احتمال فقدان الأحداث بلطف. على سبيل المثال، قد يكون من الضروري إعادة بناء جزء أو كل الخبيئة الخاصة بالتطبيق. (أحد الأساليب البسيطة، ولكن ربما المكلفة، هو إغلاق واصف ملف inotify، وتفريغ الخبيئة، وإنشاء واصف ملف inotify جديد، ثم إعادة إنشاء المراقبات وإدخالات الخبيئة للكائنات المراد مراقبتها.)

إذا تم وصل نظام ملفات فوق دليل مراقَب، لا يتم إنشاء أي حدث، ولا يتم إنشاء أحداث للكائنات الموجودة مباشرة تحت نقطة الوصل الجديدة. إذا تم فك وصل نظام الملفات لاحقًا، فسيتم إنشاء أحداث لاحقًا للدليل والكائنات التي يحتويها.

  التعامل مع أحداث rename()
كما ذكر أعلاه، يمكن مطابقة زوج الأحداث IN_MOVED_FROM و IN_MOVED_TO الذي يولده rename(2) من خلال قيمة الكوكي المشتركة. ومع ذلك، فإن مهمة المطابقة تواجه بعض التحديات.

عادةً ما يكون هذان الحدثان متتاليين في تدفق الأحداث المتاح عند القراءة من واصف ملف inotify. ومع ذلك، هذا غير مضمون. إذا كانت عمليات متعددة تُطلق أحداثًا لكائنات مراقَبة، فقد تظهر (في حالات نادرة) عدد عشوائي من الأحداث الأخرى بين حدثي IN_MOVED_FROM و IN_MOVED_TO. علاوة على ذلك، ليس مضمونًا أن يتم إدراج زوج الأحداث بشكل ذري في قائمة الانتظار: قد تكون هناك فترة وجيزة حيث يظهر IN_MOVED_FROM، ولكن IN_MOVED_TO لم يظهر بعد.

وبالتالي، فإن مطابقة زوج الأحداث IN_MOVED_FROM و IN_MOVED_TO الذي يولده rename(2) هو أمر محفوف بالمخاطر بطبيعته. (لا تنس أنه إذا تمت إعادة تسمية كائن خارج دليل مراقَب، فقد لا يكون هناك حتى حدث IN_MOVED_TO.) يمكن استخدام الأساليب الاستكشافية (مثل افتراض أن الأحداث متتالية دائمًا) لضمان تطابق في معظم الحالات، لكنها ستفتقد حتمًا بعض الحالات، مما يتسبب في أن يرى التطبيق حدثي IN_MOVED_FROM و IN_MOVED_TO كحدثين غير مرتبطين. إذا تم تدمير واصفات المراقبة وإعادة إنشائها نتيجة لذلك، فإن واصفات المراقبة هذه ستكون غير متسقة مع واصفات المراقبة في أي أحداث معلقة. (قد تكون إعادة إنشاء واصف ملف inotify وإعادة بناء الخبيئة مفيدة للتعامل مع هذا السيناريو.)

يجب على التطبيقات أيضًا أن تسمح باحتمال أن يكون حدث IN_MOVED_FROM هو آخر حدث يمكن وضعه في المخزن المؤقت الذي أرجعته الاستدعاء الحالي لـ read(2)، وأن حدث IN_MOVED_TO المصاحب قد يتم جلبه فقط في استدعاء read(2) التالي، والذي يجب أن يتم مع مهلة (صغيرة) لمراعاة حقيقة أن إدراج زوج الأحداث IN_MOVED_FROM+IN_MOVED_TO ليس ذريًا، وأيضًا احتمال عدم وجود أي حدث IN_MOVED_TO.

العلل
قبل لينكس 3.19، لم ينشئ fallocate(2) أي أحداث inotify. منذ لينكس 3.19، تولد استدعاءات fallocate(2) أحداث IN_MODIFY.

قبل لينكس 2.6.16، لا تعمل علامة IN_ONESHOT mask.

كما تم تصميمه وتنفيذه أصلاً، لم تتسبب علامة IN_ONESHOT في إنشاء حدث IN_IGNORED عند إسقاط المراقبة بعد حدث واحد. ومع ذلك، كأثر غير مقصود لتغييرات أخرى، منذ لينكس 2.6.36، يتم إنشاء حدث IN_IGNORED في هذه الحالة.

قبل لينكس 2.6.25، كان رمز النواة الذي كان يهدف إلى دمج الأحداث المتطابقة المتتالية (أي، كان من الممكن دمج أحدث حدثين إذا لم يتم قراءة الأقدم بعد) يتحقق بدلاً من ذلك مما إذا كان يمكن دمج أحدث حدث مع أقدم حدث غير مقروء.

عند إزالة واصف مراقبة عن طريق استدعاء inotify_rm_watch(2) (أو بسبب حذف ملف مراقَب أو فك وصل نظام الملفات الذي يحتويه)، تظل أي أحداث معلقة غير مقروءة لواصف المراقبة هذا متاحة للقراءة. نظرًا لأنه يتم تخصيص واصفات المراقبة لاحقًا باستخدام inotify_add_watch(2)، تقوم النواة بالتنقل عبر نطاق واصفات المراقبة الممكنة (من 1 إلى INT_MAX) بشكل تدريجي. عند تخصيص واصف مراقبة حر، لا يتم إجراء أي فحص لمعرفة ما إذا كان رقم واصف المراقبة هذا يحتوي على أي أحداث معلقة غير مقروءة في قائمة انتظار inotify. وبالتالي، يمكن أن يحدث أن يتم إعادة تخصيص واصف مراقبة حتى عندما توجد أحداث معلقة غير مقروءة لتجسيد سابق لرقم واصف المراقبة ذلك، مما يؤدي إلى أن التطبيق قد يقرأ تلك الأحداث ويفسرها على أنها تنتمي إلى الملف المرتبط بواصف المراقبة المعاد تدويره حديثًا. من الناحية العملية، قد يكون احتمال الوصول إلى هذا الخطأ منخفضًا للغاية، لأنه يتطلب أن يتنقل التطبيق عبر INT_MAX من واصفات المراقبة، ويحرر واصف مراقبة مع ترك أحداث غير مقروءة لواصف المراقبة ذلك في قائمة الانتظار، ثم يعيد تدوير واصف المراقبة ذلك. لهذا السبب، ولأنه لم ترد تقارير عن حدوث الخطأ في التطبيقات الواقعية، فاعتبارًا من لينكس 3.15، لم يتم إجراء أي تغييرات على النواة بعد لإزالة هذا الخطأ المحتمل.

أمثلة
يُظهر البرنامج التالي استخدام واجهة برمجة تطبيقات inotify. يحدد الأدلة التي تم تمريرها كوسائط سطر أوامر وينتظر أحداثًا من النوع IN_OPEN و IN_CLOSE_NOWRITE و IN_CLOSE_WRITE.

تم تسجيل المخرجات التالية أثناء تحرير الملف /home/user/temp/foo وسرد الدليل /tmp. قبل فتح الملف والدليل، حدثت أحداث IN_OPEN. بعد إغلاق الملف، حدث حدث IN_CLOSE_WRITE. بعد إغلاق الدليل، حدث حدث IN_CLOSE_NOWRITE. انتهى تنفيذ البرنامج عندما ضغط المستخدم على مفتاح ENTER.

  مثال على الخرج
    $ ./a.out /tmp /home/user/temp;
    Press enter key to terminate.
    Listening for events.
    IN_OPEN: /home/user/temp/foo [file]
    IN_CLOSE_WRITE: /home/user/temp/foo [file]
    IN_OPEN: /tmp/ [directory]
    IN_CLOSE_NOWRITE: /tmp/ [directory]

    Listening for events stopped.

  مصدر البرنامج
    #include <errno.h>
    #include <poll.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/inotify.h>
    #include <unistd.h>
    #include <string.h>

    /* Read all available inotify events from the file descriptor 'fd'.
       wd is the table of watch descriptors for the directories in argv.
       argc is the size of wd and argv.
       argv is the list of watched directories.
       Entry 0 of wd and argv is unused.  */

    static void
    handle_events(int fd, int *wd, int argc, char* argv[])
    {
        /* Some systems cannot read integer variables if they are not
           properly aligned.  On other systems, incorrect alignment may
           decrease performance.  Hence, the buffer used for reading from
           the inotify file descriptor should have the same alignment as
           struct inotify_event.  */

        char buf[4096]
            __attribute__ ((aligned(__alignof__(struct inotify_event))));
        const struct inotify_event *event;
        ssize_t size;

        /* Loop while events can be read from inotify file descriptor.  */

        for (;;) {

            /* Read some events.  */

            size = read(fd, buf, sizeof(buf));
            if (size == -1 && errno != EAGAIN) {
                perror("read");
                exit(EXIT_FAILURE);
            }

            /* If the nonblocking read() found no events to read, then
               it returns -1 with errno set to EAGAIN.  In that case,
               we exit the loop.  */

            if (size <= 0)
                break;

            /* Loop over all events in the buffer.  */

            for (char *ptr = buf; ptr < buf + size;
                    ptr += sizeof(struct inotify_event) + event->len) {

                event = (const struct inotify_event *) ptr;

                /* Print event type.  */

                if (event->mask & IN_OPEN)
                    printf("IN_OPEN: ");
                if (event->mask & IN_CLOSE_NOWRITE)
                    printf("IN_CLOSE_NOWRITE: ");
                if (event->mask & IN_CLOSE_WRITE)
                    printf("IN_CLOSE_WRITE: ");

                /* Print the name of the watched directory.  */

                for (size_t i = 1; i < argc; ++i) {
                    if (wd[i] == event->wd) {
                        printf("%s/", argv[i]);
                        break;
                    }
                }

                /* Print the name of the file.  */

                if (event->len)
                    printf("%s", event->name);

                /* Print type of filesystem object.  */

                if (event->mask & IN_ISDIR)
                    printf(" [directory]\n");
                else
                    printf(" [file]\n");
            }
        }
    }

    int
    main(int argc, char* argv[])
    {
        char buf;
        int fd, i, poll_num;
        int *wd;
        nfds_t nfds;
        struct pollfd fds[2];

        if (argc < 2) {
            printf("Usage: %s PATH [PATH ...]\n", argv[0]);
            exit(EXIT_FAILURE);
        }

        printf("Press ENTER key to terminate.\n");

        /* Create the file descriptor for accessing the inotify API.  */

        fd = inotify_init1(IN_NONBLOCK);
        if (fd == -1) {
            perror("inotify_init1");
            exit(EXIT_FAILURE);
        }

        /* Allocate memory for watch descriptors.  */

        wd = calloc(argc, sizeof(int));
        if (wd == NULL) {
            perror("calloc");
            exit(EXIT_FAILURE);
        }

        /* Mark directories for events
           - file was opened
           - file was closed */

        for (i = 1; i < argc; i++) {
            wd[i] = inotify_add_watch(fd, argv[i],
                                      IN_OPEN | IN_CLOSE);
            if (wd[i] == -1) {
                fprintf(stderr, "Cannot watch '%s': %s\n",
                        argv[i], strerror(errno));
                exit(EXIT_FAILURE);
            }
        }

        /* Prepare for polling.  */

        nfds = 2;

        fds[0].fd = STDIN_FILENO;       /* Console input */
        fds[0].events = POLLIN;

        fds[1].fd = fd;                 /* Inotify input */
        fds[1].events = POLLIN;

        /* Wait for events and/or terminal input.  */

        printf("Listening for events.\n");
        for (;;) {
            poll_num = poll(fds, nfds, -1);
            if (poll_num == -1) {
                if (errno == EINTR)
                    continue;
                perror("poll");
                exit(EXIT_FAILURE);
            }

            if (poll_num > 0) {

                if (fds[0].revents & POLLIN) {

                    /* Console input is available.  Empty stdin and quit.  */

                    while (read(STDIN_FILENO, &buf, 1) > 0 && buf != '\n')
                        continue;
                    break;
                }

                if (fds[1].revents & POLLIN) {

                    /* Inotify events are available.  */

                    handle_events(fd, wd, argc, argv);
                }
            }
        }

        printf("Listening for events stopped.\n");

        /* Close inotify file descriptor.  */

        close(fd);

        free(wd);
        exit(EXIT_SUCCESS);
    }

انظر أيضًا
inotifywait(1), inotifywatch(1), inotify_add_watch(2), inotify_init(2), inotify_init1(2), inotify_rm_watch(2), read(2), stat(2), fanotify(7)

Documentation/filesystems/inotify.rst في شجرة مصدر نواة لينكس

ترجمة
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 (https://www.gnu.org/licenses/gpl-3.0.html) أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.