Éviter l'inversion des priorités

Cet article explique comment le système audio d'Android tente d'éviter l'inversion de priorité, et met en évidence les techniques que vous pouvez également utiliser.

Ces techniques peuvent être utiles aux développeurs les applications audio, les OEM et les fournisseurs de SoC qui implémentent une solution CARL. Veuillez noter que la mise en œuvre de ces techniques pour éviter les glitchs et autres défaillances, en particulier si utilisé en dehors du contexte audio. Vos résultats peuvent varier, et vous devez mener votre propre l'évaluation et les tests.

Arrière-plan

Serveur audio Android AudioFlinger et AudioTrack/AudioRecord l'implémentation du client sont en cours de modification de l'architecture pour réduire la latence. Ce travail a débuté avec Android 4.1 et s'est poursuivi avec d'autres améliorations. dans 4.2, 4.3, 4.4 et 5.0.

Pour atteindre cette latence plus faible, de nombreuses modifications ont été nécessaires dans l'ensemble du système. Un le changement important consiste à attribuer les ressources CPU avec une règle de planification plus prévisible. Planification fiable permet de réduire la taille et le nombre de tampons audio tout en en évitant les sous-utilisations et les dépassements.

Inversion des priorités

Inversion des priorités est un mode de défaillance classique des systèmes en temps réel, où une tâche à priorité plus élevée est bloquée pendant une durée d'attente illimitée pour une tâche de priorité inférieure visant à libérer une ressource telle que est protégé par) mutex.

Dans un système audio, l'inversion de priorité se manifeste généralement comme bug (clic, pop-up, abandon), audio répété lorsque la mise en mémoire tampon circulaire ou un retard dans la réponse à une commande.

Une solution courante pour l'inversion de priorité consiste à augmenter la taille des tampons audio. Cependant, cette méthode augmente la latence et masque le problème. au lieu de le résoudre. Il est préférable de comprendre et d'empêcher comme dans l'exemple ci-dessous.

Dans l'implémentation audio Android, l'inversion de priorité susceptibles de se produire à ces endroits. C'est pourquoi vous devez vous concentrer sur ces points:

  • entre le thread Mixer normal et le thread Mixer rapide dans AudioFlinger.
  • entre les threads de rappel de l'application pour un AudioTrack rapide et thread de mixage rapide (ils ont tous deux une priorité élevée, mais priorités différentes)
  • entre le thread de rappel de l'application pour un enregistrement audio rapide et Thread de capture rapide (semblable au précédent)
  • dans la mise en œuvre de la couche d'abstraction matérielle (HAL, Hardware Abstraction Layer) audio, par exemple pour la téléphonie ou l'annulation de l'écho
  • dans le pilote audio du noyau
  • entre le thread de rappel AudioTrack ou AudioRecord et les autres threads d'application (ceci n'est pas sous notre contrôle)

Solutions courantes

Voici quelques solutions typiques:

  • désactivation des interruptions
  • mutex d'héritage de priorité

La désactivation des interruptions n’est pas réalisable dans l’espace utilisateur Linux et ne fonctionnent pas avec les SMP (Symmetric Multi-Processors).

