تخصيص الذاكرة الديناميكية في لغة C
يشير تخصيص الذاكرة الديناميكي في لغة C إلى إدارة الذاكرة يدويًا لتخصيص الذاكرة الديناميكي في لغة البرمجة C عبر مجموعة من الدوال في مكتبة C القياسية ، وأهمها mallocدوال reallocو callocو aligned_allocو free. [ 1 ] [ 2 ] [ 3 ]
تتضمن لغة البرمجة C++ هذه الدوال؛ ومع ذلك، فإن المعاملين new و delete يوفران وظائف مماثلة ويوصي بهما مطورو تلك اللغة. [ 4 ] ومع ذلك، توجد عدة حالات لا يكون فيها استخدام / مناسبًا، مثل كود جمع البيانات المهملة أو الكود الحساس للأداء، وقد يكون من الضروري استخدام مزيج من المعاملين بدلًا من المعامل ذي المستوى الأعلى.newdeletemallocnewnew
تتوفر العديد من التطبيقات المختلفة لآلية تخصيص الذاكرة الفعلية التي تستخدمها دالة malloc . ويختلف أداؤها من حيث وقت التنفيذ والذاكرة المطلوبة.
الأساس المنطقي
تُدير لغة البرمجة C الذاكرة إما بشكل ثابت أو تلقائي أو ديناميكي . تُخصص المتغيرات ذات المدة الثابتة في الذاكرة الرئيسية، عادةً مع الشيفرة التنفيذية للبرنامج، وتستمر طوال فترة تشغيل البرنامج. أما المتغيرات ذات المدة التلقائية فتُخصص على المكدس، وتُستدعى وتُحذف عند استدعاء الدوال وعودتها. بالنسبة للمتغيرات ذات المدة الثابتة والتلقائية، يجب أن يكون حجم التخصيص ثابتًا وقت الترجمة (باستثناء حالة المصفوفات التلقائية ذات الطول المتغير [ 5 ] ). إذا لم يكن الحجم المطلوب معروفًا إلا وقت التشغيل (على سبيل المثال، عند قراءة بيانات ذات حجم عشوائي من المستخدم أو من ملف على القرص)، فإن استخدام كائنات بيانات ذات حجم ثابت غير كافٍ.
قد يُثير عمر الذاكرة المُخصصة بعض المخاوف. فليست الذاكرة ذات المدة الثابتة ولا الذاكرة ذات المدة التلقائية كافية لجميع الحالات. لا يمكن للبيانات المُخصصة تلقائيًا أن تبقى بعد استدعاءات متعددة للدوال، بينما تبقى البيانات الثابتة طوال مدة تشغيل البرنامج سواءً كانت مطلوبة أم لا. في كثير من الأحيان، يحتاج المبرمج إلى مرونة أكبر في إدارة عمر الذاكرة المُخصصة.
يتم تجاوز هذه القيود باستخدام تخصيص الذاكرة الديناميكي ، حيث تُدار الذاكرة بشكل أكثر وضوحًا (ولكن بمرونة أكبر)، عادةً عن طريق تخصيصها من الكومة (مساحة التخزين الحرة)، وهي منطقة ذاكرة مُهيكلة لهذا الغرض. في لغة C، mallocتُستخدم دالة المكتبة لتخصيص كتلة من الذاكرة في الكومة. يصل البرنامج إلى هذه الكتلة من الذاكرة عبر مؤشر يُعيد mallocقيمة. عندما لا تعود الذاكرة مطلوبة، يُمرر المؤشر إلى freeدالة أخرى تُحرر الذاكرة بحيث يمكن استخدامها لأغراض أخرى.
أشار الوصف الأصلي للغة C إلى أن ` callocand` و`and` cfreeموجودتان في المكتبة القياسية، ولكن ليس `and` . وقُدِّمَ mallocرمزٌ برمجيٌّ لتنفيذ نموذج بسيط لمدير تخزين لنظام يونكس،alloc حيثُ `and` و`and` freeهما وظيفتا واجهة المستخدم، مع استخدام sbrkاستدعاء النظام `system` لطلب الذاكرة من نظام التشغيل. [ 6 ] وتُشير وثائق يونكس، الإصدار السادس، allocإلى freeأن `and` و`and` هما وظيفتا تخصيص الذاكرة على مستوى منخفض. [ 7 ] أما روتينات ` mallocand` و`and` freeبصيغتها الحديثة، فقد وُصِفَت بالكامل في دليل يونكس، الإصدار السابع. [ 8 ] [ 9 ]
توفر بعض المنصات استدعاءات للمكتبات أو الدوال المضمنة التي تسمح بتخصيص الذاكرة ديناميكيًا أثناء التشغيل من مكدس لغة C بدلاً من الكومة (على سبيل المثال alloca()[ 10 ] ). ويتم تحرير هذه الذاكرة تلقائيًا عند انتهاء الدالة المستدعِية.
نظرة عامة على الوظائف
تُعرَّف دوال تخصيص الذاكرة الديناميكية في لغة C في <stdlib.h>ملف رأس ( <cstdlib>ملف رأس في لغة C++). [ 1 ]
| وظيفة | وصف |
|---|---|
malloc | يخصص العدد المحدد من البايتات |
aligned_alloc | يخصص العدد المحدد من البايتات عند المحاذاة المحددة |
realloc | يزيد أو يقلل حجم كتلة الذاكرة المحددة، وينقلها إذا لزم الأمر. |
calloc | يقوم بتخصيص عدد محدد من البايتات وتهيئتها إلى الصفر |
free | يُحرر كتلة الذاكرة المحددة ويعيدها إلى النظام |
الاختلافات بين malloc()وcalloc()
malloc()يأخذ وسيطًا واحدًا (مقدار الذاكرة المراد تخصيصها بالبايت)، بينماcalloc()يأخذ وسيطين - عدد العناصر وحجم كل عنصر.malloc()يقوم فقط بتخصيص الذاكرة، بينماcalloc()يقوم بتخصيص وتعيين البايتات في المنطقة المخصصة إلى الصفر. [ 11 ]
مثال على الاستخدام
إنشاء مصفوفة من عشرة أعداد صحيحة بنطاق تلقائي أمر بسيط في لغة C:
int a [ 10 ];مع ذلك، يُحدد حجم المصفوفة عند وقت الترجمة. إذا رغب المستخدم في تخصيص مصفوفة مماثلة ديناميكيًا دون استخدام مصفوفة متغيرة الطول ، وهو أمر غير مضمون الدعم في جميع تطبيقات C11 ، فيمكن تخصيص مصفوفة باستخدام mallocدالة تُرجع مؤشرًا فارغًا (مما يشير إلى أنه مؤشر إلى منطقة ذات نوع بيانات غير معروف)، والذي يمكن تحويله لأغراض الأمان .
int * a = ( int * ) malloc ( 10 * sizeof ( int ));يقوم هذا بحساب عدد البايتات التي تشغلها عشرة أعداد صحيحة في الذاكرة، ثم يطلب هذا العدد من البايتات من mallocويسند النتيجة إلى مؤشر يسمى a(بسبب بناء جملة C، يمكن استخدام المؤشرات والمصفوفات بشكل متبادل في بعض الحالات).
لأنه mallocقد لا يكون قادراً على تلبية الطلب، فقد يُرجع مؤشراً فارغاً ، ومن الممارسات البرمجية الجيدة التحقق من ذلك:
int * a = ( int * ) malloc ( 10 * sizeof ( int )); if ( ! a ) { fprintf ( stderr , "فشل تخصيص الذاكرة \n " ); return -1 ; }عندما لا يعود البرنامج بحاجة إلى المصفوفة الديناميكية ، يجب عليه في النهاية استدعاء الدالة freeلإعادة الذاكرة التي تشغلها إلى الذاكرة الحرة:
حر ( أ )؛الذاكرة المخصصة mallocغير مهيأة وقد تحتوي على بيانات زائدة : بقايا بيانات مستخدمة ومهملة سابقًا. بعد التخصيص malloc، تصبح عناصر المصفوفة متغيرات غير مهيأة . سيعيد الأمر callocتخصيصًا تم مسحه مسبقًا.
int * a = ( int * ) calloc ( 10 , sizeof ( int ));باستخدام دالة إعادة تخصيص الذاكرة (realloc)، يمكننا تغيير حجم الذاكرة التي يشير إليها المؤشر. على سبيل المثال، إذا كان لدينا مؤشر يعمل كمصفوفة بحجمونريد تغييرها إلى مصفوفة بحجم، يمكننا استخدام إعادة تخصيص الذاكرة.
int * a = ( int * ) malloc ( 2 * sizeof ( int )); a [ 0 ] = 1 ; a [ 1 ] = 2 ; a = ( int * ) realloc ( a , 3 * sizeof ( int )); a [ 2 ] = 3 ;لاحظ أنه يجب افتراض أن عملية إعادة التخصيص (realloc) قد غيّرت العنوان الأساسي للكتلة (أي إذا فشلت في توسيع حجم الكتلة الأصلية، وبالتالي قامت بتخصيص كتلة جديدة أكبر في مكان آخر ونسخت المحتويات القديمة إليها). لذلك، فإن أي مؤشرات إلى عناوين داخل الكتلة الأصلية لم تعد صالحة.
أمان النوع
كما ذُكر سابقًا، mallocتُعيد هذه الدالة مؤشرًا فارغًا ( void pointer void*)، مما يُشير إلى أنه مؤشر إلى منطقة ذات نوع بيانات غير معروف. يُعدّ استخدام التحويل ضروريًا في لغة C++ نظرًا لنظام الأنواع القوي، بينما لا ينطبق هذا على لغة C. يُمكن تحويل هذا المؤشر إلى نوع مُحدد (انظر تحويل الأنواع ).
// بدون تحويل نوع البيانات int * ptr1 = malloc ( 10 * sizeof ( * ptr ));// مع تحويل نوع البيانات int * ptr2 = ( int * ) malloc ( 10 * sizeof ( * ptr ));هناك مزايا وعيوب لإجراء عملية تحويل النوع. يُحسّن تضمين هذه العملية التوافق بين لغتي C و C++، مما يسمح بتجميع أو استخدام كود C كلغة C++. علاوة على ذلك، تسمح عملية التحويل باستخدام إصدارات ما قبل عام 1989 التي mallocكانت تُرجع في الأصل قيمة من نوع char*. [ 12 ] بالإضافة إلى ذلك، يمكن أن تساعد عملية التحويل المطور في تحديد التناقضات في تحديد حجم النوع في حال تغير نوع مؤشر الوجهة، خاصةً إذا تم تعريف المؤشر بعيدًا عن malloc()استدعاء الدالة (على الرغم من أن المترجمات الحديثة والمحللات الثابتة يمكنها التنبيه إلى هذا السلوك دون الحاجة إلى عملية التحويل [ 13 ] ).
مع ذلك، وفقًا لمعيار لغة C، فإن التحويل ليس ضروريًا، وقد يؤدي إضافته إلى إخفاء فشل تضمين ملف الرأس `<header> <stdlib.h>` الذي يحتوي على نموذج الدالة `<type>`. [ 12 ] [ 14 ] في حال عدم وجود نموذج للدالة `<type>` ، يشترط معيار C90 أن يفترض مُصرّف لغة C أن ` <type>` تُرجع قيمة من نوع `<type>`. إذا لم يكن هناك تحويل، يتطلب معيار C90 تشخيصًا عند إسناد هذا العدد الصحيح إلى المؤشر؛ ولكن مع التحويل، لن يُنتج هذا التشخيص، مما يُخفي خطأً برمجيًا. في بعض البنى ونماذج البيانات (مثل LP64 على أنظمة 64 بت، حيث تكون المؤشرات `<type>` و`<type>` من نوع 64 بت و` <type>` من نوع 32 بت)، قد يؤدي هذا الخطأ إلى سلوك غير مُعرّف، حيث تُرجع الدالة المُعلنة ضمنيًا قيمة من نوع 32 بت بينما تُرجع الدالة المُعرّفة فعليًا قيمة من نوع 64 بت. اعتمادًا على اصطلاحات الاستدعاء وتخطيط الذاكرة، قد يؤدي هذا إلى تجاوز سعة المكدس . من غير المرجح أن تمر هذه المشكلة دون ملاحظة في المترجمات الحديثة، لأن معيار C99 لا يسمح بالتصريحات الضمنية، لذا يجب على المترجم إصدار تشخيص حتى لو افترض قيمة الإرجاع. إضافةً إلى ذلك، إذا تم تغيير نوع المؤشر عند تعريفه، فسيتعين تحديث الأسطر التي يتم فيها استدعاء الدالة وعملية التحويل.mallocmallocmallocintlongintmallocintmalloc
في لغة C++، إذا كان std::mallocلا بد من استخدام التحويل، فمن الأفضل static_castاستخدام التحويل المباشر بدلاً من التحويل الخام:
int * p = static_cast < int *> ( malloc ( 10 * sizeof ( * ptr )));الأخطاء الشائعة
قد يكون الاستخدام غير السليم لتخصيص الذاكرة الديناميكية مصدراً متكرراً للأخطاء البرمجية. وتشمل هذه الأخطاء ثغرات أمنية أو أعطالاً في البرامج، وغالباً ما يكون ذلك بسبب أخطاء تجزئة الذاكرة .
أكثر الأخطاء شيوعاً هي كما يلي: [ 15 ]
- عدم التحقق من حالات فشل التخصيص
- لا يُضمن نجاح عملية تخصيص الذاكرة، وقد تُعيد مؤشرًا فارغًا. استخدام القيمة المُعادة دون التحقق من نجاح التخصيص يُؤدي إلى سلوك غير مُحدد . عادةً ما يُؤدي هذا إلى تعطل النظام (بسبب خطأ تجزئة الذاكرة الناتج عن محاولة الوصول إلى المؤشر الفارغ)، ولكن لا يوجد ضمان لحدوث التعطل، لذا فإن الاعتماد على ذلك قد يُؤدي أيضًا إلى مشاكل.
- تسريبات الذاكرة
- يؤدي عدم تحرير الذاكرة المخصصة
freeإلى تراكم ذاكرة غير قابلة لإعادة الاستخدام، والتي لم يعد البرنامج يستخدمها. وهذا يهدر موارد الذاكرة وقد يؤدي إلى فشل التخصيص عند استنفاد هذه الموارد. - الأخطاء المنطقية
- يجب أن تتبع جميع عمليات تخصيص الذاكرة النمط نفسه: التخصيص باستخدام `f`
malloc، ثم الاستخدام لتخزين البيانات، ثم تحرير الذاكرة باستخدام `f`free. يؤدي عدم الالتزام بهذا النمط، مثل استخدام الذاكرة بعد استدعاء `f`free( مؤشر معلق ) أو قبل استدعاء `f`malloc( مؤشر غير صالح )، أو استدعاء `f`freeمرتين ("تحرير مزدوج")، وما إلى ذلك، عادةً إلى حدوث خطأ في تجزئة الذاكرة، مما ينتج عنه تعطل البرنامج. قد تكون هذه الأخطاء عابرة ويصعب تصحيحها؛ فعلى سبيل المثال، لا يستعيد نظام التشغيل الذاكرة المحررة عادةً على الفور، وبالتالي قد تستمر المؤشرات المعلقة لفترة من الوقت وتبدو وكأنها تعمل.
بالإضافة إلى ذلك، وباعتبارها واجهةً تسبق معيار ANSI C، mallocفإنّ وظائفها المرتبطة بها لها سلوكيات تُركت عمدًا للتنفيذ لتحديدها بنفسه. أحد هذه السلوكيات هو تخصيص الذاكرة ذات الطول الصفري، والذي يُعدّ مشكلةً أكبر reallocنظرًا لشيوع تغيير حجم الذاكرة إلى الصفر. [ 16 ] على الرغم من أنّ كلاً من معيار POSIX ومواصفات يونكس الموحدة (Single Unix Specification) يشترطان التعامل السليم مع تخصيصات الذاكرة ذات الحجم الصفري إمّا عن طريق إرجاع قيمة NULLأو أي إجراء آخر يمكن تحريره بأمان، [ 17 ] إلا أنّه ليس مطلوبًا من جميع المنصات الالتزام بهذه القواعد. ومن بين العديد من أخطاء التحرير المزدوج التي أدّت إليها، برزت ثغرة تنفيذ التعليمات البرمجية عن بُعد (RCE) في واتساب عام 2019 بشكلٍ خاص. [ 18 ] إحدى طرق تغليف هذه الدوال لجعلها أكثر أمانًا هي ببساطة التحقق من عمليات تخصيص الذاكرة ذات الحجم 0 وتحويلها إلى عمليات تخصيص بحجم 1. (للإرجاع NULLمشاكله الخاصة: فهو يشير إلى فشل نفاد الذاكرة. في هذه الحالة، reallocكان سيشير إلى أن الذاكرة الأصلية لم تُنقل ولم تُحرر، وهو ما لا يحدث في حالة الحجم 0، مما يؤدي إلى تحرير مزدوج.) [ 19 ]
التطبيقات
يعتمد تنفيذ إدارة الذاكرة بشكل كبير على نظام التشغيل وبنية النظام. توفر بعض أنظمة التشغيل مُخصِّص ذاكرة ديناميكي (malloc)، بينما توفر أنظمة أخرى دوالًا للتحكم في مناطق معينة من البيانات. غالبًا ما يُستخدم مُخصِّص الذاكرة الديناميكي نفسه لتنفيذ كليهما، mallocوكذلك عامل التشغيل newفي لغة C++ . [ 20 ]
قائم على الكومة
كان تنفيذ مُخصِّصات الذاكرة القديمة يتم عادةً باستخدام جزء الكومة . وكان مُخصِّص الذاكرة يقوم عادةً بتوسيع وتقليص الكومة لتلبية طلبات التخصيص.
تعاني طريقة الكومة من بعض العيوب المتأصلة:
- لا يمكن لمخصص الذاكرة الخطي أن يتقلص إلا إذا تم تحرير آخر تخصيص. حتى لو كان حجم الذاكرة غير مستخدم إلى حد كبير، فقد "يعلق" عند حجم كبير جدًا بسبب تخصيص صغير ولكنه طويل الأمد في طرفه، مما قد يهدر أي قدر من مساحة العناوين، على الرغم من أن بعض مخصصات الذاكرة في بعض الأنظمة قد تكون قادرة على تحرير صفحات وسيطة فارغة تمامًا لنظام التشغيل.
- يُعدّ مُخصِّص الذاكرة الخطي حساسًا للتجزئة . سيحاول مُخصِّص الذاكرة الجيد تتبُّع وإعادة استخدام المساحات الفارغة في جميع أنحاء الذاكرة، ولكن مع اختلاط أحجام التخصيص وفترات صلاحيته، قد يكون من الصعب والمكلف العثور على أجزاء فارغة كبيرة بما يكفي لاستيعاب طلبات التخصيص الجديدة أو دمجها.
- يتمتع المخصص الخطي بخصائص تزامن سيئة للغاية، حيث أن مقطع الكومة خاص بكل عملية، ويتعين على كل مؤشر ترابط مزامنته عند التخصيص، كما أن التخصيصات المتزامنة من مؤشرات الترابط التي قد يكون لديها أحمال عمل مختلفة للغاية تضخم المشكلتين السابقتين.
dlmalloc و ptmalloc
قام دوغ ليا بتطوير مكتبة dlmalloc ("Doug Lea's Malloc")، وهي مكتبة عامة متاحة للجميع، كأداة تخصيص ذاكرة للأغراض العامة، بدءًا من عام 1987. وتُشتق مكتبة GNU C (glibc) من مكتبة ptmalloc ("pthreads malloc") الخاصة بولفرام غلوغر، وهي نسخة معدلة من dlmalloc مع تحسينات متعلقة بالخيوط. [ 21 ] [ 22 ] [ 23 ] واعتبارًا من نوفمبر 2023، كان أحدث إصدار من dlmalloc هو الإصدار 2.8.6 الصادر في أغسطس 2012. وفي عام 2023، تم تغيير ترخيصها من CC0 إلى MIT-0 . [ 24 ]
dlmalloc هو مُخصِّص علامات حدودية. تُخصَّص الذاكرة في الكومة على شكل "قطع"، وهي بنية بيانات مُحاذية بحجم 8 بايت تحتوي على رأس وذاكرة قابلة للاستخدام. تحتوي الذاكرة المُخصَّصة على 8 أو 16 بايت إضافية لحجم القطعة وعلامات الاستخدام (على غرار متجه dope ). كما تُخزِّن القطع غير المُخصَّصة مؤشرات إلى قطع أخرى حرة في مساحة الذاكرة القابلة للاستخدام، مما يجعل الحد الأدنى لحجم القطعة 16 بايت على أنظمة 32 بت و24/32 بايت (حسب المحاذاة) على أنظمة 64 بت. [ 22 ] [ 24 ] : 2.8.6، الحد الأدنى لحجم التخصيص
تُجمّع الذاكرة غير المخصصة في " مجموعات " ذات أحجام متقاربة، ويتم ذلك باستخدام قائمة مرتبطة ثنائية من الأجزاء (مع تخزين المؤشرات في المساحة غير المخصصة داخل الجزء). تُصنّف المجموعات حسب الحجم إلى ثلاث فئات: [ 22 ] [ 24 ] : هياكل بيانات متراكبة
- بالنسبة للطلبات التي تقل عن 256 بايت (طلب "صغير الحجم")، يتم استخدام مُخصِّص بسيط مُلائم للقوة الثانية. إذا لم تكن هناك كتل فارغة في تلك الخانة، يتم تقسيم كتلة من الخانة الأعلى التالية إلى قسمين.
- بالنسبة للطلبات التي تبلغ 256 بايت أو أكثر ولكنها أقل من عتبة mmap ، يستخدم dlmalloc منذ الإصدار 2.8.0 خوارزمية شجرة ثنائية موضعية ("treebin"). إذا لم تكن هناك مساحة حرة كافية لتلبية الطلب، يحاول dlmalloc زيادة حجم الكومة، عادةً عبر استدعاء النظام brk . أُضيفت هذه الميزة بعد فترة طويلة من إنشاء ptmalloc (من الإصدار 2.7.x)، وبالتالي فهي ليست جزءًا من glibc، التي ترث مُخصِّص الذاكرة القديم الأنسب.
- بالنسبة للطلبات التي تتجاوز عتبة mmap (طلب "largebin")، يتم تخصيص الذاكرة دائمًا باستخدام استدعاء النظام mmap . عادةً ما تكون العتبة 128 كيلوبايت. [ 25 ] تتجنب طريقة mmap مشاكل المخازن المؤقتة الضخمة التي قد تحبس تخصيصًا صغيرًا في النهاية بعد انتهاء صلاحيتها، ولكنها دائمًا ما تخصص صفحة كاملة من الذاكرة، والتي يبلغ حجمها 4096 بايت في العديد من البنى. [ 26 ]
يرى مطور الألعاب أدريان ستون أن dlmallocمُخصِّص علامات الحدود، باعتباره مُخصِّصًا غير مناسب لأنظمة الألعاب التي تحتوي على ذاكرة افتراضية ولكنها لا تدعم الترحيل حسب الطلب . ويعود ذلك إلى أن وظائف الاستدعاء الخاصة بتقليص وتوسيع مجمع الذاكرة ( sysmalloc/ systrim) لا يمكن استخدامها لتخصيص صفحات فردية من الذاكرة الافتراضية وتثبيتها. وفي غياب الترحيل حسب الطلب، يصبح تجزئة الذاكرة مصدر قلق أكبر. [ 27 ]
jemalloc في FreeBSD و NetBSD
منذ إصداري FreeBSD 7.0 و NetBSD 5.0، استُبدلت النسخة القديمة malloc( phkmallocمن تطوير بول هينينغ كامب ) بـ jemalloc ، التي كتبها جيسون إيفانز. والسبب الرئيسي لذلك هو افتقارها إلى قابلية التوسع phkmallocفي تطبيقات تعدد الخيوط. ولتجنب التنازع على الأقفال، jemallocتستخدم jemalloc "ساحات" منفصلة لكل وحدة معالجة مركزية . وقد أظهرت التجارب التي تقيس عدد عمليات التخصيص في الثانية في تطبيقات تعدد الخيوط أن هذا يجعلها تتوسع خطيًا مع عدد الخيوط، بينما كان أداء كل من phkmalloc وdlmalloc متناسبًا عكسيًا مع عدد الخيوط. [ 28 ]
malloc في نظام OpenBSD
mallocتستخدم OpenBSD في تنفيذ هذه الوظيفة دالة mmap . بالنسبة للطلبات التي يزيد حجمها عن صفحة واحدة، يتم استرداد التخصيص بالكامل باستخدام mmap mmap؛ أما الأحجام الأصغر فتُخصص من مجموعات الذاكرة التي يحتفظ بها النظام mallocضمن عدد من "صفحات التجميع"، والتي يتم تخصيصها أيضًا باستخدام mmap mmap. [ 29 ] عند استدعاء دالة mmap ، يتم تحرير الذاكرة وإلغاء تعيينها من مساحة عناوين العملية باستخدام mmap . صُمم هذا النظام لتحسين الأمان من خلال الاستفادة من ميزات عشوائية تخطيط مساحة العناوين وصفحات الفجوات المُطبقة كجزء من استدعاء النظام في OpenBSD ، وللكشف عن أخطاء الاستخدام بعد التحرير - حيث يتم إلغاء تعيين تخصيص الذاكرة الكبير بالكامل بعد تحريره، مما يؤدي إلى حدوث خطأ تجزئة وإنهاء البرنامج.freemunmapmmap
بدأ مشروع GrapheneOS في البداية بنقل مُخصِّص الذاكرة الخاص بنظام OpenBSD إلى مكتبة Bionic C الخاصة بنظام Android. [ 30 ]
مخزن مالوك
هوارد هو مُخصِّص ذاكرة يهدف إلى تحقيق أداء تخصيص ذاكرة قابل للتوسع. على غرار مُخصِّص الذاكرة في نظام OpenBSD، يستخدم هوارد mmapالذاكرة حصريًا، ولكنه يديرها على شكل كتل بحجم 64 كيلوبايت تُسمى الكتل الفائقة. تُقسَّم كومة هوارد منطقيًا إلى كومة عالمية واحدة وعدد من الكومات الخاصة بكل معالج. بالإضافة إلى ذلك، توجد ذاكرة تخزين مؤقت محلية خاصة بكل خيط يمكنها استيعاب عدد محدود من الكتل الفائقة. من خلال التخصيص فقط من الكتل الفائقة الموجودة في الكومة المحلية الخاصة بكل خيط أو معالج، ونقل الكتل الفائقة شبه الفارغة إلى الكومة العالمية ليتمكن المعالجون الآخرون من إعادة استخدامها، يُحافظ هوارد على انخفاض التجزئة مع تحقيق قابلية توسع شبه خطية مع عدد الخيوط. [ 31 ]
mimalloc
مُخصِّص ذاكرة عام ومُدمج مفتوح المصدر من مايكروسوفت ريسيرش، مع التركيز على الأداء. [ 32 ] تتكون المكتبة من حوالي 11000 سطر من التعليمات البرمجية .
تخصيص الذاكرة المؤقت للخيوط (tcmalloc)
لكل خيط تخزين محلي خاص به للتخصيصات الصغيرة. أما للتخصيصات الكبيرة، فيمكن استخدام mmap أو sbrk . يحتوي TCMalloc ، وهو دالة malloc طورتها جوجل، [ 33 ] على آلية لجمع البيانات المهملة للتخزين المحلي للخيوط غير المستخدمة. يُعتبر TCMalloc أسرع بأكثر من الضعف من ptmalloc في مكتبة glibc للبرامج متعددة الخيوط. [ 34 ] [ 35 ]
داخل النواة
تحتاج نواة نظام التشغيل إلى تخصيص الذاكرة تمامًا كما تفعل برامج التطبيقات. ومع ذلك، غالبًا ما يختلف تنفيذ تخصيص الذاكرة mallocداخل النواة اختلافًا كبيرًا عن التنفيذات المستخدمة في مكتبات لغة C. على سبيل المثال، قد تحتاج مخازن الذاكرة المؤقتة إلى التوافق مع قيود خاصة يفرضها الوصول المباشر إلى الذاكرة (DMA) ، أو قد يتم استدعاء دالة تخصيص الذاكرة من سياق المقاطعة. [ 36 ] وهذا يستلزم mallocتنفيذًا متكاملًا بإحكام مع النظام الفرعي للذاكرة الافتراضية في نواة نظام التشغيل.
تجاوز malloc
نظرًا لأنّ mallocالرموز وما يرتبط بها قد تؤثر بشكل كبير على أداء البرنامج، فليس من غير المألوف استبدال وظائف تطبيق معين بتنفيذات مخصصة مُحسّنة لأنماط تخصيص الذاكرة الخاصة بالتطبيق. لا يُحدد معيار لغة C طريقةً للقيام بذلك، لكن أنظمة التشغيل وجدت طرقًا مختلفةً لتحقيق ذلك من خلال استغلال الربط الديناميكي. إحدى هذه الطرق هي ببساطة ربط مكتبة مختلفة لاستبدال الرموز. طريقة أخرى، مُستخدمة في نظام يونكس الإصدار 3 ، هي إنشاء مؤشرات mallocللوظائف freeيمكن للتطبيق إعادة تعيينها إلى وظائف مخصصة. [ 37 ]
في شفرة المصدر، يمكن استبدال الدالة بسهولة باستخدام وحدات الماكرو للمعالجة المسبقة، مثل:
#define malloc custom_malloc #define realloc custom_realloc #تعريف مجاني مخصص
والتي تُعرَّف في ترويسة يجب أن تتضمنها جميع الملفات التي تستخدم دوال تخصيص الذاكرة. ينبغي لهذه الماكروات أيضًا أن تحل محل الدالة strdup، نظرًا لأن نتيجتها تُمرَّر إليها freeأيضًا. لا تزال البيانات المُخصَّصة داخل دوال المكتبة (مثل FILEالهياكل في stdio.h) تُعالَج مباشرةً دون استخدام الأغلفة.
الشكل الأكثر شيوعًا في الأنظمة الشبيهة بـ POSIX هو تعيين متغير البيئة LD_PRELOAD بمسار المخصص، بحيث يستخدم الرابط الديناميكي هذا الإصدار من malloc/calloc/free بدلاً من تنفيذ libc.
حدود حجم التخصيص
يعتمد أكبر حجم ممكن mallocلتخصيص كتلة الذاكرة على النظام المضيف، وخاصة حجم الذاكرة الفعلية وتطبيق نظام التشغيل.
نظريًا، يجب أن يكون أكبر عدد هو القيمة القصوى التي يمكن تخزينها في size_tنوع بيانات، وهو عدد صحيح غير مُوَقَّع يعتمد على التنفيذ، ويمثل حجم منطقة الذاكرة. في معيار C99 والإصدارات اللاحقة، يتوفر هذا العدد كثابت SIZE_MAX. على الرغم من أن معيار ISO C لا يضمن ذلك ، إلا أنه عادةً ما يكون كذلك .<stdint.h>2^(CHAR_BIT * sizeof(size_t)) - 1
في أنظمة glibc، فإن أكبر كتلة ذاكرة mallocيمكن تخصيصها هي نصف هذا الحجم فقط، أي . [ 38 ]2^(CHAR_BIT * sizeof(ptrdiff_t) - 1) - 1
في الأنظمة التي تستخدم التجزئة ، مثل بعض نماذج الذاكرة في نظام MS-DOS ، فإن أكبر كتلة ذاكرة ممكنة عادة ما تكون مقطعًا واحدًا أو أقل، وبالتالي فهي أصغر بكثير من مساحة العناوين الإجمالية، وقد size_tتكون من نوع أصغر من ذلك ptrdiff_t.
الإضافات والبدائل
قد تتضمن تطبيقات مكتبة لغة C المرفقة مع أنظمة التشغيل والمترجمات المختلفة بدائل وامتدادات للواجهة القياسية malloc. ومن أبرزها:
allocaتُخصص هذه الدالة عددًا محددًا من البايتات على مكدس الاستدعاءات . لا توجد دالة مُقابلة لإلغاء التخصيص، حيث يتم عادةً إلغاء تخصيص الذاكرة بمجرد انتهاء الدالة المُستدعِية.allocaكانت هذه الدالة موجودة في أنظمة يونكس منذ الإصدار 32/V (1978)، ولكن استخدامها قد يُسبب مشاكل في بعض السياقات (مثل الأنظمة المُدمجة). [ 39 ] على الرغم من دعمها من قِبل العديد من المُترجمات، إلا أنها ليست جزءًا من معيار ANSI-C ، وبالتالي قد لا تكون قابلة للنقل دائمًا. كما قد تُسبب مشاكل طفيفة في الأداء: فهي تؤدي إلى إطارات مكدس متغيرة الحجم، مما يستلزم إدارة كل من مؤشرات المكدس والإطار (مع إطارات المكدس ثابتة الحجم، يكون أحدهما زائدًا). [ 40 ] قد تُؤدي عمليات التخصيص الأكبر أيضًا إلى زيادة خطر السلوك غير المُحدد بسبب تجاوز سعة المكدس . [ 41 ] قدم معيار C99 مصفوفات متغيرة الطول كآلية بديلة لتخصيص المكدس ، ولكن تم تقليص هذه الميزة إلى ميزة اختيارية في معيار C11 اللاحق .- يُعرّف معيار POSIX دالةً
posix_memalignتُخصّص الذاكرة بمحاذاة يُحدّدها المُستدعي. ويتم تحرير هذه الذاكرة المُخصّصة باستخدام [ 42 ]free، لذا عادةً ما يكون تنفيذ هذه الدالة جزءًا من مكتبة malloc.
انظر أيضاً
مراجع
- 1 2 7.20.3 وظائف إدارة الذاكرة (ملف PDF) . مواصفات ISO/IEC 9899:1999 (تقرير فني). صفحة 313.
- ↑ سوميت، ستيف. "الفصل 11: تخصيص الذاكرة" . ملاحظات برمجة لغة سي . تم الاسترجاع في 11 يوليو 2020 .
- ↑ "aligned_alloc(3) - صفحة دليل لينكس" .
- ↑ ستروستروب، بيارن (2008). البرمجة: المبادئ والتطبيق باستخدام لغة C++ . أديسون ويسلي. ص 1009. ISBN 978-0-321-54372-1.
- ↑ "دليل gcc" . gnu.org . تم الاطلاع عليه بتاريخ 14-12-2008 .
- ↑ برايان دبليو. كيرنيغان، دينيس إم. ريتشي، لغة البرمجة سي ، برنتيس هول، 1978؛ القسم 7.9 (الصفحة 156) يصف
callocوcfree، والقسم 8.7 (الصفحة 173) يصف تطبيقًا لـallocوfree. - ↑ – دليل مبرمج يونكس الإصدار 6
- ↑ – دليل مبرمج يونكس الإصدار 7
- ↑ مجهول، دليل مبرمج يونكس، المجلد 1 ، هولت راينهارت ووينستون، 1983 (حقوق الطبع محفوظة لمختبرات بيل للهاتف، 1983، 1979)؛تم ذكر
manالصفحة الخاصة بـmalloc - ↑ – دليل وظائف مكتبة FreeBSD
- ↑ – دليل مبرمج لينكس – وظائف المكتبة
- 1 2 "تحويل malloc" . Cprogramming.com . تم الاسترجاع في 2007-03-09 .
- ↑ "clang: lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp ملف المصدر" . clang.llvm.org . تم الاطلاع عليه بتاريخ 2018-04-01 .
- ↑ "قائمة الأسئلة الشائعة حول لغة C، السؤال 7.7ب" . C-FAQ . تم الاطلاع عليه بتاريخ 9 مارس 2007 .
- ↑ ريك، كينيث (4 أغسطس 1997). مؤشرات على لغة سي ( الطبعة الأولى). بيرسون. رقم ISBN 9780673999863.
- ↑ "MEM04-C. احذر من تخصيصات الذاكرة ذات الطول الصفري - معيار ترميز SEI CERT C - Confluence" . wiki.sei.cmu.edu .
- ↑ "POSIX.1-2017: malloc" . pubs.opengroup.org . تم الاطلاع عليه بتاريخ 29-11-2019 .
- ↑ Awakened (2019-10-02). "كيف يتحول خطأ التحرير المزدوج في واتساب إلى تنفيذ تعليمات برمجية عن بُعد" . تم الاطلاع عليه بتاريخ 29-11-2019 .
- ↑ فيلكر، ريتش [@RichFelker] (2019-10-03). "يا للعجب! كان تنفيذ التعليمات البرمجية عن بُعد في واتساب سلوكًا خاطئًا لدالة realloc(p,0) التي تُصرّ عليها العديد من التطبيقات" ( تغريدة ) . تم الاطلاع عليها بتاريخ 2022-08-06 – عبر تويتر .
- ↑ ألكسندرسكو، أندريه (2001). تصميم لغة C++ الحديثة: البرمجة العامة وتطبيقات أنماط التصميم . أديسون-ويسلي. ص 78.
- ↑ "الصفحة الرئيسية لـ malloc الخاصة بـ Wolfram Gloger" . malloc.de . تم الاطلاع عليها بتاريخ 1 أبريل 2018 .
- 1 2 3 كايمبف، ميشيل (2001). "حيل Vudo malloc" . Phrack (57): 8. مؤرشف من الأصل في 22-01-2009 . تم الاسترجاع في 29-04-2009 .
- ↑ "Glibc: Malloc Internals" . sourceware.org Trac . تم الاسترجاع في 1 ديسمبر 2019 .
- 1 2 3 لي، دوغ. "مخصص الذاكرة" . تم الاسترجاع في 2019-12-01 .بروتوكول HTTP لشفرة المصدر
- ↑ "معاملات تخصيص الذاكرة القابلة للضبط" . جنو . تم الاسترجاع في 2009-05-02 .
- ↑ ساندرسون، بروس (12-12-2004). "ذاكرة الوصول العشوائي، والذاكرة الافتراضية، وملف الترحيل، وكل ما يتعلق بذلك" . مساعدة ودعم مايكروسوفت.
- ↑ ستون، أدريان. "الفجوة التي لا يستطيع dlmalloc ملؤها" . Game Angst . تم الاسترجاع في 1 ديسمبر 2019 .
- ↑ إيفانز، جيسون (16 أبريل 2006). "تنفيذ متزامن وقابل للتوسع لدالة malloc(3) لنظام FreeBSD" (ملف PDF) . تم الاطلاع عليه بتاريخ 18 مارس 2012 .
- ↑ "libc/stdlib/malloc.c" . مرجع BSD المتقاطع، OpenBSD src/lib/ .
- ↑ "التاريخ | GrapheneOS" . grapheneos.org . تم الاطلاع عليه بتاريخ 2023-03-02 .
- ↑ بيرغر، إي دي؛ ماكينلي، كيه إس ؛ بلوموف، آر دي؛ ويلسون، بي آر (نوفمبر 2000). هورد: مُخصِّص ذاكرة قابل للتوسع للتطبيقات متعددة الخيوط (ملف PDF) . ASPLOS -IX. وقائع المؤتمر الدولي التاسع حول الدعم المعماري للغات البرمجة وأنظمة التشغيل . الصفحات 117-128 . CiteSeerX 10.1.1.1.4174 . doi : 10.1145/378993.379232 . ISBN 1-58113-317-0.
- ↑ "أصدرت مايكروسوفت دالة malloc() المُحسّنة كمصدر مفتوح - سلاش دوت" . مؤرشف من الأصل بتاريخ 14 يونيو 2023. تم الاطلاع عليه بتاريخ 4 يوليو 2020 .
- ↑ الصفحة الرئيسية لـ TCMalloc
- ↑ غيماوات، سانجاي؛ ميناج، بول؛ TCMalloc : تخصيص الذاكرة المؤقت للخيوط
- ↑ كالاغان، مارك (18 يناير 2009). "توافر عالٍ لـ MySQL: مضاعفة إنتاجية sysbench باستخدام TCMalloc" . Mysqlha.blogspot.com . تم الاطلاع عليه بتاريخ 18 سبتمبر 2011 .
- ↑ "kmalloc()/kfree() include/linux/slab.h" . People.netfilter.org . تم الاطلاع عليه بتاريخ 18-09-2011 .
- ↑ ليفين، جون ر. (2000) [أكتوبر 1999]. "الفصل 9: المكتبات المشتركة". الروابط والمحملات . سلسلة مورغان كوفمان في هندسة البرمجيات والبرمجة ( الطبعة الأولى). سان فرانسيسكو، الولايات المتحدة الأمريكية: مورغان كوفمان . ISBN 1-55860-496-0. OCLC 42413382 . تم الاسترجاع في 12 يناير 2020 .
{{cite book}}: CS1 maint: deprecated archiveal service ( link ) Code:تصحيحات: - ↑ "malloc: اجعل malloc يفشل مع الطلبات الأكبر من PTRDIFF_MAX" . Sourceware Bugzilla . 2019-04-18 . تم الاسترجاع في 2020-07-30 .
- ↑ "لماذا لا يُعتبر استخدام دالة alloca() ممارسة جيدة؟" . stackoverflow.com . تم الاطلاع عليه بتاريخ 2016-01-05 .
- ↑ أماراسينغ، سامان؛ ليسرسون، تشارلز (2010). "6.172 هندسة أداء أنظمة البرمجيات، المحاضرة 10" . MIT OpenCourseWare . معهد ماساتشوستس للتكنولوجيا. مؤرشف من الأصل بتاريخ 22-06-2015 . تم الاطلاع عليه بتاريخ 27-01-2015 .
- ↑ "alloca(3) - صفحة دليل لينكس" . man7.org . تم الاطلاع عليه بتاريخ 2016-01-05 .
- ↑ – مرجع واجهات النظام، مواصفات يونكس الموحدة ، الإصدار 5 من مجموعة Open Group
روابط خارجية
- تعريف دالة malloc في معيار IEEE Std 1003.1
- ليا، دوغ ؛ تصميم أساس مُخصِّص الذاكرة في مكتبة glibc
- جلوجر، ولفرام. الصفحة الرئيسية بتمالوك
- بيرغر، إيمري؛ الصفحة الرئيسية لموقع ذا هورد
- دوغلاس، نيال؛ الصفحة الرئيسية لموقع nedmalloc
- إيفانز، جيسون؛ الصفحة الرئيسية لموقع جيمالوك
- جوجل؛ الصفحة الرئيسية لـ tcmalloc
- خوارزميات بسيطة لتخصيص الذاكرة على مجتمع OSDEV
- مايكل، ماجد م.؛ تخصيص الذاكرة الديناميكي القابل للتوسع بدون تأمين
- بارتليت، جوناثان؛ إدارة الذاكرة من الداخل - الخيارات والمفاضلات وتطبيقات التخصيص الديناميكي
- صفحة ويكي تقليل الذاكرة (GNOME) تحتوي على الكثير من المعلومات حول إصلاح malloc
- مسودة معيار C99، بما في ذلك TC1/TC2/TC3
- بعض المراجع المفيدة حول لغة C
- ISO/IEC 9899 – لغات البرمجة – C
- فهم وظيفة malloc في مكتبة glibc
- إدارة الذاكرة
- برنامج إدارة الذاكرة
- مكتبة C القياسية
- لغة سي++
