Tương tác kswapd và lmkd

Trang này trình bày hoạt động tương tác giữa kswapd và lmkd trên Android, minh hoạ cách hai thành phần này phối hợp với nhau để quản lý áp lực bộ nhớ.

Nền

Android sử dụng hai cơ chế chính để lấy lại bộ nhớ khi hệ thống sắp hết bộ nhớ:

  1. kswapd: Một trình nền nhân Linux tiêu chuẩn giúp lấy lại các trang bằng cách loại bỏ các trang được hỗ trợ tệp sạch hoặc hoán đổi các trang ẩn danh sang ZRAM.
  2. lmkd: Trình nền đóng các tiến trình ít cần thiết khi còn ít dung lượng (Low Memory Killer Daemon) trong không gian người dùng của Android. Khi kswapd không thể đáp ứng nhu cầu về bộ nhớ và áp lực hệ thống tăng lên (như được báo hiệu bằng PSI), lmkd sẽ can thiệp để loại bỏ các quy trình nhằm giải phóng các phần bộ nhớ lớn hơn.

kswapd và lmkd là hai hệ thống thu hồi bộ nhớ bổ sung cho nhau. Chúng hoạt động để đạt được cùng một mục tiêu (tạo bộ nhớ trống) nhưng bằng các phương tiện khác nhau. Cách so sánh và đối chiếu:

  • Có thể khôi phục so với Có tính huỷ diệt: kswapd loại bỏ các trang bộ nhớ có thể đọc lại từ bộ nhớ hoặc hoán đổi các trang bộ nhớ sang các phân vùng bộ nhớ khác hoặc bộ nhớ có thể đọc lại theo yêu cầu. lmkd tắt các quy trình, từ đó huỷ trạng thái không biến động.
  • Băng thông thu hồi: kswapd tốn nhiều CPU hơn để thu hồi một lượng bộ nhớ trống nhất định so với lmkd và mất nhiều thời gian hơn để thu hồi bộ nhớ đó. lmkd có thể lấy lại một lượng lớn bộ nhớ trong thời gian ngắn mà không tốn nhiều công sức. Hãy lưu ý rằng trên thiết bị di động, hiệu suất CPU không chỉ ảnh hưởng đến độ trễ mà còn ảnh hưởng đến thời lượng pin.

Nhóm điểm OOM

Android chỉ định mức điều chỉnh điểm số hết bộ nhớ (OOM) (oom_score_adj) cho các quy trình dựa trên mức độ quan trọng của các quy trình đó đối với người dùng. Những quy trình có điểm cao hơn sẽ bị loại bỏ trước.

Các nhóm phổ biến bao gồm:

  • Đã lưu vào bộ nhớ đệm (Điểm >= 900): Các quy trình hiện không cần thiết, được giữ lại để khởi động lại nhanh hơn.
  • Dịch vụ (Điểm 500): Các quy trình lưu trữ dịch vụ đã bắt đầu.
  • Dịch vụ nền trước (Điểm 200): Các quy trình lưu trữ dịch vụ nền trước.
  • Nền trước (Điểm 0): Ứng dụng mà người dùng hiện đang tương tác.

Tìm hiểu sâu: Kích hoạt PSI và LMKD

PSI là gì?

Thông tin về tình trạng thiếu tài nguyên (PSI) là một tính năng của nhân Linux, cung cấp một cách chuẩn hoá để đo lường tình trạng thiếu tài nguyên. Tính năng này theo dõi thời gian các tác vụ bị trì hoãn do thiếu CPU, bộ nhớ và I/O.

PSI bộ nhớ: "một số" so với "đầy đủ"

Cụ thể, PSI bộ nhớ theo dõi độ trễ do thiếu bộ nhớ (ví dụ: chờ thu hồi trang hoặc lỗi trang). Báo cáo này cung cấp hai chỉ số:

  • some: Tỷ lệ phần trăm thời gian mà ít nhất một tác vụ bị đình trệ trên bộ nhớ. Điều này cho biết tình trạng thiếu bộ nhớ đang ảnh hưởng đến hiệu suất nhưng một số công việc vẫn đang được tiến hành.
  • full: Tỷ lệ phần trăm thời gian mà tất cả tác vụ không rảnh đều bị đình trệ cùng lúc. Điều này cho biết trạng thái nghiêm trọng khi hệ thống bị chặn hoàn toàn trong khi chờ bộ nhớ.

Cách PSI kích hoạt LMKD

