Öncelik tersine çevirme

Bu makalede, Android'in ses sisteminin öncelik ters çevirmeyi nasıl önlemeye çalıştığı açıklanmakta ve sizin de kullanabileceğiniz teknikler vurgulanmaktadır.

Bu teknikler, yüksek performanslı ses uygulamaları geliştirenler, OEM'ler ve ses HAL'si uygulayan SoC sağlayıcılar için faydalı olabilir. Bu tekniklerin uygulanmasının, özellikle ses bağlamı dışında kullanıldığında aksaklıkları veya diğer hataları önleyeceğini garanti etmediğini lütfen unutmayın. Sonuçlarınız farklılık gösterebilir. Bu nedenle, kendi değerlendirme ve testlerinizi yapmanız gerekir.

Arka plan

Gecikmeyi azaltmak için Android AudioFlinger ses sunucusu ve AudioTrack/AudioRecord istemci uygulaması yeniden tasarlanıyor. Bu çalışma Android 4.1'de başladı ve 4.2, 4.3, 4.4 ve 5.0 sürümlerinde daha da geliştirildi.

Bu daha düşük gecikme süresine ulaşmak için sistemde birçok değişiklik yapılması gerekti. Önemli bir değişiklik, CPU kaynaklarını zaman açısından kritik olan iş parçacıklarına daha öngörülebilir bir planlama politikasıyla atamaktır. Güvenilir planlama, ses arabelleği boyutlarının ve sayılarının azaltılmasına olanak tanırken yetersiz ve aşırı arabellek kullanımını önlemeye devam eder.

Öncelik ters çevirme

Önceliğin ters dönmesi, gerçek zamanlı sistemlerin klasik bir hata modudur. Bu modda, daha yüksek öncelikli bir görev, daha düşük öncelikli bir görevin mutex gibi bir kaynağı (paylaşılan durum tarafından korunan) serbest bırakmasını beklerken sınırsız bir süre boyunca engellenir.

Bir ses sisteminde öncelik tersine çevrilmesi genellikle aksaklık (tıklama, patlama, kesilme), dairesel arabellekler kullanıldığında tekrarlanan ses veya komuta yanıt vermede gecikme olarak kendini gösterir.

Öncelik ters çevirme için yaygın bir geçici çözüm, ses arabelleği boyutlarını artırmaktır. Ancak bu yöntem gecikmeyi artırır ve sorunu çözmek yerine yalnızca gizler. Aşağıda gösterildiği gibi, öncelik ters çevirmeyi anlamak ve önlemek daha iyidir.

Android ses uygulamasında öncelik tersine çevrilmesinin en olası olduğu yerler şunlardır: Bu nedenle, dikkatinizi şu noktalara odaklamanız gerekir:

  • AudioFlinger'daki normal karıştırıcı iş parçacığı ile hızlı karıştırıcı iş parçacığı arasında
  • Hızlı bir AudioTrack için uygulama geri çağırma iş parçacığı ile hızlı karıştırıcı iş parçacığı arasında (her ikisi de yüksek önceliğe sahiptir ancak öncelikleri biraz farklıdır)
  • Hızlı AudioRecord için uygulama geri çağırma iş parçacığı ile hızlı yakalama iş parçacığı arasında (öncekiyle benzer)
  • Ses donanımı soyutlama katmanı (HAL) uygulaması içinde (ör. telefon veya yankı giderme için)
  • çekirdekteki ses sürücüsünde
  • AudioTrack veya AudioRecord geri çağırma iş parçacığı ile diğer uygulama iş parçacıkları arasında (bu bizim kontrolümüz dışındadır)

Sık karşılaşılan çözümler

Genel çözümler şunlardır:

  • kesintileri devre dışı bırakma
  • öncelik devralma mutex'leri

Kesmeleri devre dışı bırakmak Linux kullanıcı alanında mümkün değildir ve Simetrik Çoklu İşlemciler (SMP) için çalışmaz.

