كيف تعمل malloc
الكومة وقوائم الذاكرة الحرة
كيف تعمل malloc درس مجاني في C Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C Academy 4 دروس في المجموع.
ما الذي يفعله malloc فعلًا؟
عند استدعاء malloc(n)، تمنحك مكتبة C مؤشرًا إلى n بايتات قابلة للاستخدام على الأقل. لكن الكومة ليست سوى منطقة من ذاكرة العملية يديرها مخصص الذاكرة نيابةً عنك.
تتمثل مهمة المخصص في إدارة السجلات: تتبع البايتات المستخدمة والحرة، وإعادة استخدام الذاكرة المحررة بكفاءة.
تأتي الكومة من نظام التشغيل
لا ينشئ المخصص الذاكرة من العدم، بل يطلب من نظام التشغيل كتلًا كبيرة عبر استدعاءات النظام مثل brk/sbrk أو mmap.
ثم يقسم تلك الكتل إلى كتل أصغر لتلبية استدعاءات malloc. وطلب الذاكرة من نظام التشغيل مكلف، لذلك يطلب المخصص الذاكرة بكميات كبيرة ويعيد تدويرها.
/* Conceptual: grow the heap by 4096 bytes */
void *base = sbrk(4096);
if (base == (void *)-1) {
/* out of memory */
}sbrk وفاصل البرنامج
ينقل sbrk(n) «فاصل البرنامج» إلى الأعلى بمقدار n بايت، ويعيد الفاصل السابق. وتصبح المنطقة المكشوفة حديثًا مساحة كومة متاحة.
هذه الطريقة خطية وبسيطة، لكنها لا تستطيع بسهولة إعادة الذاكرة الموجودة في الوسط. ويفضل المخصصون الحديثون استخدام mmap للطلبات الكبيرة.
void *prev_break = sbrk(0); /* current break */
sbrk(1024); /* grow by 1 KB */
/* prev_break now points to fresh memory */بيانات تعريف الكتلة
يخزن المخصص لكل عملية تخصيص ترويسة صغيرة بجوار البيانات: حجمها وما إذا كانت حرة. وتتيح هذه الترويسة لـ free العمل باستخدام مؤشر البيانات الذي تعيده إليها فقط.
يشير المؤشر الذي تحصل عليه من malloc إلى ما بعد الترويسة، لذلك تبقى بيانات التعريف مخفية عنك.
typedef struct block {
size_t size;
int free;
struct block *next;
} block_t;المؤشر بعد الترويسة مباشرة
من الحيل الشائعة حساب المؤشرات: يكون مؤشر المستخدم header + 1. وباستخدام مؤشر المستخدم، توجد الترويسة قبلَه بمقدار block_t واحد.
بهذه الطريقة يستعيد free(p) حجم الكتلة التي خصصتها دون أن تمرره إليه بنفسك.
block_t *hdr = (block_t *)user_ptr - 1;
printf("block size = %zu\n", hdr->size);عرض توضيحي لتخطيط ترويسة صغيرة
لنضع ترويسة فوق مخزن مؤقت ثابت ثم نقرأها مجددًا. يوضح ذلك كيفية تقسيم مخصص حقيقي لمنطقة إلى ترويسة وحمولة.
لا تُستخدم أي استدعاءات لنظام التشغيل، لذلك يعمل البرنامج في أي مكان.
#include <stdio.h>
#include <stddef.h>
typedef struct { size_t size; int free; } block_t;
static char buffer[256];
int main(void) {
block_t *h = (block_t *)buffer;
h->size = 64;
h->free = 0;
void *payload = (char *)buffer + sizeof(block_t);
printf("header bytes = %zu\n", sizeof(block_t));
printf("payload offset = %ld\n", (long)((char *)payload - buffer));
printf("size field = %zu\n", h->size);
return 0;
}فكرة القائمة الحرة
تربط كثير من المخصصات الكتل الحرة في قائمة مرتبطة. وعند استدعاء malloc، يجتاز المخصص هذه القائمة بحثًا عن كتلة كبيرة بما يكفي.
وعند استدعاء free، تُعلَّم الكتلة بأنها حرة وتُعاد إلى القائمة لإعادة استخدامها لاحقًا، مما يتجنب طلبًا آخر من نظام التشغيل.
block_t *find_free(block_t *head, size_t size) {
block_t *b = head;
while (b && !(b->free && b->size >= size))
b = b->next;
return b;
}ما الذي يجب أن يفعله free؟
يعثر free(p) على ترويسة p، ويعلّمها بأنها حرة، ويدمجها، من الناحية المثالية، مع الكتل الحرة المجاورة لها (الدمج) لمقاومة التجزئة.
يؤدي استدعاء free مرتين للمؤشر نفسه أو تحرير مؤشر ليس من الكومة إلى سلوك غير محدد، لأن بيانات التعريف تصبح تالفة.
void my_free(void *p) {
if (!p) return;
block_t *hdr = (block_t *)p - 1;
hdr->free = 1;
/* real allocators coalesce neighbors here */
}التجزئة
بمرور الوقت، يؤدي تحرير أحجام مختلفة وتخصيصها إلى ترك فجوات. تعني التجزئة الخارجية وجود ذاكرة حرة، لكنها متناثرة في قطع صغيرة جدًا لتلبية طلب.
أما التجزئة الداخلية فهي المساحة المهدرة داخل كتلة أكبر من الحاجة، وغالبًا ما تنتج عن المحاذاة أو التقريب.
متطلبات المحاذاة
يجب أن يعيد malloc ذاكرةً بمحاذاة مناسبة لأي نوع. ويعني ذلك في معظم الأنظمة ذات 64 بت محاذاةً بمقدار 16 بايت، بما يفي بمتطلبات max_align_t.
قد تتسبب المؤشرات غير المحاذاة في تعطل بعض وحدات المعالجة المركزية أو إبطاء الوصول في وحدات أخرى، لذلك يقرّب المخصصون الحمولات دائمًا إلى حد محاذاة.
#include <stdalign.h>
/* alignof(max_align_t) is the strictest required alignment */
size_t a = alignof(max_align_t);جمع الأجزاء معًا
يحتاج المخصص الأدنى، إذن، إلى: مصدر للذاكرة (مخزن مؤقت ثابت أو sbrk أو mmap)، وترويسات لكل كتلة، واستراتيجية للعثور على المساحة الحرة، ومعالجة المحاذاة.
في الدروس التالية سنبني هذه الأجزاء: مخصصًا متزايدًا أولًا، ثم قوائم حرة، ثم المحاذاة وتقسيم الكتل.
/* The four pillars of a custom allocator */
/* 1. memory source 2. block headers */
/* 3. free-block search 4. alignment */تحقق سريع
اختبر فهمك للأجزاء الداخلية للمخصص.
مراجعة
يدير malloc كومةً يحصل عليها من نظام التشغيل عبر sbrk أو mmap، ويقسمها إلى كتل ذات ترويسات مخفية تتتبع الحجم وحالة التحرير.
تتيح القوائم الحرة إعادة الاستخدام، وتحافظ المحاذاة على ملاءمة الذاكرة لكل نوع، بينما تمثل التجزئة التحدي الأساسي. وتقود هذه الأفكار المخصص الذي سنبنيه لاحقًا.
الأسئلة الشائعة
هل درس «كيف تعمل malloc» مجاني؟
نعم — نص درس «كيف تعمل malloc» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C Academy 4 دروس في المجموع.
ماذا ستتعلم في «كيف تعمل malloc»؟
الكومة وقوائم الذاكرة الحرة تتمرن على C Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C Academy؟
لا تُشترط خبرة سابقة. C Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «كيف تعمل malloc»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C Academy هذا؟
نعم. كل درس في C Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.