في الأقسام السابقة، رأينا كيف يدير النواة نظام الذاكرة على مستوى النظام. في هذا الفصل، سنتعمّق في مجموعات التحكّم في الذاكرة (memcg)، وهي الآلية التي يستخدمها نظام التشغيل Android لتقسيم استخدام الذاكرة والتحكّم فيه لكل تطبيق على حدة.
من المهم فهم memcg لأنّه المستوى الذي يمكن للنظام استهداف التطبيقات الفردية فيه لاستعادة الذاكرة أو ضبط حدود الذاكرة لها، ما يتيح الإدارة الاستباقية لاستهلاك التطبيقات للذاكرة.
مجموعات التحكّم في الذاكرة (memcg)
مجموعات التحكّم (cgroups) هي إحدى ميزات نواة Linux التي تتيح تنظيم العمليات في مجموعات هرمية وتوزيع موارد النظام (مثل وحدة المعالجة المركزية والذاكرة والإدخال/الإخراج) بينها. memcg هي أداة التحكّم في مجموعة التحكّم (cgroup) الخاصة بالذاكرة.
المفاهيم الرئيسية في memcg
- Memcg Charge: عندما تخصّص عملية في memcg صفحة من الذاكرة (إما مجهولة المصدر أو مستندة إلى ملف)، يتم "تحميل" هذه الصفحة إلى memcg. إجمالي تكلفة memcg هو مجموع جميع الصفحات التي تستخدمها جميع العمليات ضمنها.
- المحاسبة الهرمية: يتم احتساب استخدام الذاكرة في العرض التدرّجي. يُحتسب أيضًا أي رسوم في مجموعة تحكّم فرعية ضمن استخدام المجموعة الرئيسية.
- حدود الذاكرة: يمكن أن يكون لكل memcg حدود (مثل
memory.maxأوmemory.high) تؤدي إلى استعادة الذاكرة أو حتى إلى تشغيل أداة OOM killer في حال تجاوزها، وذلك بشكل مستقل عن ذاكرة النظام العامة. - استرداد الذاكرة لكل مجموعة تحكّم في الذاكرة: عندما تتجاوز مجموعة تحكّم في الذاكرة الحدّ المسموح به، أو عندما يحتاج النظام إلى ذاكرة، يمكن أن تستهدف النواة مجموعة تحكّم في الذاكرة معيّنة لاستردادها. ويعني ذلك إزالة صفحات الملفات أو تبديل صفحاتها المجهولة إلى ZRAM.
التسلسل الهرمي لمجموعة الذاكرة والتحكّم في Android
يستخدم نظام التشغيل Android تسلسلاً هرميًا محدّدًا لإدارة عمليات التطبيقات. تسمح هذه البنية للنظام بتطبيق سياسات مختلفة على أنواع مختلفة من التطبيقات (مثل التطبيقات التي تعمل في المقدّمة مقابل التطبيقات التي تعمل في الخلفية).