Héritage de la priorité futexes (mutex rapides dans l'espace utilisateur) ne sont pas utilisés dans le système audio car ils sont relativement lourds. et parce qu'elle s'appuie sur un client de confiance.

Techniques utilisées par Android

Tests commencés avec la méthode "trylock" et verrouiller avec un délai d'inactivité. Il s'agit des variantes de blocage non bloquantes et limitées du verrouillage du mutex opération. Le verrouillage et le verrouillage avec expiration fonctionnent assez bien, mais ont été sensibles à quelques modes de défaillance obscurs : l'accès à l'état partagé n'était pas garanti si le serveur le client était occupé et le délai d'expiration cumulé pouvait être trop long s'il y avait une longue séquence de verrous non liés qui a expiré.

Nous utilisons également Opérations atomiques par exemple:

  • augmenter
  • "ou" au niveau du bit
  • "and" au niveau du bit

Tous ces éléments renvoient la valeur précédente et incluent les éléments Obstacles liés aux réseaux sociaux. L'inconvénient est qu'elles peuvent nécessiter un nombre illimité de tentatives. En pratique, nous avons constaté que les nouvelles tentatives ne sont pas un problème.

Remarque:Les opérations atomiques et leurs interactions avec les barrières de mémoire sont mal compris et mal utilisés. Nous incluons ces méthodes ici pour garantir l'exhaustivité, mais nous vous recommandons également de lire l'article <ph type="x-smartling-placeholder"></ph> Introduction aux SMP pour Android pour en savoir plus.

Nous disposons encore et utilisons la plupart des outils ci-dessus, et nous avons récemment ont ajouté ces techniques:

  • Utiliser l'écriture unique et non bloquante Files d'attente FIFO pour les données.
  • Essayez de copie au lieu de partager entre les valeurs élevées et modules à faible priorité.
  • Si l'état doit être partagé, limitez-le au taille-maximale mot accessibles de manière atomique via un seul bus ; sans nouvelle tentative.
  • Pour un état complexe composé de plusieurs mots, utilisez une file d'attente d'états. Une file d'attente d'état est en fait un FIFO non bloquant et mono-lecteur utilisée pour l'état plutôt que pour les données, à la différence près que le rédacteur en une seule poussée.
  • Faites attention aux des barrières de mémoire pour l'exactitude des SMP.
  • Faire confiance, mais vérifier : Lors du partage state entre les processus, supposer que l'état est bien formé. Par exemple, vérifiez que les index sont dans les limites. Cette validation n'est pas nécessaire entre les threads au sein d'un même processus, entre des processus de confiance mutuelle (qui ont généralement le même UID). Il est également inutile pour les partages données comme les fichiers audio PCM, où une corruption est sans conséquence.

Algorithmes non bloquants

Algorithmes non bloquants ont fait l'objet de nombreuses études récentes. Mais à l'exception des files d'attente FIFO à lecteur unique à écriture unique, nous les avons trouvés complexes et sujets aux erreurs.

À partir d'Android 4.2, vous trouverez nos fonctions non bloquantes, cours pour un seul lecteur/rédacteur dans les pays suivants:

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

Ils ont été conçus spécifiquement pour AudioFlinger et ne sont pas à usage général. Les algorithmes non bloquants sont connus pour être difficiles à déboguer. Vous pouvez considérer ce code comme un modèle. Mais être qu'il peut y avoir des bugs, et il n'est pas garanti que les classes soient adapté à d'autres fins.

Pour les développeurs, une partie de l'exemple de code d'application OpenSL ES doit être mise à jour vers utiliser des algorithmes non bloquants ou référencer une bibliothèque Open Source non-Android.

Nous avons publié un exemple d'implémentation FIFO non bloquante spécialement conçue pour le code d'application. Ces fichiers se trouvent dans le répertoire source de la plate-forme. frameworks/av/audio_utils:

Outils

À notre connaissance, il n'existe pas d'outils automatiques pour trouver l'inversion des priorités, surtout avant qu'elle ne se produise. Un peu rechercher les outils d'analyse de code statique sont capables de déterminer la priorité s'ils peuvent accéder à l'ensemble du codebase. Bien entendu, si un code utilisateur arbitraire est impliqué (comme c'est le cas pour l'application). ou un vaste codebase (comme pour le noyau Linux et les pilotes de périphériques), l'analyse statique peut s'avérer peu pratique. Le plus important est de lisez très attentivement le code et maîtrisez l'intégralité du code le système et les interactions. Des outils tels que Systrace et ps -t -p sont utiles pour observer l'inversion des priorités après leur survenue, mais ne et non à l'avance.

Un dernier mot

Après toute cette discussion, n'ayez pas peur des mutex. mutex pour un usage ordinaire, lorsqu'ils sont utilisés et implémentés correctement dans les cas d'utilisation ordinaires non urgents. Mais entre les valeurs élevées et les tâches à faible priorité et dans les systèmes urgents, les mutex sont plus de causer des problèmes.