การโต้ตอบระหว่าง kswapd กับ lmkd

หน้านี้ครอบคลุมการโต้ตอบระหว่าง kswapd และ lmkd บน Android ซึ่งแสดงให้เห็นว่าทั้ง 2 ส่วนทำงานร่วมกันอย่างไรเพื่อจัดการกับแรงกดดันด้านหน่วยความจำ

ฉากหลัง

Android ใช้กลไกหลัก 2 อย่างเพื่อเรียกคืนหน่วยความจำเมื่อระบบมีหน่วยความจำเหลือน้อย ได้แก่

  1. kswapd: Daemon เคอร์เนล Linux มาตรฐานที่เรียกคืนหน้าเว็บโดยการลบหน้าเว็บที่สำรองข้อมูลไฟล์ที่สะอาดหรือสลับหน้าเว็บที่ไม่ระบุชื่อเป็น ZRAM
  2. lmkd: Daemon ของ Low Memory Killer ในพื้นที่ผู้ใช้ของ Android เมื่อ kswapd ไม่สามารถตอบสนองความต้องการหน่วยความจำได้และแรงกดดันของระบบเพิ่มขึ้น (ตามสัญญาณจาก PSI) lmkd จะเข้ามาแทรกแซงเพื่อหยุดกระบวนการต่างๆ เพื่อเพิ่มพื้นที่หน่วยความจำขนาดใหญ่ขึ้น

kswapd และ lmkd เป็นระบบการเรียกคืนหน่วยความจำ 2 ระบบที่ทำงานร่วมกัน ทั้ง 2 ฟีเจอร์ทำงานเพื่อให้บรรลุเป้าหมายเดียวกัน (สร้างหน่วยความจำที่ว่าง) แต่ใช้วิธีการที่แตกต่างกัน วิธีเปรียบเทียบ และหาความแตกต่าง

  • กู้คืนได้เทียบกับทำลาย: kswapd จะลบหน้าหน่วยความจำที่สามารถอ่านกลับจากพื้นที่เก็บข้อมูล หรือสลับหน้าหน่วยความจำไปยังพาร์ติชันหน่วยความจำอื่นๆ หรือพื้นที่เก็บข้อมูลที่สามารถอ่านกลับได้ตามต้องการ lmkd จะหยุดกระบวนการ ซึ่งจะทำลายสถานะแบบไม่ลบเลือน
  • เรียกคืนแบนด์วิดท์: kswapd ใช้ CPU มากกว่าในการเรียกคืนหน่วยความจำว่างจำนวนหนึ่งเมื่อเทียบกับ lmkd และใช้เวลานานกว่าในการเรียกคืนหน่วยความจำดังกล่าว lmkd สามารถเรียกคืนหน่วยความจำจำนวนมากได้ในระยะเวลาสั้นๆ โดยใช้ความพยายามค่อนข้างน้อย โปรดพิจารณาว่าในอุปกรณ์เคลื่อนที่ การทำงานของ CPU ไม่เพียงแต่ส่งผลต่อเวลาในการตอบสนองเท่านั้น แต่ยังส่งผลต่อพลังงานแบตเตอรี่ด้วย

ที่เก็บข้อมูลคะแนน OOM

Android จะกำหนดการปรับคะแนนหน่วยความจำไม่เพียงพอ (OOM) (oom_score_adj) ให้กับกระบวนการต่างๆ ตามความสำคัญต่อผู้ใช้ กระบวนการที่มีคะแนนสูงกว่า จะถูกปิดก่อน

ที่เก็บข้อมูลที่พบบ่อยมีดังนี้

  • แคช (คะแนน >= 900): กระบวนการที่ไม่จำเป็นในขณะนี้ โดยเก็บไว้เพื่อให้รีสตาร์ทได้เร็วขึ้น
  • บริการ (คะแนน 500): กระบวนการที่โฮสต์บริการที่เริ่มต้นแล้ว
  • บริการที่ทำงานอยู่เบื้องหน้า (คะแนน 200): กระบวนการที่โฮสต์บริการที่ทำงานอยู่เบื้องหน้า
  • เบื้องหน้า (คะแนน 0): แอปที่ผู้ใช้กำลังโต้ตอบอยู่

ข้อมูลเจาะลึก: การทริกเกอร์ PSI และ LMKD

PSI คืออะไร

