تجنُّب عكس الأولوية

توضّح هذه المقالة كيف يحاول نظام الصوت في Android تجنُّب انعكاس الأولوية، وتسلّط الضوء على التقنيات التي يمكنك استخدامها أيضًا.

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

خلفية

تتم إعادة تصميم بنية خادم الصوت Android AudioFlinger وتنفيذ برنامج AudioTrack/AudioRecord الخاص بالعميل بهدف تقليل وقت الاستجابة. بدأ هذا العمل في الإصدار 4.1 من نظام التشغيل Android، واستمر مع إجراء المزيد من التحسينات في الإصدارات 4.2 و4.3 و4.4 و5.0.

ولتحقيق وقت الاستجابة الأقصر هذا، كان من الضروري إجراء العديد من التغييرات في جميع أنحاء النظام. أحد التغييرات المهمة هو تخصيص موارد وحدة المعالجة المركزية (CPU) لسلاسل التعليمات الحساسة للوقت باستخدام سياسة جدولة أكثر قابلية للتوقّع. يسمح الجدولة الموثوقة بتقليل أحجام مخزن الصوت المؤقت وأعداده مع تجنُّب حالات نقص البيانات أو زيادتها.

انعكاس الأولوية

انعكاس الأولوية هو وضع تعذُّر كلاسيكي للأنظمة التي تعمل في الوقت الفعلي، حيث يتم حظر مهمة ذات أولوية أعلى لمدة غير محدودة في انتظار أن تحرر مهمة ذات أولوية أقل موردًا، مثل (حالة مشتركة محمية بواسطة) استبعاد متبادل.

في نظام صوتي، يظهر انعكاس الأولوية عادةً على شكل خلل (نقرة أو صوت فرقعة أو انقطاع) أو تكرار الصوت عند استخدام المخازن المؤقتة الدائرية، أو تأخير في الاستجابة لأحد الأوامر.

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

في عملية تنفيذ الصوت على Android، من المرجّح حدوث انعكاس الأولوية في هذه المواضع. لذا، يجب التركيز على ما يلي:

  • بين سلسلة عمليات المزج العادية وسلسلة عمليات المزج السريعة في AudioFlinger
  • بين سلسلة التعليمات الخاصة باستدعاء التطبيق لـ AudioTrack السريع وسلسلة التعليمات الخاصة بالخلاط السريع (كلاهما لهما أولوية أعلى، ولكن بأولويات مختلفة قليلاً)
  • بين سلسلة محادثات رد الاتصال الخاصة بالتطبيق لعملية AudioRecord سريعة وسلسلة محادثات الالتقاط السريع (مشابهة لما سبق)
  • ضمن تنفيذ طبقة تجريد الأجهزة (HAL) الصوتية، مثلاً للاتصال الهاتفي أو إلغاء الصدى
  • ضمن برنامج تشغيل الصوت في النواة
  • بين سلسلة استدعاء AudioTrack أو AudioRecord وسلاسل تطبيقات أخرى (هذا خارج عن سيطرتنا)

الحلول الشائعة

تشمل الحلول النموذجية ما يلي:

  • إيقاف المقاطعات
  • أقفال تبادل الاستبعاد المتبادل مع أولوية الاكتساب

لا يمكن إيقاف المقاطعات في مساحة مستخدم Linux، ولا تعمل مع المعالجات المتعددة المتماثلة (SMP).

لا يتم استخدام futexes (أو أقفال الاستبعاد المتبادل السريعة في مساحة المستخدم) التي تعتمد على أولوية التوريث في نظام الصوت لأنّها ثقيلة نسبيًا، ولأنّها تعتمد على برنامج موثوق.

التقنيات التي يستخدمها Android

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

نستخدم أيضًا عمليات ذرية مثل:

  • زيادة
  • bitwise "or"
  • bitwise "and"

تعرض جميع هذه الدوال القيمة السابقة وتتضمّن حواجز SMP اللازمة. أما العيب فهو أنّها قد تتطلّب عددًا غير محدود من عمليات إعادة المحاولة. في الواقع، تبيّن لنا أنّ عمليات إعادة المحاولة لا تشكّل مشكلة.

ملاحظة: إنّ العمليات الذرية وتفاعلاتها مع حواجز الذاكرة هي من المفاهيم التي يساء فهمها واستخدامها بشكل كبير. نضمّن هذه الطرق هنا من أجل تقديم معلومات شاملة، ولكن ننصحك أيضًا بقراءة المقالة مقدمة عن "منصة إدارة التسويق" لنظام Android للحصول على مزيد من المعلومات.

