Interaksi kswapd dan lmkd

Halaman ini membahas interaksi antara kswapd dan lmkd di Android, yang menunjukkan cara keduanya bekerja sama untuk mengelola tekanan memori.

Latar belakang

Android menggunakan dua mekanisme utama untuk mengklaim kembali memori saat sistem kehabisan memori:

  1. kswapd: Daemon kernel Linux standar yang mengklaim kembali halaman dengan mengeluarkan halaman yang didukung file bersih atau menukar halaman anonim ke ZRAM.
  2. lmkd: Daemon Low Memory Killer userspace Android. Jika kswapd tidak dapat memenuhi permintaan memori dan tekanan sistem meningkat (seperti yang ditunjukkan oleh PSI), lmkd akan menghentikan proses untuk mengosongkan bagian memori yang lebih besar.

kswapd dan lmkd adalah dua sistem klaim kembali memori yang saling melengkapi. Keduanya berfungsi untuk mencapai sasaran yang sama (membuat memori kosong), tetapi dengan cara yang berbeda. Untuk membandingkan dan membedakan:

  • Dapat Dipulihkan vs Merusak: kswapd menghapus halaman memori yang dapat dibaca kembali dari penyimpanan, atau menukar halaman memori ke partisi memori atau penyimpanan lain yang dapat dibaca kembali sesuai permintaan. lmkd menghentikan proses, yang menghancurkan status non-volatil.
  • Bandwidth Reclaim: kswapd menggunakan lebih banyak CPU untuk mengklaim kembali sejumlah memori kosong tertentu daripada lmkd, dan memerlukan waktu lebih lama untuk mengklaim kembali memori tersebut. lmkd dapat mengklaim kembali memori dalam jumlah besar dalam waktu singkat dengan upaya yang relatif sedikit. Perlu diingat bahwa pada perangkat seluler, upaya CPU tidak hanya memengaruhi latensi, tetapi juga daya baterai.

Bucket skor OOM

Android menetapkan penyesuaian skor Out-Of-Memory (OOM) (oom_score_adj) ke proses berdasarkan kepentingannya bagi pengguna. Proses dengan skor lebih tinggi akan dihentikan terlebih dahulu.

Bucket umum mencakup:

  • Cached (Skor >= 900): Proses yang saat ini tidak diperlukan, disimpan untuk memulai ulang lebih cepat.
  • Layanan (Skor 500): Proses yang menghosting layanan yang dimulai.
  • Layanan Latar Depan (Skor 200): Proses yang menghosting layanan latar depan.
  • Latar depan (Skor 0): Aplikasi yang saat ini digunakan untuk berinteraksi oleh pengguna.

Pembahasan mendalam: Pemicuan PSI dan LMKD

Apa itu PSI?

Pressure Stall Information (PSI) adalah fitur kernel Linux yang menyediakan cara kanonis untuk mengukur kekurangan resource. PSI melacak waktu penundaan tugas karena kekurangan CPU, memori, dan I/O.

PSI Memori: "some" versus "full"

PSI memori secara khusus melacak penundaan yang disebabkan oleh kekurangan memori (misalnya, menunggu reklamasi halaman atau kesalahan ulang). Metrik ini menyediakan dua metrik:

  • some: Persentase waktu saat setidaknya satu tugas terhenti di memori. Hal ini menunjukkan bahwa kekurangan memori memengaruhi performa, tetapi beberapa pekerjaan masih berjalan.
  • full: Persentase waktu saat semua tugas yang tidak ada aktivitas terhenti secara bersamaan. Hal ini menunjukkan status kritis saat sistem sepenuhnya diblokir saat menunggu memori.

Cara PSI memicu LMKD

Daemon lmkd userspace mendaftar ke kernel untuk menerima notifikasi saat nilai minimum PSI terlampaui (dengan memantau /proc/pressure/memory). Saat sistem mengalami tekanan memori yang tinggi (misalnya, stall full melebihi durasi yang dikonfigurasi), kernel akan mengaktifkan lmkd. lmkd kemudian mengevaluasi status sistem dan memutuskan apakah akan menghentikan proses, dan proses mana, berdasarkan skor dan ukuran OOM-nya.

