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

ב-Android יש שני מנגנוני עדכון: עדכוני A/B (חלקיים) ועדכונים שאינם מסוג A/B. כדי להפחית את מורכבות הקוד ולשפר את תהליך העדכון, ב-Android 11 אוחדו שני מנגנונים באמצעות A/B וירטואלי כדי לספק עדכונים חלקים מכשירים עם עלות אחסון נמוכה יותר. 12 Android אפשר להשתמש בדחיסת A/B וירטואלית כדי לדחוס מחיצות בקובצי snapshot. ב-Android 11 וב-Android 12: חלות:

  • עדכוני A/B וירטואליים הם חלקיים כמו עדכוני A/B. עדכונים לגבי Virtual A/B לצמצם את משך הזמן שבו מכשיר במצב אופליין ולא ניתן לשימוש.
  • אפשר להחזיר עדכוני A/B וירטואליים. אם לא ניתן להפעיל את מערכת ההפעלה החדשה, מכשירים חוזרים באופן אוטומטי לגרסה הקודמת.
  • עדכוני A/B וירטואליים משתמשים בשטח נוסף מינימלי על-ידי שכפול של רק המחיצות שמשמשות את תוכנת האתחול. מחיצות אחרות הניתנות לעדכון הן snapshotted.

רקע ומינוח

הקטע הזה מגדיר את המונחים ומתאר את הטכנולוגיה שתומכת בדיקת A/B וירטואלית.

מיפוי מכשירים

מיפוי המכשירים הוא שכבת בלוק וירטואלית של 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.

איור 1 מראה איך נראית המקבץ מתחת לנקודת הטעינה של /system.

ערימת מחיצות מתחת
מערכת

איור 1. ערימה מתחת לנקודת הטעינה של /system

תמונת מצב

A/B וירטואלי מסתמך על dm-snapshot, מודול מיפוי מכשירים ליצירת תמונות של במצב של התקן האחסון. כשמשתמשים ב-dm-snapshot, יש 4 מכשירים הפעלה:

  • מכשיר הבסיס הוא המכשיר שנשמר בתמונת מצב. בדף הזה הבסיס המכשיר הוא תמיד מחיצה דינמית, כמו מערכת או ספק.
  • מכשיר העתקה על כתיבה (COW) לרישום שינויים במכשיר הבסיסי. הוא הוא יכול להיות כל גודל, אבל הוא חייב להיות גדול מספיק כדי להתאים את כל השינויים של המכשיר הבסיסי.
  • מכשיר ה-snapshot נוצר באמצעות היעד snapshot. כותבת התקן תמונת המצב נכתבים במכשיר COW. קריאות מתמונת המצב קריאה מהמכשיר הבסיסי או ממכשיר COW, בהתאם האם הנתונים שאליהם מתבצעת גישה השתנו על ידי תמונת המצב.
  • מכשיר המקור נוצר באמצעות היעד snapshot-origin. מקרא ל: מכשיר המקור שנקרא ישירות מהמכשיר הבסיסי. כתיבה למקור כתובים ישירות למכשיר הבסיסי, אבל הנתונים המקוריים מגובים באמצעות כתיבה למכשיר COW.

מיפוי מכשירים עבור
תמונת מצב

איור 2. מיפוי מכשירים ל-dm-snapshot

תמונות מצב דחוסות

ב-Android מגרסה 12 ואילך, כי הדרישות של נפח האחסון מופעלות המחיצה /data יכולה להיות גבוהה, ניתן להפעיל תמונות מצב דחוסות build כדי לענות על דרישות השטח הגבוהות יותר של המחיצה /data.

תמונות מצב וירטואליות A/B דחוסות מבוססות על הרכיבים הבאים שזמינים ב-Android מגרסה 12 ואילך:

  • dm-user, מודול ליבה (kernel) שדומה ל-FUSE שמאפשר מרחב משתמשים כדי להטמיע התקני בלוקים.
  • snapuserd, דימון (daemon) במרחב המשתמשים כדי להטמיע תמונת מצב חדשה הפורמט.

