التفاعل بين kswapd وlmkd

تتناول هذه الصفحة التفاعل بين kswapd وlmkd على Android، وتوضّح كيفية عملهما معًا لإدارة الضغط على الذاكرة.

خلفية

يستخدم Android آليتَين أساسيتَين لاستعادة الذاكرة عندما تنخفض مساحة النظام:

  1. kswapd: هو برنامج خلفية عادي في نواة Linux يستعيد الصفحات من خلال إزالة الصفحات النظيفة المستندة إلى الملفات أو تبديل الصفحات المجهولة إلى ZRAM.
  2. lmkd: برنامج daemon لإنهاء العمليات عند انخفاض الذاكرة في مساحة المستخدم في Android. عندما لا يتمكّن kswapd من مواكبة متطلبات الذاكرة ويزداد الضغط على النظام (كما يشير PSI)، يتدخّل lmkd لإنهاء العمليات من أجل إخلاء أجزاء أكبر من الذاكرة.

‫kswapd وlmkd هما نظامان متكاملان لاستعادة الذاكرة. ويعملان على تحقيق الهدف نفسه (توفير مساحة في الذاكرة) ولكن بطرق مختلفة. للمقارنة والتباين:

  • الاسترداد مقابل التدمير: يتجاهل kswapd صفحات الذاكرة التي يمكن قراءتها من وحدة التخزين، أو ينقل صفحات الذاكرة إلى أقسام ذاكرة أخرى أو وحدة تخزين يمكن قراءتها عند الطلب. يؤدي استخدام lmkd إلى إنهاء العمليات، ما يؤدي إلى تدمير الحالة غير المتغيّرة.
  • استعادة معدل نقل البيانات: يستهلك kswapd قدرًا أكبر من وحدة المعالجة المركزية (CPU) لاستعادة مقدار معيّن من الذاكرة الخالية مقارنةً بـ lmkd، ويستغرق وقتًا أطول لاستعادة هذه الذاكرة. يمكن لـ lmkd استعادة كميات كبيرة من الذاكرة في دفعات قصيرة من الجهد القليل نسبيًا. يُرجى العِلم أنّ جهد وحدة المعالجة المركزية على الأجهزة الجوّالة لا يؤدي إلى زيادة وقت الاستجابة فحسب، بل يؤدي أيضًا إلى استهلاك طاقة البطارية.

مجموعات نتائج نفاد الذاكرة (OOM)

يخصّص نظام Android تعديلاً لنتيجة نفاد الذاكرة (OOM) (oom_score_adj) للعمليات استنادًا إلى مدى أهميتها للمستخدم. يتم إنهاء العمليات التي حصلت على نتائج أعلى أولاً.

تشمل المجموعات الشائعة ما يلي:

  • العمليات المخزَّنة مؤقتًا (النتيجة >= 900): هي العمليات غير المطلوبة حاليًا، ويتم الاحتفاظ بها لإعادة تشغيلها بشكل أسرع.
  • الخدمة (النتيجة 500): تعالج الخدمات التي بدأت الاستضافة.
  • خدمة تعمل في المقدّمة (النتيجة 200): العمليات التي تستضيف خدمات تعمل في المقدّمة.
  • في المقدّمة (النتيجة 0): التطبيق الذي يتفاعل معه المستخدم حاليًا.

نظرة متعمّقة: PSI وLMKD

ما هي PSI؟

معلومات توقُّف الضغط (PSI) هي ميزة في Linux kernel توفّر طريقة أساسية لقياس النقص في الموارد. وتتتبّع الوقت الذي تتأخر فيه المهام بسبب نقص في وحدة المعالجة المركزية (CPU) والذاكرة والإدخال/الإخراج.

مؤشر PSI للذاكرة: "بعض" مقابل "كامل"

يتتبّع مؤشر 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: true
  • ‫ro.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)

تحقَّق من العمليات ونتائج نفاد الذاكرة باستخدام 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: التطبيقات التي تعمل في المقدّمة (النتيجة 0 في تقييم نفاد الذاكرة)
  • ‫prcp: مرئية (خدمة تعمل في المقدّمة، درجة نفاد الذاكرة 200)
  • svc: تم بدء الخدمة (النتيجة 500 في اختبار نفاد الذاكرة)
  • cch: عملية مخزَّنة مؤقتًا (نتيجة نفاد الذاكرة 900)

2. تفعيل الضغط على الذاكرة

