| mmap(2) | System Calls Manual | mmap(2) |
الاسم¶
mmap، munmap - يخطط أو يفصل الملفات أو الأجهزة في الذاكرة
المكتبة¶
Standard C library (libc, -lc)
موجز¶
#include <sys/mman.h>
void *mmap(size_t length;
void addr[length], size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(size_t length;
void addr[length], size_t length);
انظر VERSIONS للحصول على معلومات حول متطلبات ماكرو اختبار الميزات.
الوصف¶
تُنشئ mmap() تخطيطًا جديدًا في مساحة العناوين الافتراضية للعملية المستدعِية. يُحدد العنوان الابتدائي للتخطيط الجديد في addr. يُحدد المعامل length طول التخطيط (الذي يجب أن يكون أكبر من 0).
إذا كان addr هو NULL، فإن النواة تختار العنوان (المحاذي للصفحة) الذي ستُنشئ التخطيط عنده؛ وهذه هي الطريقة الأكثر قابلية للنقل لإنشاء تخطيط جديد. أما إذا لم يكن addr هو NULL، فإن النواة تعتبره تلميحًا حول مكان وضع التخطيط؛ وفي لينكس، ستختار النواة حدود صفحة قريبة (ولكن دائمًا أعلى من أو تساوي القيمة المحددة في /proc/sys/vm/mmap_min_addr) وتحاول إنشاء التخطيط هناك. وإذا وجد تخطيط آخر هناك بالفعل، تختار النواة عنوانًا جديدًا قد يعتمد أو لا يعتمد على التلميح. ويُعاد عنوان التخطيط الجديد كناتج للاستدعاء.
تُهيأ محتويات تخطيط الملف (على عكس التخطيط المجهول؛ انظر MAP_ANONYMOUS أدناه)، باستخدام بايتات بطول length بدءًا من الإزاحة offset في الملف (أو أي كائن آخر) المشار إليه بواسطة واصف الملف fd. يجب أن تكون offset مضاعفًا لحجم الصفحة كما يعيده sysconf(_SC_PAGE_SIZE).
بعد عودة استدعاء mmap()، يمكن إغلاق واصف الملف، fd، فورًا دون إبطال التخطيط.
يصف المعامل prot حماية الذاكرة المطلوبة للتخطيط (ويجب ألا يتعارض مع وضع فتح الملف). وهو إما PROT_NONE أو ناتج عملية OR الثنائية لعلامة أو أكثر من العلامات التالية:
- PROT_EXEC
- يمكن تنفيذ الصفحات.
- PROT_READ
- يمكن قراءة الصفحات.
- PROT_WRITE
- يمكن الكتابة في الصفحات.
- PROT_NONE
- لا يمكن الوصول إلى الصفحات.
معامل العلامات (flags)¶
يحدد المعامل flags ما إذا كانت التحديثات على التخطيط مرئية للعمليات الأخرى التي تخطط نفس المنطقة، وما إذا كانت التحديثات تُنقل إلى الملف الأساسي. ويُحدد هذا السلوك بتضمين قيمة واحدة فقط من القيم التالية في flags:
- MAP_SHARED
- مشاركة هذا التخطيط. تكون التحديثات على التخطيط مرئية للعمليات الأخرى التي تخطط نفس المنطقة، و(في حالة التخطيطات المدعومة بملفات) تُنقل إلى الملف الأساسي. (يتطلب التحكم الدقيق في وقت نقل التحديثات إلى الملف الأساسي استخدام msync(2)).
- MAP_SHARED_VALIDATE (منذ لينكس 4.15)
- توفر هذه العلامة نفس سلوك MAP_SHARED باستثناء أن تخطيطات MAP_SHARED تتجاهل العلامات غير المعروفة في flags. وبالمقابل، عند إنشاء تخطيط باستخدام MAP_SHARED_VALIDATE، تتحقق النواة من أن جميع العلامات الممرة معروفة وتُفشل التخطيط بالخطأ EOPNOTSUPP للعلامات غير المعروفة. كما أن هذا النوع من التخطيط مطلوب للتمكن من استخدام بعض علامات التخطيط (مثل MAP_SYNC).
- MAP_PRIVATE
- إنشاء تخطيط خاص بنظام النسخ عند الكتابة (copy-on-write). لا تكون التحديثات على التخطيط مرئية للعمليات الأخرى التي تخطط نفس الملف، ولا تُنقل إلى الملف الأساسي. وغير محدد ما إذا كانت التغييرات التي أُجريت على الملف بعد استدعاء mmap() ستكون مرئية في المنطقة المخططة.
كلا العلامتين MAP_SHARED و MAP_PRIVATE موصوفتان في POSIX.1-2001 و POSIX.1-2008. أما MAP_SHARED_VALIDATE فهي امتداد للينكس.
بالإضافة إلى ذلك، يمكن إجراء عملية OR لصفر أو أكثر من القيم التالية في flags:
- MAP_32BIT (منذ لينكس 2.4.20، 2.6)
- وضع التخطيط في أول 2 جيجابايت من مساحة عناوين العملية. تدعم هذه العلامة فقط على معمارية x86-64، للبرامج ذات 64 بت. أُضيفت للسماح بتخصيص كدسات الخيوط في مكان ما ضمن أول 2 جيجابايت من الذاكرة، لتحسين أداء تبديل السياق في بعض معالجات 64 بت المبكرة. لم تعد معالجات x86-64 الحديثة تعاني من مشكلة الأداء هذه، لذا لا يشترط استخدام هذه العلامة في تلك الأنظمة. تُتجاهل العلامة MAP_32BIT عند ضبط MAP_FIXED.
- MAP_ANON
- مرادف لـ MAP_ANONYMOUS؛ توفر للتوافق مع التنفيذات الأخرى.
- MAP_ANONYMOUS
- التخطيط غير مدعوم بأي ملف؛ وتُهيأ محتوياته إلى الصفر. يُتجاهل المعامل fd؛ ومع ذلك، تتطلب بعض التنفيذات أن يكون fd مساويًا لـ -1 إذا حُددت MAP_ANONYMOUS (أو MAP_ANON)، ويجب أن تضمن التطبيقات القابلة للنقل ذلك. يجب أن يكون المعامل offset صفرًا. أُضيفت دعم MAP_ANONYMOUS مع MAP_SHARED في لينكس 2.4.
- MAP_DENYWRITE
- تُتجاهل هذه العلامة. (منذ زمن بعيد -لينكس 2.0 وما قبله- كانت تشير إلى أن محاولات الكتابة إلى الملف الأساسي يجب أن تفشل بالخطأ ETXTBSY. ولكن هذا كان مصدرًا لهجمات حجب الخدمة).
- MAP_EXECUTABLE
- تُتجاهل هذه العلامة.
- MAP_FILE
- علامة توافقية. تُتجاهل.
- MAP_FIXED
- لا تفسر addr كتلميح: ضع التخطيط عند هذا العنوان تمامًا. يجب محاذاة addr بشكل مناسب: في معظم المعماريات يكفي أن يكون مضاعفًا لحجم الصفحة؛ ومع ذلك، قد تفرض بعض المعماريات قيودًا إضافية. إذا كانت منطقة الذاكرة المحددة بواسطة addr و length تتداخل مع صفحات أي تخطيط (أو تخطيطات) موجودة، فسيُهمل الجزء المتداخل من التخطيط (التخطيطات) الموجودة. إذا تعذر استخدام العنوان المحدد، فستفشل mmap().
- يجب على البرمجيات التي تطمح لأن تكون قابلة للنقل استخدام العلامة MAP_FIXED بحذر، مع الأخذ في الاعتبار أنه يُسمح بتغيير المخطط الدقيق لتخطيطات ذاكرة العملية بشكل كبير بين إصدارات لينكس، وإصدارات مكتبة سي، وإصدارات أنظمة التشغيل. اقرأ بعناية مناقشة هذه العلامة في قسم الملاحظات (NOTES)!
- MAP_FIXED_NOREPLACE (منذ لينكس 4.17)
- توفر هذه العلامة سلوكًا مشابهًا لـ MAP_FIXED فيما يتعلق بفرض العنوان addr، ولكنها تختلف في أن MAP_FIXED_NOREPLACE لا تمسح أبدًا أي نطاق مخطط مسبقًا. إذا اصطدم النطاق المطلوب مع تخطيط موجود، فإن هذا الاستدعاء يفشل بالخطأ EEXIST. لذا يمكن استخدام هذه العلامة كوسيلة لمحاولة تخطيط نطاق عناوين بشكل ذري (فيما يتعلق بالخيوط الأخرى): سينجح خيط واحد؛ وستبلغ جميع الخيوط الأخرى عن الفشل.
- لاحظ أن النوى القديمة التي لا تتعرف على العلامة MAP_FIXED_NOREPLACE ستتراجع عادةً (عند اكتشاف تصادم مع تخطيط موجود مسبقًا) إلى سلوك من نوع "غير MAP_FIXED": حيث ستعيد عنوانًا يختلف عن العنوان المطلوب. لذا، يجب على البرمجيات المتوافقة مع الإصدارات السابقة التحقق من العنوان المعاد ومقارنته بالعنوان المطلوب.
- MAP_GROWSDOWN
- تُستخدم هذه العلامة للكدسات. وهي تشير لنظام الذاكرة الافتراضية في النواة بأن التخطيط يجب أن يمتد للأسفل في الذاكرة. العنوان المُعاد هو صفحة واحدة أقل من منطقة الذاكرة التي أُنشئت فعليًا في مساحة العناوين الافتراضية للعملية. سيؤدي الوصول لعنوان في صفحة "الحماية" أسفل التخطيط إلى نمو التخطيط بمقدار صفحة. يمكن تكرار هذا النمو حتى ينمو التخطيط ليصل إلى مسافة صفحة واحدة من الطرف العلوي للتخطيط الأدنى التالي، وعندها سيؤدي الوصول لصفحة "الحماية" إلى إشارة SIGSEGV.
- MAP_HUGETLB (منذ لينكس 2.6.32)
- تخصيص التخطيط باستخدام الصفحات "الضخمة". انظر ملف مصدر نواة لينكس Documentation/admin-guide/mm/hugetlbpage.rst لمزيد من المعلومات، بالإضافة إلى قسم الملاحظات (NOTES) أدناه.
- MAP_HUGE_2MB
- MAP_HUGE_1GB (منذ لينكس 3.8)
- تُستخدم مع MAP_HUGETLB لاختيار أحجام بديلة لصفحات hugetlb (على التوالي، 2 ميجابايت و 1 جيجابايت) في الأنظمة التي تدعم أحجامًا متعددة لصفحات hugetlb.
- بشكل عام، يمكن ضبط حجم الصفحة الضخمة المطلوب بترميز اللوغاريتم للأساس 2 لحجم الصفحة المطلوب في البتات الستة عند الإزاحة MAP_HUGE_SHIFT. (قيمة الصفر في حقل البتات هذا توفر حجم الصفحة الضخمة المبدئي؛ يمكن اكتشاف حجم الصفحة الضخمة المبدئي عبر حقل Hugepagesize المعروض في /proc/meminfo). وبذلك، يُعرف الثابتان المذكوران أعلاه كالتالي:
-
#define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT) #define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT)
- يمكن اكتشاف نطاق أحجام الصفحات الضخمة التي يدعمها النظام بسرد الأدلة الفرعية في /sys/kernel/mm/hugepages.
- MAP_LOCKED (منذ لينكس 2.5.37)
- تعليم المنطقة المخططة لتُقفل بنفس طريقة mlock(2). سيحاول هذا التنفيذ ملء النطاق بالكامل مسبقًا ولكن لا يفشل استدعاء mmap() بالخطأ ENOMEM إذا فشل ذلك. لذا قد تحدث أخطاء جسيمة (major faults) لاحقًا. وبذلك فإن الدلالة ليست بقوة mlock(2). يجب استخدام mmap() بالإضافة لـ mlock(2) عندما لا تكون الأخطاء الجسيمة مقبولة بعد تهيئة التخطيط. تُتجاهل العلامة MAP_LOCKED في النوى القديمة.
- MAP_NONBLOCK (منذ لينكس 2.5.46)
- هذه العلامة ذات معنى فقط عند استخدامها مع MAP_POPULATE. لا تقم بالقراءة المسبقة: أنشئ مدخلات جداول الصفحات فقط للصفحات الموجودة بالفعل في RAM. منذ لينكس 2.6.23، تجعل هذه العلامة MAP_POPULATE لا تفعل شيئًا. قد يُعاد تنفيذ الجمع بين MAP_POPULATE و MAP_NONBLOCK يومًا ما.
- MAP_NORESERVE
- عدم حجز مساحة تبديل (swap) لهذا التخطيط. عند حجز مساحة التبديل، يكون هناك ضمان بإمكانية تعديل التخطيط. وعندما لا تُحجز مساحة التبديل، قد تحصل على إشارة SIGSEGV عند الكتابة إذا لم تتوفر ذاكرة فيزيائية. انظر أيضًا مناقشة الملف /proc/sys/vm/overcommit_memory في proc_sys_vm(5). قبل لينكس 2.6، كان لهذه العلامة تأثير فقط على التخطيطات الخاصة القابلة للكتابة.
- MAP_POPULATE (منذ لينكس 2.5.46)
- ملء (خطأ مسبق) جداول الصفحات للتخطيط. بالنسبة لتخطيط الملفات، يؤدي هذا للقراءة المسبقة للملف. سيساعد هذا في تقليل الحجب بسبب أخطاء الصفحات لاحقًا. لا يفشل استدعاء mmap() إذا تعذر ملء التخطيط (على سبيل المثال، بسبب قيود على عدد الصفحات الضخمة المخططة عند استخدام MAP_HUGETLB). أُضيفت دعم MAP_POPULATE مع التخطيطات الخاصة في لينكس 2.6.23.
- MAP_STACK (منذ لينكس 2.6.27)
- تخصيص التخطيط عند عنوان مناسب لكدسة عملية أو خيط.
- هذه العلامة حاليًا لا تفعل شيئًا في لينكس. ومع ذلك، باستخدام هذه العلامة، يمكن للتطبيقات ضمان حصولها بشكل شفاف على الدعم إذا نُفذت العلامة في المستقبل. وبذلك، تُستخدم في تنفيذ الخيوط في glibc لمراعاة حقيقة أن بعض المعماريات قد تتطلب (لاحقًا) معاملة خاصة لتخصيصات الكدسة. سبب آخر لاستخدام هذه العلامة هو قابلية النقل: فالعلامة MAP_STACK موجودة (ولها تأثير) في بعض الأنظمة الأخرى (مثل بعض أنظمة BSD).
- MAP_SYNC (منذ لينكس 4.15)
- تتوفر هذه العلامة فقط مع نوع التخطيط MAP_SHARED_VALIDATE؛ وستتجاهل التخطيطات من نوع MAP_SHARED هذه العلامة بصمت. تُدعم هذه العلامة فقط للملفات التي تدعم DAX (التخطيط المباشر للذاكرة المستديمة). أما للملفات الأخرى، فإن إنشاء تخطيط بهذه العلامة يؤدي لخطأ EOPNOTSUPP.
- توفر تخطيطات الملفات المشتركة بهذه العلامة ضمانًا بأنه بينما تكون بعض الذاكرة مخططة كقابلة للكتابة في مساحة عناوين العملية، فإنها ستكون مرئية في نفس الملف عند نفس الإزاحة حتى بعد تعطل النظام أو إعادة تشغيله. وبالتزامن مع استخدام تعليمات CPU المناسبة، يوفر هذا لمستخدمي هذه التخطيطات طريقة أكثر كفاءة لجعل تعديلات البيانات مستديمة.
- MAP_UNINITIALIZED (منذ لينكس 2.6.33)
- عدم مسح الصفحات المجهولة. تهدف هذه العلامة لتحسين الأداء في الأجهزة المدمجة. لا تُحترم هذه العلامة إلا إذا ضُبطت النواة بخيار CONFIG_MMAP_ALLOW_UNINITIALIZED. وبسبب الآثار الأمنية، لا يُفعل هذا الخيار عادةً إلا في الأجهزة المدمجة (أي الأجهزة التي يمتلك فيها الشخص تحكمًا كاملاً في محتويات ذاكرة المستخدم).
من بين العلامات المذكورة أعلاه، حُددت MAP_FIXED فقط في POSIX.1-2001 و POSIX.1-2008. ومع ذلك، تدعم معظم الأنظمة أيضًا MAP_ANONYMOUS (أو مرادفها MAP_ANON).
munmap()¶
يحذف استدعاء النظام munmap() التخطيطات لنطاق العناوين المحدد، ويؤدي إلى أن تولد المراجع اللاحقة للعناوين داخل النطاق مراجع ذاكرة غير صالحة. كما يُفصل النطاق آليًا عند إنهاء العملية. ومن ناحية أخرى، فإن إغلاق واصف الملف لا يفصل النطاق.
يجب أن يكون العنوان addr مضاعفًا لحجم الصفحة (ولكن length لا يشترط ذلك). تُفصل جميع الصفحات التي تحتوي على جزء من النطاق المشار إليه، وستولد المراجع اللاحقة لهذه الصفحات إشارة SIGSEGV. ولا يعتبر خطأً إذا لم يحتوي النطاق المشار إليه على أي صفحات مخططة.
قيمة الإرجاع¶
عند النجاح، تعيد mmap() مؤشرًا إلى المنطقة المخططة. وعند حدوث خطأ، تُعاد القيمة MAP_FAILED (أي (void *) -1)، وتُضبط errno للإشارة إلى الخطأ.
عند النجاح، تعيد الدالة munmap() القيمة 0. وعند الفشل، تعيد -1، وتُضبط قيمة errno لتبين الخطأ (غالبًا إلى EINVAL).
الأخطاء¶
- EACCES
- واصف الملف يشير إلى ملف غير منتظم. أو طُلب تخطيط ملف، ولكن fd ليس مفتوحًا للقراءة. أو طُلب MAP_SHARED وضُبطت PROT_WRITE، ولكن fd ليس مفتوحًا في وضع القراءة/الكتابة (O_RDWR). أو ضُبطت PROT_WRITE، ولكن الملف مخصص للإلحاق فقط.
- EAGAIN
- قُفل الملف، أو قُفلت مساحة كبيرة جدًا من الذاكرة (انظر setrlimit(2)).
- EBADF
- fd ليس واصف ملف صالحًا (ولم تُضبط MAP_ANONYMOUS).
- EEXIST
- حُددت الراية MAP_FIXED_NOREPLACE في flags، ويتعارض النطاق الذي يغطيه addr و length مع تخطيط موجود.
- EINVAL
- قيم addr أو length أو offset غير مقبولة (على سبيل المثال، هي كبيرة جدًا، أو غير محاذية لحدود الصفحة).
- EINVAL
- (منذ لينكس 2.6.12) كانت قيمة length هي 0.
- EINVAL
- لم تتضمن flags أيًا من MAP_PRIVATE أو MAP_SHARED أو MAP_SHARED_VALIDATE.
- ENFILE
- وُصل إلى الحد الأقصى لإجمالي عدد الملفات المفتوحة على مستوى النظام.
- ENODEV
- نظام ملفات الملف المحدد لا يدعم تخطيط الذاكرة.
- ENOMEM
- لا توجد ذاكرة متاحة.
- ENOMEM
- تجاوز العملية للعدد الأقصى من التخطيطات. يمكن أن يحدث هذا الخطأ أيضًا مع munmap() عند إلغاء تخطيط منطقة في منتصف تخطيط موجود، لأن هذا ينتج عنه تخطيطان أصغر على جانبي المنطقة التي أُلغي تخطيطها.
- ENOMEM
- (منذ لينكس 4.7) سيتم تجاوز حد RLIMIT_DATA للعملية، الموصوف في getrlimit(2).
- ENOMEM
- رُفض العنوان addr لأنه يتجاوز فضاء العناوين الافتراضية للمعالج.
- EOVERFLOW
- في معمارية 32 بت مع امتداد الملفات الكبيرة (أي باستخدام off_t ذي 64 بت): عدد الصفحات المستخدمة لـ length بالإضافة لعدد الصفحات المستخدمة لـ offset سيؤدي لفيض في unsigned long (32 بت).
- EPERM
- تطلب المعطاة prot الصلاحية PROT_EXEC لكن المنطقة المخططة تنتمي إلى ملف في نظام ملفات وُصل بخيار منع التنفيذ no-exec.
- EPERM
- مُنعت العملية بواسطة ختم ملف؛ انظر fcntl(2).
- EPERM
- حُددت الراية MAP_HUGETLB، لكن المستدعِي caller ليس لديه امتيازات (لا يملك القدرة CAP_IPC_LOCK) وليس عضوًا في المجموعة hugetlb_shm_group؛ انظر وصف /proc/sys/vm/hugetlb_shm_group في proc_sys_vm(5).
- ETXTBSY
- ضُبطت MAP_DENYWRITE ولكن الكائن المحدد بواسطة fd مفتوح للكتابة.
يمكن أن يؤدي استخدام منطقة مخططة إلى هذه الإشارات:
السمات¶
للاطلاع على شرح للمصطلحات المستخدمة في هذا القسم، انظر attributes(7).
| الواجهة | السمة | القيمة |
| mmap(), munmap() | سلامة الخيوط | MT-Safe |
الإصدارات¶
في بعض معماريات العتاد (مثل i386)، تتضمن الصلاحية PROT_WRITE ضمنيًا PROT_READ. ويعتمد الأمر على المعمارية فيما إذا كانت PROT_READ تتضمن PROT_EXEC أم لا. يجب على البرامج المنقولة تعيين PROT_EXEC دائمًا إذا كانت تنوي تنفيذ كود في التخطيط الجديد.
الطريقة المنقولة لإنشاء تخطيط هي تحديد addr بقيمة 0 (NULL)، وحذف MAP_FIXED من flags. في هذه الحالة، يختار النظام عنوان التخطيط؛ ويُختار العنوان بحيث لا يتعارض مع أي تخطيط موجود، ولن يكون 0. إذا حُددت الراية MAP_FIXED وكان addr هو 0 (NULL)، فسيكون العنوان المخطط 0 (NULL).
تُعرف ثوابت معينة في flags فقط إذا عُرفت وحدات ماكرو لاختبار الميزات المناسبة (ربما بشكل مبدئي آلي): _DEFAULT_SOURCE مع glibc 2.19 أو أحدث؛ أو _BSD_SOURCE أو _SVID_SOURCE في glibc 2.19 وما قبله. (استخدام _GNU_SOURCE يكفي أيضًا، وكان طلب هذا الماكرو تحديدًا أكثر منطقية، لأن هذه الرايات كلها خاصة بلينكس). الرايات ذات الصلة هي: MAP_32BIT، و MAP_ANONYMOUS (ومرادفها MAP_ANON)، و MAP_DENYWRITE، و MAP_EXECUTABLE، و MAP_FILE، و MAP_GROWSDOWN، و MAP_HUGETLB، و MAP_LOCKED، و MAP_NONBLOCK، و MAP_NORESERVE، و MAP_POPULATE، و MAP_STACK.
الاختلافات بين مكتبة C والنواة¶
تصف هذه الصفحة الواجهة التي توفرها دالة الغلاف mmap() في glibc. في الأصل، كانت هذه الدالة تستدعي نداء نظام بنفس الاسم. ومنذ لينكس 2.4، حل نداء النظام mmap2(2) محل هذا النداء، وحاليًا تستدعي دالة غلاف mmap() في glibc نداء mmap2(2) مع قيمة معدلة لـ offset.
المعايير¶
POSIX.1-2024.
التاريخ¶
SVr4, 4.4BSD, SUSv1, POSIX.1-1996.
في أنظمة POSIX التي تتوفر فيها mmap() و msync(2) و munmap()، يُعرَّف _POSIX_MAPPED_FILES في <unistd.h> بقيمة أكبر من 0. (انظر أيضًا sysconf(3)).
ملاحظات¶
تُحفظ الذاكرة المخططة بواسطة mmap() عبر fork(2)، بنفس السمات.
يُخطط الملف بمضاعفات حجم الصفحة. بالنسبة للملف الذي لا يمثل طوله مضاعفًا لحجم الصفحة، تُصفّر البايتات المتبقية في الصفحة الجزئية في نهاية التخطيط عند التخطيط، ولا تُكتب التعديلات على تلك المنطقة في الملف. إن تأثير تغيير حجم الملف الأساسي للتخطيط على الصفحات التي تقابل المناطق المضافة أو المزالة من الملف غير محدد.
يمكن للتطبيق تحديد الصفحات من التخطيط الموجودة حاليًا في خبيئة Cache الصفحة/المخزن المؤقت باستخدام mincore(2).
استخدام MAP_FIXED بأمان¶
الاستخدام الآمن الوحيد لـ MAP_FIXED هو عندما يكون نطاق العناوين المحدد بواسطة addr و length قد حُجز مسبقًا باستخدام تخطيط آخر؛ وبخلاف ذلك، فإن استخدام MAP_FIXED يعد خطرًا لأنه يزيل التخطيطات الموجودة مسبقًا قسرًا، مما يسهل على العملية متعددة الخيوط إفساد فضاء عناوينها الخاص.
على سبيل المثال، افترض أن الخيط A يبحث في /proc/pid/maps لتحديد موقع نطاق عناوين غير مستخدم يمكنه تخطيطه باستخدام MAP_FIXED، بينما يحصل الخيط B في الوقت نفسه على جزء من نفس نطاق العناوين هذا أو كله. عندما يستخدم الخيط A لاحقًا mmap(MAP_FIXED)، فإنه سيمحو فعليًا التخطيط الذي أنشأه الخيط B. في هذا السيناريو، لا يحتاج الخيط B لإنشاء تخطيط مباشرة؛ إذ يكفي مجرد إجراء نداء لمكتبة تستخدم داخليًا dlopen(3) لتحميل مكتبة مشتركة أخرى. سيقوم نداء dlopen(3) بتخطيط المكتبة في فضاء عناوين العملية. علاوة على ذلك، يمكن تنفيذ أي نداء مكتبة تقريبًا بطريقة تضيف تخطيطات ذاكرة إلى فضاء العناوين، إما بهذه التقنية أو بمجرد تخصيص الذاكرة. تشمل الأمثلة brk(2)، و malloc(3)، و pthread_create(3)، ومكتبات PAM http://www.linux-pam.org.
منذ لينكس 4.17، يمكن لبرنامج متعدد الخيوط استخدام الراية MAP_FIXED_NOREPLACE لتجنب الخطر الموصوف أعلاه عند محاولة إنشاء تخطيط في عنوان ثابت لم يُحجز بواسطة تخطيط موجود مسبقًا.
تغييرات الطوابع الزمنية للتخطيطات المدعومة بملفات¶
بالنسبة للتخطيطات المدعومة بملفات، قد يُحدث الحقل st_atime للملف المخطط في أي وقت بين mmap() وإلغاء التخطيط المقابل؛ وسيقوم أول وصول لصفحة مخططة بتحديث الحقل إذا لم يكن قد حُدث بالفعل.
سيُحدث الحقلان st_ctime و st_mtime لملف مخطط بـ PROT_WRITE و MAP_SHARED بعد الكتابة إلى المنطقة المخططة، وقبل نداء msync(2) لاحق باستخدام الراية MS_SYNC أو MS_ASYNC، إن وجد.
تخطيطات الصفحات الضخمة (Huge TLB)¶
بالنسبة للتخطيطات التي تستخدم صفحات ضخمة، تختلف متطلبات معطيات mmap() و munmap() نوعًا ما عن متطلبات التخطيطات التي تستخدم حجم صفحة النظام الأصلي.
بالنسبة لـ mmap()، يجب أن يكون offset مضاعفًا لحجم الصفحة الضخمة الأساسي. يضبط النظام آليًا length ليكون مضاعفًا لحجم الصفحة الضخمة الأساسي.
بالنسبة لـ munmap()، يجب أن يكون كل من addr و length مضاعفًا لحجم الصفحة الضخمة الأساسي.
تحذيرات¶
على عكس تطبيقات malloc(3) النموذجية، لا يمنع mmap() إنشاء كائنات أكبر من PTRDIFF_MAX. الكائنات الأكبر من PTRDIFF_MAX تعمل بطرق محدودة فقط في لغة C (على وجه الخصوص، يؤدي طرح المؤشرات إلى سلوك غير محدد إذا كانت النتيجة أكبر من PTRDIFF_MAX). علاوة على ذلك، يفترض GCC أيضًا أنه لا يوجد كائن أكبر من PTRDIFF_MAX. عادة ما يكون PTRDIFF_MAX نصف حجم فضاء العناوين؛ لذا بالنسبة للعمليات ذات 32 بت، يكون عادةً 0x7fffffff (حوالي 2 جيجابايت).
العلل¶
في لينكس، لا توجد ضمانات مثل تلك المقترحة أعلاه تحت MAP_NORESERVE. مبدئيًا، يمكن قتل أي عملية في أي لحظة عندما تنفد ذاكرة النظام.
قبل لينكس 2.6.7، لم يكن للراية MAP_POPULATE تأثير إلا إذا حُددت prot بأنها PROT_NONE.
يحدد SUSv3 أن mmap() يجب أن يفشل إذا كانت length هي 0. ومع ذلك، قبل لينكس 2.6.12، كان mmap() ينجح في هذه الحالة: لم يُنشأ أي تخطيط وأعاد النداء القيمة addr. ومنذ لينكس 2.6.12، يفشل mmap() مع الخطأ EINVAL في هذه الحالة.
يحدد POSIX أن النظام سيقوم دائمًا بملء أي صفحة جزئية في نهاية الكائن بالأصفار وأن النظام لن يكتب أبدًا أي تعديل للكائن خارج نهايته. في لينكس، عند كتابة بيانات إلى مثل هذه الصفحة الجزئية بعد نهاية الكائن، تظل البيانات في خبيئة Cache الصفحة حتى بعد إغلاق الملف وإلغاء تخطيطه وبالرغم من أن البيانات لا تُكتب أبدًا في الملف نفسه، إلا أن التخطيطات اللاحقة قد ترى المحتوى المعدل. في بعض الحالات، يمكن إصلاح ذلك باستدعاء msync(2) قبل إلغاء التخطيط؛ ومع ذلك، لا يعمل هذا في tmpfs(5) (على سبيل المثال، عند استخدام واجهة ذاكرة POSIX المشتركة الموثقة في shm_overview(7)).
أمثلة¶
يطبع البرنامج التالي جزءًا من الملف المحدد في معامل سطر الأوامر الأول الخاص به إلى المخرج المعياري. يُحدد نطاق البايتات المراد طباعتها عبر قيم الإزاحة والطول في معاملي سطر الأوامر الثاني والثالث. يُنشئ البرنامج تخطيط ذاكرة للصفحات المطلوبة من الملف ثم يستخدم write(2) لإخراج البايتات المطلوبة.
مصدر البرنامج¶
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
int fd;
char *addr;
off_t offset, pa_offset;
size_t length;
ssize_t s;
struct stat sb;
if (argc < 3 || argc > 4) {
fprintf(stderr, "%s file offset [length]\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY);
if (fd == -1)
err(EXIT_FAILURE, "open");
if (fstat(fd, &sb) == -1) /* To obtain file size */
err(EXIT_FAILURE, "fstat");
offset = atoi(argv[2]);
pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
/* offset for mmap() must be page aligned */
if (offset >= sb.st_size) {
fprintf(stderr, "offset is past end of file\n");
exit(EXIT_FAILURE);
}
if (argc == 4) {
length = atoi(argv[3]);
if (offset + length > sb.st_size)
length = sb.st_size - offset;
/* Can't display bytes past end of file */
} else { /* No length arg ==> display to end of file */
length = sb.st_size - offset;
}
addr = mmap(NULL, length + offset - pa_offset, PROT_READ,
MAP_PRIVATE, fd, pa_offset);
if (addr == MAP_FAILED)
err(EXIT_FAILURE, "mmap");
s = write(STDOUT_FILENO, addr + offset - pa_offset, length);
if (s != length) {
if (s == -1)
err(EXIT_FAILURE, "write");
fprintf(stderr, "partial write");
exit(EXIT_FAILURE);
}
munmap(addr, length + offset - pa_offset);
close(fd);
exit(EXIT_SUCCESS);
}
انظر أيضًا¶
ftruncate(2), getpagesize(2), memfd_create(2), mincore(2), mlock(2), mmap2(2), mprotect(2), mremap(2), msync(2), remap_file_pages(2), setrlimit(2), shmat(2), userfaultfd(2), shm_open(3), shm_overview(7)
أوصاف الملفات التالية في proc(5): /proc/pid/maps، و /proc/pid/map_files، و /proc/pid/smaps.
ب. أ. جالميستر، POSIX.4، دار أورايلي، ص. 128–129 و 389–391.
ترجمة¶
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>
هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.
إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.
| 11 فبراير 2026 | صفحات دليل لينكس 6.17 |