הרכיבים האלה מפעילים את הדחיסה. השינויים הנדרשים האחרים בוצעו ב להטמיע את היכולות של תמונות המצב הדחוסות שמפורטות בקטעים הבאים: פורמט COW לקובצי מצב דחוסים, dm-user ו-Snapuserd.

פורמט COW לקובצי מצב דחוסים

ב-Android מגרסה 12 ואילך, תמונות מצב דחוסות משתמשות ב פורמט COW. דומה לפורמט המובנה של הליבה, שמשמש לפעולות לא דחוסות תמונות מצב, לפורמט COW עבור תמונות המצב הדחוסות יש קטעים מתחלפים מטא-נתונים ונתונים. מטא-נתונים של הפורמט המקורי מותרים רק לצורך החלפה פעולות: החלפת בלוק X בתמונת הבסיס בתוכן של בלוק Y בתמונת המצב. הפורמט COW של תמונות המצב הדחוסים יותר אקספרסיבי תומכת בפעולות הבאות:

  • העתקה: צריך להחליף את הבלוק X במכשיר הבסיסי בבלוק Y, במכשיר הבסיסי.
  • החלפה: צריך להחליף את הבלוק X במכשיר הבסיסי בתוכן. של בלוק Y בתמונת המצב. כל אחד מהבלוקים האלה דחוס ב-gz.
  • אפס: צריך להחליף את הבלוק X במכשיר הבסיסי בכל האפסים.
  • XOR: מכשיר COW מאחסן בייטים דחוסים של XOR בין הבלוק X בלוק Y. (התכונה זמינה ב-Android מגרסה 13 ואילך).

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

dm-user ב-Android 12

מודול הליבה של ה-Dm-user מאפשר ל-userspace להטמיע בלוק של מיפוי מכשירים מכשירים. רשומה בטבלה של משתמשי dm יוצרת מכשיר שונה תחת /dev/dm-user/<control-name> תהליך של userspace יכול לדגום את המכשיר לקבל בקשות קריאה וכתיבה מהליבה. לכל בקשה יש מאגר נתונים זמני למרחב המשתמשים כדי לאכלס (לקריאה) או להפיץ (לצורך כתיבה).

מודול הליבה dm-user מספק ממשק ליבה (kernel) חדש שגלוי למשתמש שלא חלק מבסיס הקוד של kernel.org ב-upstream. עד אז, Google שומרת לעצמה את הזכות לשנות את הממשק של dm-user ב-Android.

Snapuserd

רכיב מרחב המשתמשים snapuserd ב-dm-user מיישם A/B וירטואלי דחיסה.

בגרסה הלא דחוסה של Virtual A/B (ב-Android 11 ומטה, או ב-Android 12 בלי האפשרות 'תמונת מצב דחוסה') המכשיר COW הוא קובץ גולמי. כאשר דחיסה מופעלת, פונקציות COW במקום זאת כמכשיר dm-user, שמחובר למופע של דימון (snapuserd).

הליבה לא משתמשת בפורמט החדש COW. אז הרכיב snapuserd מתרגמת בקשות בין פורמט Android COW לבין פורמט הליבה פורמט:

בקשות תרגום של רכיבי Snapuserd בין פורמט COW של Android ליבה
מובנה
פורמט

איור 3. תרשים זרימה של Snapuserd כמתרגם בין Android ל-Kernel פורמטים של COW

התרגום והביטול דחיסה האלה אף פעם לא מתרחשים בדיסק. snapuserd שמיירט את פעולות הקריאה והכתיבה של COW שמתרחשות בליבה (kernel), שמטמיעה אותן בפורמט Android COW.

דחיסת XOR

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

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

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

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

קריאת מטא-נתונים (Android 12)

מטא-נתונים בנויים באמצעות דימון (daemon) מסוג snapuserd. המטא-נתונים הם בעיקר מיפוי של שני מזהים, כש-8 בייט כל אחד מהם, שמייצגים את המגזרים שרוצים למזג. בdm-snapshot השם נקרא disk_exception.