سيبدأ التطبيق تلقائيًا في تخصيص الذاكرة في العملية الرئيسية. لجعل الاختبار فعالاً على الأجهزة ذات ذاكرة الوصول العشوائي العالية، يطلب التطبيق أيضًا من العمليات الفرعية تخصيص الذاكرة (على سبيل المثال، 500 ميغابايت لكل منها) لجعلها أهدافًا أفضل لـ LMKD.

مراقبة إيقاف العملية الفرعية نهائيًا

يراقب التطبيق توقُّف هذه العمليات الفرعية باستخدام آلية IBinder.linkToDeath:

  1. ترتبط العملية الرئيسية بخدمة في العملية المستهدَفة باستخدام bindService.
  2. عند الاتصال، يحصل على عنصر IBinder الذي يمثّل الخدمة.
  3. يتم تسجيل تنفيذ IBinder.DeathRecipient من خلال استدعاء linkToDeath في المجلّد.
  4. عند توقُّف العملية المستهدَفة (على سبيل المثال، عند إنهائها من خلال LMKD)، يستدعي النظام الطريقة binderDied()، ما يسمح للتطبيق بتسجيل حدث التوقُّف بشكل غير متزامن.

لمنع هذا الربط من رفع أولوية العمليات المستهدَفة (ما قد يؤدي إلى إبطال الغرض من التمرين)، يستخدم التطبيق العلامة Context.BIND_WAIVE_PRIORITY.

نموذج التطبيق أثناء العمل

في ما يلي لقطة شاشة للتطبيق بعد اكتمال عملية اختبار على هاتف Google Pixel 8 Pro (الذي تبلغ سعة ذاكرة الوصول العشوائي الإجمالية فيه 12 غيغابايت تقريبًا).

لقطة شاشة لنموذج تطبيق بعد اكتمال عملية الاختبار
الشكل 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 (الأقل قابلية للإنهاء).

لقطة شاشة لواجهة مستخدم Perfetto تعرض نشاط kswapd وlmkd
الشكل 2. تتبُّع Perfetto يعرض عمليات استرداد kswapd وعمليات إنهاء lmkd

يمكنك ملاحظة أنّ MemFree منخفضة إلى حدّ ما وتتعرّض لضغط مستمر. يعمل kswapd باستمرار ويخفف الضغط عن طريق إسقاط صفحات mem.rss.file ونقل mem.rss.anon إلى mem.swap من جميع عمليات kswapdlmkdlab (العملية الرئيسية والعمليات الفرعية)، ومن المحتمل من عمليات أخرى غير معروضة في لقطة الشاشة هذه. وفي الوقت نفسه، يستمر تطبيق kswapdlmkdlab في تخصيص الذاكرة، ما يزيد من الضغط بشكل أسرع من قدرة kswapd على استردادها.

عندما يصبح الضغط مرتفعًا جدًا، كما هو الحال في psi.mem لفترة زمنية كافية، يتم تنشيط lmkd وإنهاء بعض العمليات. يمكنك ملاحظة أنّ lmkd يراعي نتائج نفاد الذاكرة، فينهي kswapdlmkdlab:cached أولاً، ثم :service، ثم :foreground.

راجِع المخطط الزمني للأحداث وفكِّر في ما يلي:

  • كيفية تكامل kswapd وlmkd مع بعضهما البعض: kswapd هو أول من يستجيب لضغط الذاكرة، ولكن lmkd يساعد عندما لا يلبي عرض النطاق الترددي للاسترداد في kswapd طلب التخصيص.
  • معدّل إخلاء الذاكرة: راقِب وقت وحدة المعالجة المركزية المستغرَق في kswapd والذاكرة التي تتم استعادتها من خلال إسقاط mem.rss.file أو تبديل mem.rss.anon إلى mem.swap. قارِن ذلك بالنشاط القصير والمكثّف في lmkd الذي يؤدي إلى إخلاء مساحة كبيرة من الذاكرة الخاصة بالعملية.

طلبات البحث

يمكنك تشغيل طلبات البحث هذه في واجهة مستخدم Perfetto (طلب بحث SQL) لاستخراج البيانات من التتبُّع.

‫1. نشاط kswapd

لمعرفة متى كان برنامج 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 (مساحة إبدال ZRAM منخفضة للغاية وحدثت مشكلة في الذاكرة)
    • 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: الحد الأقصى للنسبة المئوية لتبديل الصفحات في وقت الإنهاء.

نموذج الناتج:

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، ما يوضّح فعالية إنهاء العملية في تخفيف الضغط. ________________________________________________________________________________

← استعادة الذاكرة: الإخلاء والإبدال | ↑ للأعلى | إعادة صفحات الذاكرة إلى نظام التشغيل →