בדיקת A/B וירטואלית היא מנגנון העדכון הראשי של Android. בדיקות A/B וירטואליות מבוססות על עדכוני A/B מדור קודם (ראו עדכוני מערכת A/B) ועל עדכונים שאינם A/B, שהוצאו משימוש בגרסה 15 כדי לצמצם את שטח האחסון שנדרש לעדכונים.
למעשה, לניסוי A/B וירטואלי אין מקום נוסף למחיצות דינמיות. אפשר לקרוא מידע נוסף במאמר מחיצות דינמיות. במקום זאת, השינוי נכתב בתמונה מיידית (snapshot) ולאחר מכן מתמזג עם המחיצה הבסיסית אחרי שמוודאים שההפעלה בוצעה בהצלחה. בבדיקה הווירטואלית של A/B נעשה שימוש בפורמט snapshot ספציפי ל-Android. כדאי לעיין במאמר פורמט COW לקובצי snapshot דחוסים, שמאפשר לדחוס קובצי snapshot ולצמצם את השימוש במקום בדיסק. ב-OTA מלא, גודל קובץ ה-snapshot מצטמצם בכ-45% לאחר דחיסה, וב-OTA מצטמצם בכ-55%.
ב-Android 12 יש אפשרות לדחיסה וירטואלית של בדיקות A/B כדי לדחוס מחיצות של קובצי snapshot. בניסוי A/B וירטואלי אפשר:
- עדכוני A/B וירטואליים מתבצעים ללא הפרעה (העדכון מתבצע לגמרי ברקע בזמן שהמכשיר פועל), כמו עדכוני A/B. עדכוני A/B וירטואליים מקצרים את הזמן שבו המכשיר במצב אופליין ולא ניתן לשימוש.
- אפשר לבטל עדכוני A/B וירטואליים. אם מערכת ההפעלה החדשה לא תצליח להפעיל את המכשיר, הוא יחזור לגרסה הקודמת באופן אוטומטי.
- עדכוני A/B וירטואליים משתמשים במינימום מקום נוסף על ידי שכפול רק של המחיצות שבהן bootloader משתמש. מחיצות אחרות שניתן לעדכן נשמרות בקובץ snapshot.
רקע וטרמינולוגיה
בקטע הזה מוסבר מהי המונחולוגיה ומתוארת הטכנולוגיה שתומכת בבדיקות A/B וירטואליות. במהלך התקנה דרך OTA, נתוני מערכת ההפעלה החדשים נכתבים בחריץ החדש שלהם למחיצות פיזיות, או במכשיר COW ספציפי ל-Android. אחרי הפעלה מחדש של המכשיר, נתוני המחיצות הדינמיות מוזגו חזרה למכשיר הבסיסי באמצעות השימוש ב-dm-user וב-snapuserd daemon. התהליך הזה מתרחש כולו במרחב המשתמש.
Device-mapper
Device-mapper הוא שכבת בלוקים וירטואלית של Linux שמשמשת לעיתים קרובות ב-Android. במחיצות דינמיות, מחיצות כמו /system
הן מקבץ של מכשירים בשכבות:
- בחלק התחתון של הסטאק נמצא המחיצה הפיזית super (לדוגמה,
/dev/block/by-name/super
). - במרכז מופיע מכשיר
dm-linear
, שמציין אילו בלוקים במחיצה העל-עליונה יוצרים את המחיצה הדינמית הנתונה. הערך הזה מופיע כ-/dev/block/mapper/system_[a|b]
במכשיר A/B או כ-/dev/block/mapper/system
במכשיר שאינו A/B. - בחלק העליון מופיע מכשיר
dm-verity
שנוצר למחיצות מאומתות. המכשיר הזה מאמת שהחסימות במכשירdm-linear
חתומות בצורה נכונה. הוא מופיע בתור/dev/block/mapper/system-verity
והוא המקור של נקודת הטעינה/system
.
איך נראה הסטאק מתחת לנקודת הטעינה /system
מוצג באיור 1.
איור 1. סטאק מתחת לנקודת הטעינה /system
קובצי snapshot דחוסים
ב-Android מגרסה 12 ואילך, בגלל שדרישות האחסון במחיצה /data
יכולות להיות גבוהות, אפשר להפעיל קובצי snapshot דחוסים ב-build כדי לעמוד בדרישות האחסון הגבוהות יותר של המחיצה /data
.
קובצי snapshot וירטואליים דחוסים של A/B מבוססים על הרכיבים הבאים, שזמינים ב-Android מגרסה 12 ואילך:
dm-user
, מודול ליבה שדומה ל-FUSE ומאפשר למרחב המשתמש להטמיע התקני בלוקים.snapuserd
, דימון של מרחב המשתמש להטמעת פורמט חדש של קובץ snapshot.
הרכיבים האלה מאפשרים את הדחיסה. השינויים האחרים שנדרשו כדי להטמיע את היכולות של קובצי snapshot דחוסים מפורטים בקטעים הבאים: פורמט COW לתמונות מצב דחוסות, dm-user ו-snapuserd.
פורמט COW עבור קובצי snapshot דחוסים
ב-Android 12 ואילך, קובצי snapshot דחוסים משתמשים בפורמט COW ספציפי ל-Android. פורמט COW מכיל מטא-נתונים על ה-OTA, ויש לו מאגרים נפרדים שמכילים פעולות COW ונתונים חדשים של מערכת ההפעלה. בהשוואה לפורמט של קובץ snapshot של הליבה, שמאפשר רק פעולות replace (החלפת בלוק X בתמונה הבסיסית בתוכן של בלוק Y בקובץ ה-snapshot), הפורמט COW של קובצי snapshot דחוסים של Android הוא פורמט גמיש יותר ותומך בפעולות הבאות:
- העתקה: צריך להחליף את הבלוק X במכשיר הבסיס בבלוק Y במכשיר הבסיס.
- החלפה: צריך להחליף את בלוק X במכשיר הבסיס בתוכן של בלוק Y ב-snapshot. כל אחד מהבלוקים האלה עבר דחיסת gz.
- אפס: צריך להחליף את הבלוק X במכשיר הבסיס בכל אפסים.
- XOR: מכשיר ה-COW מאחסן ביטים דחוסים של XOR בין בלוק X לבין בלוק Y. (התכונה זמינה ב-Android מגרסה 13 ואילך).
עדכונים מלאים דרך OTA מורכבים רק מפעולות replace ו-zero. בעדכונים מצטברים של OTA יכולות להיות גם פעולות העתקה.
הפריסה המלאה של קובץ snapshot בדיסק נראית כך:
איור 2. פורמט COW של Android בדיסק
dm-user
מודול הליבה dm-user מאפשר ל-userspace
להטמיע מכשירי בלוק של device-mapper. רשומה בטבלה dm-user יוצרת מכשיר 'אחר' בקטע /dev/dm-user/<control-name>
. תהליך userspace
יכול לבצע סקירה של המכשיר כדי לקבל מהליבה בקשות קריאה וכתיבה. לכל בקשה יש מאגר משויך שמרחב המשתמש מאכלס (לקריאה) או מעביר (לכתיבה).
מודול הליבה dm-user
מספק ממשק חדש של הליבה שגלוי למשתמשים, ולא נכלל בבסיס הקוד של kernel.org ב-upstream. עד אז, Google שומרת לעצמה את הזכות לשנות את הממשק של dm-user
ב-Android.
snapuserd
הרכיב של מרחב המשתמש snapuserd
ל-dm-user
מטמיע דחיסת A/B וירטואלית. Snapuserd הוא דימון במרחב המשתמש שאחראי על הכתיבה והקריאה במכשירי Android COW. כל הקלט והפלט של קובץ ה-snapshot חייבים לעבור דרך השירות הזה.
במהלך התקנה OTA, נתוני מערכת הפעלה חדשים נכתבים ב-snapshot על ידי snapuserd (עם דחיסה). כאן מתבצע גם הניתוח של המטא-נתונים והפריסה של נתוני הבלוק החדשים.
דחיסת XOR
במכשירים שמופעלת בהם מערכת ההפעלה Android מגרסה 13 ואילך, תכונת דחיסת XOR מופעלת כברירת מחדל ומאפשרת לשמור בייטים דחוסים ב-XOR בין בלוקים ישנים לבלוקים חדשים ב-snapshots של מרחב המשתמש. כשרק כמה בייטים בבלוק משתנים בעדכון A/B וירטואלי, סכימה של אחסון דחוס XOR משתמשת בפחות מקום מאשר סכימה ברירת המחדל של אחסון, כי בתמונות המצב לא מאוחסנים 4,000 בייטים מלאים. הפחתת הגודל של קובץ snapshot אפשרית כי נתוני XOR מכילים הרבה אפסים, וקל יותר לדחוס אותם מאשר נתוני בלוק גולמיים. במכשירי Pixel, דחיסת XOR מפחיתה את גודל קובץ ה-snapshot ב-25% עד 40%.
במכשירים שמשודרגים ל-Android מגרסה 13 ואילך, צריך להפעיל את דחיסת XOR. פרטים נוספים זמינים במאמר דחיסת XOR.
מיזוג קובצי snapshot
במכשירים שמריצים Android מגרסה 13 ואילך, תהליכי תמונת המצב והמיזוג של תמונת המצב ב-Virtual A/B compression מתבצעים על ידי הרכיב snapuserd
במרחב המשתמש. במכשירים שמשודרגים ל-Android מגרסה 13 ואילך, צריך להפעיל את התכונה הזו. מידע נוסף זמין במאמר מיזוג של Userspace.
בהמשך מתואר תהליך דחיסת ה-A/B הווירטואלי:
- המסגרת מחברת את המחיצה
/system
למכשירdm-verity
, שממוקם מעל מכשירdm-user
. המשמעות היא שכל פעולת קלט/פלט ממערכת הקבצים ברמה הבסיסית (root) מנותבת אלdm-user
. dm-user
מפנה את הקלט/פלט לדימוןsnapuserd
במרחב המשתמש, שמטפל בבקשת הקלט/פלט.- בסיום פעולת המיזוג, המסגרת מרכזת את
dm-verity
מעלdm-linear
(system_base
) ומסירה אתdm-user
.
איור 3. תהליך דחיסת A/B וירטואלי
תהליך המיזוג של קובצי snapshot יכול להיפסק. אם המכשיר יופעל מחדש במהלך תהליך המיזוג, תהליך המיזוג ימשיך אחרי ההפעלה מחדש.
מעברים של Init
כשמפעילים את המכונה באמצעות קובצי snapshot דחוסים, השלב הראשון של init צריך להפעיל את snapuserd
כדי לטעון את המחיצות. הבעיה היא: כשsepolicy
נטען ומופעל, snapuserd
מועבר להקשר הלא נכון ובקשות הקריאה שלו נכשלות, עם דחיות של selinux.
כדי לטפל בבעיה הזו, snapuserd
עובר מעבר במקביל ל-init
, באופן הבא:
- בשלב הראשון,
init
מפעיל אתsnapuserd
מה-ramdisk ושומר בו מתאר קובץ פתוח במשתנה סביבה. - בשלב הראשון,
init
מעביר את מערכת הקבצים ברמה הבסיסית למחיצה של המערכת, ואז מפעיל את העותק שלinit
במערכת. - עותק המערכת של
init
קורא את מדיניות האבטחה המשולבת למחרוזת. Init
מפעיל אתmlock()
בכל הדפים שמגובים ב-ext4. לאחר מכן, הוא משבית את כל הטבלאות של device-mapper למכשירי snapshot, ומפסיק אתsnapuserd
. לאחר מכן אסור לקרוא מהמחיצות, כי הפעולה הזו גורמת לנעילה מרובת משתמשים (deadlock).- באמצעות מתאר הפתוח של העותק של
snapuserd
ב-ramdisk,init
מפעיל מחדש את הדימון עם ההקשר הנכון של selinux. טבלאות Device-mapper למכשירי snapshot מופעלות מחדש. - Init מפעיל את
munlockall()
– אפשר לבצע שוב פעולות IO.
שימוש במרחב המשותף
בטבלה הבאה מוצגת השוואה של השימוש במקום במנגנונים שונים של OTA, על סמך גודל מערכת ההפעלה וגודל ה-OTA של Pixel.
ההשפעה של הגודל | ללא בדיקת A/B | A/B | בדיקת A/B וירטואלית | בדיקת A/B וירטואלית (דחוסה) |
---|---|---|---|---|
Original Factory Image | 4.5GB סופר (קובץ אימג' בנפח 3.8GB + 700MB שמורים)1 | 9GB סופר (3.8GB + 700MB שמורים, לשני משבצות) | 4.5GB סופר (קובץ אימג' בנפח 3.8GB + 700M שמורים) | 4.5GB סופר (קובץ אימג' בנפח 3.8GB + 700M שמורים) |
מחיצות סטטיות אחרות | /cache | ללא | ללא | ללא |
נפח אחסון נוסף במהלך עדכון OTA (השטח מוחזר אחרי החלת העדכון) | 1.4GB ב-/data | 0 | 3.8GB2 ב-/data | 2.1GB2 ב-/data |
נפח האחסון הכולל הנדרש כדי להחיל עדכון OTA | 5.9GB3 (נתונים ו-Super) | 9GB (סופר) | 8.3GB3 (סופר ונתונים) | 6.6GB3 (מהיר ונתונים) |
1מצביע על פריסה משוערת על סמך מיפוי Pixel.
2ההנחה היא שתמונת המערכת החדשה באותו גודל כמו התמונה המקורית.
3דרישת המקום היא זמנית עד להפעלה מחדש.
Android 11 Virtual A/B
Android 11 של Virtual A/B כתב למחיצה דינמית באמצעות הפורמט Kernel COW. בסופו של דבר הפורמט הזה הוצא משימוש כי פורמט ה-COW של הליבה לא תומך בדחיסה.
Android 12 Virtual A/B
ב-Android 12 יש תמיכה בדחיסה בפורמט COW ספציפי ל-Android. בגרסה הזו של Virtual A/B נדרשה תרגום של ה-COW הספציפי ל-Android לפורמט Kernel COW. בסופו של דבר, הפורמט הזה הוחלף ב-Android 13, שבו הוסרה התלות בפורמט COW של הליבה וגם ב-dm-snapshot
.
כדי להטמיע בדיקת A/B וירטואלית או להשתמש ביכולות של קובצי snapshot דחוסים, קראו את המאמר הטמעת בדיקת A/B וירטואלית