Cómo evitar la inversión de prioridad

En este artículo, se explica cómo el sistema de audio de Android evita inversión de prioridad, y destaca las técnicas que también puedes usar.

Estas técnicas pueden ser útiles para los desarrolladores de aplicaciones las apps de audio, los OEM y los proveedores de SoC que implementan HAL. Ten en cuenta que implementar estas técnicas no es que garantiza la prevención de errores u otras fallas, en especial fuera del contexto de audio. Tus resultados pueden variar, y debes realizar los tuyos la evaluación y las pruebas.

Segundo plano

El servidor de audio de Android AudioFlinger y AudioTrack/AudioRecord de la implementación del cliente se están rediseñando para reducir la latencia. Este trabajo comenzó en Android 4.1 y continuó con mejoras adicionales. en 4.2, 4.3, 4.4 y 5.0.

Para lograr esta menor latencia, se necesitaron muchos cambios en todo el sistema. Uno cambio importante es asignar recursos de CPU a recursos subprocesos con una política de programación más predecible. Programación confiable permite reducir los tamaños y los recuentos de los búferes de audio y, al mismo tiempo, y evitar los subdesbordamientos y los excesos.

Inversión de prioridad

Inversión de prioridad es un modo de falla clásico de los sistemas en tiempo real, en las que se bloquea una tarea de mayor prioridad durante un tiempo de espera ilimitado para una tarea de menor prioridad para liberar un recurso, como (compartido protegido por) un mutex.

En un sistema de audio, la inversión de prioridad suele manifestarse como un error (clic, ventana emergente, retirada), audio repetido cuando los búferes circulares se usan o un retraso en la respuesta a un comando.

Una solución común para la inversión de prioridad es aumentar los tamaños de los búferes de audio. Sin embargo, este método aumenta la latencia y simplemente oculta el problema en lugar de resolverlo. Es mejor entender y evitar las prioridades inversa, como se muestra a continuación.

En la implementación de audio de Android, la inversión de prioridad es la más más probabilidades de ocurrir en estos lugares. Por lo tanto, debes centrar tu atención aquí:

  • entre el subproceso del mezclador normal y el subproceso del mezclador rápido en AudioFlinger
  • entre el subproceso de devolución de llamada de la aplicación para obtener un audioTrack rápido y un subproceso de mezclador rápido (ambos tienen prioridad elevada, pero una prioridades diferentes)
  • entre el subproceso de devolución de llamada de la aplicación para obtener un AudioRecord rápido y subproceso de captura rápida (similar al anterior)
  • dentro de la implementación de la capa de abstracción de hardware (HAL) de audio, p.ej., para telefonía o cancelación del eco
  • en el controlador de audio en el kernel
  • entre el subproceso de devolución de llamada de AudioTrack o AudioRecord y otros subprocesos de la app (esto está fuera de nuestro control)

Soluciones habituales

Las soluciones típicas incluyen lo siguiente:

  • inhabilitar interrupciones
  • exclusiones mutuas de herencia de prioridad

Inhabilitar las interrupciones no es posible en el espacio de usuario de Linux y no funciona con multiprocesadores simétricos (SMP).

Herencia de prioridad futexes (las exclusiones mutuas rápidas del espacio del usuario) no se utilizan en el sistema de audio porque son relativamente pesados, y porque dependen de un cliente de confianza.

Técnicas utilizadas por Android

Experimentos que comenzaron con un "intento de bloqueo" y bloquear con tiempo de espera. Son variantes de bloqueo delimitados y sin bloqueo del bloqueo de exclusión mutua una sola operación. Las pruebas de bloqueo y bloqueo con tiempo de espera funcionaron bastante bien, pero no susceptibles a un par de modos de falla oscuros: el no se garantizaba que el servidor pudiese acceder al estado compartido si el cliente estaba ocupado y el tiempo de espera acumulado podría sería demasiado largo si había una larga secuencia de bloqueos no relacionados que Se agotó el tiempo de espera.

