الحقيقة الكاملة عن RTOS. المادة رقم 10. المجدول: الميزات المتقدمة والحفاظ على السياق



في مقال سابق ، نظرنا إلى أنواع التخطيط المختلفة التي يدعمها نظام RTOS والقدرات ذات الصلة في Nucleus SE. في هذه المقالة ، سنلقي نظرة على خيارات التخطيط الإضافية في Nucleus SE وعملية حفظ السياق واستعادته.

المقالات السابقة في السلسلة:
المادة رقم 9. المجدول: التنفيذ
المادة رقم 8. Nucleus SE: التصميم الداخلي والنشر
المادة رقم 7. Nucleus SE: مقدمة
المادة رقم 6. خدمات RTOS الأخرى
المادة رقم 5. تفاعل المهام والمزامنة
المادة رقم 4. المهام وتبديل السياق والمقاطعات
المادة رقم 3. المهام والتخطيط
المادة رقم 2. RTOS: البنية ووضع الوقت الحقيقي
المادة رقم 1. RTOS: مقدمة.

ميزات اختيارية


أثناء تطوير Nucleus SE ، جعلت الحد الأقصى من الوظائف اختياريًا ، مما يوفر على الذاكرة و / أو الوقت.

تعليق المهام


كما ذكرنا سابقًا في برنامج الجدولة: مقالة التنفيذ ، يدعم Nucleus SE العديد من الخيارات لإيقاف المهام مؤقتًا ، ولكن هذه الميزة اختيارية ويتم تضمينها بواسطة رمز NUSE_SUSPEND_ENABLE في nuse_config.h . إذا تم الضبط على TRUE ، فسيتم تعريف بنية البيانات على أنها NUSE_Task_Status [] . ينطبق هذا النوع من التعليق على جميع المهام. الصفيف من النوع U8 ، حيث يتم استخدام 2 قضم بشكل منفصل. تحتوي البتات السفلية الأربعة على حالة المهمة:
NUSE_READY ، NUSE_PURE_SUSPEND ، NUSE_SLEEP_SUSPEND ، NUSE_MAILBOX_SUSPEND ، إلخ. إذا تم تعليق المهمة بواسطة مكالمة API (على سبيل المثال ، NUSE_MAILBOX_SUSPEND ) ، فإن البتات العالية الأربعة تحتوي على فهرس الكائن الذي تم تعليق المهمة عليه. يتم استخدام هذه المعلومات عندما يصبح المورد متاحًا وللاتصال بواجهة برمجة التطبيقات ، يلزمك معرفة المهام المعلقة التي يجب استئنافها.

لتنفيذ تعليق المهمة ، يتم استخدام زوج من وظائف الجدولة: NUSE_Suspend_Task () و NUSE_Wake_Task () .

رمز NUSE_Suspend_Task () كما يلي:



تحفظ الوظيفة الحالة الجديدة للمهمة (كل 8 بتات) ، التي تم الحصول عليها كمعلمة suspend_code. عند تمكين القفل (انظر "قفل مكالمات API" أدناه) ، يتم حفظ رمز الإرجاع NUSE_SUCCESS . بعد ذلك ، يتم استدعاء NUSE_Reschedule () لنقل التحكم إلى المهمة التالية.

كود NUSE_Wake_Task () بسيط للغاية:



يتم تعيين حالة المهمة إلى NUSE_READY . إذا لم يتم استخدام جدولة الأولوية ، فستستمر المهمة الحالية في شغل المعالج حتى يحين الوقت لتحرير المورد. إذا تم استخدام جدولة الأولوية ، يتم استدعاء NUSE_Reschedule () مع فهرس المهام كدليل على الاكتمال ، حيث قد تكون للمهمة أولوية أعلى ويجب تنفيذها على الفور.

مكالمات API قفل


يدعم Nucleus RTOS عددًا من مكالمات API التي يمكن للمطور من خلالها إيقاف (حظر) المهمة مؤقتًا في حالة عدم توفر الموارد. سيتم استئناف المهمة عندما تتوفر الموارد مرة أخرى. يتم تنفيذ هذه الآلية أيضًا في Nucleus SE ويمكن تطبيقها على عدد من كائنات kernel: يمكن حظر مهمة في قسم الذاكرة أو في مجموعة حدث أو صندوق بريد أو قائمة انتظار أو قناة أو إشارة. ولكن ، مثل معظم الأدوات في Nucleus SE ، فهي اختيارية ومحددة برمز NUSE_BLOCKING_ENABLE في nuse_config.h . إذا تم الضبط على TRUE ، فسيتم تعريف الصفيف NUSE_Task_Blocking_Return [] ، الذي يحتوي على رمز الإرجاع لكل مهمة ؛ يمكن أن يكون NUSE_SUCCESS أو الرمز NUSE_MAILBOX_WAS_RESET ، مما يشير إلى أن الكائن تم إعادة تعيينه عندما تم قفل المهمة. عند تشغيل القفل ، يتم تضمين الرمز المقابل في وظائف واجهة برمجة التطبيقات باستخدام الترجمة الشرطية.

