Daemon de administración de memoria

Android 17 y versiones posteriores admiten el daemon de administración de memoria (mmd), un daemon del sistema que controla la configuración del daemon, los parámetros ajustables y las tareas de mantenimiento en curso de intercambio o ZRAM.

Fondo

Antes de la introducción de mmd, las configuraciones de ZRAM de Android estaban fragmentadas y ofrecían una personalización limitada. mmd aborda este problema centralizando la administración de ZRAM, lo que permite una lógica de configuración más sofisticada y simplifica la incorporación de nuevas funciones y mejoras arquitectónicas. mmd también establece una separación clara de las responsabilidades entre el proceso system_server basado en Java y el intercambio a nivel del kernel o la administración de memoria.

Arquitectura y administración de ZRAM

Cuando se completa el inicio (es decir, cuando sys.boot_completed=1), mmd_setup intenta configurar ZRAM con los parámetros especificados. Una vez que se completa la configuración de ZRAM, el sistema habilita el servicio mmd, que controla las tareas de mantenimiento en curso.

Con el proyecto mmd, las operaciones de mantenimiento se inician desde system_server enviando solicitudes de Binder a mmd con la interfaz IMmd. mmd controla las tareas de mantenimiento de la escritura diferida de ZRAM, la recompresión y la escritura diferida por proceso según su propio motor de políticas interno. Tanto la programación de ActivityManagerService como las políticas de mantenimiento de ZRAM se pueden configurar con propiedades del sistema.

Integración del servidor del sistema (system_server)

El proceso system_server basado en Java determina cuándo se invoca mmd. El proceso separa las limpiezas de mantenimiento globales de las optimizaciones de memoria específicas para cada app.

Mantenimiento normal de posprocesamiento

El mantenimiento global de ZRAM se controla con ActivityManagerService a través de com.android.server.memory.ZramMaintenance.

zram-maintenance

Figura 1: Es el flujo de programación del mantenimiento de ZRAM.

  • Motor de programación: ZramMaintenance registra un trabajo periódico en segundo plano con JobScheduler de Android.
  • Restricciones del trabajo: Para evitar la interrupción de la IU en primer plano o la contención de la CPU, el trabajo se configura de forma explícita con setRequiresDeviceIdle(true) y setRequiresBatteryNotLow(true).
  • Activación del vinculador: Cuando el programador activa onStartJob(), system_server invoca mmd.doZramMaintenanceAsync(). Esta es una llamada de Binder asíncrona unidireccional; system_server no se bloquea a la espera de que finalicen los análisis de mantenimiento. mmd pone en cola esta operación en un subproceso de trabajo en segundo plano para realizar la recompresión y la escritura de forma secuencial.

Escritura diferida por proceso

ActivityManagerService administra el desalojo de memoria por proceso segmentado con com.android.server.am.CachedAppOptimizer.

mmd-writeback

Figura 2: Flujo de escritura diferida por proceso de mmd

Cuando un proceso pasa a un estado almacenado en caché en segundo plano, ActivityManager realiza una compactación de memoria. Si la eliminación del proceso por falta de memoria sería visible para el usuario, es decir, si el proceso aloja una Activity y si la escritura diferida de ZRAM por proceso llevaría el espacio de memoria del proceso a casi cero, el sistema sigue estos pasos:

  1. Después de la compactación, CachedAppOptimizer publica un mensaje retrasado (ZRAM_WRITEBACK_MSG) en su controlador de compactación interno (retrasado por mZramWritebackWaitSeconds).
  2. Cuando vence la demora, ActivityManager abre un descriptor de archivo de proceso seguro pidfd.
  3. El servidor del sistema llama a mmd.asyncWritebackProcessZramMemory(pfd, callback).
  4. mmd ejecuta el ioctl de escritura diferida por proceso y envía un informe con IMmdProcessWritebackCallback. Si se realiza correctamente, ActivityManager marca el registro del proceso (setIsZramWrittenBack(app, true)) para aumentar el oom_score_adj del proceso y registra métricas en FrameworkStatsLog.ZRAM_WRITEBACK_EVENT.

Carga previa por proceso