Penyesuaian LMKD

Perilaku LMKD dapat disesuaikan menggunakan properti sistem.

Menemukan nilai penyesuaian di perangkat

Anda dapat menemukan nilai penyesuaian LMKD saat ini di perangkat terhubung dengan menjalankan:

adb shell getprop | grep lmk

Properti LMKD umum

Berikut adalah beberapa properti utama yang digunakan untuk menyesuaikan LMKD (lihat Dokumentasi Android untuk mengetahui daftar lengkapnya):

  • ro.lmk.use_psi: Tetapkan ke true untuk menggunakan monitor PSI untuk deteksi tekanan memori (default di Android 10+).
  • ro.lmk.psi_partial_stall_ms: Nilai minimum untuk durasi stall some (parsial) dalam milidetik untuk memicu penghentian (default 70 md pada perangkat normal, 200 md pada perangkat dengan RAM rendah).
  • ro.lmk.psi_complete_stall_ms: Nilai minimum untuk durasi stall full dalam milidetik untuk memicu penghentian (default 700 md).
  • ro.lmk.kill_heaviest_task: Jika true, LMKD akan menghentikan proses yang menggunakan memori terbanyak di antara kandidat dalam bucket OOM yang sama.

Nilai penyesuaian yang relevan di perangkat pengujian (Google Pixel 8 Pro)

(Catatan: Default umum untuk perangkat kelas atas yang menjalankan Android 15)

  • ro.lmk.use_psi: true
  • ro.lmk.psi_partial_stall_ms: 70 (standar)
  • ro.lmk.psi_complete_stall_ms: 700 (standar)
  • ro.lmk.kill_heaviest_task: true

Verifikasi ZRAM dan swap

Kapasitas ZRAM sangat memengaruhi seberapa cepat swap terisi dan memicu penghentian LMKD. Anda dapat memeriksa status ZRAM dan swap di perangkat menggunakan perintah berikut:

# Check ZRAM sizing and usage
adb shell free -m

# Check swap partition details
adb shell cat /proc/swaps

Latihan praktis: memicu kswapd dan lmkd

Dalam latihan ini, Anda akan menggunakan aplikasi contoh KswapdLmkdLab untuk membuat proses di bucket OOM yang berbeda, lalu menggunakan memori dalam proses utama untuk memicu kswapd dan lmkd.

Aplikasi dikonfigurasi untuk otomatis menghasilkan subproses dan mengalokasikan memori agar menjadi target yang menarik bagi LMKD.

1. Membuat subproses

Aplikasi akan otomatis memunculkan subproses berikut saat diluncurkan:

  • Proses yang Di-cache (oom_score_adj >= 900)
  • Proses Layanan (oom_score_adj 500)
  • Proses Layanan Latar Depan (oom_score_adj 200)

Verifikasi proses dan skor OOM-nya menggunakan ADB:

adb shell dumpsys activity processes | grep com.android.kswapdlmkdlab

Contoh Cuplikan Output:

    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)

Singkatan status proses sesuai dengan:

  • fg: Latar depan (skor OOM 0)
  • prcp: Terlihat (Layanan Latar Depan, skor OOM 200)
  • svc: Layanan yang Dimulai (skor OOM 500)
  • cch: Proses yang Di-cache (skor OOM 900)

2. Memicu tekanan memori

Aplikasi akan otomatis mulai mengalokasikan memori dalam proses utama. Agar pengujian efektif pada perangkat dengan RAM tinggi, aplikasi juga memerintahkan subproses untuk mengalokasikan memori (misalnya, masing-masing 500 MB) agar menjadi target yang lebih baik untuk LMKD.

Memantau penghentian subproses