عداد المجدول


يحسب Nucleus RTOS عدد مرات جدولة المهمة منذ إنشائها وإعادة تعيينها آخر مرة. يتم تنفيذ هذه الميزة أيضًا في Nucleus SE ، ولكنها اختيارية ومحددة برمز NUSE_SCHEDULE_COUNT_SUPPORT في nuse_config.h . إذا تم الضبط على TRUE ، يتم إنشاء صفيف من NUSE_Task_Schedule_Count [] من النوع U16 ، والذي يخزن عداد كل مهمة في التطبيق.

الحالة الأولية للمهمة


عند إنشاء مهمة في Nucleus RTOS ، يمكنك تحديد حالتها: جاهزة أو متوقفة مؤقتًا. في Nucleus SE ، تكون جميع المهام جاهزة افتراضيًا عند بدء التشغيل. يتيح لك الخيار المحدد برمز NUSE_INITIAL_TASK_STATE_SUPPORT في nuse_config.h تحديد حالة بدء التشغيل. يتم تعريف الصفيف NUSE_Task_Initial_State [] في nuse_config.c ويتطلب تهيئة NUSE_READY أو NUSE_PURE_SUSPEND لكل مهمة في التطبيق.

حفظ السياق


تم تقديم فكرة الحفاظ على سياق المهمة مع أي نوع من برامج الجدولة ، باستثناء RTC (تشغيل حتى الاكتمال) ، في المادة رقم 3 "المهام والجدولة". كما سبق ذكره ، هناك عدة طرق للحفاظ على السياق. بالنظر إلى أن Nucleus SE غير مصمم للمعالجات 32 بت ، اخترت استخدام الجداول ، وليس المكدس ، للحفاظ على السياق.

يتم استخدام صفيف ثنائي الأبعاد من نوع ADDR NUSE_Task_Context [] [] لحفظ السياق لكافة المهام في التطبيق. الصفوف هي NUSE_TASK_NUMBER (عدد المهام في التطبيق) ، والأعمدة هي NUSE_REGISTERS (عدد السجلات التي يجب حفظها ؛ يعتمد على المعالج ويتم تعيينه على nuse_types.h) .

بالطبع ، يعتمد الحفاظ على السياق واستعادة التعليمات البرمجية على المعالج. وهذا هو رمز Nucleus SE الوحيد المرتبط بجهاز معين (وبيئة التطوير). سأعطيك مثالاً على رمز الحفظ / الاستعادة لمعالج ColdFire. على الرغم من أن هذا الاختيار قد يبدو غريباً بسبب معالج قديم ، إلا أن مجمعه أسهل في القراءة من مجمعات معظم المعالجات الحديثة. الكود بسيط بما يكفي لاستخدامه كأساس لإنشاء تبديل سياق لمعالجات أخرى:



عندما يكون تبديل السياق مطلوبًا ، يتم استدعاء هذا الرمز في NUSE_Context_Swap. يتم استخدام متغيرين : NUSE_Task_Active ، فهرس المهمة الحالية ، التي يجب الحفاظ على سياقها ؛ NUSE_Task_Next ، فهرس المهمة التي تريد تحميل سياقها (راجع قسم البيانات العامة).

تعمل عملية الحفاظ على السياق على النحو التالي:

  • يتم تخزين السجلات A0 و D0 مؤقتًا على المكدس ؛
  • تم تكوين A0 للإشارة إلى صفيف من كتل السياق NUSE_Task_Context [] [] ؛
  • يتم تحميل D0 باستخدام NUSE_Task_Active وضربه في 72 (يحتوي ColdFire على 18 تسجيلًا ، يتطلب 72 بايت للتخزين) ؛
  • ثم تتم إضافة D0 إلى A0 ، والتي تشير الآن إلى كتلة سياق للمهمة الحالية ؛
  • ثم يتم تخزين السجلات في كتلة السياق ؛ أولاً A0 و D0 (من المكدس) ، ثم D1-D7 و A1-A6 ، ثم SR و PC (من المكدس ، سننظر في تبديل السياق الذي بدأ بسرعة) ، وفي النهاية يتم حفظ مؤشر المكدس.

عملية تحميل السياق هي نفس تسلسل الإجراءات بالترتيب العكسي:

  • تم تكوين A0 للإشارة إلى صفيف من كتل السياق NUSE_Task_Context [] [] ؛
  • يتم تحميل D0 باستخدام NUSE_Task_Active ، وزيادة ومضاعفة في 72 ؛
  • ثم تتم إضافة D0 إلى A0 ، والتي تشير الآن إلى كتلة السياق للمهمة الجديدة (حيث يجب أن يتم تحميل السياق في العملية العكسية لحفظ التسلسل ، فإن مؤشر المكدس مطلوب أولاً) ؛
  • ثم يتم استعادة السجلات من كتلة السياق ؛ أولاً ، يتم دفع مؤشر المكدس ، ثم PC و SR على المكدس ، ثم يتم تحميل D1-D7 و A1-A6 ، وفي نهاية D0 و A0 .