Cuando un usuario reinicia una app almacenada en caché anteriormente (descongelada debido a UNFREEZE_REASON_ACTIVITY), ActivityManager minimiza la latencia de inicio de la app causada por errores de página importantes del almacenamiento de respaldo:

  1. CachedAppOptimizer intercepta el evento de descongelación y, luego, invoca prefetchZram(app).
  2. El servidor del sistema envía el pidfd de la app a través de Binder con mmd.asyncPrefetchProcessZramMemory(pfd). mmd emite el ioctl ZRAM_ANDROID_IOC_PROCESS_PREFETCH, lo que indica al kernel que realice una carga previa asíncrona de las páginas intercambiadas en la RAM mientras se inicializa el subproceso de IU principal de la app.

Descripción general de las tareas de mantenimiento y procesamiento posterior

En esta sección, se describen las operaciones de mantenimiento en segundo plano y las tareas de procesamiento posterior que ejecuta mmd para optimizar el espacio de intercambio y la memoria del sistema.

Mantenimiento en mmd

En mmd, el término mantenimiento hace referencia a las verificaciones de mantenimiento programadas en segundo plano que optimizan el espacio de intercambio y la utilización de la memoria física sin afectar el rendimiento en primer plano del usuario activo. En lugar de realizar barridos continuos y síncronos (que provocarían activaciones graves de la CPU y bloqueos de la IU), el mantenimiento se realiza de forma asíncrona:

  1. system_server activa doZramMaintenanceAsync() periódicamente en Binder.

  2. mmd coloca la solicitud en una cola de trabajo en segundo plano LowPrioWorkItem::ZramMaintenance.

  3. Hay un solo subproceso de trabajo en mmd que administra una cola de prioridad alta y una cola de prioridad baja. Los elementos de trabajo de prioridad alta (como la recuperación previa por proceso) se procesan primero y pueden interrumpir los elementos de trabajo de prioridad baja. El mantenimiento y la escritura diferida por proceso operan como elementos de trabajo de baja prioridad. Cuando se extrae, el subproceso de trabajo ejecuta dos operaciones de mantenimiento principales de forma secuencial:

    • Recompresión de ZRAM: Analiza las páginas de intercambio existentes y recompresa las páginas inactivas con un algoritmo de compresión secundario de mayor proporción, por ejemplo, zstd.

    • Escritura diferida de zRAM: Analiza las páginas inactivas y las expulsa por completo de la RAM al almacenamiento flash de respaldo, un dispositivo de bucle desde un archivo en /data.

Tareas de posprocesamiento en ZRAM

En el módulo ZRAM del kernel de Linux y la arquitectura mmd, las tareas de procesamiento posterior son las transformaciones asíncronas que se aplican a las páginas de memoria después de que el kernel ya las haya intercambiado con las rutas de recuperación estándar del kernel (kswapd o compactación).

Cuando se reemplaza una página inicialmente, el sistema prioriza la velocidad: usa un algoritmo de compresión principal rápido (como lz4) y almacena la página comprimida en la RAM. Sin embargo, con el tiempo, muchas páginas intercambiadas se vuelven inactivas, por ejemplo, las apps almacenadas en caché en segundo plano que no se reanudan durante horas. Dejar páginas frías en la ZRAM rápida y ligeramente comprimida es ineficiente.

Canalización de posprocesamiento

mmd implementa un ciclo de vida de procesamiento posterior de varias etapas para optimizar estas páginas:

mmd-page-lifecycle

Figura 3: Ciclo de vida de la página mmd.

  1. Etapa 1: Intercambio inicial (compresión rápida): Primero, se recupera la memoria a través de kswapd o la compactación de la app. Por lo general, esta primera recuperación se realiza con un algoritmo de compresión rápido, como lz4, y el contenido se almacena en la RAM.

  2. Etapa 2: Marcado de inactividad (antigüedad y seguimiento): El seguimiento de inactividad de mmd accede al seguimiento de la memoria del kernel (CONFIG_ZRAM_TRACK_ENTRY_ACTIME) o usa su marcador de inactividad de software para hacer un seguimiento del tiempo que las páginas permanecieron sin tocar.

  3. Etapa 3: Postprocesamiento 1: recompresión (recuperación en memoria): Las páginas que alcanzan la antigüedad de inactividad de recompresión (min_idle_seconds a max_idle_seconds) se someten a recompresión. mmd escribe en /sys/block/zram0/recompress para indicarle al kernel que descomprima la página lz4 y la vuelva a comprimir con zstd. Esto recupera la RAM física sin generar desgaste por escritura en la memoria flash.

  4. Etapa 4: Postprocesamiento 2: Escritura diferida (desalojo al almacenamiento flash): Si la presión de memoria continúa y las páginas alcanzan la antigüedad de inactividad de escritura diferida (por lo general, 20 horas o más), mmd activa la escritura diferida. mmd escribe en /sys/block/zram0/idle y /sys/block/zram0/writeback para expulsar la página comprimida por completo de la RAM al almacenamiento flash de respaldo.