Aplikasi memantau penghentian subproses ini menggunakan mekanisme IBinder.linkToDeath:

  1. Proses utama mengikat ke layanan dalam proses target menggunakan bindService.
  2. Setelah terhubung, class ini akan mendapatkan objek IBinder yang mewakili layanan.
  3. Fungsi ini mendaftarkan implementasi IBinder.DeathRecipient dengan memanggil linkToDeath pada binder.
  4. Saat proses target berhenti (misalnya, saat dihentikan oleh LMKD), sistem akan memanggil metode binderDied(), sehingga aplikasi dapat mencatat peristiwa penghentian secara asinkron.

Untuk mencegah binding ini meningkatkan prioritas proses target (yang akan menggagalkan tujuan latihan), aplikasi menggunakan flag Context.BIND_WAIVE_PRIORITY.

Cara kerja aplikasi contoh

Berikut screenshot aplikasi setelah uji coba selesai di Google Pixel 8 Pro (yang memiliki total RAM sekitar 12 GB).

Screenshot aplikasi contoh setelah pengujian selesai dijalankan.
Gambar 1. Aplikasi contoh setelah pengujian selesai dijalankan di Pixel 8 Pro.

Analisis rekaman aktivitas Perfetto

Untuk merekam interaksi, gunakan konfigurasi Perfetto berikut untuk merekam penjadwalan, penghitung memori, penghentian LMK, dan Pressure Stall Information (PSI).

Konfigurasi Perfetto