ما زلنا نملك معظم الأدوات المذكورة أعلاه ونستخدمها، وقد أضفنا مؤخرًا التقنيات التالية:

  • استخدِم قوائم انتظار FIFO غير الحظرية ذات القارئ الواحد والكاتب الواحد للبيانات.
  • حاوِل نسخ الحالة بدلاً من مشاركتها بين الوحدات ذات الأولوية العالية والمنخفضة.
  • عندما يكون من الضروري مشاركة الحالة، يجب أن تقتصر الحالة على الكلمة ذات الحجم الأقصى التي يمكن الوصول إليها بشكل ذري في عملية واحدة للحافلة بدون إعادة محاولة.
  • بالنسبة إلى الحالات المعقّدة التي تتضمّن عدة كلمات، استخدِم قائمة انتظار للحالات. قائمة انتظار الحالة هي في الأساس قائمة انتظار FIFO غير حظرية ذات قارئ واحد وكاتب واحد تُستخدَم للحالة بدلاً من البيانات، باستثناء أنّ الكاتب يدمج عمليات الدفع المتجاورة في عملية دفع واحدة.
  • انتبه إلى حواجز الذاكرة لضمان صحة SMP.
  • الثقة، ولكن مع التحقّق: عند مشاركة الحالة بين العمليات، لا تفترض أنّ الحالة صحيحة التنسيق. على سبيل المثال، تأكَّد من أنّ الفهارس ضمن الحدود. ولا تكون عملية التحقّق هذه ضرورية بين سلاسل المحادثات في العملية نفسها، أو بين العمليات التي تثق ببعضها البعض (والتي يكون لها عادةً المعرّف الفريد نفسه). لا يكون ذلك ضروريًا أيضًا بالنسبة إلى البيانات المشترَكة، مثل الصوت بتنسيق PCM، حيث لا يكون التلف مهمًا.

الخوارزميات غير الحظرية

الخوارزميات غير الحظرية كانت موضوعًا للعديد من الدراسات الحديثة. ولكن باستثناء قوائم انتظار FIFO التي تتضمّن قارئًا واحدًا وكاتبًا واحدًا، وجدنا أنّها معقّدة وعرضة للأخطاء.

بدءًا من الإصدار 4.2 من نظام التشغيل Android، يمكنك العثور على فئات القراءة والكتابة غير الحظرية والمفردة في المواقع التالية:

  • frameworks/av/include/media/nbaio/
  • frameworks/av/media/libnbaio/
  • frameworks/av/services/audioflinger/StateQueue*

وقد تم تصميمها خصيصًا لـ AudioFlinger، وهي ليست متعددة الأغراض. تشتهر الخوارزميات غير الحظرية بصعوبة تصحيح أخطائها. يمكنك اعتبار هذا الرمز نموذجًا. يُرجى العلم أنّه قد تحدث أخطاء، ولا يمكن ضمان ملاءمة الفئات لأغراض أخرى.

بالنسبة إلى المطوّرين، يجب تعديل بعض نماذج الرمز البرمجي لتطبيقات OpenSL ES لاستخدام خوارزميات غير حظر أو الرجوع إلى مكتبة مفتوحة المصدر غير تابعة لنظام Android.

لقد نشرنا مثالاً على تنفيذ FIFO غير محظور مصمّم خصيصًا للرمز البرمجي للتطبيق. يمكنك الاطّلاع على هذه الملفات في دليل ملفات مصدر النظام الأساسي frameworks/av/audio_utils:

الأدوات

حسب معلوماتنا، لا تتوفّر أدوات تلقائية للعثور على حالات عكس الأولوية، خاصةً قبل حدوثها. يمكن لبعض أدوات تحليل الرموز البرمجية الثابتة المخصّصة للأبحاث العثور على حالات عكس الأولوية إذا كان بإمكانها الوصول إلى قاعدة الرموز البرمجية بأكملها. بالطبع، إذا كان الأمر يتضمّن رموزًا برمجية عشوائية للمستخدمين (كما هو الحال هنا بالنسبة إلى التطبيق) أو إذا كان هناك قاعدة رموز برمجية كبيرة (كما هو الحال بالنسبة إلى نواة Linux وبرامج تشغيل الأجهزة)، قد يكون التحليل الثابت غير عملي. الأهم هو قراءة الرمز البرمجي بعناية شديدة وفهم النظام بأكمله والتفاعلات. تُعد أدوات مثل systrace و ps -t -p مفيدة لرصد مشكلة انعكاس الأولوية بعد حدوثها، ولكنها لا تخبرك بها مسبقًا.

كلمة أخيرة

بعد كل هذه المناقشة، لا تخف من استخدام عمليات الإغلاق المتبادل. تُعدّ عمليات الإغلاق المتبادل مفيدة للاستخدام العادي، وذلك عند استخدامها وتنفيذها بشكل صحيح في حالات الاستخدام العادية غير الحرجة من حيث الوقت. ولكن بين المهام ذات الأولوية العالية والمنخفضة وفي الأنظمة الحساسة للوقت، من المرجح أن تسبب عمليات الإغلاق المتبادل مشاكل.