Pressure Stall Information (PSI) เป็นฟีเจอร์เคอร์เนล Linux ที่ให้วิธีมาตรฐานในการวัดการขาดแคลนทรัพยากร โดยจะติดตามเวลาที่งานล่าช้าเนื่องจาก CPU, หน่วยความจำ และ I/O ไม่เพียงพอ

PSI ของหน่วยความจำ: "บางส่วน" เทียบกับ "ทั้งหมด"

PSI ของหน่วยความจำจะติดตามความล่าช้าที่เกิดจากการขาดแคลนหน่วยความจำโดยเฉพาะ (เช่น การรอการเรียกคืนหน้าเว็บหรือการเกิดข้อผิดพลาดซ้ำ) โดยจะแสดงเมตริก 2 รายการดังนี้

  • some: เปอร์เซ็นต์ของเวลาที่มีงานอย่างน้อย 1 งานหยุดชะงักเนื่องจากหน่วยความจำ ซึ่งบ่งชี้ว่าหน่วยความจำไม่เพียงพอส่งผลต่อประสิทธิภาพ แต่ยังคงทำงานบางอย่างได้อยู่
  • full: เปอร์เซ็นต์ของเวลาที่งานที่ไม่ไม่มีการใช้งานทั้งหมดหยุดชะงักพร้อมกัน ซึ่งบ่งชี้ถึงสถานะวิกฤตที่ระบบถูกบล็อกโดยสมบูรณ์เพื่อรอหน่วยความจำ

วิธีที่ PSI ทริกเกอร์ LMKD

Daemon 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 มิลลิวินาทีในอุปกรณ์ที่มี RAM ต่ำ)
  • 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: true
  • ro.lmk.psi_partial_stall_ms: 70 (ปกติ)
  • ro.lmk.psi_complete_stall_ms: 700 (ปกติ)
  • ro.lmk.kill_heaviest_task: true

การยืนยัน ZRAM และ Swap

ความจุ 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: สังเกตได้ (บริการที่ทำงานอยู่เบื้องหน้า, คะแนน OOM 200)
  • svc: บริการที่เริ่มต้น (คะแนน OOM 500)
  • cch: กระบวนการที่แคชไว้ (คะแนน OOM 900)

2. ทริกเกอร์แรงดันหน่วยความจำ

แอปจะเริ่มจัดสรรหน่วยความจำในกระบวนการหลักโดยอัตโนมัติ เพื่อให้การทดสอบมีประสิทธิภาพในอุปกรณ์ที่มี RAM สูง แอปยังสั่งให้กระบวนการย่อยจัดสรรหน่วยความจำ (เช่น กระบวนการละ 500 MB) เพื่อให้เป็นเป้าหมายที่ดีขึ้นสำหรับ LMKD

ตรวจสอบการสิ้นสุดการประมวลผลย่อย

แอปจะตรวจสอบการสิ้นสุดของกระบวนการย่อยเหล่านี้โดยใช้กลไก IBinder.linkToDeath ดังนี้

  1. กระบวนการหลักจะเชื่อมโยงกับบริการในกระบวนการเป้าหมายโดยใช้ bindService
  2. เมื่อเชื่อมต่อแล้ว ระบบจะรับออบเจ็กต์ IBinder ที่แสดงถึงบริการ
  3. โดยจะลงทะเบียนการติดตั้งใช้งาน IBinder.DeathRecipient ด้วยการเรียกใช้ linkToDeath ใน Binder
  4. เมื่อกระบวนการเป้าหมายสิ้นสุดลง (เช่น เมื่อถูก LMKD สิ้นสุด) ระบบจะเรียกใช้เมธอด binderDied() ซึ่งช่วยให้แอปบันทึกเหตุการณ์การสิ้นสุดแบบอะซิงโครนัสได้

แอปจะใช้แฟล็ก Context.BIND_WAIVE_PRIORITY เพื่อป้องกันไม่ให้การเชื่อมโยงนี้เพิ่มลำดับความสำคัญของกระบวนการเป้าหมาย (ซึ่งจะขัดต่อวัตถุประสงค์ของการดำเนินการ)

การใช้งานแอปตัวอย่าง

นี่คือภาพหน้าจอของแอปหลังจากการทดสอบเสร็จสมบูรณ์ใน Google Pixel 8 Pro (ซึ่งมี RAM ทั้งหมดประมาณ 12 GB)

ภาพหน้าจอของแอปตัวอย่างหลังจากการทดสอบเสร็จสมบูรณ์
รูปที่ 1 แอปตัวอย่างหลังจากการทดสอบเสร็จสมบูรณ์ใน Pixel 8 Pro