Rekam interaksi dengan menjalankan Perfetto dengan penjadwalan, penghitung memori, penghentian LMK, dan Pressure Stall Information (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

Meninjau rekaman aktivitas

Screenshot berikut diambil dari rekaman aktivitas yang menunjukkan jangka waktu sebelum proses :cached (paling dapat dihentikan) dihentikan, hingga proses :foreground (paling tidak dapat dihentikan) dihentikan.

Screenshot UI Perfetto yang menampilkan aktivitas kswapd dan lmkd.
Gambar 2. Rekaman aktivitas Perfetto yang menampilkan klaim ulang kswapd dan penghentian lmkd.

Anda dapat melihat bahwa MemFree sudah cukup rendah dan terus mengalami tekanan. kswapd terus bekerja dan mengurangi tekanan dengan menghapus halaman mem.rss.file dan memindahkan mem.rss.anon ke mem.swap dari semua proses kswapdlmkdlab (proses utama dan subproses), dan kemungkinan dari proses lain yang tidak ditampilkan dalam screenshot ini. Pada saat yang sama, aplikasi kswapdlmkdlab terus mengalokasikan memori, sehingga meningkatkan tekanan lebih cepat daripada yang dapat diklaim kembali oleh kswapd.

Saat tekanan menjadi terlalu tinggi, seperti yang terlihat pada psi.mem yang tinggi selama jangka waktu yang cukup, lmkd akan aktif dan menghentikan beberapa proses. Anda dapat melihat bahwa lmkd mematuhi skor OOM, menghentikan kswapdlmkdlab:cached terlebih dahulu, lalu :service, kemudian :foreground.

Tinjau linimasa peristiwa dan renungkan hal berikut:

  • Cara kswapd dan lmkd saling melengkapi: kswapd adalah yang pertama merespons tekanan memori, tetapi lmkd membantu saat bandwidth reclaim kswapd tidak memenuhi permintaan alokasi.
  • Tingkat memori yang dikosongkan: amati waktu CPU yang dihabiskan di kswapd dan memori yang diklaim kembali dengan menghapus mem.rss.file atau menukar mem.rss.anon ke mem.swap. Bandingkan ini dengan aktivitas singkat di lmkd yang menyebabkan sejumlah besar memori pribadi proses dirilis.

Kueri

Jalankan kueri ini di Perfetto UI (Query SQL) untuk mengekstrak data dari trace Anda.

1. Aktivitas kswapd

Untuk menemukan kapan daemon swap kernel aktif dan berjalan, jalankan:

SELECT ts, dur, state
FROM thread_state
JOIN thread USING (utid)
WHERE thread.name LIKE 'kswapd%'
  AND state = 'Running'
LIMIT 5;

Contoh Output:

ts dur state
30317633052969 159180 Berjalan
30317633398347 3999186 Berjalan
30317638448111 1847941 Berjalan
30317641455354 1031006 Berjalan
30317643272615 888387 Berjalan

2. Penghentian LMKD

LMKD menulis penghentian proses akibat memori hampir habis ke tabel instant. Untuk mengkueri semua penghentian LMKD:

SELECT ts, name
FROM instant
WHERE name LIKE 'lmk,%'
ORDER BY ts ASC;

Format nama peristiwa adalah: lmk,<pid>,<kill_reason>,<oom_score_adj>,<min_oom_score>,<max_thrashing>.

  • kill_reason: Bilangan bulat yang menunjukkan alasan lmkd memutuskan untuk menghentikan proses (ditentukan dalam system/memory/lmkd/statslog.h):
    • 0: PRESSURE_AFTER_KILL
    • 1: NOT_RESPONDING
    • 2: LOW_SWAP_AND_THRASHING (Ruang swap ZRAM sangat rendah dan terjadi thrashing)
    • 3: LOW_MEM_AND_SWAP (Sistem kehabisan memori kosong dan swap ZRAM)
    • 4: LOW_MEM_AND_THRASHING
    • 5: DIRECT_RECL_AND_THRASHING
    • 6: LOW_MEM_AND_SWAP_UTIL
    • 7: LOW_FILECACHE_AFTER_THRASHING
    • 8: LOW_MEM
    • 9: DIRECT_RECL_STUCK
  • max_thrashing: Persentase thrashing halaman maksimum pada saat penghentian.

Contoh Output:

ts nama
30326148679967 lmk,2308,3,900,201,0
30330439763710 lmk,2309,3,500,201,0
30334985999837 lmk,2310,2,200,0,307

Visualisasi

Untuk memvisualisasikan data, Anda dapat menggunakan skrip gnuplot berikut untuk memetakan memori bebas sistem, aktivitas kswapd, penghentian lmkd, dan PSI pada satu deret waktu. Skrip ini mengasumsikan bahwa skrip dijalankan dari direktori teratas build Android.

Contoh plot output

Plot berikut dibuat menggunakan skrip sebelumnya dengan data sampel yang mewakili uji coba.

Plot memori bebas, aktivitas kswapd, PSI, dan penghentian lmkd.
Gambar 3. Plot tekanan memori yang menunjukkan memori bebas, kswapd, PSI, dan penghentian lmkd.

Penjelasan Plot:

Plot ini menggambarkan interaksi dinamis antara berbagai komponen manajemen memori selama uji ketahanan:

  1. Penurunan Memori Bebas: Saat aplikasi utama mengalokasikan memori, memori bebas sistem (garis ungu) terus menurun.
  2. Aktivasi kswapd: Saat memori bebas turun di bawah batas tertentu, kswapd akan aktif (impuls biru) untuk mengklaim kembali memori dengan melakukan pertukaran ke ZRAM. Hal ini terlihat sebagai lonjakan yang terputus-putus.
  3. Lonjakan PSI: Saat kswapd kesulitan mengimbangi tingkat alokasi yang tinggi, thread mulai terhenti pada alokasi memori, sehingga menyebabkan PSI (Pressure Stall Information) (garis hijau) melonjak.
  4. Penghentian LMKD: Saat PSI mencapai tingkat kritis (misalnya, melebihi batas ro.lmk.psi_complete_stall_ms secara konseptual) dan memori bebas sangat rendah, lmkd akan mengintervensi dengan menghentikan proses berprioritas rendah (impuls oranye pada waktu 30 dan 45) untuk mengosongkan sebagian besar memori. Perhatikan bahwa segera setelah penghentian, memori bebas meningkat dan PSI menurun, yang menunjukkan efektivitas penghentian dalam mengurangi tekanan. ________________________________________________________________________________

← Pengambilan kembali memori: penghapusan dan swap | ↑ Atas | Mengembalikan halaman memori ke OS →