Trình nền lmkd không gian người dùng đăng ký với nhân hệ điều hành để nhận thông báo khi vượt quá ngưỡng PSI (bằng cách giám sát /proc/pressure/memory). Khi hệ thống gặp phải áp lực bộ nhớ cao (ví dụ: full bị đình trệ vượt quá thời lượng đã định cấu hình), nhân hệ điều hành sẽ đánh thức lmkd. Sau đó, lmkd sẽ đánh giá trạng thái của hệ thống và quyết định xem có nên tắt các quy trình hay không, cũng như tắt quy trình nào, dựa trên điểm OOM và kích thước của các quy trình đó.

Điều chỉnh LMKD

Bạn có thể điều chỉnh hành vi của LMKD bằng các thuộc tính hệ thống.

Tìm giá trị điều chỉnh trên thiết bị

Bạn có thể tìm thấy các giá trị điều chỉnh LMKD hiện tại trên một thiết bị thông minh bằng cách chạy:

adb shell getprop | grep lmk

Các thuộc tính LMKD phổ biến

Sau đây là một số thuộc tính chính dùng để điều chỉnh LMKD (tham khảo Tài liệu về Android để xem danh sách đầy đủ):

  • ro.lmk.use_psi: Đặt thành true để sử dụng trình giám sát PSI nhằm phát hiện áp lực bộ nhớ (mặc định trên Android 10 trở lên).
  • ro.lmk.psi_partial_stall_ms: Ngưỡng cho thời lượng bị treo (một phần) some tính bằng mili giây để kích hoạt lệnh tắt (mặc định là 70 mili giây trên thiết bị thông thường, 200 mili giây trên thiết bị có RAM thấp).
  • ro.lmk.psi_complete_stall_ms: Ngưỡng cho thời lượng bị treo full tính bằng mili giây để kích hoạt lệnh tắt (mặc định là 700 mili giây).
  • ro.lmk.kill_heaviest_task: Nếu true, LMKD sẽ tắt quy trình sử dụng nhiều bộ nhớ nhất trong số các ứng viên trong cùng một bộ chứa OOM.

Các giá trị điều chỉnh có liên quan trên thiết bị thử nghiệm (Google Pixel 8 Pro)

(Lưu ý: Giá trị mặc định thông thường cho các thiết bị cao cấp chạy Android 15)

  • ro.lmk.use_psi: true
  • ro.lmk.psi_partial_stall_ms: 70 (thông thường)
  • ro.lmk.psi_complete_stall_ms: 700 (thông thường)
  • ro.lmk.kill_heaviest_task: true

Xác minh ZRAM và hoán đổi

Dung lượng ZRAM ảnh hưởng lớn đến tốc độ lấp đầy của vùng hoán đổi và kích hoạt các lệnh tắt của LMKD. Bạn có thể kiểm tra trạng thái ZRAM và hoán đổi trên thiết bị bằng các lệnh sau:

# Check ZRAM sizing and usage
adb shell free -m

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

Bài tập thực hành: kích hoạt kswapd và lmkd

Trong bài tập này, bạn sẽ sử dụng ứng dụng mẫu KswapdLmkdLab để tạo các quy trình trong nhiều bộ chứa OOM, sau đó sử dụng bộ nhớ trong quy trình chính để kích hoạt kswapd và lmkd.

Ứng dụng được định cấu hình để tự động tạo các quy trình con và phân bổ bộ nhớ để biến chúng thành mục tiêu hấp dẫn cho LMKD.

1. Tạo quy trình con

Ứng dụng tự động tạo các quy trình con sau đây khi khởi chạy:

  • Quy trình được lưu vào bộ nhớ đệm (oom_score_adj >= 900)
  • Quy trình dịch vụ (oom_score_adj 500)
  • Quy trình dịch vụ nền trước (oom_score_adj 200)

Xác minh các quy trình và điểm OOM của chúng bằng ADB:

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

Đoạn mã đầu ra mẫu:

    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)

Các từ viết tắt của trạng thái quy trình tương ứng với:

  • fg: Nền trước (điểm OOM là 0)
  • prcp: Có thể nhận biết (Dịch vụ nền trước, điểm OOM 200)
  • svc: Dịch vụ đã khởi động (điểm OOM 500)
  • cch: Tiến trình được lưu vào bộ nhớ đệm (điểm OOM 900)

2. Kích hoạt áp lực bộ nhớ

Ứng dụng sẽ tự động bắt đầu phân bổ bộ nhớ trong quy trình chính. Để thử nghiệm hiệu quả trên các thiết bị có RAM cao, ứng dụng cũng ra lệnh cho các quy trình con phân bổ bộ nhớ (ví dụ: 500 MB mỗi quy trình) để biến chúng thành mục tiêu tốt hơn cho LMKD.

