mlock(2)

الاسم
mlock, mlock2, munlock, mlockall, munlockall - قفل وفتح الذاكرة

المكتبة
مكتبة سي المعيارية (libc، -lc)

موجز
#include <sys/mman.h>

int mlock(size_t size;
          const void addr[size], size_t size);
int mlock2(size_t size;
          const void addr[size], size_t size, unsigned int flags);
int munlock(size_t size;
          const void addr[size], size_t size);

int mlockall(int flags);
int munlockall(void);

الوصف
mlock() و mlock2() و mlockall() تقفل جزءًا أو كل مساحة العنوان الافتراضية لعملية الاستدعاء في RAM، مما يمنع ترحيل تلك الذاكرة إلى منطقة المبادلة.

munlock() و munlockall() تنفذ العملية المعاكسة، بفتح جزء أو كل مساحة العنوان الافتراضية لعملية الاستدعاء، بحيث يمكن إخراج الصفحات في نطاق العنوان الافتراضي المحدد إلى المبادلة مرة أخرى إذا تطلب مدير ذاكرة النواة ذلك.

تُقفل الذاكرة وفتحها بوحدات من الصفحات الكاملة.

  mlock() و mlock2() و munlock()
mlock() تقفل الصفحات في نطاق العنوان الذي يبدأ من addr ويستمر لـ size بايت. جميع الصفحات التي تحتوي على جزء من نطاق العنوان المحدد مضمونة أن تكون مقيمة في RAM عند نجاح الاستدعاء؛ الصفحات مضمونة أن تبقى في RAM حتى تُفتح لاحقًا.

mlock2() تقفل أيضًا الصفحات في النطاق المحدد الذي يبدأ من addr ويستمر لـ size بايت. ومع ذلك، فإن حالة الصفحات الموجودة في ذلك النطاق بعد نجاح الاستدعاء ستعتمد على القيمة في وسيط flags.

يمكن أن يكون وسيط flags إما 0 أو الثابت التالي:

MLOCK_ONFAULT
    تقفل الصفحات المقيمة حاليًا وتضع علامة على النطاق بأكمله بحيث تُقفل الصفحات غير المقيمة المتبقية عند ملئها بخطأ صفحة.

إذا كان flags هو 0، فإن mlock2() تتصرف تمامًا مثل mlock().

munlock() تفتح الصفحات في نطاق العنوان الذي يبدأ من addr ويستمر لـ size بايت. بعد هذا الاستدعاء، يمكن للنواة نقل جميع الصفحات التي تحتوي على جزء من نطاق الذاكرة المحدد إلى مساحة المبادلة الخارجية مرة أخرى.

  mlockall() و munlockall()
mlockall() تقفل جميع الصفحات المعينة في مساحة العنوان لعملية الاستدعاء. يشمل ذلك صفحات مقطع الكود والبيانات والمكدس، بالإضافة إلى المكتبات المشتركة وبيانات النواة في مساحة المستخدم والذاكرة المشتركة والملفات المعينة في الذاكرة. جميع الصفحات المعينة مضمونة أن تكون مقيمة في RAM عند نجاح الاستدعاء؛ الصفحات مضمونة أن تبقى في RAM حتى تُفتح لاحقًا.

يُبنى وسيط flags كـ OR بتاتي لواحد أو أكثر من الثوابت التالية:

MCL_CURRENT
    تقفل جميع الصفحات المعينة حاليًا في مساحة العنوان للعملية.

MCL_FUTURE
    تقفل جميع الصفحات التي ستصبح معينة في مساحة العنوان للعملية في المستقبل. يمكن أن تكون هذه، على سبيل المثال، صفحات جديدة مطلوبة بواسطة كومة ومكدس متزايدين بالإضافة إلى ملفات جديدة معينة في الذاكرة أو مناطق ذاكرة مشتركة.

MCL_ONFAULT (منذ Linux 4.4)
    يُستخدم مع MCL_CURRENT أو MCL_FUTURE أو كليهما. يضع علامة على جميع التعيينات الحالية (مع MCL_CURRENT) أو المستقبلية (مع MCL_FUTURE) لقفل الصفحات عند إدخالها بخطأ. عند استخدامه مع MCL_CURRENT، تُقفل جميع الصفحات الموجودة، لكن mlockall() لن تسبب خطأ في الصفحات غير الموجودة. عند استخدامه مع MCL_FUTURE، ستُوضع علامة على جميع التعيينات المستقبلية لقفل الصفحات عند إدخالها بخطأ، لكنها لن تُملأ بالقفل عند إنشاء التعيين. يجب استخدام MCL_ONFAULT مع إما MCL_CURRENT أو MCL_FUTURE أو كليهما.

