---
title: "فهم تطبيقات OTP في Elixir، الجزء 3: الوقت الفعلي (المرن)"
description: "سلسلة قصيرة عن المبادئ الأساسية للغتي Erlang وElixir. يناقش هذا الجزء لماذا تُعدّ Elixir لغة تعمل في الوقت الفعلي المرن (soft real-time)"
canonical: https://oozou.com/ar/blog/understanding-elixir-otp-applications-part-3-soft-real-time-150
locale: ar
date: 2021-01-01
author: "OOZOU Team"
---

# فهم تطبيقات OTP في Elixir، الجزء 3: الوقت الفعلي (المرن)

> أستعرض في هذه السلسلة القصيرة المبادئ الأساسية للغتي Erlang وElixir واحدًا تلو الآخر. وفي هذا المقال أناقش لماذا تُعدّ Elixir لغة تعمل في الوقت الفعلي المرن (soft real-time)

Published: 2021-01-01 · Author: OOZOU Team (Engineering) · 5 min read

ناقشت في [المقال السابق](https://oozou.com/blog/understanding-elixir-otp-applications-part-2-fault-tolerance-138) كيف تتعامل Elixir مع تحمّل الأعطال. والآن سنستعرض الأسباب التي تجعل Erlang وElixir تُعدّان لغتين تعملان في الوقت الفعلي، والآليات التي تمنحهما هذه القدرة.

# ما لغة الوقت الفعلي؟

تعرّفها [Wikipedia](https://en.wikipedia.org/wiki/Real-time_computing) كما يلي:

> يجب أن تضمن برامج الوقت الفعلي الاستجابة ضمن قيود زمنية محددة، يُشار إليها غالبًا باسم "المواعيد النهائية" (deadlines)

في نظام _الوقت الفعلي الصارم_ (hard real-time)، يعني تفويت موعد نهائي واحد فشل النظام بالكامل. أما نظام _الوقت الفعلي المرن_ (soft real-time) فيستطيع أن يضمن الالتزام بمجموعة فرعية من المواعيد النهائية، مع تحسينها بحسب كل نظام. فمثلًا، تُضمن جميع العمليات التي لا تتضمن إدخالًا وإخراجًا (IO) ضمن الموعد النهائي، بينما قد لا تُضمن عمليات IO.
من المهم ملاحظة أن _الوقت الفعلي_ لا يعني السرعة. قد يكون النظام سريعًا، لكن السرعة ليست شرطًا. يشير الوقت الفعلي إلى موثوقية الاستجابة ضمن إطار زمني معين. يجب أن يكون النظام قابلًا للتنبؤ. فإذا كان الموعد النهائي سنة واحدة وأعدنا الاستجابة خلال أسبوع واحد، فالنظام يعمل في الوقت الفعلي.

# لماذا تُعدّ Erlang لغة وقت فعلي؟

فهم نشأة Erlang يساعد على تفسير قرارات التصميم التي اتُّخذت في اللغة. صُمّمت Erlang لبرمجة مقاسم الهاتف. أراد مصمموها استجابات بأجزاء من الثانية، والأهم أنهم أرادوها ثابتة. ولم يريدوا أن تعطّل العمليات الحسابية الطويلة عملياتٍ أخرى يمكن حسابها بشكل أسرع.
وكما رأينا في المقال السابق، إذا فشلت عملية أو فوّتت موعدًا نهائيًا، تُعاد محاولتها. ولهذا تُعدّ Erlang لغة وقت فعلي مرن. فهي لا تحاول أن تكون خالية من الأعطال تمامًا، بل متحمّلة لها فقط.
باختصار، صُمّمت Erlang للتعامل مع عدد كبير من العمليات طويلة العمر لكنها خاملة، فمثلًا تبدأ مكالمة، ثم ينتظر النظام انتهاءها. وأحيانًا تستهلك عملية ما قدرًا أكبر من المعالجة، لكنها لا تعطّل العمليات الأخرى.
هذا النمط من الأنظمة يناسب تمامًا تطبيقات أخرى مثل خوادم الويب متعددة الإرسال (multiplexing). فآلة BEAM قادرة على التبديل السريع بين السياقات (context switching)، والإنشاء السريع للعمليات، والتمرير السريع للرسائل. والعمليات معزولة وغير معطِّلة (non-blocking). وهذا مثالي لـ web-sockets أو للأعداد الكبيرة من الطلبات المتزامنة، أو لكليهما.

# ما قرارات التصميم التي تساعد Erlang على تحقيق متطلبات الوقت الفعلي؟

**إدارة الذاكرة:** في معظم اللغات، توقف عملية جمع المهملات (garbage collection) النظام بأكمله مؤقتًا. وفي الأنظمة التي تعمل لفترات طويلة، قد يؤدي ذلك إلى توقف العمليات المتزامنة حين تنفد الذاكرة. تتبع BEAM نهجًا مختلفًا. فلكل عملية heap خاص بها في الذاكرة يمكن جمع مهملاته على حدة، بمعزل تام عن جميع العمليات الأخرى. وهذا لا يمنع جمع المهملات من تعطيل جميع العمليات فحسب، بل يجعل دورات جمع المهملات أسرع بكثير أيضًا، لأن كل heap أصغر بكثير.
تنقسم البيانات في هذه الـ heaps الخاصة إلى جيلين: جيل شاب وجيل قديم. الجيل الشاب هو البيانات التي أُنشئت منذ آخر دورة لجمع المهملات. والجيل القديم هو البيانات التي نجت من آخر دورة لجمع المهملات.
يعمل GC (جمع المهملات) في كل مرة يتجاوز فيها حجم الـ heap إعداد min_heap_size. وإذا لم تصل عملية قصيرة العمر إلى **min_heap_size**، فلن يعمل GC لتلك العملية أبدًا.
يعمل GC بنمطين: المسح الكامل (full sweep) وجمع المهملات حسب الأجيال (generational). يفحص المسح الكامل عناصر الجيلين الشاب والقديم. أما جمع المهملات حسب الأجيال فيفحص الجيل الشاب، ويجمع ما يمكن التخلص منه، ثم ينقل العناصر المتبقية إلى الـ heap القديم.
في كل مرة يصل فيها حجم الـ heap إلى min_heap_size، يُنفَّذ مسح حسب الأجيال. وإذا تجاوز الـ heap الحد الذي يضبطه [`spawn_opt(fun(),[{fullsweep_after, N}])`](http://erlang.org/doc/man/erlang.html#spawn_opt-4). تحدد هذه الدالة عدد دورات جمع مهملات الجيل الشاب التي يجب تنفيذها قبل تشغيل مسح كامل آخر.
وإذا لم يتمكن GC من تحرير مساحة كافية، يزداد حجم الـ heap تلقائيًا.
**الجدولة في الوقت الفعلي:** تستخدم آلة BEAM الافتراضية مجدولًا استباقيًا (preemptive scheduler)، أي أن كل عملية تُمنح قدرًا صغيرًا من وقت المعالج من دون تعاونها، فالمجدول مثلًا لا يستأذن قبل إيقاف مهمة لاستئناف المهمة التالية أو بدئها. يبدّل المجدول بين السياقات باستمرار، ويستأنف العمليات واحدة تلو الأخرى. وهذا يسمح لجميع العمليات بمواصلة العمل حتى حين تحتاج إحداها إلى عدد كبير من دورات المعالج.
ومع أن المجدول لا يتطلب تعاون المهام، فإنه يجدولها انتقائيًا بحسب الأولوية. ويمكن تحديد الأولوية عند إنشاء العملية باستخدام الدالة [erlang:process_flag/2](http://erlang.org/documentation/doc-5.9.1/erts-5.9.1/doc/html/erlang.html#process_flag-2). توضع العملية في الطابور، ثم يختار المجدول العملية التي سيستأنفها بحسب الأولوية.
ومن العوامل الأخرى في الجدولة ما يُعرف بالاختزالات (Reductions). الاختزال عدّاد يزداد في كل مرة تُستدعى فيها دالة. وهو تقدير لمقدار وقت المعالج المستهلك. يمكن للعملية أن تسجّل وقت نظام التشغيل، لكن ذلك مكلف، لذا فالاختزال تقريب أسرع. في كل مرة تُستأنف فيها عملية، تُمنح "ميزانية" من _الاختزالات_ قدرها 2000. وحين تُنفَّذ 2000 عملية اختزال، يُجري المجدول التبديل التالي بين السياقات.

# الخلاصة

بفضل هذه الآليات، تتميز Erlang وElixir بقابلية عالية للتنبؤ. وهذا يتيح لنا بناء أنظمة موثوقة وقابلة للتوسّع. وفي العادة لا تحتاجون إلى معرفة الكثير عن طريقة عمل ذلك عند استخدام Elixir، لأن كثيرًا منه يُدار نيابةً عنكم.

سنلقي نظرة في الحلقة القادمة على قدرة Elixir على استبدال الكود أثناء التشغيل (hot-swap).

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/ar/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