Theo dõi quá trình buộc tắt quy trình con

Ứng dụng giám sát việc chấm dứt các quy trình con này bằng cơ chế IBinder.linkToDeath:

  1. Quy trình chính liên kết với một dịch vụ trong quy trình mục tiêu bằng cách sử dụng bindService.
  2. Khi kết nối, ứng dụng sẽ nhận được đối tượng IBinder đại diện cho dịch vụ.
  3. Nó đăng ký một phương thức triển khai IBinder.DeathRecipient bằng cách gọi linkToDeath trên liên kết.
  4. Khi quy trình mục tiêu bị buộc tắt (ví dụ: khi bị LMKD buộc tắt), hệ thống sẽ gọi phương thức binderDied(), cho phép ứng dụng ghi lại sự kiện buộc tắt một cách không đồng bộ.

Để ngăn việc liên kết này nâng cao mức độ ưu tiên của các quy trình mục tiêu (điều này sẽ làm mất đi mục đích của bài tập), ứng dụng sẽ sử dụng cờ Context.BIND_WAIVE_PRIORITY.

Ứng dụng mẫu trong thực tế

Sau đây là ảnh chụp màn hình của ứng dụng sau khi hoàn tất một lần chạy thử nghiệm trên Google Pixel 8 Pro (có tổng dung lượng RAM khoảng 12 GB).

Ảnh chụp màn hình ứng dụng mẫu sau khi hoàn tất một lần chạy kiểm thử.
Hình 1. Ứng dụng mẫu sau khi hoàn tất một lần chạy thử nghiệm trên Pixel 8 Pro.

Phân tích dấu vết Perfetto

Để ghi lại hoạt động tương tác, hãy sử dụng cấu hình Perfetto sau đây để ghi lại lịch biểu, bộ đếm bộ nhớ, lệnh tắt LMK và Thông tin về áp lực (PSI).

Cấu hình Perfetto