تتمثل صعوبة تنفيذ تبديل السياق في صعوبة الوصول إلى سجل الدولة للعديد من المعالجات (بالنسبة لـ ColdFire ، هذا هو SR ). الحل المشترك هو المقاطعة ، أي انقطاع البرنامج أو مقاطعة الفرع المشروط ، مما يؤدي إلى تحميل SR على المكدس مع الكمبيوتر . هذه هي الطريقة التي يعمل بها Nucleus SE على ColdFire. تم تعيين الماكرو NUSE_CONTEXT_SWAP () في nuse_types.h ، والذي يمتد إلى:
asm ("trap # 0") ؛

ما يلي هو رمز التهيئة ( NUSE_Init_Task () في nuse_init.c ) لكتل ​​السياق:



هذه هي الطريقة التي تتم بها تهيئة مؤشر المكدس و PC و SR . الأولين لهما قيم محددة من قبل المستخدم في nuse_config.c . يتم تعريف قيمة SR كحرف NUSE_STATUS_REGISTER في nuse_types.h . بالنسبة لـ ColdFire ، هذه القيمة هي 0x40002000 .

البيانات العالمية


يتطلب برنامج Nucleus SE جدولة ذاكرة صغيرة جدًا لتخزين البيانات ، ولكنه بالطبع يستخدم هياكل البيانات المرتبطة بالمهام ، والتي سيتم مناقشتها بالتفصيل في المقالات التالية.

بيانات ذاكرة الوصول العشوائي


لا يستخدم المجدول البيانات الموجودة في ROM ، ويتم وضع من 2 إلى 5 متغيرات عامة في ذاكرة الوصول العشوائي (يتم تعيينها جميعًا في nuse_globals.c ) ، اعتمادًا على الجدولة المستخدمة:

  • NUSE_Task_Active - متغير من النوع U8 يحتوي على فهرس المهمة الحالية ؛
  • NUSE_Task_State - متغير من النوع U8 يحتوي على قيمة تشير إلى حالة الشفرة التي يتم تنفيذها حاليًا ، والتي قد تكون مهمة أو معالج مقاطعة أو رمز بدء التشغيل ؛ القيم المحتملة هي: NUSE_TASK_CONTEXT و NUSE_STARTUP_CONTEXT و NUSE_NISR_CONTEXT و NUSE_MISR_CONTEXT ؛
  • NUSE_Task_Saved_State - متغير من النوع U8 يستخدم لحماية قيمة NUSE_Task_State في مقاطعة مُدارة ؛
  • NUSE_Task_Next - متغير من النوع U8 يحتوي على فهرس المهمة التالية ، والذي يجب جدولته لجميع الجدولة باستثناء RTC ؛
  • NUSE_Time_Slice_Ticks - متغير من النوع U16 يحتوي على عداد شرائح الوقت ؛ تستخدم فقط مع جدولة TS.

البصمة البيانات المجدول


لا يستخدم برنامج Nucleus SE بيانات ROM. يختلف مقدار بيانات ذاكرة الوصول العشوائي بالضبط وفقًا للجدولة المستخدمة:

  • لـ RTC - 2 بايت ( NUSE_Task_Active و NUSE_Task_State ) ؛
  • لـ RR والأولوية - 4 بايت ( NUSE_Task_Active و NUSE_Task_State و NUSE_Task_Saved_State و NUSE_Task_Next ) ؛
  • لـ TS - 6 بايت ( NUSE_Task_Active و NUSE_Task_State و NUSE_Task_Saved_State و NUSE_Task_Next و NUSE_Time_Slice_Ticks ).

تنفيذ آليات التخطيط الأخرى


على الرغم من حقيقة أن Nucleus SE يقدم خيارًا من 4 جداول ، تغطي معظم الحالات ، تتيح لك البنية المفتوحة تنفيذ الفرص للحالات الأخرى.

تقطيع الوقت مع مهمة الخلفية


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

الأولوية و Round Robin (RR)


في معظم النوى في الوقت الفعلي ، يدعم برنامج جدولة الأولوية العديد من المهام في كل مستوى أولوية ، على عكس Nucleus SE ، حيث يكون لكل مهمة مستوى فريد. أعطيت الأفضلية للخيار الأخير ، لأنه يبسط إلى حد كبير هياكل البيانات ، وبالتالي ، رمز المجدول. ستكون هناك حاجة إلى العديد من جداول ROM و RAM لدعم معماريات أكثر تعقيدًا.

نبذة عن الكاتب: يعمل Colin Walls في صناعة الإلكترونيات لأكثر من ثلاثين عامًا ، ويكرس معظم وقته للبرامج الثابتة. وهو الآن مهندس برامج ثابتة في Mentor Embedded (قسم من Mentor Graphics). غالبًا ما يتحدث كولين وولز في المؤتمرات والندوات ، مؤلف العديد من المقالات الفنية وكتابين عن البرامج الثابتة. يعيش في المملكة المتحدة. مدونة كولين المهنية ، البريد الإلكتروني: colin_walls@mentor.com.

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

Source: https://habr.com/ru/post/ar423967/


All Articles