También usamos operaciones atómicas por ejemplo:

  • incremento
  • "or" a nivel de bits
  • "and" a nivel de bits

Todos estos muestran el valor anterior y, además, incluyen los atributos Obstáculos de SMP. La desventaja es que pueden requerir reintentos ilimitados. En la práctica, descubrimos que los reintentos no son un problema.

Nota: Operaciones atómicas y sus interacciones con barreras de memoria se malinterpretan y usan de forma incorrecta. Incluimos estos métodos aquí para ver la información completa, pero te recomendamos que también leas el artículo Manual básico de SMP para Android para obtener más información.

Todavía tenemos y usamos la mayoría de las herramientas anteriores, y recientemente agregó estas técnicas:

  • Usa un lector de una sola escritura de un solo lector sin bloqueo Colas FIFO para los datos.
  • Intenta copia en lugar de compartir estado entre alto y de baja prioridad.
  • Cuando sea necesario compartir el estado, limítalo a los tamaño máximo palabra a las que se puede acceder automáticamente en una operación de un bus sin reintentos.
  • Para un estado complejo de varias palabras, usa una cola de estado. Una cola de estado es básicamente un FIFO de una sola escritura y lector de un solo lector sin bloqueo para el estado y no para los datos, salvo que el escritor se contrae envíos adyacentes en un solo push.
  • Presta atención a barreras de memoria para la corrección de SMP.
  • Confiar, pero verificar. Al compartir estado entre procesos, no que el estado tiene el formato correcto. Por ejemplo, comprueba que los índices están dentro de los límites. Esta verificación no es necesaria entre subprocesos en el mismo proceso, entre procesos de confianza mutua (que normalmente tienen el mismo UID). Tampoco es necesario incluir datos como el audio PCM, en el que una corrupción es intrascendente.

Algoritmos sin bloqueo

Algoritmos sin bloqueo han sido tema de un estudio muy reciente. Sin embargo, con la excepción de las colas FIFO de un solo lector de una sola escritura, son complejos y propensos a errores.

A partir de Android 4.2, puedes encontrar nuestra app de lectura y escritura en estas ubicaciones:

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

Se diseñaron específicamente para AudioFlinger y no son de uso general. Los algoritmos que no generan bloqueos son conocidos por difíciles de depurar. Puedes ver este código como un modelo. Pero prepárate que puede haber errores y que no se garantiza que las clases adecuado para otros fines.

Para los desarrolladores, parte del código de la aplicación de muestra de OpenSL ES debe actualizarse para Usar algoritmos sin bloqueo o hacer referencia a una biblioteca de código abierto que no sea de Android

Publicamos un ejemplo de implementación de FIFO sin bloqueo que está diseñada específicamente para el código de la aplicación. Observa estos archivos ubicados en el directorio del código fuente de la plataforma. frameworks/av/audio_utils:

Herramientas

Hasta donde sabemos, no hay herramientas automáticas para y encuentra la inversión de prioridad, especialmente antes de que suceda. Algunos Las herramientas de análisis de código estático de investigación son capaces de determinar las prioridades si puede acceder a toda la base de código. Por supuesto, si está involucrado el código de usuario arbitrario (como sucede aquí para la aplicación). o que sea una base de código grande (como el kernel de Linux y los controladores de dispositivos), el análisis estático puede resultar poco práctico. Lo más importante es leer el código con mucho cuidado y comprender bien todo sistema operativo y las interacciones. Herramientas como Systrace y ps -t -p son útiles para ver la inversión de prioridad después de que ocurre, pero no decirte con anticipación.

Una última palabra

Después de todo este debate, no tengas miedo de las exclusiones mutuas. exclusiones mutuas Se recomienda para el uso normal, cuando se usan e implementan correctamente. en casos de uso comunes que no son críticos. Pero entre las tasas altas y las tareas de baja prioridad y, en los sistemas urgentes, las exclusiones mutuas son más causar problemas.