Artikel ini menjelaskan bagaimana sistem audio Android berupaya menghindari inversi prioritas, dan menyoroti teknik yang dapat Anda gunakan juga.
Teknik ini mungkin berguna bagi developer aplikasi berperforma tinggi aplikasi audio, OEM, dan penyedia SoC yang menerapkan audio HAL. Perhatikan bahwa menerapkan teknik ini tidak dijamin mencegah gangguan atau kegagalan lainnya, terutama jika digunakan di luar konteks audio. Hasil Anda mungkin berbeda, dan Anda harus melakukan sendiri evaluasi dan pengujian.
Latar belakang
Server audio AudioFlinger Android dan AudioTrack/AudioRecord implementasi klien sedang dirancang ulang untuk mengurangi latensi. Pekerjaan ini dimulai pada Android 4.1, dan dilanjutkan dengan peningkatan lebih lanjut di 4.2, 4.3, 4.4, dan 5.0.
Untuk mencapai latensi yang lebih rendah ini, banyak perubahan yang diperlukan di seluruh sistem. paket Premium AI penting adalah menetapkan sumber daya CPU ke waktu yang kritis thread dengan kebijakan penjadwalan yang lebih dapat diprediksi. Penjadwalan yang andal memungkinkan ukuran dan jumlah buffer audio dikurangi saat diam menghindari {i>underruns<i} dan {i>overrun<i}.
Inversi prioritas
Inversi prioritas adalah mode kegagalan klasik dari sistem waktu nyata, saat tugas dengan prioritas lebih tinggi diblokir untuk waktu tunggu tanpa batas untuk tugas dengan prioritas lebih rendah guna melepaskan sumber daya seperti (bersama negara bagian yang dilindungi oleh) mutex.
Dalam sistem audio, inversi prioritas biasanya berwujud sebagai gangguan (klik, pop, cabut), audio berulang ketika buffer sirkular digunakan, atau penundaan dalam merespons suatu perintah.
Solusi umum untuk inversi prioritas adalah dengan meningkatkan ukuran buffer audio. Akan tetapi, metode ini meningkatkan latensi dan hanya menyembunyikan bukan menyelesaikannya. Lebih baik memahami dan mencegah prioritas inversi, seperti yang terlihat di bawah ini.
Dalam implementasi audio Android, inversi prioritas adalah mungkin terjadi di tempat tersebut. Jadi, Anda harus memfokuskan perhatian Anda di sini:
- antara thread mixer normal dan thread mixer cepat di AudioFlinger
- di antara thread callback aplikasi untuk AudioTrack yang cepat dan thread mixer cepat (keduanya memiliki prioritas yang lebih tinggi, tetapi sedikit prioritas yang berbeda)
- di antara thread callback aplikasi untuk AudioRecord yang cepat dan thread pengambilan cepat (mirip dengan yang sebelumnya)
- dalam implementasi Hardware Abstraksi Layer (HAL) audio, misalnya untuk telepon atau pengurangan gema
- dalam {i>driver<i} audio di {i>kernel<i}
- antara thread callback AudioTrack atau AudioRecord dan thread aplikasi lainnya (ini di luar kendali kami)
Solusi umum
Solusi umumnya meliputi:
- menonaktifkan interupsi
- mutex pewarisan prioritas
Menonaktifkan interupsi tidak mungkin dilakukan di ruang pengguna Linux, dan memang tidak berfungsi untuk Symmetric Multi-Processors (SMP).
Pewarisan prioritas futex (mutex ruang pengguna cepat) tidak digunakan dalam sistem audio karena relatif berat, dan karena mereka bergantung pada klien yang dapat dipercaya.
Teknik yang digunakan oleh Android
Eksperimen dimulai dengan "coba kunci" dan kunci dengan waktu tunggu. Berikut adalah varian pemblokiran yang tidak bersifat memblokir dan terbatas dari kunci mutex operasi. Coba kunci dan kunci dengan waktu tunggu bekerja cukup baik, tapi rentan terhadap beberapa mode kegagalan yang tidak jelas: server tidak dijamin dapat mengakses status bersama jika klien kebetulan sedang sibuk, dan waktu tunggu kumulatifnya bisa terlalu panjang jika ada rangkaian panjang kunci tidak terkait yang semua waktu habis.
Kami juga menggunakan operasi atomik seperti:
- penambahan
- bitwise "atau"
- bitwise "dan"
Semua ini mengembalikan nilai sebelumnya dan menyertakan Hambatan SMP. Kekurangannya adalah metode ini dapat memerlukan percobaan ulang yang tidak terikat. Dalam praktiknya, kami menemukan bahwa percobaan ulang bukanlah masalah.
Catatan: Operasi atomik dan interaksinya dengan batasan memori sangat disalahpahami dan digunakan secara tidak benar. Kami menyertakan metode ini di sini untuk kelengkapan tetapi sarankan Anda juga membaca artikel SMP Primer untuk Android untuk informasi lebih lanjut.
Kami masih memiliki dan menggunakan sebagian besar alat di atas, dan baru-baru ini menambahkan teknik berikut:
- Gunakan penulis tunggal dengan pembaca tunggal yang tidak memblokir Antrean FIFO untuk data.
- Coba salinan negara bagian, bukan bagikan status antara tinggi dan modul prioritas rendah.
- Jika status perlu dibagikan, batasi status ke ukuran maksimum kata yang dapat diakses secara atomik dalam operasi satu bus tanpa percobaan ulang.
- Untuk status multi-kata yang kompleks, gunakan antrean status. Antrean status pada dasarnya hanyalah FIFO penulis tunggal yang tidak memblokir antrean digunakan untuk status, bukan data, kecuali penulis menciutkan push yang berdekatan menjadi satu push.
- Perhatikan hambat memori untuk ketepatan SMP.
- Percayai, tetapi lakukan verifikasi. Saat berbagi negara bagian di antara proses, jangan mengasumsikan bahwa keadaan terbentuk dengan baik. Misalnya, periksa bahwa indeks berada dalam batas. Verifikasi ini tidak diperlukan di antara rangkaian pesan dalam proses yang sama, di antara proses kepercayaan bersama (yang biasanya memiliki UID yang sama). Juga tidak diperlukan untuk data seperti audio PCM yang kerusakan yang tidak penting.
Algoritma non-pemblokiran
Algoritma non-pemblokiran telah menjadi subjek dari banyak penelitian baru-baru ini. Namun dengan pengecualian antrean FIFO dengan satu pembaca tunggal, kami menemukan bahwa kode ini rumit dan rentan terhadap kesalahan.
Mulai Android 4.2, Anda dapat menemukan fungsi kelas pembaca/penulis tunggal di lokasi berikut:
- framework/av/include/media/nbaio/
- framework/av/media/libnbaio/
- framework/av/services/audioflinger/StateQueue*
Ini dirancang khusus untuk AudioFlinger dan tidak tujuan umum. Algoritma non-pemblokiran dikenal karena sulit untuk di-debug. Anda dapat melihat kode ini sebagai model. Tapi bersikaplah menyadari mungkin ada {i>bug<i}, dan kelas tidak dijamin akan cocok untuk tujuan lain.
Bagi developer, beberapa contoh kode aplikasi OpenSL ES harus diupdate menjadi menggunakan algoritma yang tidak memblokir atau mereferensikan library open source non-Android.
Kami telah memublikasikan contoh penerapan FIFO non-pemblokiran yang dirancang khusus untuk
pada kode aplikasi Anda. Lihat file ini yang berada di direktori sumber platform
frameworks/av/audio_utils
:
Alat
Sepengetahuan kami, tidak ada alat otomatis untuk
menemukan inversi prioritas, terutama sebelum itu terjadi. Agak besar
alat analisis kode statis penelitian
mampu menemukan prioritas
jika dapat mengakses seluruh codebase. Tentu saja, jika
kode pengguna arbitrer terlibat (seperti yang ada di sini untuk aplikasi)
atau merupakan codebase besar (seperti untuk kernel Linux dan driver perangkat),
analisis statis mungkin tidak praktis. Yang paling penting adalah
baca kode dengan sangat hati-hati dan
memahami seluruh bagian
sistem dan interaksinya. Alat seperti
systrace
dan
ps -t -p
berguna untuk melihat inversi prioritas setelah terjadi, tetapi jangan
tidak memberi tahu
Anda sebelumnya.
Kata terakhir
Setelah semua diskusi ini, jangan takut mutex. mutex cocok untuk penggunaan biasa, jika digunakan dan diterapkan dengan benar dalam kasus penggunaan non-kritis biasa. Tapi antara tinggi dan pada tugas prioritas rendah dan dalam sistem yang sensitif terhadap waktu, {i>mutex<i} lebih berpotensi menimbulkan masalah.