إذا حُدد MCL_FUTURE، فقد يفشل استدعاء نظام لاحق (مثل mmap(2) أو sbrk(2) أو malloc(3)) إذا تسبب في تجاوز عدد البايتات المقفلة الحد الأقصى المسموح به (انظر أدناه). في نفس الظروف، قد يفشل نمو المكدس بالمثل: سترفض النواة توسيع المكدس وترسل إشارة SIGSEGV إلى العملية.

munlockall() تفتح جميع الصفحات المعينة في مساحة العنوان لعملية الاستدعاء.

قيمة الإرجاع
عند النجاح، تعيد استدعاءات النظام هذه 0. عند الخطأ، يُرجع -1، ويُعين errno للإشارة إلى الخطأ، ولا يُجرى أي تغييرات على أي أقفال في مساحة العنوان للعملية.

الأخطاء

EAGAIN
    (mlock() و mlock2() و munlock()) لا يمكن قفل بعض أو كل نطاق العنوان المحدد.

EINVAL
    (mlock() و mlock2() و munlock()) كانت نتيجة الجمع addr+size أقل من addr (على سبيل المثال، قد يكون الجمع قد أدى إلى تجاوز).

EINVAL
    (mlock2()) حُدد flags غير معروفة.

EINVAL
    (mlockall()) حُدد flags غير معروفة أو حُدد MCL_ONFAULT دون MCL_FUTURE أو MCL_CURRENT.

EINVAL
    (ليس على لينكس) addr لم يكن مضاعفًا لحجم الصفحة.

ENOMEM
    (mlock(), mlock2(), و munlock()) بعض نطاق العناوين المحدد لا يتوافق مع صفحات معينة في مساحة عنوان العملية.

ENOMEM
    (mlock(), mlock2(), و munlock()) قفل أو فتح منطقة سيؤدي إلى تجاوز العدد الإجمالي للتعيينات ذات السمات المميزة (مثل المقفلة مقابل غير المقفلة) الحد الأقصى المسموح به. (على سبيل المثال، فتح نطاق في منتصف تعيين مقفل حاليًا سيؤدي إلى ثلاثة تعيينات: تعيينان مقفلان في كل طرف وتعيين غير مقفل في المنتصف.)

ENOMEM
    (لينكس 2.6.9 وما بعده) كان لدى المتصل حد مورد ناعم RLIMIT_MEMLOCK غير صفري، لكنه حاول قفل ذاكرة أكثر مما يسمح به الحد. لا يُرفض هذا الحد إذا كانت العملية مميزة (CAP_IPC_LOCK).

ENOMEM
    (لينكس 2.4 وما قبله) حاولت العملية المتصلة قفل أكثر من نصف ذاكرة الوصول العشوائي.

EPERM
    المتصل ليس مميزًا، لكنه يحتاج إلى امتياز (CAP_IPC_LOCK) لتنفيذ العملية المطلوبة.

EPERM
    (munlockall()) (لينكس 2.6.8 وما قبله) لم يكن المتصل مميزًا (CAP_IPC_LOCK).

الإصدارات

  لينكس
تحت لينكس، mlock()، mlock2()، و munlock() تقوم آليًا بتقريب addr لأسفل إلى أقرب حد صفحة. ومع ذلك، تسمح مواصفات POSIX.1 لـ mlock() و munlock() للتنفيذ بأن يتطلب محاذاة addr للصفحة، لذا يجب على التطبيقات المحمولة ضمان ذلك.

حقل VmLck في ملف /proc/pid/status الخاص بلينكس يظهر عدد الكيلوبايتات من الذاكرة التي قامت العملية ذات المعرف PID بقفلها باستخدام mlock()، mlock2()، mlockall()، و mmap(2) MAP_LOCKED.

المعايير

mlock()
munlock()
mlockall()
munlockall()
    POSIX.1-2024.

mlock2()
    لينكس.

على أنظمة POSIX التي تتوفر فيها mlock() و munlock()، تُعرف _POSIX_MEMLOCK_RANGE في <unistd.h> ويمكن تحديد عدد البايتات في الصفحة من الثابت PAGESIZE (إذا تم تعريفه) في <limits.h> أو عن طريق استدعاء sysconf(_SC_PAGESIZE).

على أنظمة POSIX التي تتوفر فيها mlockall() و munlockall()، تُعرف _POSIX_MEMLOCK في <unistd.h> بقيمة أكبر من 0. (انظر أيضًا sysconf(3).)

التاريخ

mlock()
munlock()
mlockall()
munlockall()
    SVr4, POSIX.1-1996.

mlock2()
    لينكس 4.4، glibc 2.27.

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

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

