Daemon de gestion de la mémoire

Android 17 et versions ultérieures sont compatibles avec le daemon de gestion de la mémoire (mmd), un daemon système qui gère la configuration du daemon, les paramètres réglables et les tâches de maintenance continues d'échange ou de ZRAM.

Arrière-plan

Avant l'introduction de mmd, les configurations ZRAM d'Android étaient fragmentées et offraient une personnalisation limitée. mmd résout ce problème en centralisant la gestion de ZRAM, ce qui permet une logique de configuration plus sophistiquée et simplifie l'ajout de nouvelles fonctionnalités et d'améliorations architecturales. mmd établit également une séparation claire des préoccupations entre le processus system_server basé sur Java et la gestion de la mémoire ou du swap au niveau du noyau.

Architecture et gestion de ZRAM

Une fois le démarrage terminé (c'est-à-dire lorsque sys.boot_completed=1), mmd_setup tente de configurer ZRAM avec les paramètres spécifiés. Une fois la configuration de ZRAM terminée, le système active le service mmd qui gère les tâches de maintenance en cours.

Avec le projet mmd, les opérations de maintenance sont initiées à partir de system_server en envoyant des requêtes Binder à mmd à l'aide de l'interface IMmd. mmd gère les tâches de maintenance liées à l'écriture différée ZRAM, à la recompression et à l'écriture différée par processus en fonction de son propre moteur de règles internes. La planification à partir de ActivityManagerService et les règles de maintenance ZRAM peuvent être configurées à l'aide des propriétés système.

Intégration du serveur système (system_server)

Le processus system_server basé sur Java détermine quand mmd est appelé. Le processus sépare les opérations de maintenance globales des optimisations de mémoire ciblées par application.

Maintenance normale du post-traitement

La maintenance globale de ZRAM est gérée par ActivityManagerService à l'aide de com.android.server.memory.ZramMaintenance.

zram-maintenance

Figure 1. Flux de planification de la maintenance ZRAM.

  • Moteur de planification : ZramMaintenance enregistre un job périodique en arrière-plan avec JobScheduler d'Android.
  • Contraintes de tâche : pour éviter les saccades de l'UI au premier plan ou la contention du processeur, la tâche est explicitement configurée avec setRequiresDeviceIdle(true) et setRequiresBatteryNotLow(true).
  • Déclenchement du binder : lorsque le planificateur déclenche onStartJob(), system_server appelle mmd.doZramMaintenanceAsync(). Il s'agit d'un appel Binder asynchrone unidirectionnel. system_server ne bloque pas l'attente de la fin des opérations de maintenance. mmd met cette opération en file d'attente sur un thread de worker en arrière-plan pour effectuer la recompression et la réécriture de manière séquentielle.

Réécriture par processus

L'éviction ciblée de la mémoire par processus est gérée par ActivityManagerService à l'aide de com.android.server.am.CachedAppOptimizer.

mmd-writeback

Figure 2 : Flux de réécriture par processus MMD.

Lorsqu'un processus passe à un état mis en cache en arrière-plan, ActivityManager effectue une compaction de la mémoire. Si un plantage dû à une faible mémoire du processus est visible par l'utilisateur (c'est-à-dire que le processus héberge une activité) et si la réécriture ZRAM par processus ramènerait l'espace mémoire utilisé du processus à presque zéro, le système procède comme suit :

  1. Après la compaction, CachedAppOptimizer publie un message différé (ZRAM_WRITEBACK_MSG) à son gestionnaire de compaction interne (différé de mZramWritebackWaitSeconds).
  2. Une fois le délai expiré, ActivityManager ouvre un descripteur de fichier de processus sécurisé pidfd.
  3. Le serveur système appelle mmd.asyncWritebackProcessZramMemory(pfd, callback).
  4. mmd exécute l'ioctl de réécriture par processus et renvoie des informations à l'aide de IMmdProcessWritebackCallback. Si l'opération réussit, ActivityManager signale l'enregistrement du processus (setIsZramWrittenBack(app, true)) pour améliorer le oom_score_adj du processus et enregistre les métriques dans FrameworkStatsLog.ZRAM_WRITEBACK_EVENT.

Préfetche par processus

Lorsqu'un utilisateur relance une application précédemment mise en cache (décongelée en raison de UNFREEZE_REASON_ACTIVITY), ActivityManager réduit la latence de démarrage de l'application causée par les principaux défauts de page du stockage de sauvegarde :

  1. CachedAppOptimizer intercepte l'événement de déblocage et appelle prefetchZram(app).
  2. Le serveur système distribue le pidfd de l'application sur Binder à l'aide de mmd.asyncPrefetchProcessZramMemory(pfd). mmd émet l'ioctl ZRAM_ANDROID_IOC_PROCESS_PREFETCH, en demandant au noyau de précharger de manière asynchrone les pages permutées dans la RAM pendant l'initialisation du thread d'UI principal de l'application.

Présentation des tâches de maintenance et de post-traitement

Cette section décrit les opérations de maintenance en arrière-plan et les tâches de post-traitement que mmd exécute pour optimiser l'espace d'échange et la mémoire système.

Maintenance en mmd

Dans mmd, le terme maintenance fait référence aux opérations de maintenance planifiées en arrière-plan qui optimisent l'utilisation de l'espace d'échange et de la mémoire physique sans affecter les performances au premier plan des utilisateurs actifs. Au lieu d'effectuer des balayages synchrones continus (qui entraîneraient de graves réveils du processeur et des à-coups dans l'UI), la maintenance est effectuée de manière asynchrone :

  1. system_server déclenche périodiquement doZramMaintenanceAsync() dans Binder.

  2. mmd place la requête dans une file d'attente de tâches en arrière-plan LowPrioWorkItem::ZramMaintenance.

  3. mmd comporte un seul thread de nœud de calcul qui gère à la fois une file d'attente de haute priorité et une file d'attente de basse priorité. Les éléments de travail à priorité élevée (comme la prélecture par processus) sont traités en premier et peuvent prendre le pas sur les éléments de travail à faible priorité. La maintenance et l'écriture différée par processus fonctionnent comme des éléments de travail de faible priorité. Lorsqu'il est dépilé, le thread de travail exécute séquentiellement deux opérations de maintenance principales :

    • Recompression ZRAM : parcourt les pages de swap existantes et recompressent les pages inactives à l'aide d'un algorithme de compression secondaire à rapport plus élevé, par exemple zstd.

    • Réécriture ZRAM : analyse les pages inactives et les supprime entièrement de la RAM pour les stocker dans la mémoire flash de sauvegarde, un périphérique de boucle à partir d'un fichier sur /data.

Tâches de post-traitement dans ZRAM

Dans le module ZRAM du noyau Linux et l'architecture mmd, les tâches de post-traitement sont les transformations asynchrones appliquées aux pages mémoire après qu'elles ont déjà été permutées par les chemins de récupération standards du noyau (kswapd ou compaction).

Lorsqu'une page est initialement supprimée, le système privilégie la vitesse : il utilise un algorithme de compression primaire rapide (comme lz4) et stocke la page compressée dans la RAM. Toutefois, au fil du temps, de nombreuses pages permutées deviennent froides ou inactives (par exemple, les applications mises en cache en arrière-plan qui ne sont pas reprises pendant des heures). Il est inefficace de laisser des pages froides dans la ZRAM rapide et légèrement compressée.

Pipeline de post-traitement

mmd implémente un cycle de vie de post-traitement en plusieurs étapes pour optimiser ces pages :

mmd-page-lifecycle

Figure 3. cycle de vie de la page mmd.

  1. Étape 1 : Échange initial (compression rapide) : la mémoire est d'abord récupérée par le biais de kswapd ou de la compaction d'application. En règle générale, cette première récupération est effectuée à l'aide d'un algorithme de compression rapide tel que lz4, et le contenu est stocké dans la RAM.

  2. Étape 2 : Marquage de l'inactivité (vieillissement et suivi) : le suivi de l'inactivité mmd accède au suivi de la mémoire du noyau (CONFIG_ZRAM_TRACK_ENTRY_ACTIME) ou utilise son marqueur d'inactivité logicielle pour suivre la durée pendant laquelle les pages sont restées inutilisées.

  3. Étape 3 : Post-traitement 1 – Recompression (récupération en mémoire) : Les pages qui atteignent l'âge d'inactivité de recompression (de min_idle_seconds à max_idle_seconds) sont recompressées. mmd écrit dans /sys/block/zram0/recompress pour demander au noyau de décompresser la page lz4 et de la recompresser à l'aide de zstd. Cela permet de récupérer la RAM physique sans entraîner d'usure de l'écriture flash.

  4. Étape 4 : Post-traitement 2 – Écriture différée (éviction vers le stockage flash) : si la pression sur la mémoire persiste et que les pages atteignent l'âge d'inactivité d'écriture différée (généralement 20 heures ou plus), mmd déclenche l'écriture différée. mmd écrit dans /sys/block/zram0/idle et /sys/block/zram0/writeback pour évincer complètement la page compressée de la RAM vers le stockage flash de sauvegarde.

Configuration de ZRAM

mmd charge et traite les propriétés de configuration ZRAM suivantes :

Propriété Utiliser Par défaut
mmd.zram.enabled Indique si la configuration ZRAM mmd est activée. false
mmd.zram.num_devices Nombre d'appareils ZRAM à configurer. Pour un nombre N, les appareils zram0 à zram<N-1> doivent être présents avant que le système ne définisse sys.boot_completed=1. Les propriétés de la liste des appareils ZRAM peuvent être configurées individuellement. 1
mmd.zram.device_priority Valeurs de priorité à transmettre lors de l'appel de swapon. Non défini
mmd.zram.comp_algorithm Algorithme de compression ZRAM. Si aucun algorithme n'est spécifié, l'algorithme de compression par défaut du noyau est utilisé. Non défini
mmd.zram.size Taille de l'appareil ZRAM en octets ou pourcentage de la taille de la RAM de l'appareil (par exemple, 75%). 50%
mmd.zram.writeback.enabled Indique si le writeback ZRAM doit être activé. false
mmd.zram.writeback.device_size Taille du périphérique de réécriture en octets ou en pourcentage de la partition de données. La taille réelle de l'appareil peut être ajustée en fonction de l'espace disponible sur la partition de données. 1073741824 (1 Gio)
mmd.zram.writeback.min_free_space_mib Espace libre minimal en Mio qui doit être disponible après la configuration du périphérique de réécriture. 1536 (1,5 Gio)
mmd.zram.writeback.use_nr_tags_prop Lorsque true, utilise la valeur dans mmd.zram.writeback.nr_tags pour configurer la profondeur de file d'attente de l'écriture différée ZRAM de sauvegarde du périphérique de boucle. Il s'agit d'une solution de contournement pour les situations où la règle SELinux du fournisseur ne peut pas être configurée pour permettre à mmd de lire directement nr_tags de la sauvegarde du périphérique de bloc /data. false
mmd.zram.writeback.nr_tags Consultez les mmd.zram.writeback.use_nr_tags_prop. Non défini
mmd.zram.recompression.enabled Indique s'il faut activer la fonctionnalité de recompression ZRAM. false
mmd.zram.recompression.algorithm Algorithme de recompression ZRAM secondaire. zstd

Propriétés des appareils par ZRAM

Lorsque mmd.zram.num_devices est supérieur à un, des propriétés spécifiques peuvent éventuellement être configurées pour chaque appareil ZRAM en définissant la propriété sur une valeur séparée par des virgules contenant exactement mmd.zram.num_devices éléments. Ces propriétés incluent :

  • 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

Abandon de la configuration ZRAM existante

Bien que swapon_all soit toujours disponible dans Android pour configurer l'espace d'échange ZRAM et basé sur le disque, mmd est l'approche privilégiée pour la gestion de ZRAM, car elle facilite la configuration et offre des fonctionnalités avancées telles que la recompression ZRAM.

Lorsque la configuration ZRAM mmd est activée par mmd.zram.enabled :

  • La configuration ZRAM dans l'implémentation swapon_all devient une opération sans effet.
  • Les configurations ZRAM existantes, telles que config_zramWriteback dans le fichier de superposition config.xml et les propriétés système d'écriture différée ro.zram.*, sont ignorées.

Paramètres réglables de maintenance ZRAM

La maintenance ZRAM devrait fonctionner immédiatement. Vous pouvez l'affiner davantage à l'aide des propriétés système de cette section.

Planification de la maintenance ZRAM

Ces propriétés contrôlent comment et quand les tâches de maintenance ZRAM sont planifiées par system_server.

Propriété Utiliser Par défaut
mm.zram.maintenance.first_delay_seconds Délai avant le lancement de la première maintenance ZRAM. 3600 (1 heure)
mm.zram.maintenance.periodic_delay_seconds Délai entre les planifications de maintenance ZRAM ultérieures. 3600 (1 heure)
mm.zram.maintenance.require_device_idle Indique s'il faut n'initier la maintenance ZRAM que lorsque l'appareil est inactif. true
mm.zram.maintenance.require_battery_not_low Indique s'il faut que la batterie ne soit pas faible avant de lancer la maintenance ZRAM. true

Stratégie de réécriture ZRAM

Les paramètres suivants contrôlent quand et quel type de mémoire est écrit sur le périphérique de stockage :

Propriété Utiliser Par défaut
mmd.zram.writeback.backoff_seconds Temps d'attente depuis la dernière opération d'écriture. 600 (10 minutes)
mmd.zram.writeback.min_idle_seconds Combiné à mmd.zram.writeback.max_idle_seconds pour calculer l'âge d'inactivité d'une page afin qu'elle soit éligible à la réécriture en fonction de la fraction d'utilisation de la mémoire. L'âge d'inactivité calculé est interpolé de manière exponentielle entre les deux paramètres pour minimiser le travail lorsque la mémoire n'est pas sous pression. 72000 (20 heures)
mmd.zram.writeback.max_idle_seconds Nombre maximal de secondes utilisées pour calculer l'âge de la page inactive de manière dynamique en fonction de l'utilisation de la mémoire. 90000 (25 heures)
mmd.zram.writeback.huge.enabled Indique si la réécriture de la page HUGE doit être activée. false
mmd.zram.writeback.idle.enabled Indique si la réécriture de la page IDLE doit être activée. true
mmd.zram.writeback.huge_idle.enabled Indique si la réécriture de la page HUGE_IDLE doit être activée. true
mmd.zram.writeback.min_bytes Nombre minimal d'octets à réécrire en une seule série de réécriture en cas d'inactivité. 5242880 (5 Mio)
mmd.zram.writeback.max_bytes Nombre maximal d'octets à réécrire en une seule fois lors de la réécriture en cas d'inactivité. 314572800 (300 Mio)
mmd.zram.writeback.max_bytes_per_day Nombre maximal d'octets à réécrire sur une période de 24 heures. 25769803776 (24 Gio)
mmd.zram.writeback.limit.enabled Indique si la comptabilisation de la limite de budget de réécriture quotidienne doit être activée. true

Règle de recompression ZRAM

Les paramètres suivants contrôlent le moment et le type de mémoire à recompresser :

Propriété Utiliser Par défaut
mmd.zram.recompression.backoff_seconds Temps d'attente depuis la dernière recompression. 1800 (30 minutes)
mmd.zram.recompression.min_idle_seconds Combiné à mmd.zram.recompression.max_idle_seconds pour calculer l'âge d'inactivité d'une page afin qu'elle soit éligible à la recompression en fonction de la fraction d'utilisation de la mémoire. L'âge d'inactivité calculé est interpolé de manière exponentielle entre les deux paramètres pour minimiser le travail lorsqu'il n'y a pas de pression sur la mémoire. 7200 (2 heures)
mmd.zram.recompression.max_idle_seconds Nombre maximal de secondes utilisées pour calculer dynamiquement l'ancienneté de la page inactive. 14400 (4 heures)
mmd.zram.recompression.threshold_bytes Taille minimale en octets des pages ZRAM considérées pour la recompression. 1024 (1 Kio)
mmd.zram.recompression.huge.enabled Indique si la recompression de la page HUGE doit être activée. true
mmd.zram.recompression.idle.enabled Indique si la recompression de la page IDLE doit être activée. true
mmd.zram.recompression.huge_idle.enabled Indique si la recompression de la page HUGE_IDLE doit être activée. true

Suivi des pages inactives de la ZRAM

mmd La maintenance de zRAM marque les pages zRAM comme inactives en fonction du temps écoulé depuis leur dernier accès. Cette fonctionnalité nécessite l'activation des configurations du noyau CONFIG_ZRAM_TRACK_ENTRY_ACTIME ou CONFIG_ZRAM_MEMORY_TRACKING. CONFIG_ZRAM_TRACK_ENTRY_ACTIME est activé par défaut sur les noyaux GKI 6.18 et versions ultérieures. Sur les anciens noyaux, il présente une surcharge de mémoire et n'est pas activé par défaut.

Si la configuration du noyau n'est pas activée, la maintenance mmd ZRAM revient à une logique de substitution logicielle pour suivre les pages ZRAM inactives :

  1. Marquez toutes les pages ZRAM comme inactives lorsque mmd démarre.

  2. Ignorer les prochaines maintenances ZRAM jusqu'à ce que la période de désactivation requise soit écoulée.

  3. La zRAM réécrit ou recompressent les pages inactives. S'il reste des pages inactives en raison des limites de réécriture, mmd continue de réécrire les pages lors de la prochaine maintenance sans marquer de nouvelles pages comme inactives (en ignorant l'étape 4).

  4. Si toutes les pages inactives sont réécrites, marquez à nouveau toutes les pages ZRAM comme inactives et revenez à l'étape 2. Si la réécriture ZRAM est désactivée, mmd marque toutes les pages ZRAM comme inactives lorsque la recompression ZRAM a lieu après la durée d'inactivité de recompression.

Conseils de dépannage et de validation

Suivez les étapes de validation et les procédures de dépannage ci-dessous pour vérifier et diagnostiquer les opérations mmd et ZRAM.

Valider la configuration de ZRAM

Pour vérifier que mmd a bien configuré ZRAM au démarrage :

  1. Vérifiez l'algorithme de compression actif et la taille du disque :

    cat /sys/block/zram0/comp_algorithm
    cat /sys/block/zram0/disksize
    
  2. Vérifiez les propriétés du système mmd et l'état du service en cours d'exécution :

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

Valider la maintenance et l'écriture différée de ZRAM

Vérifiez que les tâches de maintenance de réécriture et de recompression ZRAM fonctionnent :

  1. Vérifiez l'état du périphérique de bloc de stockage :

    cat /sys/block/zram0/bd_stat
    
  2. Vérifiez l'efficacité de la recompression en surveillant /sys/block/zram0/mm_stat. Les modifications de la taille des données compressées devraient apparaître après les cycles de maintenance.

Valider le renvoi des données par processus

Les éléments suivants peuvent être utilisés pour valider le bon fonctionnement de la réécriture par processus :

  • Consultez adb logcat -s mmd pour obtenir les journaux d'écriture différée réussie ou les diagnostics d'échec.

Problèmes courants et diagnostics

Voici quelques situations d'erreur courantes que l'utilisateur peut rencontrer :

  • WritebackDailyLimitExceeded : cette erreur indique que le quota mmd.zram.writeback.max_bytes_per_day a été atteint. Dans ce cas, mmd suspend l'écriture différée inactive jusqu'à ce que la période de 24 heures avance.
  • Process prefetch or writeback failed : cette erreur peut être observée dans logcat lorsqu'un ioctl échoue. Voici les causes les plus courantes :
    • EBADF ou ESRCH : le processus cible s'est terminé avant que mmd puisse envoyer pidfd au noyau.
    • ENOSPC : la partition de stockage sous-jacente est pleine ou la file d'attente du périphérique de boucle est épuisée.
  • ZRAM non configuré : si mmd ne parvient pas à configurer ZRAM au démarrage, cela peut être dû au fait que les anciens scripts d'initialisation swapon_all ou ceux du fournisseur ont verrouillé /dev/block/zram0 avant que mmd puisse s'exécuter.