struct disk_exception {
    uint64_t old_chunk;
    uint64_t new_chunk;
};

כאשר מקטע נתונים ישן מוחלף במקטע נתונים חדש, המערכת משתמשת בחריגת דיסק.

דימון (daemon) של snapuserd קורא את קובץ ה-COW הפנימי דרך הספרייה של COW, בונה את המטא-נתונים לכל אחת מפעולות COW שקיימות בקובץ COW.

קריאות מטא-נתונים מבוצעות מ-dm-snapshot בליבה (kernel) בזמן יצירת המכשיר dm- snapshot.

האיור הבא מספק דיאגרמת רצף לנתיב ה-IO של המטא-נתונים עבודות בדרך.

דיאגרמת רצף, נתיב IO למטא-נתונים
עבודות בדרך

איור 4. תהליך רצף לנתיב ה-IO בבניית מטא-נתונים

מיזוג (Android 12)

לאחר שתהליך האתחול הושלם, מנוע העדכון מסמן את החריץ כאתחול מצליח ויוזם את המיזוג על ידי שינוי היעד dm-snapshot יעד dm-snapshot-merge.

dm-snapshot עובר דרך המטא-נתונים ומבצע IO של מיזוג לכל דיסק חריג. למטה מוצגת סקירה כללית ברמה גבוהה של נתיב ה-IO של המיזוג.

מיזוג נתיב IO

איור 5. סקירה כללית של נתיב ה-IO של המיזוג

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

יצירת שכבות של מיפוי מכשירים

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

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

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

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

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

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

התחלת מעברים

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

כדי לפתור את הבעיה, snapuserd מועבר בשלב הנעילה עם init, באופן הבא:

  1. שלב ראשון init מפעיל את snapuserd מ-Rramdisk ושומר חלון פתוח ומתאר קובץ במשתנה סביבה.
  2. בשלב הראשון init מעביר את מערכת הקבצים הבסיסית למחיצת המערכת, ואז מפעיל את עותק המערכת של init.
  3. עותק המערכת של init קורא את המדיניות המשולבת במחרוזת.
  4. Init מפעיל את mlock() בכל הדפים שמגובים ב-ext4. לאחר מכן המערכת משביתה את כל הפריטים טבלאות מיפוי מכשירים לקובצי snapshot, ועוצר snapuserd. לאחר מכן אסור לקרוא ממחיצות, מפני שפעולה זו גורמת לקיבוע.
  5. שימוש במתאר פתוח בעותק של Radisk של snapuserd, init הפעלה מחדש של הדימון עם ההקשר הנכון של סלינוקס. טבלאות של מיפוי מכשירים עבור מכשירים בתמונת מצב יופעלו מחדש.
  6. הפעלה מפעילה munlockall() – אפשר לבצע שוב הזמנת נתונים (IO)

שימוש במרחב המשותף

בטבלה הבאה מוצגת השוואה של נפח האחסון בין יעדי OTA שונים מנגנונים על סמך גדלי מערכת ההפעלה ו-OTA של Pixel.

השפעת הגודל שאינו A/B בדיקת A/B בדיקת A/B וירטואלית בדיקת A/B וירטואלית (דחוסה)
תמונת היצרן המקורית 4.5GB Super (תמונת 3.8G + 700 מיליון שמורים)1 9GB Super (3.8G + 700M שמורים, לשני משבצות) 4.5GB Super (תמונת 3.8G + 700 מיליון מקומות שמורים) 4.5GB Super (תמונת 3.8G + 700 מיליון מקומות שמורים)
מחיצות סטטיות אחרות /cache ללא ללא ללא
נפח אחסון נוסף במהלך OTA (שטח שהוחזר לאחר החלת OTA) 1.4GB ב-/data 0 3.8GB 2 ב-/data 2.1GB 2 ב-/data
נפח האחסון הכולל שנדרש כדי להחיל OTA 5.9GB 3 (סופר-נתונים ונתונים) 9GB (מעולה) 8.3GB 3 (סופר-נתונים ונתונים) 6.6GB 3 (סופר-נתונים ונתונים)

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

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

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

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