Configuración de ZRAM

mmd carga y procesa las siguientes propiedades de configuración de ZRAM:

Propiedad Usar Predeterminado
mmd.zram.enabled Indica si está habilitada la configuración de mmd ZRAM. false
mmd.zram.num_devices Es la cantidad de dispositivos ZRAM que se configurarán. Para un número N, los dispositivos zram0 a zram<N-1> deben estar presentes antes de que el sistema establezca sys.boot_completed=1. Las propiedades de la lista de dispositivos por ZRAM se pueden configurar por dispositivo. 1
mmd.zram.device_priority Son los valores de prioridad que se deben pasar cuando se llama a swapon. Sin establecer
mmd.zram.comp_algorithm Algoritmo de compresión de ZRAM. Si no se especifica, se usa el algoritmo de compresión predeterminado del kernel. Sin establecer
mmd.zram.size Tamaño del dispositivo zRAM en bytes o un porcentaje del tamaño de la RAM del dispositivo, por ejemplo, 75%. 50%
mmd.zram.writeback.enabled Indica si se debe habilitar la escritura diferida de ZRAM. false
mmd.zram.writeback.device_size Tamaño del dispositivo de escritura diferida en bytes o porcentaje de la partición de datos. El tamaño real del dispositivo se puede ajustar según el espacio disponible en la partición de datos. 1073741824 (1 GiB)
mmd.zram.writeback.min_free_space_mib Es el espacio libre mínimo en MiB que debe estar disponible después de configurar el dispositivo de escritura diferida. 1536 (1.5 GiB)
mmd.zram.writeback.use_nr_tags_prop Cuando es true, usa el valor de mmd.zram.writeback.nr_tags para configurar la profundidad de la cola de la escritura diferida de ZRAM que respalda el dispositivo de bucle. Esta es una solución alternativa para situaciones en las que no se puede configurar la política de SELinux del proveedor para permitir que mmd lea directamente nr_tags del dispositivo de bloqueo que respalda /data. false
mmd.zram.writeback.nr_tags Consulta mmd.zram.writeback.use_nr_tags_prop. Sin establecer
mmd.zram.recompression.enabled Indica si se debe habilitar la función de recompresión de ZRAM. false
mmd.zram.recompression.algorithm Es el algoritmo secundario de recompresión de ZRAM. zstd

Propiedades del dispositivo por ZRAM

Cuando mmd.zram.num_devices es mayor que uno, se pueden configurar propiedades específicas de forma opcional para cada dispositivo ZRAM estableciendo la propiedad en un valor separado por comas que contenga exactamente mmd.zram.num_devices elementos. Estas propiedades incluyen la siguiente información:

  • mmd.zram.size
  • mmd.zram.comp_algorithm
  • mmd.zram.device_priority
  • mmd.zram.recompression.enabled
  • mmd.zram.recompression.huge_idle.enabled
  • mmd.zram.recompression.idle.enabled
  • mmd.zram.recompression.huge.enabled
  • mmd.zram.recompression.threshold_bytes
  • mmd.zram.recompression.algorithm
  • mmd.zram.writeback.device_size
  • mmd.zram.writeback.huge_idle.enabled
  • mmd.zram.writeback.idle.enabled
  • mmd.zram.writeback.huge.enabled

Baja de la configuración existente de ZRAM

Si bien swapon_all sigue disponible en Android para configurar el espacio de intercambio basado en disco y ZRAM, mmd es el enfoque preferido para la administración de ZRAM, ya que facilita la configuración y ofrece funciones avanzadas, como la recompresión de ZRAM.

