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

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

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

پس‌زمینه

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

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

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

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

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

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

در پیاده‌سازی صوتی Android، وارونگی اولویت به احتمال زیاد در این مکان‌ها رخ می‌دهد. و بنابراین باید توجهتان را به اینجا معطوف کنید:

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

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

راه‌حل‌های معمول شامل موارد زیر است:

  • غیرفعال کردن وقفه‌ها
  • انحصار متقابل وراثت اولویت

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

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

تکنیک‌های مورد استفاده Android

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

همچنین از عملیات اتمی استفاده می‌کنیم، ازجمله:

  • افزایش
  • «یا» بیتی
  • «و» بیتی

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

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

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

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

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

الگوریتم‌های غیرمسدودکننده موضوع بسیاری از مطالعات اخیر بوده‌اند. اما به‌جز صف‌های FIFO تک‌خوان تک‌نویس، متوجه شده‌ایم که این صف‌ها پیچیده و مستعد خطا هستند.

از Android 4.2، می‌توانید کلاس‌های غیرمسدودکننده، تک‌خواننده/نویسنده ما را در این مکان‌ها پیدا کنید:

  • 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 برای دیدن وارونگی اولویت پس‌از وقوع آن مفید هستند، اما ازقبل به شما اطلاع نمی‌دهند.

سخن آخر

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