-
/sys/fs/cgroup/apps/: هو الجذر لجميع تطبيقات Android. uid_<UID>/: دليل لكل معرّف مستخدم خاص بكل تطبيق. وتتشارك جميع العمليات التي تنتمي إلى حزمة التطبيق نفسها في هذه المجموعة.-
pid_<PID>/: دليل لكل عملية فردية وأي عمليات فرعية تم إنشاؤها منها يتيح ذلك التحكّم الدقيق في التطبيقات التي تتضمّن عمليات متعددة واحتسابها.
تمرين عملي: استكشاف memcg
في هذا التمرين، ستعثر على دليل memcg الخاص بـ MemoryLab وستراقب رسوم الذاكرة في الوقت الفعلي.
1. تشغيل MemoryLab
تأكَّد من تشغيل تطبيق MemoryLab على جهازك.
2. العثور على memcg في MemoryLab
أولاً، احصل على معرّف العملية لتطبيق MemoryLab قيد التشغيل:
adb shell pidof com.android.memorylab
# Example output: 11672
الآن، حدِّد موقع دليل cgroup الخاص به. يمكنك العثور على هذا الخيار في /proc/<PID>/cgroup:
adb shell cat /proc/11672/cgroup
# Example output: 0::/apps/uid_10274/pid_11672
في الإصدار 2 من cgroup، يمثّل المسار بعد 0:: التدرّج الهرمي لـ memcg بالنسبة إلى /sys/fs/cgroup. وبالتالي، يكون المسار الكامل هو
/sys/fs/cgroup/apps/uid_10274/pid_11672/.
3- قراءة إحصاءات memcg
انتقِل إلى هذا الدليل واطّلِع على ملفات المفاتيح:
# Current memory usage (in bytes)
adb shell cat /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.current
# Example output:
# 1591324672
تعرض memory.current إجمالي مقدار الذاكرة (بالبايت) التي يتم تحصيل رسومها حاليًا
من مجموعة التحكّم هذه.
# Detailed statistics
adb shell cat /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.stat | head -n 10
# Example output:
# anon 1585098752
# file 741376
# kernel 5484544
# kernel_stack 393216
# pagetables 4583424
# sec_pagetables 0
# percpu 216
# sock 0
# vmalloc 4096
# shmem 20480
تقدّم memory.stat تفصيلاً على النحو التالي: * anon: مقدار الذاكرة المجهولة الهوية
(الكومات والمكدّسات). * file: مقدار الذاكرة الاحتياطية للملفات (ذاكرة التخزين المؤقت للصفحات) *
swap: مقدار الذاكرة التي تم تبديلها إلى ZRAM
4. مراقبة التغييرات
- افتح MemoryLab.
- دوِّن قيمة
memory.current. - انقر على تخصيص ذاكرة Java (10 ميغابايت) عدة مرات.
- اقرأ
memory.currentمرة أخرى. من المفترض أن تلاحظ زيادة في حجمها بمقدار 10 ميغابايت تقريبًا لكل نقرة. - انقر على تخصيص صور نقطية. تحقَّق من
memory.statلمعرفة الزيادة فيanon.
استرداد المساحة بشكل استباقي باستخدام memory.reclaim
من الميزات الفعّالة في memcg (الإصدار 2) الملف memory.reclaim. تؤدي كتابة قيمة
في هذا الملف إلى توجيه النواة لمحاولة استرداد مقدار الذاكرة هذا
من memcg وأي memcg ضمنه على الفور.
طريقة استخدام Android memory.reclaim
يستخدم CachedAppOptimizer في Android هذه الميزة لاستعادة أكبر قدر ممكن من الذاكرة من التطبيقات بعد تجميدها. تضمن ميزة تجميد التطبيقات في Android أن تستهلك التطبيقات المخزّنة مؤقتًا أقل قدر ممكن من ذاكرة الوصول العشوائي (RAM) عندما لا تكون قيد التشغيل. عندما ينتقل تطبيق إلى الخلفية ويتم تجميده، يكتب النظام مقدار استخدام الذاكرة الحالي للتطبيق في ملف memory.reclaim الخاص به. يؤدي ذلك إلى إجبار النواة على إخلاء جميع صفحات الملفات الممكنة ونقل جميع الصفحات المجهولة إلى ZRAM، ما يقلّل من حجم التطبيق في الذاكرة.
يمكنك تحقيق "الاسترداد الأقصى" نفسه يدويًا من خلال قراءة memory.current
وكتابته مرة أخرى في memory.reclaim:
# Force reclaim of everything
adb shell "cat /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.current > /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.reclaim"
التمرين: فرض استرداد البيانات وتتبُّعها
سنفرض الآن على النواة استعادة الذاكرة من MemoryLab وتسجيل النشاط في بيانات تتبُّع Perfetto.
- الإعداد: تأكَّد من أنّ MemoryLab يتضمّن بعض عمليات التخصيص (Java وBitmaps).
بدء التتبُّع: استخدِم إعدادات التتبُّع المضمّنة هذه لتسجيل أحداث الجدولة والاسترداد وذاكرة التخزين المؤقت للصفحة.
# Use a config that captures scheduling, reclaim and page cache events adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/reclaim.perfetto-trace <<EOF buffers: { size_kb: 131072 fill_policy: RING_BUFFER } data_sources: { config { name: "linux.ftrace" ftrace_config { ftrace_events: "sched/sched_switch" ftrace_events: "sched/sched_wakeup" ftrace_events: "kmem/rss_stat" ftrace_events: "mm_filemap_add_to_page_cache" ftrace_events: "mm_filemap_delete_from_page_cache" ftrace_events: "vmscan/mm_vmscan_direct_reclaim_begin" ftrace_events: "vmscan/mm_vmscan_direct_reclaim_end" ftrace_events: "vmscan/mm_vmscan_memcg_reclaim_begin" ftrace_events: "vmscan/mm_vmscan_memcg_reclaim_end" symbolize_ksyms: true } } } data_sources: { config { name: "linux.process_stats" process_stats_config { scan_all_processes_on_start: true } } } EOFالضغط على زر التشغيل واستعادة البيانات:
- في MemoryLab، انقر على تخصيص الذاكرة الأصلية (1 غيغابايت). سيؤدي ذلك إلى زيادة الضغط على مستوى النظام.
في نافذة طرفية منفصلة، استردّ 200 ميغابايت:
adb shell "echo 200M > /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.reclaim"
الرجوع إلى الوضع السابق: يمكنك التبديل إلى MemoryLab. انقر على Thrash Pagecache (Refault test).
إيقاف التتبُّع: اضغط على Ctrl+C في نافذة التتبُّع الطرفية.
تحليل عملية الاسترداد في Perfetto
عند فتح عملية التتبُّع، يمكنك الاطّلاع على عملية استرداد الذاكرة على مستوى النظام وعلى مستوى كل تطبيق:

ابحث عن ما يلي في التتبُّع:
- kswapd: ابحث عن
kswapd0ضمن سلاسل عمليات النواة (في لقطة الشاشة أعلاه، تم تثبيته يدويًا في الأعلى). وستلاحظ أنّها تستيقظ وتعمل (الشرائح الخضراء) عندما يواجه النظام صعوبة في العثور على صفحات مجانية. - استرداد الذاكرة المباشر: اطّلِع على سلاسل عمليات
com.android.memorylab. ستظهر شرائح أحداث ftrace باللون الأرجواني (مثلmm_vmscan_direct_reclaim_begin) أسفل مسار جدولة سلسلة التعليمات البرمجية مباشرةً. يشير ذلك إلى أنّ سلسلة التعليمات الخاصة بالتطبيق متوقّفة مؤقتًا في انتظار أن يتيح النواة صفحات. - عدادات RSS وSwap:
mem.rss.anon: يزداد كلّما نقرت على أزرار التخصيص.mem.swap: تزداد بشكل مطّرد مع ضغطkswapdوسلاسل التطبيق الخاصة (الخاضعة للاسترداد المباشر) لهذه الصفحات المجهولة الهوية في ZRAM.
- استرداد memcg: إذا كبّرت اللحظة التي بدأت فيها عملية الاسترداد اليدوي، ستلاحظ انخفاضًا حادًا في كل من
rss.anonوrss.file، مصحوبًا بأحداثmm_vmscan_memcg_reclaim.
← على مستوى النظام | ↑ للأعلى | تفاعل kswapd وlmkd →