Önceliğin tersine çevrilmesinden kaçının

Bu makale, Android'in ses sisteminin önceliklerin tersine çevrilmesini nasıl önlemeye çalıştığını açıklıyor ve sizin de kullanabileceğiniz teknikleri vurguluyor.

Bu teknikler, yüksek performanslı ses uygulamaları geliştiricileri, OEM'ler ve ses HAL uygulayan SoC sağlayıcıları için yararlı olabilir. Lütfen bu tekniklerin uygulanmasının, özellikle ses bağlamı dışında kullanıldığında aksaklıkları veya diğer arızaları önleme garantisi vermediğini unutmayın. Sonuçlarınız farklılık gösterebilir ve kendi değerlendirmenizi ve testinizi yapmalısınız.

Arka plan

Android AudioFlinger ses sunucusu ve AudioTrack/AudioRecord istemci uygulaması, gecikmeyi azaltmak için yeniden tasarlanıyor. Bu çalışma Android 4.1'de başladı ve 4.2, 4.3, 4.4 ve 5.0'daki iyileştirmelerle devam etti.

Bu düşük gecikmeyi elde etmek için sistem genelinde birçok değişikliğe ihtiyaç vardı. Önemli bir değişiklik, CPU kaynaklarını daha öngörülebilir bir zamanlama politikasıyla zaman açısından kritik iş parçacıklarına atamaktır. Güvenilir zamanlama, ses arabellek boyutlarının ve sayımlarının azaltılmasına olanak tanırken, yine de yetersiz çalışma ve aşırı çalıştırmaları önler.

Öncelik dönüşümü

Önceliği ters çevirme, daha yüksek öncelikli bir görevin sınırsız bir süre boyunca engellendiği, daha düşük öncelikli bir görevin bir mutex (paylaşılan durum tarafından korunan) gibi bir kaynağı serbest bırakmasını beklediği, gerçek zamanlı sistemlerin klasik bir arıza modudur.

Bir ses sisteminde, önceliğin tersine çevrilmesi tipik olarak bir aksaklık (tıklama, patlama, düşme), dairesel arabellekler kullanıldığında tekrarlanan ses veya bir komuta yanıt vermede gecikme olarak kendini gösterir.

Önceliği ters çevirmeye yönelik yaygın bir geçici çözüm, ses arabellek boyutlarını artırmaktır. Ancak bu yöntem gecikmeyi artırır ve sorunu çözmek yerine yalnızca gizler. Aşağıda görüldüğü gibi önceliklerin tersine çevrilmesini anlamak ve önlemek daha iyidir.

Android ses uygulamasında, önceliklerin tersine çevrilmesi büyük olasılıkla bu yerlerde meydana gelir. Bu yüzden dikkatinizi buraya odaklamalısınız:

  • AudioFlinger'da 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 ikisinin de önceliği yüksektir, ancak biraz farklı öncelikleri vardır)
  • hızlı bir 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, örneğin telefon veya yankı iptali için
  • çekirdekteki ses sürücüsünde
  • AudioTrack veya AudioRecord geri arama dizisi ile diğer uygulama konuları arasında (bu bizim kontrolümüz dışındadır)

Ortak çözümler

Tipik çözümler şunları içerir:

  • kesintileri devre dışı bırakmak
  • öncelikli miras muteksleri

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

Öncelikli miras futeksleri (hızlı kullanıcı alanı muteksleri), nispeten ağır oldukları ve güvenilir bir istemciye dayandıkları için ses sisteminde kullanılmaz.

Android'in kullandığı teknikler

Deneyler "kilitlemeyi dene" ile başladı ve zaman aşımı ile kilitlendi. Bunlar, muteks kilitleme işleminin engellemeyen ve sınırlı engelleme çeşitleridir. Kilitlemeyi ve zaman aşımı ile kilitlemeyi deneyin, oldukça iyi çalıştı ancak birkaç belirsiz hata moduna karşı hassastı: istemci meşgulse sunucunun paylaşılan duruma erişebileceği garanti edilmiyordu ve toplu zaman aşımı çok uzun sürebilirdi. hepsi zaman aşımına uğrayan uzun bir ilgisiz kilit dizisi vardı.

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

  • artış
  • bit bazında "veya"
  • bit bazında "ve"

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