Cuando mmd.zram.enabled habilita la configuración de mmd ZRAM, ocurre lo siguiente:

  • La configuración de ZRAM en la implementación de swapon_all se convierte en una operación sin efecto.
  • Se ignoran las configuraciones de ZRAM existentes, como config_zramWriteback en el archivo de superposición config.xml y las propiedades del sistema de escritura diferida ro.zram.*.

Parámetros de ajuste de mantenimiento de ZRAM

El mantenimiento de ZRAM debería funcionar de inmediato, y puedes ajustarlo aún más con las propiedades del sistema que se indican en esta sección.

Programación del mantenimiento de ZRAM

Estas propiedades controlan cómo y cuándo system_server programa las tareas de mantenimiento de ZRAM.

Propiedad Usar Predeterminado
mm.zram.maintenance.first_delay_seconds Es la demora antes de que se inicie el primer mantenimiento de ZRAM. 3600 (1 hora)
mm.zram.maintenance.periodic_delay_seconds Es la demora entre las programaciones de mantenimiento de ZRAM posteriores. 3600 (1 hora)
mm.zram.maintenance.require_device_idle Indica si solo se debe iniciar el mantenimiento de ZRAM cuando el dispositivo está inactivo. true
mm.zram.maintenance.require_battery_not_low Indica si se debe requerir que la batería no esté baja antes de iniciar el mantenimiento de ZRAM. true

Política de escritura diferida de ZRAM

Los siguientes parámetros controlan cuándo y qué tipo de memoria se escribe en el dispositivo de respaldo:

Propiedad Usar Predeterminado
mmd.zram.writeback.backoff_seconds Es el tiempo de espera desde la última operación de escritura diferida. 600 (10 minutos)
mmd.zram.writeback.min_idle_seconds Se combina con mmd.zram.writeback.max_idle_seconds para calcular la antigüedad de inactividad de una página y determinar si es apta para la escritura diferida según la fracción de utilización de la memoria. La antigüedad de inactividad calculada se interpola de forma exponencial entre los dos parámetros para minimizar el trabajo cuando no hay presión de memoria. 72000 (20 horas)
mmd.zram.writeback.max_idle_seconds Cantidad máxima de segundos que se usan para calcular la antigüedad de la página inactiva de forma dinámica según el uso de la memoria. 90000 (25 horas)
mmd.zram.writeback.huge.enabled Indica si se debe habilitar la escritura diferida de la página HUGE. false
mmd.zram.writeback.idle.enabled Indica si se debe habilitar la escritura diferida de la página IDLE. true
mmd.zram.writeback.huge_idle.enabled Indica si se debe habilitar la escritura diferida de la página HUGE_IDLE. true
mmd.zram.writeback.min_bytes Es la cantidad mínima de bytes que se deben escribir en una ronda de escritura en segundo plano inactiva. 5242880 (5 MiB)
mmd.zram.writeback.max_bytes Cantidad máxima de bytes que se pueden escribir en una ronda de escritura en segundo plano inactiva. 314572800 (300 MiB)
mmd.zram.writeback.max_bytes_per_day Cantidad máxima de bytes que se pueden escribir en un período de 24 horas. 25769803776 (24 GiB)
mmd.zram.writeback.limit.enabled Indica si se debe habilitar la contabilización del límite de presupuesto de reversión diaria. true

Política de recompresión de ZRAM

Los siguientes parámetros controlan cuándo y qué tipo de memoria se vuelve a comprimir:

Propiedad Usar Predeterminado
mmd.zram.recompression.backoff_seconds Es el tiempo de espera desde la última recompresión. 1800 (30 minutos)
mmd.zram.recompression.min_idle_seconds Se combina con mmd.zram.recompression.max_idle_seconds para calcular la antigüedad de inactividad de una página y determinar si es apta para la recompresión según la fracción de utilización de la memoria. La antigüedad de inactividad calculada se interpola de forma exponencial entre los dos parámetros para minimizar el trabajo cuando no hay presión de memoria. 7200 (2 horas)
mmd.zram.recompression.max_idle_seconds Es la cantidad máxima de segundos que se usa para calcular de forma dinámica la antigüedad de la página inactiva. 14400 (4 horas)
mmd.zram.recompression.threshold_bytes Es el tamaño mínimo en bytes de las páginas de ZRAM que se consideran para la recompresión. 1024 (1 KiB)
mmd.zram.recompression.huge.enabled Indica si se debe habilitar la recompresión de la página HUGE. true
mmd.zram.recompression.idle.enabled Indica si se debe habilitar la recompresión de la página IDLE. true
mmd.zram.recompression.huge_idle.enabled Indica si se debe habilitar la recompresión de la página HUGE_IDLE. true