Ghi lại hoạt động tương tác bằng cách chạy Perfetto với tính năng lập lịch biểu, bộ đếm bộ nhớ, lệnh tắt LMK và Thông tin về áp lực (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

Xem lại dấu vết

Ảnh chụp màn hình sau đây được lấy từ một dấu vết cho thấy khoảng thời gian ngay trước khi quá trình :cached (dễ bị tắt nhất) bị tắt, cho đến khi quá trình :foreground (ít bị tắt nhất) bị tắt.

Ảnh chụp màn hình giao diện người dùng Perfetto cho thấy hoạt động của kswapd và lmkd.
Hình 2. Dấu vết Perfetto cho thấy hoạt động thu hồi kswapd và lmkd tắt ứng dụng.

Bạn có thể thấy MemFree đã khá thấp và liên tục chịu áp lực. kswapd liên tục hoạt động và giảm áp lực bằng cách loại bỏ mem.rss.file trang và di chuyển mem.rss.anon sang mem.swap từ tất cả các quy trình kswapdlmkdlab (quy trình chính và quy trình phụ) và có thể từ các quy trình khác không xuất hiện trong ảnh chụp màn hình này. Đồng thời, ứng dụng kswapdlmkdlab tiếp tục phân bổ bộ nhớ, làm tăng áp lực nhanh hơn mức mà kswapd có thể thu hồi.

Khi áp lực trở nên quá cao, như khi psi.mem ở mức cao trong một khoảng thời gian đủ dài, lmkd sẽ thức dậy và tắt một số quy trình. Bạn có thể thấy lmkd tuân thủ điểm OOM, tắt kswapdlmkdlab:cached trước, sau đó đến :service, rồi đến :foreground.

Xem lại dòng thời gian của các sự kiện và suy ngẫm về những điều sau:

  • Cách kswapd và lmkd bổ trợ cho nhau: kswapd là thành phần đầu tiên phản hồi áp lực bộ nhớ, nhưng lmkd sẽ hỗ trợ khi băng thông thu hồi của kswapd không đáp ứng được nhu cầu phân bổ.
  • Tốc độ giải phóng bộ nhớ: quan sát Thời gian của CPU dành cho kswapd và bộ nhớ được thu hồi bằng cách thả mem.rss.file hoặc hoán đổi mem.rss.anon thành mem.swap. Hãy so sánh điều này với các hoạt động ngắn trong lmkd dẫn đến việc giải phóng một lượng lớn bộ nhớ riêng tư của quy trình.

Cụm từ tìm kiếm

Chạy các truy vấn này trong giao diện người dùng Perfetto (Truy vấn SQL) để trích xuất dữ liệu từ dấu vết của bạn.

1. Hoạt động kswapd

Để tìm thời điểm trình nền hoán đổi của hạt nhân hoạt động và chạy, hãy thực thi:

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

Kết quả mẫu:

ts dur tiểu bang
30317633052969 159180 Đang tải trước
30317633398347 3999186 Đang tải trước
30317638448111 1847941 Đang tải trước
30317641455354 1031006 Đang tải trước
30317643272615 888387 Đang tải trước

2. Số lần LMKD buộc dừng ứng dụng

LMKD ghi các lỗi tắt ứng dụng do thiếu bộ nhớ vào bảng instant. Cách truy vấn tất cả các lần tắt do LMKD:

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

Định dạng tên sự kiện là: lmk,<pid>,<kill_reason>,<oom_score_adj>,<min_oom_score>,<max_thrashing>.

  • kill_reason: Một số nguyên cho biết lý do lmkd quyết định tắt quy trình (được xác định trong system/memory/lmkd/statslog.h):
    • 0: PRESSURE_AFTER_KILL
    • 1: NOT_RESPONDING
    • 2: LOW_SWAP_AND_THRASHING (Không gian hoán đổi ZRAM cực kỳ thấp và đã xảy ra hiện tượng thrashing)
    • 3: LOW_MEM_AND_SWAP (Hệ thống sắp hết cả bộ nhớ trống và bộ nhớ hoán đổi 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: Tỷ lệ phần trăm tối đa của lỗi trang tại thời điểm bị tắt.

Kết quả mẫu:

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

Hình ảnh trực quan

Để trực quan hoá dữ liệu, bạn có thể sử dụng tập lệnh gnuplot sau để vẽ biểu đồ bộ nhớ trống của hệ thống, hoạt động kswapd, lệnh tắt lmkd và PSI trên một chuỗi thời gian. Tập lệnh giả định rằng tập lệnh được chạy từ thư mục cấp cao nhất của bản dựng Android.

Biểu đồ đầu ra mẫu

Biểu đồ sau đây được tạo bằng tập lệnh trước đó với dữ liệu mẫu đại diện cho lần chạy kiểm thử.

Biểu đồ về bộ nhớ trống, hoạt động kswapd, PSI và các lần tắt ứng dụng do lmkd.
Hình 3. Biểu đồ áp lực bộ nhớ cho thấy bộ nhớ trống, kswapd, PSI và lmkd kills.

Giải thích cốt truyện:

Biểu đồ này minh hoạ hoạt động tương tác linh động giữa các thành phần quản lý bộ nhớ trong quá trình kiểm thử áp lực:

  1. Giảm bộ nhớ trống: Khi ứng dụng chính phân bổ bộ nhớ, bộ nhớ trống của hệ thống (đường màu tím) sẽ giảm dần.
  2. Kích hoạt kswapd: Khi bộ nhớ trống giảm xuống dưới một số hình mờ nhất định, kswapd sẽ thức dậy (xung màu xanh dương) để lấy lại bộ nhớ bằng cách hoán đổi sang ZRAM. Điều này thể hiện dưới dạng các mức tăng đột biến không liên tục.
  3. Mức tăng đột biến PSI: Khi kswapd gặp khó khăn trong việc theo kịp tốc độ phân bổ cao, các luồng bắt đầu bị đình trệ trong quá trình phân bổ bộ nhớ, khiến PSI (Thông tin về tình trạng đình trệ do áp lực) (đường màu xanh lục) tăng đột biến.
  4. LMKD tắt quy trình: Khi PSI đạt đến mức nghiêm trọng (ví dụ: vượt quá ngưỡng ro.lmk.psi_complete_stall_ms về mặt lý thuyết) và bộ nhớ trống cực kỳ thấp, lmkd sẽ can thiệp bằng cách tắt các quy trình có mức độ ưu tiên thấp (xung màu cam tại thời điểm 30 và 45) để giải phóng các phần bộ nhớ lớn. Lưu ý rằng ngay sau khi buộc dừng, bộ nhớ trống sẽ tăng lên và PSI sẽ giảm xuống, cho thấy hiệu quả của việc buộc dừng trong việc giảm áp lực. ________________________________________________________________________________

← Thu hồi bộ nhớ: loại bỏ và hoán đổi | ↑ Lên trên | Trả lại các trang bộ nhớ cho hệ điều hành →