ב-Android 17 ואילך יש תמיכה בדימון (daemon) לניהול זיכרון (mmd), שהוא דימון (daemon) של המערכת שמטפל בהגדרת הדימון (daemon), בפרמטרים שניתנים לשינוי ובמשימות שוטפות של החלפה או תחזוקה של ZRAM.
רקע
לפני ההשקה של mmd, ההגדרות של ZRAM ב-Android היו מפוצלות והציעו אפשרויות מוגבלות להתאמה אישית. mmd פותרת את הבעיה הזו על ידי ריכוז הניהול של ZRAM, ומאפשרת לוגיקת הגדרה מתוחכמת יותר ופשוטה יותר להוספה של תכונות חדשות ולשיפורים בארכיטקטורה.
mmd גם יוצר הפרדה ברורה בין תהליך system_server שמבוסס על Java לבין החלפה ברמת הליבה או ניהול זיכרון.
ארכיטקטורה וניהול ZRAM
בסיום האתחול (כלומר, כש-sys.boot_completed=1), mmd_setup מנסה להגדיר את ZRAM עם הפרמטרים שצוינו. אחרי שסיימתם להגדיר את ZRAM, המערכת מפעילה את שירות mmd שמטפל במשימות תחזוקה שוטפות.
בפרויקט mmd, פעולות התחזוקה מתחילות מ-system_server על ידי שליחת בקשות Binder אל mmd באמצעות הממשק IMmd.
mmd מטפל במשימות התחזוקה של ביצוע כתיבה חוזרת של ZRAM, דחיסה מחדש וכתיבה חוזרת לכל תהליך על סמך מנוע מדיניות פנימי משלו. אפשר להגדיר את התזמון מ-ActivityManagerService ואת מדיניות התחזוקה של ZRAM באמצעות מאפייני מערכת.
שילוב שרת מערכת (system_server)
תהליך system_server מבוסס-Java קובע מתי מתבצעת קריאה ל-mmd. התהליך מפריד בין סריקות גלובליות של תחזוקה לבין אופטימיזציות ממוקדות של זיכרון לכל אפליקציה.
תחזוקה רגילה של פוסט-פרודקשן
התחזוקה הגלובלית של ZRAM מופעלת על ידי ActivityManagerService באמצעות com.android.server.memory.ZramMaintenance.

איור 1. תהליך תזמון התחזוקה של ZRAM.
- מנוע התזמון:
ZramMaintenanceרושם משימת רקע תקופתית ב-JobSchedulerשל Android. - הגבלות על משימות: כדי למנוע גמגום בממשק המשתמש של האפליקציה או תחרות על משאבי המעבד, המשימה מוגדרת באופן מפורש עם
setRequiresDeviceIdle(true)ו-setRequiresBatteryNotLow(true). - הפעלת Binder: כשהמתזמן מפעיל את
onStartJob(),system_serverמפעיל אתmmd.doZramMaintenanceAsync(). זוהי קריאה חד-כיוונית ל-Binder באופן אסינכרוני.system_serverלא חוסם את ההמתנה לסיום של סריקות תחזוקה. mmdמעביר את הפעולה הזו לתור של תהליך רקע כדי לבצע דחיסה מחדש וכתיבה חוזרת ברצף.
החזרת נתונים לכל תהליך
הסרת זיכרון ממוקדת לכל תהליך מנוהלת על ידי ActivityManagerService באמצעות com.android.server.am.CachedAppOptimizer.