Öncelik devralma futexes (hızlı kullanıcı alanı mutex'leri), nispeten ağır oldukları ve güvenilir bir istemciye bağlı oldukları için ses sisteminde kullanılmaz.

Android tarafından kullanılan teknikler

"Try lock" ve zaman aşımıyla kilitleme ile denemelere başlandı. Bunlar, karşılıklı dışlama kilidi işleminin engellemeyen ve sınırlı engelleme varyantlarıdır. Zaman aşımıyla kilitleme ve kilidi açma yöntemi oldukça iyi çalışıyordu ancak birkaç belirsiz hata moduna karşı hassastı: İstemci meşgul olduğunda sunucunun paylaşılan duruma erişebileceği garanti edilmiyordu ve birbiriyle alakasız, zaman aşımına uğrayan uzun bir kilit dizisi varsa toplam zaman aşımı çok uzun olabiliyordu.

Ayrıca aşağıdaki gibi atomik işlemleri de kullanırız:

  • artır
  • bit düzeyinde "veya"
  • Bit düzeyinde "ve"

Bunların tümü önceki değeri döndürür ve gerekli SMP engellerini içerir. Dezavantajı, sınırsız yeniden deneme gerektirebilmeleridir. Pratikte, yeniden denemelerin sorun olmadığını gördük.

Not: Atomik işlemler ve bunların bellek bariyerleriyle etkileşimleri kötü bir şekilde yanlış anlaşılır ve yanlış kullanılır. Bu yöntemleri eksiksiz olması için buraya ekliyoruz ancak daha fazla bilgi için Android için SMP Temelleri başlıklı makaleyi de okumanızı öneririz.

Yukarıdaki araçların çoğunu hâlâ kullanıyoruz ve kısa süre önce şu teknikleri ekledik:

  • Veriler için engellemeyen tek okuyuculu tek yazıcılı FIFO kuyruklarını kullanın.
  • Yüksek ve düşük öncelikli modüller arasında durumu paylaşmak yerine kopyalamayı deneyin.
  • Durumun paylaşılması gerektiğinde, durumu yeniden deneme olmadan tek veri yolu işleminde atomik olarak erişilebilen maksimum boyuttaki kelimeyle sınırlayın.
  • Karmaşık çok kelimeli durum için durum sırası kullanın. Durum kuyruğu, yazıcının bitişik göndermeleri tek bir göndermede daraltması dışında, temelde veriler yerine durum için kullanılan, engellemeyen tek okuyuculu tek yazıcılı bir FIFO kuyruğudur.
  • SMP doğruluğu için bellek bariyerlerine dikkat edin.
  • Güvenin ancak doğrulayın. Süreçler arasında durum paylaşırken durumun iyi biçimlendirilmiş olduğunu varsaymayın. Örneğin, dizinlerin sınırlar içinde olup olmadığını kontrol edin. Bu doğrulama, aynı işlemdeki iş parçacıkları arasında ve karşılıklı güvenen işlemler (genellikle aynı UID'ye sahiptir) arasında gerekli değildir. Ayrıca, bozulmanın önemsiz olduğu PCM ses gibi paylaşılan verilerde de gereksizdir.

Engellemeyen algoritmalar

Engellemeyen algoritmalar son zamanlarda çok fazla çalışmaya konu olmuştur. Ancak tek okuyuculu tek yazıcılı FIFO kuyrukları hariç olmak üzere bunların karmaşık ve hataya açık olduğunu gördük.

Android 4.2'den itibaren, engellemeyen, tek okuyucu/yazıcı sınıflarımızı şu konumlarda bulabilirsiniz:

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

Bunlar, AudioFlinger için özel olarak tasarlanmıştır ve genel amaçlı değildir. Engellemeyen algoritmaların hata ayıklaması zordur. Bu kodu model olarak kullanabilirsiniz. Ancak hatalar olabileceğini ve sınıfların başka amaçlar için uygun olacağının garanti edilmediğini unutmayın.

Geliştiriciler için, örnek OpenSL ES uygulama kodunun bir kısmı, engellemeyen algoritmalar kullanacak veya Android dışı bir açık kaynak kitaplığına referans verecek şekilde güncellenmelidir.

We have published an example non-blocking FIFO implementation that is specifically designed for application code. Platform kaynak dizininde bulunan şu dosyalara bakın: frameworks/av/audio_utils:

Araçlar

Bildiğimiz kadarıyla, özellikle gerçekleşmeden önce öncelik tersine çevrilmesini bulmaya yönelik otomatik araçlar yoktur. Bazı araştırma statik kod analizi araçları, kod tabanının tamamına erişebiliyorsa öncelik tersine çevirmelerini bulabilir. Elbette, rastgele kullanıcı kodu söz konusuysa (burada uygulamada olduğu gibi) veya büyük bir kod tabanı varsa (Linux çekirdeği ve cihaz sürücülerinde olduğu gibi) statik analiz pratik olmayabilir. En önemli şey, kodu çok dikkatli bir şekilde okumak ve tüm sistemi ve etkileşimleri iyi anlamaktır. systrace ve ps -t -p gibi araçlar, öncelik ters çevrilmesi gerçekleştikten sonra bunu görmek için yararlıdır ancak önceden bilgi vermez.

Son olarak

Tüm bu tartışmalardan sonra mutex'lerden korkmayın. Mutex'ler, normal kullanımda ve zaman açısından kritik olmayan kullanım alanlarında doğru şekilde kullanıldığında ve uygulandığında en iyi yardımcınızdır. Ancak yüksek ve düşük öncelikli görevler arasında ve zamana duyarlı sistemlerde karşılıklı dışlamalar sorunlara neden olma olasılığı daha yüksektir.