Not: Atomik işlemler ve bunların bellek bariyerleriyle olan etkileşimleri, herkesin bildiği gibi, çok yanlış anlaşılıyor ve yanlış kullanılıyor. Eksiksiz olması açısından bu yöntemleri buraya dahil ettik ancak daha fazla bilgi için Android için SMP Primer makalesini de okumanızı öneririz.

Yukarıdaki araçların çoğuna hâlâ sahibiz ve bunları kullanıyoruz ve yakın zamanda şu teknikleri ekledik:

  • Veriler için engellemeyen tek okuyuculu tek yazarlı FIFO kuyruklarını kullanın.
  • Yüksek ve düşük öncelikli modüller arasında durumu paylaşmak yerine durumu kopyalamayı deneyin.
  • Durumun paylaşılması gerektiğinde, durumu, tek veri yolu işleminde yeniden denemeye gerek kalmadan atomik olarak erişilebilen maksimum boyutlu sözcükle sınırlandırın.
  • Karmaşık çok kelimeli durum için durum kuyruğunu kullanın. Bir durum kuyruğu, temel olarak, yazarın bitişik itmeleri tek bir itmeye daraltması dışında, veri yerine durum için kullanılan, yalnızca engellemeyen, tek okuyuculu, tek yazarlı bir FIFO kuyruğudur.
  • SMP doğruluğu için bellek engellerine dikkat edin.
  • Güven ama doğrula . Süreçler arasında durumu paylaşırken durumun iyi biçimlendiğini varsaymayın. Örneğin endekslerin sınırlar içinde olup olmadığını kontrol edin. Bu doğrulama, aynı işlemdeki iş parçacıkları arasında veya karşılıklı güvene dayalı işlemler arasında (genellikle aynı UID'ye sahip olan) gerekli değildir. Ayrıca, bozulmanın önemsiz olduğu PCM sesi gibi paylaşılan veriler için de gereksizdir.

Engellemeyen algoritmalar

Engellemeyen algoritmalar son zamanlarda yapılan birçok çalışmanın konusu olmuştur. Ancak tek okuyuculu, tek yazarlı FIFO kuyrukları dışında bunların karmaşık ve hataya açık olduğunu gördük.

Android 4.2'den başlayarak, engellemesiz, tek okuyuculu/yazar sınıflarımızı şu konumlarda bulabilirsiniz:

  • çerçeveler/av/include/media/nbaio/
  • çerçeveler/av/media/libnbaio/
  • çerçeveler/av/hizmetler/audioflinger/StateQueue*

Bunlar özellikle AudioFlinger için tasarlanmıştır ve genel amaçlı değildir. Engellemeyen algoritmalar hata ayıklamanın zor olmasıyla ünlüdür. Bu koda model olarak bakabilirsiniz. Ancak hatalar olabileceğini ve sınıfların başka amaçlara uygun olduğunun garanti edilmediğini unutmayın.

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

Uygulama kodu için özel olarak tasarlanmış, engellemeyen bir FIFO uygulaması örneği yayınladık. Platform kaynak dizininde frameworks/av/audio_utils bulunan şu dosyalara bakın:

Aletler

Bildiğimiz kadarıyla, özellikle bu gerçekleşmeden önce öncelik tersine çevirmeyi bulmak için otomatik bir araç yoktur. Bazı araştırma statik kod analizi araçları, kod tabanının tamamına erişilebiliyorsa öncelikli dönüşümleri bulma yeteneğine sahiptir. Elbette, keyfi kullanıcı kodu söz konusuysa (burada uygulama için olduğu gibi) veya büyük bir kod tabanı varsa (Linux çekirdeği ve aygıt sürücüleri için olduğu gibi), statik analiz pratik olmayabilir. En önemli şey kodu çok dikkatli okumak ve tüm sistemi ve etkileşimleri iyi kavramaktır. Systrace ve ps -t -p gibi araçlar, öncelikli dönüşümün gerçekleşmesinden sonra görülmesi açısından faydalıdır, ancak bunu size önceden söylemezler.

Son bir söz

Tüm bu tartışmalardan sonra mutekslerden korkmayın. Muteksler, zaman açısından kritik olmayan sıradan kullanım durumlarında doğru şekilde kullanıldığında ve uygulandığında, sıradan kullanım için arkadaşınızdır. Ancak yüksek ve düşük öncelikli görevler arasında ve zamana duyarlı sistemlerde mutekslerin sorun yaratma olasılığı daha yüksektir.