איור 2. זרימת כתיבה חוזרת של mmd לכל תהליך.
כשמעבירים תהליך למצב שמור במטמון ברקע, ActivityManager מבצע דחיסה של הזיכרון. אם הפסקת התהליך בגלל מחסור בזיכרון תהיה גלויה למשתמש, כלומר התהליך מארח Activity, ואם כתיבה חוזרת של ZRAM לכל תהליך תביא את הזיכרון שבשימוש של התהליך קרוב לאפס, המערכת תפעל לפי השלבים הבאים:
- אחרי הדחיסה,
CachedAppOptimizerמפרסם הודעה מושהית (ZRAM_WRITEBACK_MSG) למטפל הדחיסה הפנימי שלו (ההשהיה היא שלmZramWritebackWaitSeconds). - כשזמן ההשהיה מסתיים, ActivityManager פותח תיאור קובץ של תהליך מאובטח
pidfd. - שרת המערכת קורא ל-
mmd.asyncWritebackProcessZramMemory(pfd, callback). -
mmdמבצע את פעולת ה-ioctl של הכתיבה החוזרת לכל תהליך (per-process writeback) ומדווח בחזרה באמצעותIMmdProcessWritebackCallback. אם הפעולה תצליח, ActivityManager יסמן את רשומת התהליך (setIsZramWrittenBack(app, true)) כדי לשפר אתoom_score_adjשל התהליך, ויתעד את המדדים ב-FrameworkStatsLog.ZRAM_WRITEBACK_EVENT.
אחזור מראש לכל תהליך
כשמשתמש מפעיל מחדש אפליקציה ששמורה במטמון (ההקפאה שלה בוטלה בגלל UNFREEZE_REASON_ACTIVITY), ActivityManager מצמצם את זמן האחזור של הפעלת האפליקציה שנגרם בגלל תקלות חמורות בדפים מאחסון הגיבוי:
-
CachedAppOptimizerמיירט את אירוע הביטול של ההקפאה ומפעיל אתprefetchZram(app). - שרת המערכת שולח את
pidfdשל האפליקציה באמצעות Binder באמצעותmmd.asyncPrefetchProcessZramMemory(pfd). mmdמנפיק אתZRAM_ANDROID_IOC_PROCESS_PREFETCHioctl, ומורה לליבה לבצע שליפה מראש באופן אסינכרוני של דפים שהועברו בחזרה ל-RAM בזמן ששרשור ה-UI הראשי של האפליקציה עובר אתחול.
סקירה כללית של משימות תחזוקה ועיבוד לאחר העיבוד
בקטע הזה מתוארות פעולות התחזוקה ברקע ומשימות העיבוד שאחרי ש-mmd מבצע כדי לבצע אופטימיזציה של שטח ההחלפה וזיכרון המערכת.
תחזוקה בפורמט mmd
ב-mmd, תחזוקה מתייחסת לסריקות תחזוקה מתוזמנות ברקע, שמבצעות אופטימיזציה של השימוש באזור ההחלפה ובזיכרון הפיזי בלי להשפיע על הביצועים של משתמשים פעילים בחזית. במקום לבצע סריקות סינכרוניות רציפות (שיגרמו להפעלות חמורות של המעבד ולבעיות בממשק המשתמש), התחזוקה מתבצעת באופן אסינכרוני:
system_serverמופעל מעת לעתdoZramMaintenanceAsync()ב-Binder.
mmdמעביר את הבקשה לתור של עבודות רקעLowPrioWorkItem::ZramMaintenance.ב-
mmdיש שרשור עובד יחיד שמנהל תור בעדיפות גבוהה ותור בעדיפות נמוכה. פריטי עבודה בעדיפות גבוהה (כמו שליפה מראש לכל תהליך) מקבלים עדיפות ויכולים להפסיק לפני הזמן פריטי עבודה בעדיפות נמוכה. תחזוקה וכתיבה חוזרת לכל תהליך פועלות כפריטי עבודה בעדיפות נמוכה. כשפותחים את חלון ה-pop-up, ה-thread של העובד מבצע שתי פעולות תחזוקה עיקריות ברצף:דחיסה מחדש של ZRAM: סורקת דפי החלפה קיימים ודוחסת מחדש דפים לא פעילים באמצעות אלגוריתם דחיסה משני עם יחס גבוה יותר, לדוגמה,
zstd.ZRAM writeback: סורק דפים לא פעילים ומפנה אותם לחלוטין מ-RAM לאחסון פלאש תומך, מכשיר לולאה מקובץ ב-
/data.
משימות עיבוד תמונה (Post Processing) ב-ZRAM
במודול ZRAM של ליבת Linux ובארכיטקטורה של mmd, משימות של עיבוד פוסט הן טרנספורמציות אסינכרוניות שמוחלות על דפי זיכרון אחרי שהן כבר הוחלפו על ידי נתיבי ההחזרה הרגילים של הליבה (kswapd או compaction).
כשדף מוחלף בהתחלה, המערכת נותנת עדיפות למהירות: היא משתמשת באלגוריתם דחיסה מהיר ראשי (כמו lz4) ומאחסנת את הדף הדחוס ב-RAM. עם זאת, לאורך זמן, הרבה דפים שהועברו לזיכרון הווירטואלי הופכים ללא פעילים, למשל אפליקציות ששמורות במטמון ברקע ולא נפתחות מחדש במשך שעות. השארת דפים לא פעילים ב-ZRAM מהיר ודחוס קלות היא לא יעילה.
צינור עיבוד נתונים (pipeline)
mmd מטמיע מחזור חיים של עיבוד שלאחר מכן, שכולל כמה שלבים, כדי לבצע אופטימיזציה של הדפים האלה:

איור 3. mmd מחזור החיים של הדף.
שלב 1: החלפה ראשונית (דחיסה מהירה): הזיכרון משוחרר קודם באמצעות kswapd או דחיסת אפליקציות. בדרך כלל, השחזור הראשון מתבצע באמצעות אלגוריתם דחיסה מהיר כמו
lz4, והתוכן מאוחסן ב-RAM.שלב 2: סימון של חוסר פעילות (התיישנות ומעקב):
mmdמעקב חוסר פעילות ניגש למעקב זיכרון ליבה (CONFIG_ZRAM_TRACK_ENTRY_ACTIME) או משתמש בסמן חוסר הפעילות של התוכנה כדי לעקוב אחרי משך הזמן שדפים נשארו ללא מגע.שלב 3: עיבוד שלאחר מכן 1 – דחיסה מחדש (שחזור בזיכרון): דחיסה מחדש מתבצעת בדפים שמגיעים לגיל סרק של דחיסה מחדש (
min_idle_secondsעדmax_idle_seconds). mmdכותב ל-/sys/block/zram0/recompressכדי להנחות את ליבת המערכת לבטל את הדחיסה של הדףlz4ולדחוס אותו מחדש באמצעותzstd. הפעולה הזו מפנה זיכרון RAM פיזי בלי לגרום לשחיקה של צריבת ה-ROM (flash).שלב 4: עיבוד אחרי – כתיבה חוזרת (פינוי לאחסון פלאש): אם העומס על הזיכרון נמשך והדפים מגיעים לגיל סרק של כתיבה חוזרת (בדרך כלל 20 שעות או יותר),
mmdמפעיל כתיבה חוזרת. mmdכותב ל-/sys/block/zram0/idleו-/sys/block/zram0/writebackכדי להוציא את הדף הדחוס לגמרי מ-RAM לאחסון פלאש לגיבוי.
הגדרת תצורה של ZRAM
הפקודה mmd טוענת ומעבדת את מאפייני ההגדרה הבאים של ZRAM:
| מאפיין (property) | שימוש | ברירת מחדל |
|---|---|---|
mmd.zram.enabled |
האם ההגדרה mmd ZRAM מופעלת. |
false |
mmd.zram.num_devices |
מספר מכשירי ה-ZRAM שצריך להגדיר. כדי להגדיר מספר N, צריך לוודא שמכשירים zram0 עד zram<N-1> נמצאים בסביבה לפני שהמערכת מגדירה את sys.boot_completed=1.
אפשר להגדיר את המאפיינים ברשימת המכשירים עם ZRAM לכל מכשיר בנפרד.
|
1 |
mmd.zram.device_priority |
ערכי עדיפות להעברה בעת קריאה ל-swapon. |
לא מוגדר |
mmd.zram.comp_algorithm |
אלגוריתם דחיסה של ZRAM. אם לא מציינים אלגוריתם, המערכת משתמשת באלגוריתם הדחיסה שמוגדר כברירת מחדל בקרנל. | לא מוגדר |
mmd.zram.size |
גודל מכשיר ZRAM בבייט, או אחוז מגודל זיכרון ה-RAM של המכשיר, לדוגמה,
75%.
|
50% |
mmd.zram.writeback.enabled |
האם להפעיל ZRAM writeback. | false |
mmd.zram.writeback.device_size |
הגודל של מכשיר הכתיבה החוזרת בבייטים או באחוזים ממחיצת הנתונים. אפשר לשנות את הגודל של המכשיר בפועל בהתאם לנפח האחסון הזמין במחיצת הנתונים. | 1073741824 (1 GiB) |
mmd.zram.writeback.min_free_space_mib |
השטח הפנוי המינימלי ב-MiB שצריך להיות זמין אחרי שמגדירים את מכשיר הכתיבה. | 1536 (1.5 GiB) |
mmd.zram.writeback.use_nr_tags_prop |
כש-true משתמש בערך ב-mmd.zram.writeback.nr_tags כדי להגדיר את עומק התור של מכשיר הלולאה שמגבה את ZRAM writeback. זהו פתרון עקיף למצבים שבהם אי אפשר להגדיר את מדיניות SELinux של הספק כך שתאפשר ל-mmd לקרוא ישירות את nr_tags של מכשיר הבלוק שמגבה את /data.
|
false |
mmd.zram.writeback.nr_tags |
מומלץ לקרוא את mmd.zram.writeback.use_nr_tags_prop. |
לא מוגדר |
mmd.zram.recompression.enabled |
האם להפעיל את התכונה ZRAM recompression. | false |
mmd.zram.recompression.algorithm |
אלגוריתם משני לדחיסה מחדש של ZRAM. | zstd |
מאפייני מכשיר לכל ZRAM
אם הערך של mmd.zram.num_devices גדול מ-1, אפשר להגדיר מאפיינים ספציפיים לכל מכשיר ZRAM בנפרד. לשם כך, צריך להגדיר את המאפיין לערך מופרד בפסיקים שמכיל בדיוק mmd.zram.num_devices רכיבים.
המאפיינים האלה כוללים:
mmd.zram.sizemmd.zram.comp_algorithmmmd.zram.device_prioritymmd.zram.recompression.enabledmmd.zram.recompression.huge_idle.enabledmmd.zram.recompression.idle.enabledmmd.zram.recompression.huge.enabledmmd.zram.recompression.threshold_bytesmmd.zram.recompression.algorithmmmd.zram.writeback.device_sizemmd.zram.writeback.huge_idle.enabledmmd.zram.writeback.idle.enabledmmd.zram.writeback.huge.enabled
הוצאה משימוש של הגדרת ZRAM קיימת
אפשר עדיין להשתמש ב-swapon_all ב-Android כדי להגדיר ZRAM ושטח החלפה מבוסס-דיסק, אבל mmd היא הגישה המומלצת לניהול ZRAM, כי היא מאפשרת הגדרה קלה יותר ותכונות מתקדמות כמו דחיסה מחדש של ZRAM.
כשההגדרה של mmd ZRAM מופעלת על ידי mmd.zram.enabled:
- הגדרת ZRAM בהטמעה של
swapon_allהופכת לפעולה שלא עושה כלום. - המערכת מתעלמת מהגדרות ZRAM קיימות, כמו
config_zramWritebackבקובץ שכבת העלconfig.xmlומאפייני המערכת שלro.zram.*writeback.
פרמטרים שניתנים לשינוי לתחזוקת ZRAM
תחזוקת ZRAM אמורה לפעול מחוץ לקופסה, ואפשר לבצע כוונון עדין נוסף באמצעות מאפייני המערכת שמופיעים בקטע הזה.
תזמון תחזוקה של ZRAM
המאפיינים האלה קובעים איך ומתי system_server יתזמן משימות תחזוקה של ZRAM.
| מאפיין (property) | שימוש | ברירת מחדל |
|---|---|---|
mm.zram.maintenance.first_delay_seconds |
ההשהיה לפני התחלת התחזוקה הראשונה של ZRAM. | 3600 (שעה אחת) |
mm.zram.maintenance.periodic_delay_seconds |
ההשהיה בין תזמון תחזוקה עוקב של ZRAM. | 3600 (שעה אחת) |
mm.zram.maintenance.require_device_idle |
האם להפעיל תחזוקת ZRAM רק כשהמכשיר בלי פעילות. | true |
mm.zram.maintenance.require_battery_not_low |
האם נדרש שהסוללה לא תהיה חלשה לפני הפעלת תחזוקת ZRAM. | true |
מדיניות writeback של ZRAM
הפרמטרים הבאים קובעים מתי ואיזה סוג של זיכרון נכתב במכשיר הגיבוי:
| מאפיין (property) | שימוש | ברירת מחדל |
|---|---|---|
mmd.zram.writeback.backoff_seconds |
הזמן שחלף מאז פעולת הכתיבה החוזרת האחרונה. | 600 (10 דקות) |
mmd.zram.writeback.min_idle_seconds |
בשילוב עם mmd.zram.writeback.max_idle_seconds כדי לחשב את משך הזמן שהדף לא היה פעיל, כדי לקבוע אם הוא עומד בדרישות לכתיבה חוזרת על סמך חלק ניצול הזיכרון. הגיל המחושב של חוסר הפעילות הוא אינטרפולציה אקספוננציאלית בין שני הפרמטרים כדי למזער את העבודה בזמן שאין לחץ על הזיכרון.
|
72000 (20 שעות) |
mmd.zram.writeback.max_idle_seconds |
מספר השניות המקסימלי שמשמש לחישוב דינמי של משך הזמן שהדף לא פעיל, על סמך ניצול הזיכרון. | 90000 (25 שעות) |
mmd.zram.writeback.huge.enabled |
האם להפעיל HUGE כתיבה חוזרת לדף. |
false |
mmd.zram.writeback.idle.enabled |
האם להפעיל IDLE כתיבה חוזרת לדף. |
true |
mmd.zram.writeback.huge_idle.enabled |
האם להפעיל HUGE_IDLE כתיבה חוזרת לדף. |
true |
mmd.zram.writeback.min_bytes |
מספר הבייטים המינימלי לכתיבה חוזרת בסבב אחד של כתיבה חוזרת במצב סרק. | 5242880 (5 MiB) |
mmd.zram.writeback.max_bytes |
מספר הבייטים המקסימלי לכתיבה חוזרת בסיבוב אחד של כתיבה חוזרת במצב סרק. | 314572800 (300 MiB) |
mmd.zram.writeback.max_bytes_per_day |
המספר המקסימלי של בייטים לכתיבה חוזרת בפרק זמן של 24 שעות. | 25769803776 (24 GiB) |
mmd.zram.writeback.limit.enabled |
האם להפעיל את ניהול הרישום של מכסת התקציב היומי להחזרת נתונים. | true |
מדיניות דחיסה מחדש של ZRAM
הפרמטרים הבאים קובעים מתי ואיזה סוג של זיכרון יידחס מחדש:
| מאפיין (property) | שימוש | ברירת מחדל |
|---|---|---|
mmd.zram.recompression.backoff_seconds |
הזמן שחלף מאז הדחיסה מחדש האחרונה. | 1800 (30 דקות) |
mmd.zram.recompression.min_idle_seconds |
בשילוב עם mmd.zram.recompression.max_idle_seconds כדי לחשב את משך הזמן שהדף לא פעיל, כדי לקבוע אם הוא עומד בדרישות לדחיסה מחדש על סמך חלק ניצול הזיכרון. הגיל המחושב של בלי פעילות הוא אינטרפולציה אקספוננציאלית בין שני הפרמטרים כדי למזער את העבודה בזמן שאין לחץ על הזיכרון.
|
7200 (שעתיים) |
mmd.zram.recompression.max_idle_seconds |
מספר השניות המקסימלי שמשמש לחישוב דינמי של משך הזמן שהדף היה בלי פעילות. | 14400 (4 שעות) |
mmd.zram.recompression.threshold_bytes |
הגודל המינימלי בבייטים של דפי ZRAM שנלקחים בחשבון לצורך דחיסה מחדש. | 1024 (1 KiB) |
mmd.zram.recompression.huge.enabled |
האם להפעיל דחיסה מחדש של דף HUGE. |
true |
mmd.zram.recompression.idle.enabled |
האם להפעיל דחיסה מחדש של דף IDLE. |
true |
mmd.zram.recompression.huge_idle.enabled |
האם להפעיל דחיסה מחדש של דף HUGE_IDLE. |
true |
מעקב אחר דפים לא פעילים ב-ZRAM
mmd תחזוקת ZRAM מסמנת דפי ZRAM כלא פעילים על סמך משך הזמן שעבר מאז הגישה האחרונה אליהם. כדי להשתמש בתכונה הזו צריך להפעיל את ההגדרות של ליבת CONFIG_ZRAM_TRACK_ENTRY_ACTIME או CONFIG_ZRAM_MEMORY_TRACKING. CONFIG_ZRAM_TRACK_ENTRY_ACTIME מופעל כברירת מחדל בקרנלים של GKI בגרסה 6.18 ומעלה. בליבות קודמות,
יש תקורה של זיכרון והיא לא מופעלת כברירת מחדל.
אם הגדרת הליבה לא מופעלת, mmd תחזוקת ZRAM חוזרת ללוגיקה של תחליף תוכנה כדי לעקוב אחרי דפי ZRAM בלי פעילות:
סימון כל דפי ה-ZRAM כלא פעילים כשמפעילים את
mmd.מדלגים על תחזוקת ZRAM הבאה עד שתקופת ההשהיה הנדרשת לפני ניסיון חוזר מסתיימת.
ZRAM writeback or recompress idle pages. אם נשארו דפים לא פעילים בגלל מגבלות הכתיבה החוזרת,
mmdממשיך לכתוב בחזרה דפים בתחזוקה הבאה בלי לסמן דפים חדשים כלא פעילים (דילוג על שלב 4).אם כל הדפים הלא פעילים נכתבים בחזרה, מסמנים את כל דפי ה-ZRAM כלא פעילים שוב וחוזרים לשלב 2. אם ההגדרה ZRAM writeback מושבתת,
mmdמסמן את כל דפי ZRAM כלא פעילים כשמתבצעת דחיסה מחדש של ZRAM אחרי משך הזמן של recompression idle.
הנחיות לפתרון בעיות ולאימות
כדי לאמת את הפעולות של mmd ו-ZRAM ולאבחן אותן, צריך לפעול לפי השלבים הבאים לאימות ולפתרון בעיות.
אימות ההגדרה של ZRAM
כדי לוודא ש-mmd הגדיר בהצלחה את ZRAM במהלך האתחול:
בודקים את אלגוריתם הדחיסה הפעיל ואת גודל הדיסק:
cat /sys/block/zram0/comp_algorithm cat /sys/block/zram0/disksizeבודקים את
mmdמאפייני המערכת ואת מצב השירות הפועל:getprop | grep mmd.zram dumpsys -l | grep mmd
אימות התחזוקה והכתיבה החוזרת של ZRAM
מוודאים שמשימות התחזוקה של כתיבה חוזרת ודחיסה מחדש של ZRAM פועלות:
בודקים את הסטטוס של מכשיר הבלוקים הבסיסי:
cat /sys/block/zram0/bd_statכדי לבדוק את יעילות הדחיסה מחדש, עוקבים אחרי
/sys/block/zram0/mm_stat. שינויים בגדלים של נתונים דחוסים אמורים להופיע אחרי מחזורי תחזוקה.
אימות של כתיבה חוזרת לכל תהליך
אפשר להשתמש בדרכים הבאות כדי לוודא שהכתיבה החוזרת לכל תהליך פועלת:
- בודקים את
adb logcat -s mmdכדי לראות אם יש יומנים של כתיבה חוזרת מוצלחת או אבחון של כשלים.
בעיות נפוצות וניתוחים
אלה מצבי השגיאה הנפוצים שבהם המשתמשים עשויים להיתקל:
-
WritebackDailyLimitExceeded: השגיאה הזו מציינת שהגעתם למכסה שלmmd.zram.writeback.max_bytes_per_day. במקרה כזה,mmdמשהה את הכתיבה למטמון בלי פעילות עד שחלון 24 השעות המתגלגל מתקדם. -
Process prefetch or writeback failed: השגיאה הזו יכולה להופיע ב-logcat כש-ioctl נכשל. היא מופיעה בדרך כלל מהסיבות הבאות:-
EBADFאוESRCH: תהליך היעד הסתיים לפני ש-mmdיכול היה לשלוח אתpidfdלליבה. -
ENOSPC: מחיצת האחסון של הגיבוי מלאה, או שהתור של מכשיר הלולאה מוצה.
-
- ZRAM לא מוגדר: אם
mmdלא מצליח להגדיר ZRAM במהלך האתחול, יכול להיות שהסיבה לכך היא שסקריפטים של init מדור קודםswapon_allאו של ספקים נעלו את/dev/block/zram0לפני ש-mmdהצליח לפעול.