סקירה כללית על בדיקת A/B וירטואלית

בדיקת A/B וירטואלית היא מנגנון העדכון העיקרי של Android. התכונה 'Virtual A/B' מתבססת על עדכוני A/B מדור קודם (למידע נוסף על עדכוני מערכת A/B) ועל נתונים אחרים של A/B, שהוצאו משימוש ב-15, כדי לצמצם את תקורת המרחב של העדכונים.

למעשה, לאינטראקציה וירטואלית מסוג A/B אין מקום נוסף למחיצות דינמיות. אפשר לקרוא מידע נוסף במאמר מחיצות דינמיות. במקום זאת, השינוי נכתב בתמונה מיידית (snapshot) ולאחר מכן מתמזג עם המחיצה הבסיסית אחרי שמוודאים שההפעלה בוצעה בהצלחה. A/B וירטואלי משתמש בפורמט תמונת מצב ספציפי ל-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 יכולות להיות גם פעולות העתקה.

פריסת תמונת המצב המלאה בדיסק נראית כך:

פורמט פרה

איור 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. כל קלט/פלט (I/O) בתמונת המצב חייב לעבור דרך השירות הזה. במהלך התקנה OTA, נתוני מערכת הפעלה חדשים נכתבים ב-snapshot על ידי snapuserd (עם דחיסה). כאן מתבצע גם הניתוח של המטא-נתונים והפריסה של נתוני הבלוק החדשים.

דחיסת XOR

במכשירים שמושקים עם Android 13 ואילך, תכונת הדחיסה XOR, שמופעלת כברירת מחדל, מאפשרת לצילום תמונות ממרחב המשתמשים לאחסן בייטים דחוסים מסוג XOR בין בלוקים ישנים לבלוקים חדשים. כשרק כמה בייטים בבלוק משתנים בעדכון A/B וירטואלי, סכימה של אחסון דחוס XOR משתמשת בפחות מקום מאשר סכימה ברירת המחדל של אחסון, כי בתמונות המצב לא מאוחסנים 4,000 בייטים מלאים. ההקטנה הזו של קובץ ה-snapshot אפשרית כי נתוני XOR מכילים הרבה אפסים וקל יותר לדחוס אותם מאשר נתוני בלוקים גולמיים. במכשירי Pixel, דחיסת XOR מפחיתה את גודל קובץ ה-snapshot ב-25% עד 40%.

במכשירים שמשודרגים ל-Android מגרסה 13 ואילך, צריך להפעיל את דחיסת XOR. פרטים נוספים זמינים במאמר דחיסת XOR.

מיזוג קובצי snapshot

במכשירים שמושקים עם Android 13 ואילך, תהליכי המיזוג של קובצי snapshot ו-snapshot בדחיסת A/B וירטואלית מבוצעים על ידי רכיב מרחב המשתמשים snapuserd. במכשירים שמשודרגים ל-Android מגרסה 13 ואילך, צריך להפעיל את התכונה הזו. לפרטים נוספים, ראו מיזוג מרחב משתמשים.

בהמשך מתואר תהליך הדחיסה של Virtual A/B:

  1. המסגרת מחברת את המחיצה /system למכשיר dm-verity, שממוקם מעל מכשיר dm-user. המשמעות היא שכל פעולת קלט/פלט ממערכת הקבצים ברמה הבסיסית (root) מנותבת אל dm-user.
  2. dm-user מנתב את הקלט/פלט אל מרחב המשתמשים snapuserd daemon, שמטפל בבקשת הקלט/פלט.
  3. בסיום פעולת המיזוג, המסגרת מרכזת את dm-verity מעל dm-linear (system_base) ומסירה את dm-user.

תהליך דחיסת נתונים וירטואלי A/B

איור 3. תהליך דחיסת נתונים וירטואלי A/B

אפשר להפסיק את תהליך המיזוג של תמונות המצב. אם המכשיר יופעל מחדש במהלך תהליך המיזוג, תהליך המיזוג ימשיך אחרי ההפעלה מחדש.

מעברים של Init

כשמפעילים את המכונה באמצעות קובצי snapshot דחוסים, השלב הראשון של init צריך להפעיל את snapuserd כדי לטעון את המחיצות. זה יוצר בעיה: כש-sepolicy נטען ונאכף, snapuserd מועבר להקשר הלא נכון ובקשות הקריאה שלו נכשלות, בגלל דחיות של תוכן דיגיטלי.

כדי לטפל בבעיה הזו, snapuserd עובר מעבר במקביל ל-init, באופן הבא:

  1. בשלב הראשון, init מפעיל את snapuserd מה-ramdisk ושומר בו מתאר קובץ פתוח במשתנה סביבה.
  2. בשלב הראשון, init מעביר את מערכת הקבצים ברמה הבסיסית למחיצה של המערכת, ואז מפעיל את העותק של init במערכת.
  3. עותק המערכת של init קורא את מדיניות האבטחה המשולבת למחרוזת.
  4. Init מפעיל את mlock() בכל הדפים שמגובים ב-ext4. לאחר מכן היא משביתה את כל הטבלאות של מיפוי המכשירים לקובצי snapshot, ומפסיקים את snapuserd. לאחר מכן אסור לקרוא מהמחיצות, כי הפעולה הזו גורמת לנעילה מרומזת.
  5. באמצעות מתאר הפתוח של העותק של snapuserd ב-ramdisk, init מפעיל מחדש את הדימון עם ההקשר הנכון של selinux. טבלאות מיפוי המכשירים מופעלות מחדש במכשירי snapshot.
  6. 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.1GB‏2 ב-/data
נפח האחסון הכולל הנדרש כדי להחיל עדכון OTA 5.9GB‏3 (נתונים ו-Super) 9GB (סופר) 8.3GB‏3 (סופר ונתונים) 6.6GB 3 (סופר-נתונים ונתונים)

1מצביע על פריסה משוערת על סמך מיפוי Pixel.

2בהנחה שתמונת המערכת החדשה זהה לגודל המקורי.

3דרישת האחסון היא זמנית עד להפעלה מחדש.

Android 11 Virtual A/B

גרסת Android 11 של Virtual A/B כתבה למחיצה דינמית באמצעות הפורמט Kernel COW. בסופו של דבר הפורמט הזה הוצא משימוש כי פורמט ה-COW של הליבה לא תומך בדחיסה.

בדיקת A/B וירטואלית ב-Android 12

ב-Android 12, הדחיסה נתמכת בפורמט COW ספציפי ל-Android. בגרסה הזו של Virtual A/B נדרשה תרגום של ה-COW הספציפי ל-Android לפורמט Kernel COW. בסופו של דבר, הפורמט הזה הוחלף ב-Android 13, שבו הוסרה התלות בפורמט COW של הליבה וגם ב-dm-snapshot.

אם אתם רוצים להטמיע A/B וירטואלי או להשתמש ביכולות של קובץ snapshot דחוס, ראו הטמעת A/B וירטואלי