หน้านี้ครอบคลุมการโต้ตอบระหว่าง kswapd และ lmkd บน Android ซึ่งแสดงให้เห็นว่าทั้ง 2 ส่วนทำงานร่วมกันอย่างไรเพื่อจัดการกับแรงกดดันด้านหน่วยความจำ
ฉากหลัง
Android ใช้กลไกหลัก 2 อย่างเพื่อเรียกคืนหน่วยความจำเมื่อระบบมีหน่วยความจำเหลือน้อย ได้แก่
kswapd: Daemon เคอร์เนล Linux มาตรฐานที่เรียกคืนหน้าเว็บโดยการลบหน้าเว็บที่สำรองข้อมูลไฟล์ที่สะอาดหรือสลับหน้าเว็บที่ไม่ระบุชื่อเป็น ZRAMlmkd: 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: หากเป็นtrueLMKD จะหยุดกระบวนการที่ใช้หน่วยความจำมากที่สุดในบรรดาตัวเลือกในที่เก็บ 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 และ 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 ดังนี้
- กระบวนการหลักจะเชื่อมโยงกับบริการในกระบวนการเป้าหมายโดยใช้
bindService - เมื่อเชื่อมต่อแล้ว ระบบจะรับออบเจ็กต์
IBinderที่แสดงถึงบริการ - โดยจะลงทะเบียนการติดตั้งใช้งาน
IBinder.DeathRecipientด้วยการเรียกใช้linkToDeathใน Binder - เมื่อกระบวนการเป้าหมายสิ้นสุดลง (เช่น เมื่อถูก LMKD สิ้นสุด) ระบบจะเรียกใช้เมธอด
binderDied()ซึ่งช่วยให้แอปบันทึกเหตุการณ์การสิ้นสุดแบบอะซิงโครนัสได้
แอปจะใช้แฟล็ก Context.BIND_WAIVE_PRIORITY เพื่อป้องกันไม่ให้การเชื่อมโยงนี้เพิ่มลำดับความสำคัญของกระบวนการเป้าหมาย (ซึ่งจะขัดต่อวัตถุประสงค์ของการดำเนินการ)
การใช้งานแอปตัวอย่าง
นี่คือภาพหน้าจอของแอปหลังจากการทดสอบเสร็จสมบูรณ์ใน Google Pixel 8 Pro (ซึ่งมี RAM ทั้งหมดประมาณ 12 GB)
การวิเคราะห์การติดตาม 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ไม่เป็นไปตามความต้องการในการจัดสรร- อัตราการเพิ่มหน่วยความจำที่ว่าง: สังเกตเวลา 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_KILL1:NOT_RESPONDING2:LOW_SWAP_AND_THRASHING(พื้นที่ Swap ของ ZRAM เหลือน้อยมากและเกิดการ Thrashing)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: เปอร์เซ็นต์การ 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 จะลดลง ซึ่งแสดงให้เห็นถึงประสิทธิภาพของการปิดแอปเพื่อลดแรงกดดัน ________________________________________________________________________________
← การเรียกคืนหน่วยความจำ: การขับออกและการสลับ | ↑ ขึ้น | คืนหน้าหน่วยความจำไปยังระบบปฏิบัติการ →