การวิเคราะห์การติดตาม 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 (ปิดได้น้อยที่สุด) ถูกปิด

ภาพหน้าจอ UI ของ Perfetto ที่แสดงกิจกรรม kswapd และ lmkd
รูปที่ 2 Perfetto Trace แสดงการเรียกคืน kswapd และการปิด lmkd

คุณจะเห็นว่า 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 ไม่เป็นไปตามความต้องการในการจัดสรร
  • อัตราการเพิ่มหน่วยความจำที่ว่าง: สังเกตเวลา CPU ที่ใช้ใน kswapd และหน่วยความจำที่ได้รับการคืนค่าโดยการลด mem.rss.file หรือสลับ mem.rss.anon เป็น mem.swap เปรียบเทียบข้อมูลนี้กับกิจกรรมที่เกิดขึ้นเป็นช่วงสั้นๆ ใน lmkd ซึ่งส่งผลให้มีการปล่อยหน่วยความจำส่วนตัวของกระบวนการจำนวนมาก

ข้อความค้นหา

เรียกใช้การค้นหาเหล่านี้ใน UI ของ Perfetto (Query SQL) เพื่อดึงข้อมูลจากการติดตาม

1. กิจกรรม kswapd

หากต้องการดูว่า Swap Daemon ของเคอร์เนลทำงานอยู่เมื่อใด ให้เรียกใช้คำสั่งต่อไปนี้

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_KILL
    • 1: NOT_RESPONDING
    • 2: LOW_SWAP_AND_THRASHING (พื้นที่ Swap ของ ZRAM เหลือน้อยมากและเกิดการ Thrashing)
    • 3: LOW_MEM_AND_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: เปอร์เซ็นต์การ 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, PSI และการหยุดทำงานเนื่องจาก lmkd
รูปที่ 3 แผนภาพความกดดันของหน่วยความจำที่แสดงหน่วยความจำว่าง, kswapd, PSI และการปิด lmkd

คำอธิบายพล็อต:

แผนภาพนี้แสดงการโต้ตอบแบบไดนามิกระหว่างคอมโพเนนต์การจัดการหน่วยความจำต่างๆ ระหว่างการทดสอบความเครียด

  1. หน่วยความจำที่ว่างลดลง: เมื่อแอปหลักจัดสรรหน่วยความจำ หน่วยความจำที่ว่างของระบบ (เส้นสีม่วง) จะลดลงอย่างต่อเนื่อง
  2. การเปิดใช้งาน kswapd: เมื่อหน่วยความจำที่ใช้ได้ลดลงต่ำกว่าระดับที่กำหนด kswapd จะตื่นขึ้น (สัญญาณสีน้ำเงิน) เพื่อเรียกคืนหน่วยความจำโดยการสลับไปยัง ZRAM ซึ่งจะเห็นได้จากยอดที่พุ่งสูงขึ้นเป็นระยะๆ
  3. PSI พุ่งสูงขึ้น: เมื่อ kswapd พยายามตามอัตราการจัดสรรที่สูงให้ทัน เทรดจะเริ่มหยุดชะงักในการจัดสรรหน่วยความจำ ทำให้ PSI (ข้อมูลการหยุดชะงักเนื่องจากแรงกดดัน) (เส้นสีเขียว) พุ่งสูงขึ้น
  4. การปิดแอปโดย LMKD: เมื่อ PSI ถึงระดับวิกฤต (เช่น เกินเกณฑ์ ro.lmk.psi_complete_stall_ms ในทางแนวคิด) และหน่วยความจำว่างเหลือน้อยมาก lmkd จะแทรกแซงโดยการปิดกระบวนการที่มีลำดับความสำคัญต่ำ (แรงกระตุ้นสีส้มที่เวลา 30 และ 45) เพื่อเพิ่มหน่วยความจำจำนวนมาก โปรดทราบว่าทันทีหลังจากปิดแอป หน่วยความจำที่ว่างจะเพิ่มขึ้นและ PSI จะลดลง ซึ่งแสดงให้เห็นถึงประสิทธิภาพของการปิดแอปเพื่อลดแรงกดดัน ________________________________________________________________________________

← การเรียกคืนหน่วยความจำ: การขับออกและการสลับ | ↑ ขึ้น | คืนหน้าหน่วยความจำไปยังระบบปฏิบัติการ →