از وارونگی اولویت اجتناب کنید

این مقاله توضیح می‌دهد که چگونه سیستم صوتی اندروید سعی می‌کند از وارونگی اولویت جلوگیری کند و تکنیک‌هایی را که شما نیز می‌توانید استفاده کنید، برجسته می‌کند.

این تکنیک ها ممکن است برای توسعه دهندگان برنامه های صوتی با کارایی بالا، OEM ها و ارائه دهندگان SoC که در حال اجرای HAL صوتی هستند مفید باشد. لطفاً توجه داشته باشید که اجرای این تکنیک‌ها برای جلوگیری از اشکالات یا سایر خرابی‌ها تضمین نمی‌شود، به خصوص اگر خارج از زمینه صوتی استفاده شوند. نتایج شما ممکن است متفاوت باشد، و شما باید ارزیابی و آزمایش خود را انجام دهید.

زمینه

سرور صوتی Android AudioFlinger و اجرای مشتری AudioTrack/AudioRecord برای کاهش تأخیر مجدداً طراحی شده‌اند. این کار در اندروید 4.1 شروع شد و با بهبودهای بیشتر در 4.2، 4.3، 4.4 و 5.0 ادامه یافت.

برای دستیابی به این تاخیر کمتر، تغییرات زیادی در سراسر سیستم مورد نیاز بود. یکی از تغییرات مهم تخصیص منابع CPU به رشته های حساس زمانی با سیاست زمان بندی قابل پیش بینی تر است. زمان‌بندی قابل اعتماد اجازه می‌دهد تا اندازه و تعداد بافرهای صوتی کاهش یابد، در حالی که هنوز از زیر و رو کردن و اضافه کردن اجتناب می‌شود.

وارونگی اولویت

وارونگی اولویت یک حالت شکست کلاسیک سیستم‌های بلادرنگ است، که در آن یک کار با اولویت بالاتر برای مدت زمان نامحدودی مسدود می‌شود و منتظر یک کار با اولویت پایین‌تر برای انتشار منبعی مانند (حالت مشترک محافظت شده توسط) یک mutex است.

در یک سیستم صوتی، وارونگی اولویت معمولاً به صورت یک اشکال (کلیک، پاپ، حذف)، صدای مکرر هنگام استفاده از بافرهای دایره‌ای، یا تأخیر در پاسخ به یک فرمان ظاهر می‌شود.

یک راه حل رایج برای وارونگی اولویت، افزایش اندازه بافر صوتی است. با این حال، این روش تاخیر را افزایش می دهد و به جای حل مشکل، صرفاً آن را پنهان می کند. همانطور که در زیر مشاهده می کنید بهتر است وارونگی اولویت را درک کرده و از آن جلوگیری کنید.

در اجرای صوتی اندروید، وارونگی اولویت به احتمال زیاد در این مکان ها رخ می دهد. و بنابراین باید توجه خود را در اینجا متمرکز کنید:

  • بین رشته میکسر معمولی و رشته میکسر سریع در AudioFlinger
  • بین رشته پاسخ تماس برنامه برای یک AudioTrack سریع و رشته میکسر سریع (هر دو دارای اولویت بالا هستند، اما اولویت های کمی متفاوت دارند)
  • بین رشته پاسخ به تماس برنامه برای ضبط سریع صوتی و رشته ضبط سریع (مشابه قبلی)
  • در اجرای لایه انتزاعی سخت افزار صوتی (HAL)، به عنوان مثال برای تلفن یا لغو اکو
  • در درایور صوتی در هسته
  • بین رشته پاسخ تماس AudioTrack یا AudioRecord و رشته های دیگر برنامه (این خارج از کنترل ما است)

راه حل های رایج

راه حل های معمولی عبارتند از:

  • غیرفعال کردن وقفه ها
  • mutexes ارثی اولویت

غیرفعال کردن وقفه ها در فضای کاربر لینوکس امکان پذیر نیست و برای چند پردازنده متقارن (SMP) کار نمی کند.

فوکس‌های ارثی اولویت‌دار (mutexes سریع فضای کاربر) در سیستم صوتی استفاده نمی‌شوند، زیرا وزن نسبتاً سنگینی دارند و به یک کلاینت قابل اعتماد متکی هستند.

تکنیک های استفاده شده توسط اندروید

آزمایش‌ها با «قفل امتحان کنید» و با وقفه قفل شروع شدند. اینها انواع غیر مسدود کننده و مسدود کننده محدود از عملیات قفل mutex هستند. سعی کنید قفل و قفل با وقفه نسبتاً خوب کار کرد، اما مستعد چند حالت خرابی مبهم بود: سرور تضمین نمی‌شد که در صورت شلوغ بودن کلاینت، به حالت اشتراک‌گذاری شده دسترسی داشته باشد، و در صورت شلوغ شدن زمان، زمان انباشته ممکن است خیلی طولانی شود. یک توالی طولانی از قفل های نامرتبط وجود داشت که زمان تمام آنها تمام شد.

ما همچنین از عملیات اتمی مانند:

  • افزایش
  • به صورت بیتی "یا"
  • به صورت بیتی "و"

