.\" -*- coding: UTF-8 -*- .\" Copyright 2016, International Business Machines Corp. .\" Copyright 2017, Michael Kerrisk .\" Copyright, the authors of the Linux man-pages project .\" .\" SPDX-License-Identifier: Linux-man-pages-copyleft .\" .\"******************************************************************* .\" .\" This file was generated with po4a. Translate the source file. .\" .\"******************************************************************* .TH userfaultfd 2 "16 فبراير 2026" "صفحات دليل لينكس 6.18" .SH الاسم userfaultfd \- إنشاء واصف ملف لمعالجة أخطاء الصفحات في مساحة المستخدم .SH المكتبة مكتبة سي المعيارية (\fIlibc\fP،\ \fI\-lc\fP) .SH موجز .nf \fB#include \fP /* تعريف ثوابت \fBO_*\fP */ \fB#include \fP /* تعريف ثوابت \fBSYS_*\fP */ \fB#include \fP /* تعريف ثوابت \fBUFFD_*\fP */ \fB#include \fP .P \fBint syscall(SYS_userfaultfd, int \fP\fIflags\fP\fB);\fP .fi .P \fIملاحظة\fP: لا توفر glibc غلافًا لـ \fBuserfaultfd\fP()، مما يستلزم استخدام \fBsyscall\fP(2). .SH الوصف ينشئ \fBuserfaultfd\fP() كائن userfaultfd جديد يمكن استخدامه لتفويض معالجة أخطاء الصفحات إلى تطبيق في مساحة المستخدم، ويعيد واصف ملف يشير إلى الكائن الجديد. يُضبط كائن userfaultfd الجديد باستخدام \fBioctl\fP(2). .P بمجرد ضبط كائن userfaultfd، يمكن للتطبيق استخدام \fBread\fP(2) لاستقبال إشعارات userfaultfd. قد تكون القراءات من userfaultfd حاجبة أو غير حاجبة، اعتمادًا على قيمة \fIflags\fP المستخدمة لإنشاء userfaultfd أو الاستدعاءات اللاحقة لـ \fBfcntl\fP(2). .P قد تُجمع القيم التالية بعملية OR على مستوى البت في \fIflags\fP لتغيير سلوك \fBuserfaultfd\fP(): .TP \fBO_CLOEXEC\fP فعّل علم الإغلاق عند التنفيذ (close\-on\-exec) لواصف ملف userfaultfd الجديد. انظر وصف علم \fBO_CLOEXEC\fP في \fBopen\fP(2). .TP \fBO_NONBLOCK\fP يفعّل التشغيل غير الحاجب لكائن userfaultfd. انظر وصف علم \fBO_NONBLOCK\fP في \fBopen\fP(2). .TP \fBUFFD_USER_MODE_ONLY\fP هذا علم خاص بـ userfaultfd أُدخل في لينكس 5.11. عند ضبطه، سيكون كائن userfaultfd قادرًا فقط على معالجة أخطاء الصفحات الناشئة من مساحة المستخدم في المناطق المسجلة. عندما يُقدح خطأ ناشئ من النواة في النطاق المسجل باستخدام userfaultfd هذا، ستُرسل إشارة \fBSIGBUS\fP. .P .\" عند إغلاق آخر واصف ملف يشير إلى كائن userfaultfd، تُلغى تسجيلات جميع نطاقات الذاكرة التي سُجلت مع الكائن وتُفرغ الأحداث غير المقروءة. .P يدعم Userfaultfd ثلاثة أوضاع للتسجيل: .TP \fBUFFDIO_REGISTER_MODE_MISSING\fP (منذ لينكس 4.10) عند التسجيل بالوضع \fBUFFDIO_REGISTER_MODE_MISSING\fP، ستتلقى مساحة المستخدم إشعار خطأ صفحة عند الوصول إلى صفحة مفقودة. سيُوقف الخيط المتعطل عن التنفيذ حتى يُحل خطأ الصفحة من مساحة المستخدم بواسطة ioctl إما \fBUFFDIO_COPY\fP أو \fBUFFDIO_ZEROPAGE\fP. .TP \fBUFFDIO_REGISTER_MODE_MINOR\fP (منذ لينكس 5.13) عند التسجيل بالوضع \fBUFFDIO_REGISTER_MODE_MINOR\fP، ستتلقى مساحة المستخدم إشعار خطأ صفحة عند حدوث خطأ صفحة ثانوي. أي عندما تكون صفحة السند في خبيئة الصفحات، ولكن مدخلات جدول الصفحات غير موجودة بعد. سيُوقف الخيط المتعطل عن التنفيذ حتى يُحل خطأ الصفحة من مساحة المستخدم بواسطة ioctl \fBUFFDIO_CONTINUE\fP. .TP \fBUFFDIO_REGISTER_MODE_WP\fP (منذ لينكس 5.7) عند التسجيل بالوضع \fBUFFDIO_REGISTER_MODE_WP\fP، ستتلقى مساحة المستخدم إشعار خطأ صفحة عند كتابة صفحة محمية ضد الكتابة. سيُوقف الخيط المتعطل عن التنفيذ حتى تُزيل مساحة المستخدم حماية الكتابة عن الصفحة باستخدام ioctl \fBUFFDIO_WRITEPROTECT\fP. .P يمكن تمكين أوضاع متعددة في نفس الوقت لنفس نطاق الذاكرة. .P منذ لينكس 4.14، يمكن لإشعار خطأ صفحة userfaultfd أن يضمّن بشكل انتقائي معلومات معرف الخيط المتعطل في الإشعار. يحتاج المرء إلى تمكين هذه الميزة صراحةً باستخدام بت الميزة \fBUFFD_FEATURE_THREAD_ID\fP عند تهيئة سياق userfaultfd. الإبلاغ عن معرف الخيط معطل مبدئيًا. .SS الاستخدام صُممت آلية userfaultfd للسماح لخيط في برنامج متعدد الخيوط بأداء ترحيل صفحات في مساحة المستخدم للخيوط الأخرى في العملية. عند حدوث خطأ صفحة لإحدى المناطق المسجلة لكائن userfaultfd، يُوضع الخيط المتعطل في حالة سكون ويُولد حدث يمكن قراءته عبر واصف ملف userfaultfd. يقرأ خيط معالجة الأخطاء الأحداث من واصف الملف هذا ويخدمها باستخدام العمليات الموصوفة في \fBioctl_userfaultfd\fP(2). عند خدمة أحداث خطأ الصفحة، يمكن لخيط معالجة الأخطاء قدح إيقاظ للخيط النائم. .P من الممكن أن تعمل الخيوط المتعطلة وخيوط معالجة الأخطاء في سياق عمليات مختلفة. في هذه الحالة، قد تنتمي هذه الخيوط إلى برامج مختلفة، ولن يتعاون البرنامج الذي ينفذ الخيوط المتعطلة بالضرورة مع البرنامج الذي يعالج أخطاء الصفحات. في هذا الوضع غير التعاوني، تحتاج العملية التي تراقب userfaultfd وتعالج أخطاء الصفحات إلى أن تكون على دراية بالتغييرات في تخطيط الذاكرة الافتراضية للعملية المتعطلة لتجنب فساد الذاكرة. .P منذ لينكس 4.11، يمكن لـ userfaultfd أيضًا إخطار خيوط معالجة الأخطاء حول التغييرات في تخطيط الذاكرة الافتراضية للعملية المتعطلة. بالإضافة إلى ذلك، إذا استدعت العملية المتعطلة \fBfork\fP(2)، فقد تُكرر كائنات userfaultfd المرتبطة بالأصل في العملية الفرعية وسيُخطر مراقب userfaultfd (عبر \fBUFFD_EVENT_FORK\fP الموصوف أدناه) بشأن واصف الملف المرتبط بكائنات userfault المنشأة للعملية الفرعية، مما يسمح لمراقب userfaultfd بأداء ترحيل صفحات في مساحة المستخدم للعملية الفرعية. على عكس أخطاء الصفحات التي يجب أن تكون متزامنة وتتطلب إيقاظًا صريحًا أو ضمنيًا، تُسلم جميع الأحداث الأخرى بشكل غير متزامن وتستأنف العملية غير التعاونية التنفيذ بمجرد أن ينفذ مدير userfaultfd \fBread\fP(2). يجب على مدير userfaultfd مزامنة استدعاءات \fBUFFDIO_COPY\fP بعناية مع معالجة الأحداث. .P .\" Regarding the preceding sentence, Mike Rapoport says: .\" The major point here is that current events delivery model could be .\" problematic for multi-threaded monitor. I even suspect that it would be .\" impossible to ensure synchronization between page faults and non-page .\" fault events in multi-threaded monitor. .\" .P .\" FIXME elaborate about non-cooperating mode, describe its limitations .\" for kernels before Linux 4.11, features added in Linux 4.11 .\" and limitations remaining in Linux 4.11 .\" Maybe it's worth adding a dedicated sub-section... .\" النموذج غير المتزامن الحالي لتسليم الأحداث هو الأمثل لتطبيقات مدير userfaultfd غير التعاونية أحادية الخيط. .P .\" منذ لينكس 5.7، أصبح userfaultfd قادرًا على القيام بتتبع متزامن لاتساخ الصفحات باستخدام وضع التسجيل الجديد للحماية من الكتابة. يجب على المرء التحقق من بت الميزة \fBUFFD_FEATURE_PAGEFAULT_FLAG_WP\fP قبل استخدام هذه الميزة. على غرار وضع الفقدان الأصلي لـ userfaultfd، سيُولد وضع الحماية من الكتابة إشعار userfaultfd عند كتابة الصفحة المحمية. يحتاج المستخدم إلى حل خطأ الصفحة عن طريق إزالة الحماية عن الصفحة المتعطلة وركل الخيط المتعطل للمتابعة. لمزيد من المعلومات، يُرجى الرجوع إلى قسم "وضع الحماية من الكتابة في Userfaultfd". .SS "عملية Userfaultfd" بعد إنشاء كائن userfaultfd باستخدام \fBuserfaultfd\fP()، يجب على التطبيق تمكينه باستخدام عملية \fBUFFDIO_API\fP \fBioctl\fP(2). تسمح هذه العملية بمصافحة من خطوتين بين النواة ومساحة المستخدم لتحديد إصدار API والميزات التي تدعمها النواة، ثم تمكين الميزات التي تريدها مساحة المستخدم. يجب تنفيذ هذه العملية قبل أي من عمليات \fBioctl\fP(2) الأخرى الموصوفة أدناه (وإلا ستفشل تلك العمليات مع الخطأ \fBEINVAL\fP). .P بعد نجاح عملية \fBUFFDIO_API\fP، يقوم التطبيق بتسجيل نطاقات عناوين الذاكرة باستخدام عملية \fBUFFDIO_REGISTER\fP \fBioctl\fP(2). بعد الإكمال الناجح لعملية \fBUFFDIO_REGISTER\fP، سيقوم النواة بإعادة توجيه أي خطأ صفحة يحدث في نطاق الذاكرة المطلوب، ويحقق الوضع المحدد وقت التسجيل، إلى تطبيق مساحة المستخدم. يمكن للتطبيق بعد ذلك استخدام عمليات \fBioctl\fP(2) متنوعة (مثل \fBUFFDIO_COPY\fP، أو \fBUFFDIO_ZEROPAGE\fP، أو \fBUFFDIO_CONTINUE\fP) لحل خطأ الصفحة. .P منذ لينكس 4.14، إذا ضبط التطبيق بت الميزة \fBUFFD_FEATURE_SIGBUS\fP باستخدام \fBUFFDIO_API\fP \fBioctl\fP(2)، فلن يُعاد توجيه أي إشعار بخطأ صفحة إلى مساحة المستخدم. بدلاً من ذلك، تُسلم إشارة \fBSIGBUS\fP إلى العملية المتعطلة. باستخدام هذه الميزة، يمكن استخدام userfaultfd لأغراض المتانة للقبض ببساطة على أي وصول لمناطق ضمن نطاق العناوين المسجل لا تحتوي على صفحات مخصصة، دون الحاجة للاستماع لأحداث userfaultfd. لن يكون هناك حاجة لمراقب userfaultfd للتعامل مع مثل هذه الوصولات للذاكرة. على سبيل المثال، يمكن أن تكون هذه الميزة مفيدة للتطبيقات التي تريد منع النواة من تخصيص الصفحات آليًا وملء الفجوات في الملفات المتفرقة عند الوصول إلى الفجوة عبر تعيين ذاكرة. .P تُورث ميزة \fBUFFD_FEATURE_SIGBUS\fP ضمنيًا من خلال \fBfork\fP(2) إذا استُخدمت مع \fBUFFD_FEATURE_FORK\fP. .P يمكن العثور على تفاصيل عمليات \fBioctl\fP(2) المختلفة في \fBioctl_userfaultfd\fP(2). .P منذ لينكس 4.11، يمكن تمكين أحداث بخلاف أخطاء الصفحات أثناء عملية \fBUFFDIO_API\fP. .P .\" حتى لينكس 4.11، كان يمكن استخدام userfaultfd فقط مع تعيينات الذاكرة الخاصة المجهولة. منذ لينكس 4.11، يمكن أيضًا استخدام userfaultfd مع hugetlbfs وتعيينات الذاكرة المشتركة. .SS "وضع الحماية من الكتابة في Userfaultfd (منذ لينكس 5.7)" منذ لينكس 5.7، يدعم userfaultfd وضع الحماية من الكتابة للذاكرة المجهولة. يحتاج المستخدم أولاً للتحقق من توفر هذه الميزة باستخدام ioctl \fBUFFDIO_API\fP مقابل بت الميزة \fBUFFD_FEATURE_PAGEFAULT_FLAG_WP\fP قبل استخدام هذه الميزة. .P منذ لينكس 5.19، دُعم وضع الحماية من الكتابة أيضًا على أنواع الذاكرة shmem و hugetlbfs. يمكن اكتشافه ببت الميزة \fBUFFD_FEATURE_WP_HUGETLBFS_SHMEM\fP. .P للتسجيل مع وضع الحماية من الكتابة لـ userfaultfd، يحتاج المستخدم إلى بدء ioctl \fBUFFDIO_REGISTER\fP مع تعيين الوضع \fBUFFDIO_REGISTER_MODE_WP\fP. لاحظ أنه من القانوني مراقبة نفس نطاق الذاكرة بأوضاع متعددة. على سبيل المثال، يمكن للمستخدم القيام بـ \fBUFFDIO_REGISTER\fP مع تعيين الوضع إلى \fBUFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP\fP. عندما يتم تسجيل \fBUFFDIO_REGISTER_MODE_WP\fP فقط، \fIلن\fP تتلقى مساحة المستخدم أي إشعار عند كتابة صفحة مفقودة. بدلاً من ذلك، ستتلقى مساحة المستخدم إشعار خطأ صفحة للحماية من الكتابة فقط عند كتابة صفحة موجودة ولكنها محمية من الكتابة. .P بعد اكتمال ioctl \fBUFFDIO_REGISTER\fP مع تعيين وضع \fBUFFDIO_REGISTER_MODE_WP\fP، يمكن للمستخدم حماية أي ذاكرة موجودة ضمن النطاق من الكتابة باستخدام ioctl \fBUFFDIO_WRITEPROTECT\fP حيث يجب تعيين \fIuffdio_writeprotect.mode\fP إلى \fBUFFDIO_WRITEPROTECT_MODE_WP\fP. .P عند حدوث حدث حماية من الكتابة، ستتلقى مساحة المستخدم إشعار خطأ صفحة حيث سيكون \fIuffd_msg.pagefault.flags\fP مع تعيين علم \fBUFFD_PAGEFAULT_FLAG_WP\fP. ملاحظة: نظرًا لأن الكتابات فقط يمكنها تشغيل هذا النوع من الأخطاء، فإن إشعارات الحماية من الكتابة ستحتوي دائمًا على بت \fBUFFD_PAGEFAULT_FLAG_WRITE\fP مضبوطًا مع بت \fBUFFD_PAGEFAULT_FLAG_WP\fP. .P .\" لحل خطأ صفحة الحماية من الكتابة، يجب على المستخدم بدء ioctl \fBUFFDIO_WRITEPROTECT\fP آخر، حيث يجب أن يكون \fIuffd_msg.pagefault.flags\fP مع مسح العلم \fBUFFDIO_WRITEPROTECT_MODE_WP\fP على الصفحة أو النطاق المتسبب في الخطأ. .SS "وضع الخطأ الثانوي لـ Userfaultfd (منذ لينكس 5.13)" منذ لينكس 5.13، يدعم userfaultfd وضع الخطأ الثانوي. في هذا الوضع، يتم إنتاج رسائل الخطأ ليس للأخطاء الرئيسية (حيث كانت الصفحة مفقودة)، بل للأخطاء الثانوية، حيث توجد صفحة في خبيئة الصفحة، ولكن إدخالات جدول الصفحة غير موجودة بعد. يحتاج المستخدم أولاً إلى التحقق من توفر هذه الميزة باستخدام ioctl \fBUFFDIO_API\fP مع تعيين بتات الميزة المناسبة قبل استخدام هذه الميزة: \fBUFFD_FEATURE_MINOR_HUGETLBFS\fP منذ لينكس 5.13، أو \fBUFFD_FEATURE_MINOR_SHMEM\fP منذ لينكس 5.14. .P للتسجيل مع وضع الخطأ الثانوي لـ userfaultfd، يحتاج المستخدم إلى بدء ioctl \fBUFFDIO_REGISTER\fP مع تعيين الوضع \fBUFFD_REGISTER_MODE_MINOR\fP. .P عند حدوث خطأ ثانوي، ستتلقى مساحة المستخدم إشعار خطأ صفحة حيث سيكون \fIuffd_msg.pagefault.flags\fP مع تعيين علم \fBUFFD_PAGEFAULT_FLAG_MINOR\fP. .P لحل خطأ صفحة ثانوي، يجب على المعالج تحديد ما إذا كانت محتويات الصفحة الحالية تحتاج إلى تعديل أولاً أم لا. إذا كان الأمر كذلك، فيجب القيام بذلك في المكان من خلال تعيين ثانٍ غير مسجل بـ userfaultfd لنفس الصفحة الداعمة (على سبيل المثال، عن طريق تعيين ملف shmem أو hugetlbfs مرتين). بمجرد اعتبار الصفحة "محدثة"، يمكن حل الخطأ عن طريق بدء ioctl \fBUFFDIO_CONTINUE\fP، الذي يقوم بتثبيت إدخالات جدول الصفحة و(بشكل مبدئي) إيقاظ الخيوط المتسببة في الخطأ. .P .\" يدعم وضع الخطأ الثانوي فقط الذاكرة المدعومة بـ hugetlbfs (منذ لينكس 5.13) والمدعومة بـ shmem (منذ لينكس 5.14). .SS "القراءة من بنية userfaultfd" كل \fBread\fP(2) من واصف ملف userfaultfd يُرجع بنية \fIuffd_msg\fP واحدة أو أكثر، كل منها يصف حدث خطأ صفحة أو حدثًا مطلوبًا لاستخدام userfaultfd غير التعاوني: .P .in +4n .EX struct uffd_msg { __u8 event; /* Type of event */ ... union { struct { __u64 flags; /* Flags describing fault */ __u64 address; /* Faulting address */ union { __u32 ptid; /* Thread ID of the fault */ } feat; } pagefault; \& struct { /* Since Linux 4.11 */ __u32 ufd; /* Userfault file descriptor of the child process */ } fork; \& struct { /* Since Linux 4.11 */ __u64 from; /* Old address of remapped area */ __u64 to; /* New address of remapped area */ __u64 len; /* Original mapping size */ } remap; \& struct { /* Since Linux 4.11 */ __u64 start; /* Start address of removed area */ __u64 end; /* End address of removed area */ } remove; ... } arg; \& /* Padding fields omitted */ } __packed; .EE .in .P إذا توفرت أحداث متعددة وكان الوسيط (buffer) الموفر كبيراً بما يكفي، يعيد الاستدعاء \fBread\fP(2) أكبر عدد ممكن من الأحداث التي يمكن احتواؤها في الوسيط الموفر. أما إذا كان الوسيط الموفر للاستدعاء \fBread\fP(2) أصغر من حجم بنية \fIuffd_msg\fP، فيخفق الاستدعاء \fBread\fP(2) مع الخطأ \fBEINVAL\fP. .P الحقول المضبوطة في بنية \fIuffd_msg\fP هي كما يلي: .TP \fIevent\fP نوع الحدث. وبناءً على نوع الحدث، تمثل الحقول المختلفة لاتحاد \fIarg\fP (union) التفاصيل المطلوبة لمعالجة الحدث. لا تُولّد الأحداث التي ليست من نوع خطأ\-الصفحة (page\-fault) إلا عند تفعيل الميزة المناسبة أثناء مصافحة واجهة برمجة التطبيقات عبر \fBUFFDIO_API\fP \fBioctl\fP(2). .IP يمكن أن تظهر القيم التالية في حقل \fIevent\fP: .RS .TP \fBUFFD_EVENT_PAGEFAULT\fP (منذ لينكس 4.3) حدث خطأ\-صفحة. تفاصيل خطأ\-الصفحة متاحة في الحقل \fIpagefault\fP. .TP \fBUFFD_EVENT_FORK\fP (منذ لينكس 4.11) .\" FIXME describe duplication of userfault file descriptor during fork يُتولد عندما تستدعي العملية المتسببة بالخطأ \fBfork\fP(2) (أو \fBclone\fP(2) بدون علمة \fBCLONE_VM\fP). تفاصيل الحدث متوفرة في حقل \fIfork\fP. .TP \fBUFFD_EVENT_REMAP\fP (منذ لينكس 4.11) يُتولد عندما تستدعي العملية المتسببة بالخطأ \fBmremap\fP(2). تفاصيل الحدث متوفرة في حقل \fIremap\fP. .TP \fBUFFD_EVENT_REMOVE\fP (منذ لينكس 4.11) يُتولد عندما تستدعي العملية المتسببة بالخطأ \fBmadvise\fP(2) مع نصيحة \fBMADV_DONTNEED\fP أو \fBMADV_REMOVE\fP. تفاصيل الحدث متوفرة في حقل \fIremove\fP. .TP \fBUFFD_EVENT_UNMAP\fP (منذ لينكس 4.11) يُتولد عندما تلغي العملية المتسببة بالخطأ تعيين نطاق ذاكرة، إما صراحة باستخدام \fBmunmap\fP(2) أو ضمنًا أثناء \fBmmap\fP(2) أو \fBmremap\fP(2). تفاصيل الحدث متوفرة في حقل \fIremove\fP. .RE .TP \fIpagefault.address\fP العنوان الذي أثار خطأ الصفحة. .TP \fIpagefault.flags\fP قناع بتات للأعلام التي تصف الحدث. بالنسبة لـ \fBUFFD_EVENT_PAGEFAULT\fP، قد يظهر العلم التالي: .RS .TP \fBUFFD_PAGEFAULT_FLAG_WP\fP إذا كان هذا العلم مضبوطًا، فإن الخطأ كان خطأ حماية من الكتابة. .TP \fBUFFD_PAGEFAULT_FLAG_MINOR\fP إذا كان هذا العلم مضبوطًا، فإن الخطأ كان خطأ بسيطًا. .TP \fBUFFD_PAGEFAULT_FLAG_WRITE\fP إذا كان هذا العلم مضبوطًا، فإن الخطأ كان خطأ كتابة. .P إذا لم يكن أي من \fBUFFD_PAGEFAULT_FLAG_WP\fP أو \fBUFFD_PAGEFAULT_FLAG_MINOR\fP مضبوطًا، فإن الخطأ كان خطأ فقدان. .RE .TP \fIpagefault.feat.pid\fP معرف الخيط الذي أثار خطأ الصفحة. .TP \fIfork.ufd\fP واصف الملف المرتبط بكائن userfault الذي أُنشئ للطفل الذي أنشأته \fBfork\fP(2). .TP \fIremap.from\fP العنوان الأصلي لنطاق الذاكرة الذي أُعيد تعيينه باستخدام \fBmremap\fP(2). .TP \fIremap.to\fP العنوان الجديد لنطاق الذاكرة الذي أُعيد تعيينه باستخدام \fBmremap\fP(2). .TP \fIremap.len\fP الحجم الأصلي لنطاق الذاكرة الذي أُعيد تعيينه باستخدام \fBmremap\fP(2). .TP \fIremove.start\fP عنوان البداية لنطاق الذاكرة الذي حُرر باستخدام \fBmadvise\fP(2) أو أُلغي تعيينه .TP \fIremove.end\fP عنوان نهاية نطاق الذاكرة الذي تم تحريره باستخدام \fBmadvise\fP(2) أو إلغاء تعيينه .P قد تفشل عملية \fBread\fP(2) على واصف ملف userfaultfd مع الأخطاء التالية: .TP \fBEINVAL\fP لم يتم تمكين كائن userfaultfd بعد باستخدام عملية \fBUFFDIO_API\fP \fBioctl\fP(2) .P .\" FIXME What is the reason for this seemingly odd behavior with respect .\" to the O_NONBLOCK flag? (see userfaultfd_poll() in fs/userfaultfd.c). .\" Something needs to be said about this. إذا تم تمكين علامة \fBO_NONBLOCK\fP في وصف الملف المفتوح المرتبط، يمكن مراقبة واصف ملف userfaultfd باستخدام \fBpoll\fP(2) و\fBselect\fP(2) و\fBepoll\fP(7). عندما تتوفر الأحداث، يشير واصف الملف إلى أنه قابل للقراءة. إذا لم يتم تمكين علامة \fBO_NONBLOCK\fP، فإن \fBpoll\fP(2) (دائمًا) يشير إلى أن الملف لديه حالة \fBPOLLERR\fP، ويشير \fBselect\fP(2) إلى أن واصف الملف قابل للقراءة والكتابة معًا. .SH "قيمة الإرجاع" عند النجاح، تُرجع \fBuserfaultfd\fP() واصف ملف جديد يشير إلى كائن userfaultfd. عند الخطأ، تُرجع \-1، ويتم تعيين \fIerrno\fP للإشارة إلى الخطأ. .SH الأخطاء .TP \fBEINVAL\fP عُيّنت قيمة غير مدعومة في \fIflags\fP. .TP \fBEMFILE\fP تم الوصول إلى الحد الأقصى لكل عملية لعدد واصفات الملفات المفتوحة .TP \fBENFILE\fP وُصل إلى الحد الأقصى لإجمالي عدد الملفات المفتوحة على مستوى النظام. .TP \fBENOMEM\fP ذاكرة النواة المتوفرة غير كافية. .TP \fBEPERM\fP (منذ Linux 5.2) .\" cefdca0a86be517bc390fc4541e3674b8e7803b0 المتصل ليس لديه صلاحية (ليس لديه قدرة \fBCAP_SYS_PTRACE\fP في مساحة اسم المستخدم الأولية)، وقيمة \fI/proc/sys/vm/unprivileged_userfaultfd\fP هي 0. .SH المعايير لينكس. .SH التاريخ لينكس 4.3. .P تمت إضافة دعم hugetlbfs ومناطق الذاكرة المشتركة وأحداث غير خطأ الصفحة في Linux 4.11 .SH ملاحظات يمكن استخدام آلية userfaultfd كبديل لتقنيات الترحيل التقليدية في مساحة المستخدم القائمة على استخدام إشارة \fBSIGSEGV\fP و\fBmmap\fP(2). يمكن استخدامها أيضًا لتنفيذ الاستعادة البطيئة لآليات نقطة التفتيش/الاستعادة، بالإضافة إلى الترحيل بعد النسخ للسماح بالتنفيذ (شبه) المتواصل عند نقل الأجهزة الافتراضية وحاويات Linux من مضيف إلى آخر. .SH العلل إذا تم تمكين \fBUFFD_FEATURE_EVENT_FORK\fP وتم مقاطعة استدعاء نظام من عائلة \fBfork\fP(2) بواسطة إشارة أو فشل، فقد يتم إنشاء واصف userfaultfd قديم. في هذه الحالة، سيتم تسليم \fBUFFD_EVENT_FORK\fP زائف إلى مراقب userfaultfd. .SH أمثلة يوضح البرنامج أدناه استخدام آلية userfaultfd. يقوم البرنامج بإنشاء خيطين، أحدهما يعمل كمعالج خطأ الصفحة للعملية، للصفحات في منطقة طلب صفحة صفرية تم إنشاؤها باستخدام \fBmmap\fP(2). .P يأخذ البرنامج وسيطة سطر أوامر واحدة، وهي عدد الصفحات التي سيتم إنشاؤها في تعيين سيتم التعامل مع أخطاء صفحته عبر userfaultfd. بعد إنشاء كائن userfaultfd، يقوم البرنامج بعد ذلك بإنشاء تعيين خاص مجهول بالحجم المحدد ويسجل نطاق العنوان لذلك التعيين باستخدام عملية \fBUFFDIO_REGISTER\fP \fBioctl\fP(2). ثم يقوم البرنامج بإنشاء خيط ثانٍ سيقوم بمهمة معالجة أخطاء الصفحة. .P ثم يتجول الخيط الرئيسي عبر صفحات التعيين لجلب البايتات من الصفحات المتتالية. نظرًا لعدم الوصول إلى الصفحات بعد، فإن أول وصول لبايت في كل صفحة سيؤدي إلى تشغيل حدث خطأ صفحة على واصف ملف userfaultfd. .P يتم التعامل مع كل حدث من أحداث خطأ الصفحة بواسطة الخيط الثاني، الذي يجلس في حلقة لمعالجة الإدخال من واصف ملف userfaultfd. في كل تكرار للحلقة، يستدعي الخيط الثاني أولاً \fBpoll\fP(2) للتحقق من حالة واصف الملف، ثم يقرأ حدثًا من واصف الملف. يجب أن تكون كل هذه الأحداث أحداث \fBUFFD_EVENT_PAGEFAULT\fP، والتي يعالجها الخيط عن طريق نسخ صفحة من البيانات إلى المنطقة المتسببة في الخطأ باستخدام عملية \fBUFFDIO_COPY\fP \fBioctl\fP(2). .P فيما يلي مثال لما نراه عند تشغيل البرنامج: .P .in +4n .EX $\fB ./userfaultfd_demo 3\fP; Address returned by mmap() = 0x7fd30106c000 \& fault_handler_thread(): poll() returns: nready = 1; POLLIN = 1; POLLERR = 0 UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106c00f (uffdio_copy.copy returned 4096) Read address 0x7fd30106c00f in main(): A Read address 0x7fd30106c40f in main(): A Read address 0x7fd30106c80f in main(): A Read address 0x7fd30106cc0f in main(): A \& fault_handler_thread(): poll() returns: nready = 1; POLLIN = 1; POLLERR = 0 UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106d00f (uffdio_copy.copy returned 4096) Read address 0x7fd30106d00f in main(): B Read address 0x7fd30106d40f in main(): B Read address 0x7fd30106d80f in main(): B Read address 0x7fd30106dc0f in main(): B \& fault_handler_thread(): poll() returns: nready = 1; POLLIN = 1; POLLERR = 0 UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106e00f (uffdio_copy.copy returned 4096) Read address 0x7fd30106e00f in main(): C Read address 0x7fd30106e40f in main(): C Read address 0x7fd30106e80f in main(): C Read address 0x7fd30106ec0f in main(): C .EE .in .SS "مصدر البرنامج" .\" SRC BEGIN (userfaultfd.c) \& .EX /* userfaultfd_demo.c \& Licensed under the GNU General Public License version 2 or later. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include \& static int page_size; \& static void * fault_handler_thread(void *arg) { long uffd; /* userfaultfd file descriptor */ \& static int fault_cnt = 0; /* Number of faults so far handled */ static char *page = NULL; static struct uffd_msg msg; /* Data read from userfaultfd */ \& uffd = (long) arg; \& /* Create a page that will be copied into the faulting region. */ \& if (page == NULL) { page = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, \-1, 0); if (page == MAP_FAILED) err(EXIT_FAILURE, "mmap"); } \& /* Loop, handling incoming events on the userfaultfd file descriptor. */ \& for (;;) { int nready; ssize_t nread; struct pollfd pollfd; struct uffdio_copy uffdio_copy; \& /* See what poll() tells us about the userfaultfd. */ \& pollfd.fd = uffd; pollfd.events = POLLIN; nready = poll(&pollfd, 1, \-1); if (nready == \-1) err(EXIT_FAILURE, "poll"); \& printf("\[rs]nfault_handler_thread():\[rs]n"); printf(" poll() returns: nready = %d; " "POLLIN = %d; POLLERR = %d\[rs]n", nready, (pollfd.revents & POLLIN) != 0, (pollfd.revents & POLLERR) != 0); \& /* Read an event from the userfaultfd. */ \& nread = read(uffd, &msg, sizeof(msg)); if (nread == 0) { printf("EOF on userfaultfd!\[rs]n"); exit(EXIT_FAILURE); } \& if (nread == \-1) err(EXIT_FAILURE, "read"); \& /* We expect only one kind of event; verify that assumption. */ \& if (msg.event != UFFD_EVENT_PAGEFAULT) { fprintf(stderr, "Unexpected event on userfaultfd\[rs]n"); exit(EXIT_FAILURE); } \& /* Display info about the page\-fault event. */ \& printf(" UFFD_EVENT_PAGEFAULT event: "); printf("flags = %w64x; ", msg.arg.pagefault.flags); printf("address = %w64x\[rs]n", msg.arg.pagefault.address); \& /* Copy the page pointed to by \[aq]page\[aq] into the faulting region. Vary the contents that are copied in, so that it is more obvious that each fault is handled separately. */ \& memset(page, \[aq]A\[aq] + fault_cnt % 20, page_size); fault_cnt++; \& uffdio_copy.src = (unsigned long) page; \& /* We need to handle page faults in units of pages(!). So, round faulting address down to page boundary. */ \& uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address & \[ti](page_size \- 1); uffdio_copy.len = page_size; uffdio_copy.mode = 0; uffdio_copy.copy = 0; if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == \-1) err(EXIT_FAILURE, "ioctl\-UFFDIO_COPY"); \& printf(" (uffdio_copy.copy returned %w64d)\[rs]n", uffdio_copy.copy); } } \& int main(int argc, char *argv[]) { int s; char *addr; /* Start of region handled by userfaultfd */ long uffd; /* userfaultfd file descriptor */ size_t size, i; /* Size of region handled by userfaultfd */ pthread_t thr; /* ID of thread that handles page faults */ struct uffdio_api uffdio_api; struct uffdio_register uffdio_register; \& if (argc != 2) { fprintf(stderr, "Usage: %s num\-pages\[rs]n", argv[0]); exit(EXIT_FAILURE); } \& page_size = sysconf(_SC_PAGE_SIZE); size = strtoull(argv[1], NULL, 0) * page_size; \& /* Create and enable userfaultfd object. */ \& uffd = syscall(SYS_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd == \-1) err(EXIT_FAILURE, "userfaultfd"); \& /* NOTE: Two\-step feature handshake is not needed here, since this example doesn't require any specific features. \& Programs that *do* should call UFFDIO_API twice: once with `features = 0` to detect features supported by this kernel, and again with the subset of features the program actually wants to enable. */ uffdio_api.api = UFFD_API; uffdio_api.features = 0; if (ioctl(uffd, UFFDIO_API, &uffdio_api) == \-1) err(EXIT_FAILURE, "ioctl\-UFFDIO_API"); \& /* Create a private anonymous mapping. The memory will be demand\-zero paged\-\-that is, not yet allocated. When we actually touch the memory, it will be allocated via the userfaultfd. */ \& addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, \-1, 0); if (addr == MAP_FAILED) err(EXIT_FAILURE, "mmap"); \& printf("Address returned by mmap() = %p\[rs]n", addr); \& /* Register the memory range of the mapping we just created for handling by the userfaultfd object. In mode, we request to track missing pages (i.e., pages that have not yet been faulted in). */ \& uffdio_register.range.start = (unsigned long) addr; uffdio_register.range.len = size; uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == \-1) err(EXIT_FAILURE, "ioctl\-UFFDIO_REGISTER"); \& /* Create a thread that will process the userfaultfd events. */ \& s = pthread_create(&thr, NULL, fault_handler_thread, (void *) uffd); if (s != 0) { errc(EXIT_FAILURE, s, "pthread_create"); } \& /* Main thread now touches memory in the mapping, touching locations 1024 bytes apart. This will trigger userfaultfd events for all pages in the region. */ \& i = 0xf; /* Ensure that faulting address is not on a page boundary, in order to test that we correctly handle that case in fault_handling_thread(). */ while (i < size) { char c; \& c = addr[i]; printf("Read address %p in %s(): ", addr + i, __func__); printf("%c\[rs]n", c); i += 1024; usleep(100000); /* Slow things down a little */ } \& exit(EXIT_SUCCESS); } .EE .\" SRC END .SH "انظر أيضًا" \fBfcntl\fP(2), \fBioctl\fP(2), \fBioctl_userfaultfd\fP(2), \fBmadvise\fP(2), \fBmmap\fP(2) .P الملف \fIDocumentation/admin\-guide/mm/userfaultfd.rst\fP في شجرة مصدر نواة لينكس .PP .SH ترجمة تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي . .PP هذه الترجمة هي وثيقة مجانية؛ راجع .UR https://www.gnu.org/licenses/gpl-3.0.html رخصة جنو العامة الإصدار 3 .UE أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات. .PP إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: .MT kde-l10n-ar@kde.org .ME .