Seguimiento de páginas inactivas de zRAM

mmd El mantenimiento de ZRAM marca las páginas de ZRAM como inactivas según el tiempo que haya transcurrido desde el último acceso. Esta función requiere que se habiliten las configuraciones del kernel CONFIG_ZRAM_TRACK_ENTRY_ACTIME o CONFIG_ZRAM_MEMORY_TRACKING. CONFIG_ZRAM_TRACK_ENTRY_ACTIME está habilitado de forma predeterminada en los kernels de GKI 6.18 y versiones posteriores. En kernels anteriores, tiene una sobrecarga de memoria y no está habilitado de forma predeterminada.

Si la configuración del kernel no está habilitada, el mantenimiento de mmd ZRAM recurre a una lógica de sustitución de software para hacer un seguimiento de las páginas de ZRAM inactivas:

  1. Marcar todas las páginas de ZRAM como inactivas cuando se inicia mmd

  2. Se omitirán los próximos mantenimientos de ZRAM hasta que haya pasado el período de espera requerido.

  3. Escribir en zRAM o volver a comprimir las páginas inactivas Si quedan páginas inactivas debido a los límites de escritura diferida, mmd continúa escribiendo páginas en el próximo mantenimiento sin marcar páginas nuevas como inactivas (se omite el paso 4).

  4. Si se vuelven a escribir todas las páginas inactivas, vuelve a marcar todas las páginas de ZRAM como inactivas y regresa al paso 2. Si la escritura diferida de ZRAM está inhabilitada, mmd marca todas las páginas de ZRAM como inactivas cuando se produce la recompresión de ZRAM después de la duración de inactividad de recompresión.

Orientación para la solución de problemas y la validación

Usa los siguientes pasos de validación y procedimientos de solución de problemas para verificar y diagnosticar las operaciones de mmd y ZRAM.

Valida la configuración de ZRAM

Para verificar que mmd configuró correctamente ZRAM durante el inicio, haz lo siguiente:

  1. Verifica el algoritmo de compresión activo y el tamaño del disco:

    cat /sys/block/zram0/comp_algorithm
    cat /sys/block/zram0/disksize
    
  2. Verifica las propiedades del sistema mmd y el estado del servicio en ejecución:

    getprop | grep mmd.zram
    dumpsys -l | grep mmd
    

Valida el mantenimiento y la escritura diferida de ZRAM

Verifica que las tareas de mantenimiento de escritura diferida y recompresión de ZRAM funcionen correctamente:

  1. Verifica el estado del dispositivo de bloqueo de respaldo:

    cat /sys/block/zram0/bd_stat
    
  2. Supervisa /sys/block/zram0/mm_stat para verificar la eficiencia de la recompresión. Los cambios en los tamaños de los datos comprimidos deberían aparecer después de los ciclos de mantenimiento.

Valida la escritura diferida por proceso

Se puede usar lo siguiente para validar que la escritura diferida por proceso funciona:

  • Verifica adb logcat -s mmd para ver si hay registros de escritura exitosos o diagnósticos de fallas.

Problemas y diagnósticos habituales

A continuación, se muestran situaciones de error comunes que el usuario puede encontrar:

  • WritebackDailyLimitExceeded: Este error indica que se alcanzó la cuota de mmd.zram.writeback.max_bytes_per_day. Cuando esto ocurre, mmd pausa la escritura diferida inactiva hasta que avanza el período continuo de 24 horas.
  • Process prefetch or writeback failed: Este error se puede observar en logcat cuando falla un ioctl. Entre las causas comunes, se incluyen las siguientes:
    • EBADF o ESRCH: El proceso de destino finalizó antes de que mmd pudiera enviar pidfd al kernel.
    • ENOSPC: La partición de almacenamiento de respaldo está llena o se agotó la cola del dispositivo de bucle.
  • ZRAM no configurado: Si mmd no logra configurar ZRAM durante el inicio, es posible que los scripts de inicio heredados de swapon_all o del proveedor hayan bloqueado /dev/block/zram0 antes de que se pudiera ejecutar mmd.