В этой статье рассказывается о взаимодействии kswapd и lmkd на Android и о том, как они вместе управляют нагрузкой на память.
Фон
В Android есть два основных механизма освобождения памяти, когда ее не хватает:
kswapd– стандартный демон ядра Linux, который освобождает страницы, удаляя чистые страницы с файловой поддержкой или заменяя анонимные страницы на ZRAM.lmkd– фоновый процесс Android, который завершает работу приложений, когда на устройстве заканчивается память. Когдаkswapdне справляется с нагрузкой на память и давление на систему возрастает (о чем сигнализирует PSI),lmkdначинает завершать процессы, чтобы освободить больше памяти.
kswapd и lmkd – это две взаимодополняющие системы освобождения памяти. Они работают над достижением одной и той же цели (создание свободной памяти), но разными средствами. Для сравнения
и сопоставления:
- Восстановимые и невосстановимые страницы.
kswapdудаляет страницы памяти, которые можно повторно считать из хранилища, или переносит их в другие разделы памяти или хранилища, откуда их можно считать по запросу.lmkdзавершает процессы, что приводит к потере энергонезависимого состояния. - Освобождение пропускной способности:
kswapdтратит больше ресурсов ЦП на освобождение определенного объема памяти, чемlmkd, и делает это медленнее.lmkdможет освобождать большие объемы памяти за короткие промежутки времени, не требуя значительных усилий. Учтите, что на мобильных устройствах нагрузка на ЦП влияет не только на задержку, но и на расход заряда батареи.
Сегменты оценок OOM
Android присваивает процессам корректировку оценки нехватки памяти (oom_score_adj) в зависимости от их важности для пользователя. Процессы с более высокими оценками
завершаются первыми.
Вот примеры распространенных сегментов:
- Кешированные (оценка >= 900). Процессы, которые сейчас не нужны, но хранятся в памяти для быстрого перезапуска.
- Сервис (500 баллов). Процессы, в которых размещаются запущенные сервисы.
- Активная служба (200 баллов). Процессы, в которых размещены активные службы.
- Активное приложение (оценка 0). Приложение, с которым пользователь взаимодействует в данный момент.
Подробный обзор: запуск PSI и LMKD
Что такое PSI?
Pressure Stall Information (PSI) – это функция ядра Linux, которая позволяет измерять нехватку ресурсов. Она отслеживает время задержки задач из-за нехватки ресурсов ЦП, памяти и I/O.
PSI памяти: "some" и "full"
PSI памяти отслеживает задержки, вызванные нехваткой памяти (например, ожидание освобождения страниц или повторных ошибок). Он содержит два показателя:
some– процент времени, в течение которого выполнение хотя бы одной задачи было приостановлено из-за нехватки памяти. Это означает, что нехватка памяти влияет на производительность, но некоторые процессы все ещё выполняются.full– процент времени, в течение которого все небездействующие задачи были остановлены одновременно. Это указывает на критическое состояние, когда система полностью заблокирована в ожидании памяти.
Как PSI запускает LMKD
Демон lmkd в пространстве пользователя регистрируется в ядре, чтобы получать уведомления о превышении пороговых значений PSI (путем отслеживания /proc/pressure/memory). Когда система испытывает высокую нагрузку на память (например, задержка full превышает заданную продолжительность), ядро активирует lmkd. Затем lmkd оценивает состояние системы и решает, нужно ли завершать процессы и какие именно, исходя из их размера и оценки OOM.
Настройка LMKD
Поведение LMKD можно настроить с помощью системных свойств.
Как найти значения настройки на устройстве
Чтобы найти текущие значения настройки LMKD на подключенном устройстве, выполните следующую команду:
adb shell getprop | grep lmk
Общие свойства LMKD
Ниже приведены некоторые ключевые свойства, используемые для настройки LMKD (полный список см. в документации Android).
ro.lmk.use_psi: установите значениеtrue, чтобы использовать мониторы PSI для обнаружения нехватки памяти (по умолчанию в Android 10 и более поздних версиях).ro.lmk.psi_partial_stall_ms– пороговое значение длительности частичной задержкиsomeв миллисекундах, при котором происходит принудительное завершение (по умолчанию 70 мс на обычных устройствах и 200 мс на устройствах с малым объемом ОЗУ).ro.lmk.psi_complete_stall_ms– пороговое значение длительности задержкиfullв миллисекундах, при достижении которого процесс будет завершен (по умолчанию 700 мс).ro.lmk.kill_heaviest_task– еслиtrue, LMKD завершает процесс, который использует больше всего памяти среди кандидатов в том же сегменте OOM.
Значения параметров на тестовом устройстве (Google Pixel 8 Pro)
(Примечание. Это стандартные значения по умолчанию для устройств высокого класса с Android 15.)
ro.lmk.use_psi:truero.lmk.psi_partial_stall_ms:70(типичное значение)ro.lmk.psi_complete_stall_ms:700(типичное значение)ro.lmk.kill_heaviest_task:true
Проверка ZRAM и файла подкачки
Емкость ZRAM сильно влияет на то, как быстро заполняется раздел подкачки и запускается LMKD. Проверить статус ZRAM и файла подкачки на устройстве можно с помощью следующих команд:
# Check ZRAM sizing and usage
adb shell free -m
# Check swap partition details
adb shell cat /proc/swaps
Практическое упражнение: запуск kswapd и lmkd
В этом упражнении вы будете использовать пример приложения KswapdLmkdLab, чтобы создать процессы в разных сегментах OOM, а затем задействовать память в основном процессе, чтобы активировать kswapd и lmkd.
Приложение настроено так, чтобы автоматически порождать подпроцессы и выделять им память, делая их привлекательными целями для LMKD.
1. Создание подпроцессов
При запуске приложение автоматически порождает следующие подпроцессы:
- Кешированный процесс (oom_score_adj >= 900)
- Процесс сервиса (oom_score_adj 500)
- Процесс активной службы (oom_score_adj 200)
Проверьте процессы и их оценки OOM с помощью ADB:
adb shell dumpsys activity processes | grep com.android.kswapdlmkdlab
Пример фрагмента выходных данных:
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)
Сокращения состояний процесса:
fg: активное приложение (OOM-оценка 0)prcp: Perceptible (активная служба, оценка OOM 200)svc: запущенный сервис (оценка OOM – 500)cch: кешированный процесс (оценка OOM – 900)
2. Как вызвать нехватку памяти
Приложение автоматически начнет выделять память в основном процессе. Чтобы тест был эффективным на устройствах с большим объемом ОЗУ, приложение также заставляет подпроцессы выделять память (например, по 500 МБ), чтобы они становились более подходящими целями для LMKD.
Отслеживать завершение подпроцессов
Приложение отслеживает завершение этих подпроцессов с помощью механизма IBinder.linkToDeath:
- Основной процесс привязывается к сервису в целевом процессе с помощью
bindService. - После подключения он получает объект
IBinder, представляющий сервис. - Он регистрирует реализацию
IBinder.DeathRecipient, вызываяlinkToDeathв связывателе. - Когда целевой процесс завершается (например, из-за LMKD), система вызывает метод
binderDied(), позволяя приложению асинхронно записать событие завершения.
Чтобы привязка не повышала приоритет целевых процессов (что противоречит цели упражнения), приложение использует флаг Context.BIND_WAIVE_PRIORITY.
Пример приложения в действии
Ниже приведен скриншот приложения после завершения тестового запуска на Google Pixel 8 Pro (общий объем ОЗУ составляет около 12 ГБ).
Анализ трассировки Perfetto
Чтобы зафиксировать взаимодействие, используйте следующую конфигурацию Perfetto для записи планирования, счетчиков памяти, завершения процессов LMK и информации о задержках из-за нехватки ресурсов (PSI).
Конфигурация Perfetto
Запишите взаимодействие, запустив Perfetto с планированием, счетчиками памяти, завершением процессов LMK и информацией о задержках из-за нехватки ресурсов (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
Просмотр трассировки
На скриншоте ниже показан период времени, в течение которого были завершены процессы от :cached (наименее важного) до :foreground (наиболее важного).
Как видите, MemFree уже довольно низкий и постоянно снижается.
kswapd постоянно работает и снижает нагрузку, удаляя mem.rss.file страниц и перемещая mem.rss.anon в mem.swap из всех процессов kswapdlmkdlab (основного и дополнительных), а также, вероятно, из других процессов, не показанных на этом скриншоте. В то же время приложение kswapdlmkdlab продолжает выделять память, увеличивая нагрузку быстрее, чем kswapd может ее освободить.
Когда давление становится слишком высоким, о чем свидетельствует высокое значение psi.mem в течение достаточного периода времени, lmkd активируется и завершает некоторые процессы. Как видите, lmkd учитывает оценки OOM и сначала завершает процесс kswapdlmkdlab:cached, затем :service и, наконец, :foreground.
Просмотрите хронологию событий и подумайте над следующим:
- Как
kswapdиlmkdдополняют друг друга:kswapdпервым реагирует на нехватку памяти, ноlmkdпомогает, когда пропускная способностьkswapdне соответствует спросу на выделение памяти. - Скорость освобождения памяти. Обратите внимание на процессорное время, затраченное на
kswapd, и память, освобождаемую за счет удаленияmem.rss.fileили заменыmem.rss.anonнаmem.swap. Сравните это с короткими всплесками активности вlmkd, которые приводят к освобождению большого объема частной памяти процесса.
Запросы
Выполните эти запросы в интерфейсе Perfetto (Query SQL), чтобы извлечь данные из трассировки.
1. Активность kswapd
Чтобы узнать, когда управляющая программа подкачки ядра была активна и работала, выполните следующую команду:
SELECT ts, dur, state
FROM thread_state
JOIN thread USING (utid)
WHERE thread.name LIKE 'kswapd%'
AND state = 'Running'
LIMIT 5;
Пример выходных данных:
| ts | dur | штат |
|---|---|---|
| 30317633052969 | 159180 | Бег |
| 30317633398347 | 3999186 | Бег |
| 30317638448111 | 1847941 | Бег |
| 30317641455354 | 1031006 | Бег |
| 30317643272615 | 888387 | Бег |
2. Остановки LMKD
LMKD записывает сведения о принудительном завершении процессов из-за нехватки памяти в таблицу instant. Чтобы запросить все завершения LMKD, выполните следующую команду:
SELECT ts, name
FROM instant
WHERE name LIKE 'lmk,%'
ORDER BY ts ASC;
Формат названия события:
lmk,<pid>,<kill_reason>,<oom_score_adj>,<min_oom_score>,<max_thrashing>.
kill_reason– целое число, указывающее, почемуlmkdрешил завершить процесс (определено вsystem/memory/lmkd/statslog.h):0:PRESSURE_AFTER_KILL1:NOT_RESPONDING2:LOW_SWAP_AND_THRASHING(ZRAM swap space is extremely low and thrashing occurred)3:LOW_MEM_AND_SWAP(в системе мало свободной памяти и места в файле подкачки 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– максимальный процент пробуксовки страниц в момент завершения.
Пример выходных данных:
| ts | название |
|---|---|
| 30326148679967 | lmk,2308,3,900,201,0 |
| 30330439763710 | lmk,2309,3,500,201,0 |
| 30334985999837 | lmk,2310,2,200,0,307 |
Визуализация
Чтобы визуализировать данные, вы можете использовать следующий скрипт gnuplot для построения графика свободной памяти системы, активности kswapd, завершений lmkd и PSI в одном временном ряду. Предполагается, что скрипт запускается из верхнего каталога сборки Android.
Пример графика выходных данных
Следующий график был сгенерирован с помощью предыдущего скрипта с выборкой данных, представляющей тестовый запуск.
Объяснение сюжета
На этом графике показано динамическое взаимодействие между различными компонентами управления памятью во время стресс-теста:
- Снижение объема свободной памяти. По мере того как основное приложение выделяет память, объем свободной системной памяти (фиолетовая линия) неуклонно снижается.
- Активация kswapd. Когда объем свободной памяти падает ниже определенного уровня,
kswapdактивируется (синие импульсы), чтобы освободить память, перенеся данные в ZRAM. Это выглядит как периодические скачки. - Скачки PSI. Поскольку
kswapdне справляется с высокой скоростью выделения памяти, потоки начинают задерживаться на этом этапе, что приводит к скачкам PSI (зеленая линия). - Завершение процессов демоном LMKD. Когда PSI достигает критического уровня (например, превышает пороговое значение
ro.lmk.psi_complete_stall_ms) и объем свободной памяти становится крайне низким,lmkdзавершает процессы с низким приоритетом (оранжевые импульсы на отметках 30 и 45), чтобы освободить большие объемы памяти. Обратите внимание, что сразу после завершения процесса объем свободной памяти увеличивается, а PSI снижается. Это говорит о том, что завершение процесса эффективно снижает нагрузку. ________________________________________________________________________________
← Освобождение памяти: вытеснение и подкачка | ↑ Наверх | Возврат страниц памяти операционной системе →