همه اینها مقدار قبلی را برمی گرداند و موانع SMP لازم را شامل می شود. نقطه ضعف این است که آنها می توانند به تلاش های مجدد بدون محدودیت نیاز داشته باشند. در عمل، ما دریافتیم که تلاش های مجدد مشکلی ندارند.

توجه: عملیات اتمی و تعامل آنها با موانع حافظه بسیار بد درک شده و به اشتباه استفاده می شود. ما این روش‌ها را در اینجا برای کامل بودن گنجانده‌ایم، اما توصیه می‌کنیم برای اطلاعات بیشتر مقاله SMP Primer for Android را نیز مطالعه کنید.

ما هنوز اکثر ابزارهای فوق را داریم و استفاده می کنیم و اخیراً این تکنیک ها را اضافه کرده ایم:

  • از صف‌های FIFO تک‌خوان تک‌خوان برای داده‌ها استفاده کنید.
  • سعی کنید به جای اشتراک گذاری وضعیت بین ماژول های با اولویت بالا و پایین، وضعیت را کپی کنید .
  • هنگامی که وضعیت نیاز به اشتراک‌گذاری دارد، حالت را به کلمه‌ای با حداکثر اندازه محدود کنید که می‌توان به صورت اتمی در عملیات یک اتوبوس بدون تلاش مجدد به آن دسترسی پیدا کرد.
  • برای حالت چند کلمه ای پیچیده، از صف حالت استفاده کنید. صف حالت اساساً فقط یک صف FIFO تک خواننده تک‌خوان غیرمسدود کننده است که برای وضعیت به جای داده استفاده می‌شود، به جز اینکه نویسنده فشارهای مجاور را در یک فشار واحد جمع می‌کند.
  • برای صحت SMP به موانع حافظه توجه کنید.
  • اعتماد کنید، اما تأیید کنید . هنگام به اشتراک گذاشتن حالت بین فرآیندها، فرض نکنید که حالت به خوبی شکل گرفته است. به عنوان مثال، بررسی کنید که شاخص ها در محدوده هستند. این تأیید بین رشته‌ها در یک فرآیند، بین فرآیندهای اعتماد متقابل (که معمولاً UID یکسانی دارند) مورد نیاز نیست. همچنین برای داده های مشترک مانند صدای PCM که در آن خرابی بی اهمیت است، غیر ضروری است.

الگوریتم های غیر مسدود کننده

الگوریتم‌های غیر مسدود کننده موضوعی است که اخیراً مورد مطالعه بسیاری قرار گرفته است. اما به استثنای صف‌های FIFO تک‌خواننده، آن‌ها را پیچیده و مستعد خطا دانسته‌ایم.

با شروع اندروید 4.2، می‌توانید کلاس‌های بدون مسدودکننده و تک‌خواننده/نویسنده ما را در این مکان‌ها پیدا کنید:

  • Frameworks/AV/Include/media/nbaio/
  • Frameworks/AV/media/libnbaio/
  • Frameworks/av/services/audiolinger/StateQueue*

این ها به طور خاص برای AudioFlinger طراحی شده اند و همه منظوره نیستند. الگوریتم های غیرمسدود به دلیل سختی اشکال زدایی بدنام هستند. می توانید به این کد به عنوان یک مدل نگاه کنید. اما توجه داشته باشید که ممکن است اشکالاتی وجود داشته باشد و کلاس ها برای اهداف دیگر مناسب نیستند.

برای توسعه‌دهندگان، برخی از نمونه‌های کد برنامه OpenSL ES باید برای استفاده از الگوریتم‌های غیرمسدود کننده یا ارجاع به یک کتابخانه منبع باز غیر Android به‌روزرسانی شوند.

ما نمونه ای از اجرای FIFO غیر مسدود کننده را منتشر کرده ایم که به طور خاص برای کد برنامه طراحی شده است. این فایل‌ها را که در frameworks/av/audio_utils فهرست منبع پلتفرم قرار دارند، ببینید:

ابزار

تا جایی که ما می دانیم، هیچ ابزار خودکاری برای یافتن وارونگی اولویت، به ویژه قبل از وقوع، وجود ندارد. برخی از ابزارهای تجزیه و تحلیل کد استاتیک تحقیقاتی قادر به یافتن وارونگی های اولویت هستند در صورتی که بتوانند به کل پایگاه کد دسترسی داشته باشند. البته، اگر کد کاربر دلخواه درگیر باشد (همانطور که در اینجا برای برنامه وجود دارد) یا یک پایگاه کد بزرگ باشد (در مورد هسته لینوکس و درایورهای دستگاه)، تجزیه و تحلیل استاتیک ممکن است غیرعملی باشد. مهم‌ترین چیز این است که کد را با دقت بخوانید و به کل سیستم و تعاملات آن دسترسی پیدا کنید. ابزارهایی مانند systrace و ps -t -p برای مشاهده وارونگی اولویت پس از وقوع آن مفید هستند، اما از قبل به شما اطلاع نمی دهند.

کلام پایانی

بعد از این همه بحث، از موتکس ها نترسید. Mutexeها دوست شما برای استفاده معمولی هستند، وقتی که در موارد استفاده معمولی غیر بحرانی به درستی استفاده و پیاده سازی شوند. اما بین وظایف با اولویت بالا و پایین و در سیستم های حساس به زمان، mutexeها بیشتر باعث ایجاد مشکل می شوند.