Cette page traite de l'interaction entre kswapd et lmkd sur Android, et montre comment ils fonctionnent ensemble pour gérer la pression de la mémoire.
Arrière-plan
Android utilise deux mécanismes principaux pour récupérer de la mémoire lorsque le système est à court de mémoire :
kswapd: daemon de noyau Linux standard qui récupère des pages en supprimant les pages propres sauvegardées par un fichier ou en échangeant des pages anonymes vers ZRAM.lmkd: daemon du tueur de mémoire faible de l'espace utilisateur d'Android. Lorsquekswapdne peut pas répondre aux demandes de mémoire et que la pression du système augmente (comme l'indique le PSI),lmkdintervient pour arrêter les processus afin de libérer de plus grands blocs de mémoire.
kswapd et lmkd sont deux systèmes complémentaires de récupération de mémoire. Ils s'efforcent d'atteindre le même objectif (créer de la mémoire libre), mais par des moyens différents. Pour comparer et différencier :
- Récupérable ou destructeur :
kswapdsupprime les pages de mémoire qui peuvent être lues à partir de l'espace de stockage, ou échange des pages de mémoire vers d'autres partitions de mémoire ou un espace de stockage qui peut être lu à la demande.lmkdarrête les processus, ce qui détruit l'état non volatil. - Récupération de la bande passante :
kswapdutilise plus de ressources processeur pour récupérer une quantité donnée de mémoire libre quelmkd, et prend plus de temps pour récupérer cette mémoire.lmkdpeut récupérer de grandes quantités de mémoire en peu de temps et avec relativement peu d'efforts. Sachez que sur les appareils mobiles, l'effort du processeur se traduit non seulement par une latence, mais aussi par une consommation de la batterie.
Buckets de score OOM
Android attribue un ajustement du score de mémoire insuffisante (OOM) (oom_score_adj) aux processus en fonction de leur importance pour l'utilisateur. Les processus avec un score plus élevé sont arrêtés en premier.
Voici quelques buckets courants :
- En cache (score >= 900) : processus qui ne sont pas nécessaires pour le moment, conservés pour un redémarrage plus rapide.
- Service (score de 500) : processus hébergeant les services démarrés.
- Service de premier plan (score : 200) : processus hébergeant des services de premier plan.
- Premier plan (score 0) : application avec laquelle l'utilisateur interagit actuellement.
Présentation détaillée : déclenchement de PSI et LMKD
Qu'est-ce que PSI ?
Pressure Stall Information (PSI) est une fonctionnalité du noyau Linux qui fournit un moyen canonique de mesurer les pénuries de ressources. Il suit le temps de retard des tâches en raison d'un manque de CPU, de mémoire et d'E/S.
PSI de mémoire : "some" par rapport à "full"
Le PSI de mémoire suit spécifiquement les retards causés par un manque de mémoire (par exemple, l'attente de la récupération de page ou des erreurs de page). Il fournit deux métriques :
some: pourcentage de temps pendant lequel au moins une tâche a été bloquée en raison d'un manque de mémoire. Cela indique que le manque de mémoire affecte les performances, mais que certaines tâches sont toujours en cours.full: pourcentage de temps pendant lequel toutes les tâches non inactives ont été bloquées simultanément. Cela indique un état critique dans lequel le système est complètement bloqué en attendant de la mémoire.
Comment PSI déclenche LMKD
Le daemon lmkd de l'espace utilisateur s'enregistre auprès du noyau pour recevoir des notifications lorsque les seuils PSI sont dépassés (en surveillant /proc/pressure/memory). Lorsque le système subit une forte pression de mémoire (par exemple, un blocage full dépassant une durée configurée), le noyau réveille lmkd. lmkd évalue ensuite l'état du système et décide s'il faut arrêter des processus, et lesquels, en fonction de leur score OOM et de leur taille.
Réglage de LMKD
Le comportement de LMKD peut être ajusté à l'aide des propriétés système.
Rechercher des valeurs de réglage sur un appareil
Vous pouvez trouver les valeurs de réglage actuelles de LMKD sur un appareil connecté en exécutant la commande suivante :
adb shell getprop | grep lmk
Propriétés LMKD courantes
Voici quelques-unes des principales propriétés utilisées pour régler LMKD (consultez la documentation Android pour obtenir la liste complète) :
ro.lmk.use_psi: définissez cette option surtruepour utiliser les moniteurs PSI pour la détection de la pression de la mémoire (par défaut sur Android 10 ou version ultérieure).ro.lmk.psi_partial_stall_ms: seuil de durée de blocage (partiel)someen millisecondes pour déclencher un arrêt (70 ms par défaut sur les appareils normaux, 200 ms sur les appareils à faible RAM).ro.lmk.psi_complete_stall_ms: seuil de durée de blocage defullen millisecondes pour déclencher un arrêt (700 ms par défaut).ro.lmk.kill_heaviest_task: si la valeur esttrue, LMKD arrête le processus qui utilise le plus de mémoire parmi les candidats du même bucket OOM.
Valeurs de réglage pertinentes sur l'appareil de test (Google Pixel 8 Pro)
(Remarque : Valeurs par défaut standards pour les appareils haut de gamme équipés d'Android 15)
ro.lmk.use_psi:truero.lmk.psi_partial_stall_ms:70(habituel)ro.lmk.psi_complete_stall_ms:700(habituel)ro.lmk.kill_heaviest_task:true
Vérification de ZRAM et de l'espace d'échange
La capacité de ZRAM a un impact important sur la vitesse à laquelle l'espace d'échange se remplit et déclenche les arrêts de LMKD. Vous pouvez vérifier l'état de ZRAM et de l'échange sur l'appareil à l'aide des commandes suivantes :
# Check ZRAM sizing and usage
adb shell free -m
# Check swap partition details
adb shell cat /proc/swaps
Exercice pratique : déclencher kswapd et lmkd
Dans cet exercice, vous allez utiliser l'application exemple KswapdLmkdLab pour créer des processus dans différents buckets OOM, puis consommer de la mémoire dans le processus principal pour déclencher kswapd et lmkd.
L'application est configurée pour générer automatiquement des sous-processus et leur allouer de la mémoire afin d'en faire des cibles attrayantes pour LMKD.
1. Créer des sous-processus
L'application génère automatiquement les sous-processus suivants au lancement :
- Processus mis en cache (oom_score_adj >= 900)
- Processus de service (oom_score_adj 500)
- Processus de service de premier plan (oom_score_adj 200)
Vérifiez les processus et leurs scores OOM à l'aide d'ADB :
adb shell dumpsys activity processes | grep com.android.kswapdlmkdlab
Exemple d'extrait de sortie :
Proc # 0: fg T/A/TOP LCMNFUATI t: 0 18959:com.android.kswapdlmkdlab/u0a358 (top-activity)
Proc # 1: prcp b/ /FGS -------TI t: 0 19050:com.android.kswapdlmkdlab:foreground/u0a358 (fg-service)
Proc # 2: svc b/ /SVC -------TI t: 0 19077:com.android.kswapdlmkdlab:service/u0a358 (started-services)
Proc # 3: cch b/ /CACC -------TI t: 0 19014:com.android.kswapdlmkdlab:cached/u0a358 (cch-client-act)
Les abréviations de l'état du processus correspondent à :
fg: premier plan (score OOM : 0)prcp: perceptible (service de premier plan, score OOM de 200)svc: service démarré (score OOM de 500)cch: processus mis en cache (score OOM de 900)
2. Déclencher la sollicitation de la mémoire
L'application commence automatiquement à allouer de la mémoire dans le processus principal. Pour que le test soit efficace sur les appareils dotés d'une grande quantité de RAM, l'application commande également aux sous-processus d'allouer de la mémoire (par exemple, 500 Mo chacun) afin d'en faire de meilleures cibles pour le LMKD.
Surveiller l'arrêt des sous-processus
L'application surveille l'arrêt de ces sous-processus à l'aide du mécanisme IBinder.linkToDeath :
- Le processus principal est lié à un service du processus cible à l'aide de
bindService. - Lors de la connexion, il obtient l'objet
IBinderreprésentant le service. - Il enregistre une implémentation
IBinder.DeathRecipienten appelantlinkToDeathsur le binder. - Lorsque le processus cible s'arrête (par exemple, lorsqu'il est arrêté par LMKD), le système appelle la méthode
binderDied(), ce qui permet à l'application d'enregistrer l'événement d'arrêt de manière asynchrone.
Pour éviter que cette liaison n'augmente la priorité des processus cibles (ce qui irait à l'encontre de l'objectif de l'exercice), l'application utilise l'indicateur Context.BIND_WAIVE_PRIORITY.
Application exemple en action
Voici une capture d'écran de l'application après l'exécution d'un test sur un Google Pixel 8 Pro (qui dispose d'environ 12 Go de RAM au total).
Analyse des traces Perfetto
Pour capturer l'interaction, utilisez la configuration Perfetto suivante afin de capturer la planification, les compteurs de mémoire, les arrêts LMK et les informations sur les blocages de pression (PSI).
Configuration de Perfetto
Capturez l'interaction en exécutant Perfetto avec la planification, les compteurs de mémoire, les arrêts LMK et les informations sur les blocages de pression (PSI) :
adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/kswapd_lmkd.perfetto-trace <<EOF
buffers: { size_kb: 131072 }
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "lowmemorykiller/lowmemory_kill"
ftrace_events: "vmscan/mm_vmscan_kswapd_wake"
ftrace_events: "vmscan/mm_vmscan_kswapd_sleep"
ftrace_events: "psi/psi_event"
}
}
}
duration_ms: 180000
EOF
Examiner la trace
La capture d'écran suivante est extraite d'une trace montrant la période allant de peu avant l'arrêt du processus :cached (le plus susceptible d'être arrêté) jusqu'à l'arrêt du processus :foreground (le moins susceptible d'être arrêté).
Comme vous pouvez le voir, MemFree est déjà assez bas et subit une pression constante.
kswapd fonctionne en permanence et soulage la pression en supprimant les pages mem.rss.file et en déplaçant mem.rss.anon vers mem.swap à partir de tous les processus kswapdlmkdlab (le processus principal et les sous-processus), et probablement d'autres processus non affichés dans cette capture d'écran. En même temps, l'application kswapdlmkdlab continue d'allouer de la mémoire, ce qui augmente la pression plus rapidement que kswapd ne peut la récupérer.
Lorsque la pression devient trop élevée, comme l'indique une valeur élevée de psi.mem pendant une période suffisamment longue, lmkd se réveille et arrête certains processus. Vous pouvez voir que lmkd respecte les scores OOM, en supprimant d'abord kswapdlmkdlab:cached, puis :service, puis :foreground.
Examinez la chronologie des événements et réfléchissez aux questions suivantes :
- Comment
kswapdetlmkdse complètent :kswapdest le premier à répondre à la pression de la mémoire, maislmkdest utile lorsque la bande passante de récupération dekswapdne répond pas à la demande d'allocation. - Taux de libération de la mémoire : observez le temps CPU passé dans
kswapdet la mémoire récupérée en supprimantmem.rss.fileou en échangeantmem.rss.anonversmem.swap. Comparez cela aux courtes périodes d'activité danslmkd, qui entraînent la libération d'une grande quantité de mémoire privée du processus.
Requêtes
Exécutez ces requêtes dans l'interface utilisateur de Perfetto (Query SQL) pour extraire les données de votre trace.
1. Activité kswapd
Pour savoir quand le daemon de remplacement du noyau était actif et en cours d'exécution, exécutez la commande suivante :
SELECT ts, dur, state
FROM thread_state
JOIN thread USING (utid)
WHERE thread.name LIKE 'kswapd%'
AND state = 'Running'
LIMIT 5;
Exemple de sortie :
| ts | dur | state |
|---|---|---|
| 30317633052969 | 159180 | En cours d'exécution |
| 30317633398347 | 3999186 | En cours d'exécution |
| 30317638448111 | 1847941 | En cours d'exécution |
| 30317641455354 | 1031006 | En cours d'exécution |
| 30317643272615 | 888387 | En cours d'exécution |
2. Arrêts par LMKD
LMKD écrit les arrêts pour mémoire insuffisante dans la table instant. Pour interroger toutes les suppressions LMKD :
SELECT ts, name
FROM instant
WHERE name LIKE 'lmk,%'
ORDER BY ts ASC;
Le format du nom de l'événement est le suivant : lmk,<pid>,<kill_reason>,<oom_score_adj>,<min_oom_score>,<max_thrashing>.
kill_reason: entier indiquant pourquoilmkda décidé d'arrêter le processus (défini danssystem/memory/lmkd/statslog.h) :0:PRESSURE_AFTER_KILL1:NOT_RESPONDING2:LOW_SWAP_AND_THRASHING(l'espace d'échange ZRAM est extrêmement faible et un thrashing s'est produit)3:LOW_MEM_AND_SWAP(le système manque de mémoire libre et d'échange ZRAM)4:LOW_MEM_AND_THRASHING5:DIRECT_RECL_AND_THRASHING6:LOW_MEM_AND_SWAP_UTIL7:LOW_FILECACHE_AFTER_THRASHING8:LOW_MEM9:DIRECT_RECL_STUCK
max_thrashing: pourcentage maximal de thrashing de page au moment de l'arrêt.
Exemple de sortie :
| ts | nom |
|---|---|
| 30326148679967 | lmk,2308,3,900,201,0 |
| 30330439763710 | lmk,2309,3,500,201,0 |
| 30334985999837 | lmk,2310,2,200,0,307 |
Visualisation
Pour visualiser les données, vous pouvez utiliser le script gnuplot suivant pour tracer la mémoire libre du système, l'activité kswapd, les arrêts lmkd et le PSI sur une seule série temporelle. Le script suppose qu'il est exécuté à partir du répertoire supérieur de compilation Android.
Exemple de tracé de sortie
Le graphique suivant a été généré à l'aide du script précédent avec des exemples de données représentatifs du test.
Explication du graphique :
Ce graphique illustre l'interaction dynamique entre les différents composants de gestion de la mémoire pendant le test de charge :
- Baisse de la mémoire disponible : à mesure que l'application principale alloue de la mémoire, la mémoire système disponible (ligne violette) diminue régulièrement.
- Activation de kswapd : lorsque la mémoire disponible passe en dessous de certains seuils,
kswapdse réveille (impulsions bleues) pour récupérer de la mémoire en effectuant un échange vers la ZRAM. Cela se traduit par des pics intermittents. - Pics de PSI : comme
kswapda du mal à suivre le taux d'allocation élevé, les threads commencent à se bloquer lors de l'allocation de mémoire, ce qui entraîne un pic de PSI (Pressure Stall Information) (ligne verte). - Arrêts par LMKD : lorsque le PSI atteint des niveaux critiques (par exemple, en dépassant conceptuellement le seuil
ro.lmk.psi_complete_stall_ms) et que la mémoire disponible est extrêmement faible,lmkdintervient en arrêtant les processus de faible priorité (impulsions orange aux instants 30 et 45) pour libérer de grandes quantités de mémoire. Notez qu'immédiatement après un arrêt, la mémoire disponible augmente et l'indicateur PSI diminue, ce qui montre l'efficacité de l'arrêt pour réduire la pression. ________________________________________________________________________________
← Récupération de mémoire : éviction et échange | ↑ Haut | Renvoyer des pages de mémoire à l'OS →