ב-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 מבצע דחיסה של הזיכרון. אם הפסקת התהליך בגלל מחסור בזיכרון תהיה גלויה למשתמש, כלומר התהליך מארח פעילות, ואם כתיבת הנתונים מה-ZRAM לזיכרון הווירטואלי לכל תהליך תביא את הזיכרון שבשימוש של התהליך כמעט לאפס, המערכת תפעל לפי השלבים הבאים:
- אחרי הדחיסה,
CachedAppOptimizerמפרסם הודעה מושהית (ZRAM_WRITEBACK_MSG) למטפל הדחיסה הפנימי שלו (ההשהיה היאmZramWritebackWaitSeconds). - כשההשהיה מסתיימת, ActivityManager פותח תיאור קובץ מאובטח של תהליך
pidfd. - שרת המערכת קורא ל-
mmd.asyncWritebackProcessZramMemory(pfd, callback). -
mmdמבצע את ioctl של כתיבה חוזרת לכל תהליך ומדווח בחזרה באמצעות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מנפיק את ioctlZRAM_ANDROID_IOC_PROCESS_PREFETCH, ומורה לליבה לבצע אחזור מראש באופן אסינכרוני של דפים שהועברו לזיכרון הווירטואלי בחזרה ל-RAM בזמן שהתהליכון הראשי של ממשק המשתמש של האפליקציה עובר אתחול.
סקירה כללית של משימות תחזוקה ועיבוד לאחר מכן
בקטע הזה מתוארות פעולות התחזוקה ברקע ומשימות העיבוד שאחרי ש-mmd מבצע כדי לבצע אופטימיזציה של שטח ההחלפה וזיכרון המערכת.
תחזוקה בפורמט mmd
ב-mmd, תחזוקה מתייחסת לסריקות תחזוקה מתוזמנות ברקע שמבצעות אופטימיזציה של השימוש באזור החלפה ובזיכרון הפיזי, בלי לפגוע בביצועים של משתמשים פעילים בחזית. במקום לבצע סריקות סינכרוניות רציפות (שיגרמו להפעלות חמורות של המעבד ולבעיות בממשק המשתמש), התחזוקה מתבצעת באופן אסינכרוני:
system_serverperiodically firesdoZramMaintenanceAsync()across Binder.
mmdמעביר את הבקשה לתור של עבודות רקעLowPrioWorkItem::ZramMaintenance.יש שרשור עובד יחיד ב-
mmdשמנהל גם תור בעדיפות גבוהה וגם תור בעדיפות נמוכה. פריטי עבודה בעדיפות גבוהה (כמו שליפה מראש לכל תהליך) מקבלים עדיפות ויכולים להקדים פריטי עבודה בעדיפות נמוכה. תחזוקה וכתיבה חוזרת לכל תהליך פועלות כפריטי עבודה בעדיפות נמוכה. כאשר ה-Thread עובד מופעל, הוא מבצע שתי פעולות תחזוקה עיקריות ברצף:דחיסה מחדש של ZRAM: סריקה של דפי החלפה קיימים ודחיסה מחדש של דפים לא פעילים באמצעות אלגוריתם דחיסה משני עם יחס גבוה יותר, למשל,
zstd.ZRAM writeback: סורק דפים לא פעילים ומסלק אותם לחלוטין מ-RAM לאחסון פלאש תומך במכשיר לולאה מקובץ ב-
/data.
משימות עיבוד תמונה (Post Processing) ב-ZRAM
במודול ZRAM של ליבת Linux ובארכיטקטורה של mmd, משימות של עיבוד פוסט הן טרנספורמציות אסינכרוניות שמוחלות על דפי זיכרון אחרי שהם כבר הוחלפו על ידי נתיבי ההחזרה הרגילים של הליבה (kswapd או דחיסה).
כשדף מוחלף בהתחלה, המערכת נותנת עדיפות למהירות: היא משתמשת באלגוריתם דחיסה מהיר ראשי (כמו 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: עיבוד לאחר ההעברה 2 – כתיבה חוזרת (הוצאה לאחסון פלאש): אם העומס על הזיכרון נמשך והדפים מגיעים לגיל סרק של כתיבה חוזרת (בדרך כלל 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.
אפשר להגדיר את המאפיינים ברשימת המכשירים לכל מכשיר בנפרד.
|
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
המאפיינים האלה קובעים איך ומתי משימות התחזוקה של ZRAM מתוזמנות על ידי system_server.
| מאפיין (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הצליח לפעול.