لا تُورث أقفال الذاكرة إلى عملية فرعية تم إنشاؤها عبر fork(2) وتُزال آليًا (فتحها) أثناء execve(2) أو عند إنهاء العملية. إعدادات mlockall() MCL_FUTURE و MCL_FUTURE | MCL_ONFAULT لا تُورث إلى عملية فرعية مُنشأة عبر fork(2) وتُمسح أثناء execve(2).

لاحظ أن fork(2) ستحضر مساحة العنوان لعملية نسخ عند الكتابة. النتيجة هي أن أي وصول للكتابة يليه سيسبب خطأ صفحة والذي بدوره قد يسبب زمن استجابة مرتفعًا لعملية في الوقت الفعلي. لذلك، من الضروري عدم استدعاء fork(2) بعد عملية mlockall() أو mlock()—ولا حتى من خيط يعمل بأولوية منخفضة داخل عملية تحتوي أيضًا على خيط يعمل بأولوية مرتفعة.

يُزال قفل الذاكرة على نطاق عنوان آليًا إذا أُلغي تعيين نطاق العنوان عبر munmap(2).

لا تتراكم أقفال الذاكرة، أي أن الصفحات التي أُقفلت عدة مرات عن طريق استدعاءات mlock()، mlock2()، أو mlockall() ستُفتح باستدعاء واحد لـ munlock() للنطاق المقابل أو بواسطة munlockall(). تبقى الصفحات التي عُينت لعدة مواقع أو بواسطة عدة عمليات مقفلة في ذاكرة الوصول العشوائي طالما أنها مقفلة على الأقل في موقع واحد أو بواسطة عملية واحدة على الأقل.

إذا تبع استدعاء لـ mlockall() يستخدم العلم MCL_FUTURE استدعاء آخر لا يحدد هذا العلم، فستفقد التغييرات التي أجراها استدعاء MCL_FUTURE.

يسمح علم mlock2() MLOCK_ONFAULT وعلم mlockall() MCL_ONFAULT بقفل ذاكرة فعال للتطبيقات التي تتعامل مع تعيينات كبيرة حيث يُلمس جزء (صغير) فقط من الصفحات في التعيين. في مثل هذه الحالات، سيتسبب قفل جميع الصفحات في تعيين في عقوبة كبيرة لقفل الذاكرة.

  الحدود والأذونات
في لينكس 2.6.8 وما قبله، يجب أن تكون العملية مميزة (CAP_IPC_LOCK) من أجل قفل الذاكرة ويحدد حد المورد الناعم RLIMIT_MEMLOCK حدًا على مقدار الذاكرة التي قد تقفلها العملية.

منذ لينكس 2.6.9، لا تُوضع حدود على مقدار الذاكرة التي يمكن لعملية مميزة قفلها وبدلاً من ذلك يحدد حد المورد الناعم RLIMIT_MEMLOCK حدًا على مقدار الذاكرة التي قد تقفلها عملية غير مميزة.

العلل
في لينكس 4.8 وما قبله، تسبب خطأ في محاسبة النواة للذاكرة المقفلة للعمليات غير المميزة (أي، بدون CAP_IPC_LOCK) في أنه إذا تداخلت المنطقة المحددة بواسطة addr و size مع قفل موجود، فإن البايتات المقفلة بالفعل في المنطقة المتداخلة كانت تُحسب مرتين عند التحقق من الحد. يمكن أن يؤدي هذا الحساب المزدوج إلى حساب غير صحيح لقيمة "الذاكرة المقفلة الإجمالية" للعملية التي تجاوزت حد RLIMIT_MEMLOCK، مما يؤدي إلى فشل mlock() و mlock2() في الطلبات التي كان يجب أن تنجح. أُصلح هذا الخطأ في لينكس 4.9.

في سلسلة نواة لينكس 2.4 حتى لينكس 2.4.17، تسبب خطأ في توريث علامة mlockall() MCL_FUTURE عبر fork(2). صُحح هذا في لينكس 2.4.18.

منذ لينكس 2.6.9، إذا استدعت عملية مميزة mlockall(MCL_FUTURE) ثم تخلت عن الامتيازات (فقدت قدرة CAP_IPC_LOCK عن طريق، على سبيل المثال، تعيين UID الفعال إلى قيمة غير صفرية)، فإن تخصيصات الذاكرة اللاحقة (مثل mmap(2)، brk(2)) ستفشل إذا واجهت حد مورد RLIMIT_MEMLOCK.

انظر أيضًا
mincore(2)، mmap(2)، setrlimit(2)، shmctl(2)، sysconf(3)، proc(5)، capabilities(7)

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

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

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