לדלג לתוכן

מימוש מערכת קבצים ומסלול הבקשה להתקן

נניח שקריאת write הסתיימה מהר, אבל fsync מאוחר יותר נמשכה זמן רב. כדי להסביר את הפער נעקוב אחרי הפעולה מעבר ל־VFS: הקצאת extents, עדכון metadata, פרוטוקול journal או copy-on-write, בניית בקשות והמתנה להתקן. כל שכבה יכולה לדחות עבודה, לאחד אותה או להוסיף סדר שנדרש לנכונות.

במערכת עם journal, עדכונים מסוימים נרשמים תחילה במבנה שמאפשר לזהות עסקה שהושלמה ולהפעיל התאוששות. אין להסיק מכך שכל תוכן של קובץ משתמש נכתב תמיד פעמיים ליומן. למשל, מצבי העבודה של ext4 שונים בשאלה אילו נתונים נכנסים ליומן ואיך מסודרת כתיבת התוכן ביחס למטא־נתונים.

במערכת שמבוססת על copy-on-write, שינוי יכול ליצור בלוקים חדשים ולעדכן מצביעים במקום לדרוס מיד את המבנה הישן. הדבר מאפשר תכונות כמו snapshots ושיתוף extents, אבל דורש ניהול מרחב, הפניות ועקביות של עצים. גם שם write רגילה אינה מחליפה את חוזה האישור העמיד של האפליקציה.

נמדוד תחילה את הפעילות שהתקן מדווח. התוכנית הבאה קוראת את מוני הבלוק עבור התקן שנבחר בשורת הפקודה, למשל התקן וירטואלי של VM. היא קוראת metadata בלבד ואינה פותחת את ההתקן לכתיבה.

use std::{env, fs, io};
fn counters(device: &str) -> io::Result<Vec<u64>> {
    if device.is_empty() || !device.bytes().all(|b| b.is_ascii_alphanumeric() || b"_-".contains(&b)) {
        return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid device name"));
    }
    let text = fs::read_to_string(format!("/sys/class/block/{device}/stat"))?;
    let values: Vec<u64> = text.split_whitespace().map(str::parse)
        .collect::<Result<_, _>>().map_err(|_| io::Error::other("invalid counters"))?;
    if values.len() < 11 { return Err(io::Error::other("short counter record")); }
    Ok(values)
}
fn main() -> io::Result<()> {
    let device = env::args().nth(1).ok_or_else(|| io::Error::other("device required"))?;
    let before = counters(&device)?;
    println!("run the workload, then press Enter");
    let mut line = String::new();
    io::stdin().read_line(&mut line)?;
    let after = counters(&device)?;
    println!("reads={} writes={} read_sectors={} written_sectors={}",
        after[0].saturating_sub(before[0]), after[4].saturating_sub(before[4]),
        after[2].saturating_sub(before[2]), after[6].saturating_sub(before[6]));
    Ok(())
}

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

שכבת הבלוק מקבלת עבודה דרך מבנים שמייצגים טווחי נתונים ובקשות, ומנהלת תורים אל התקנים. במערכת עם blk-mq יש התאמה בין תורי תוכנה, תורי חומרה וטופולוגיית מעבדים לפי המימוש וההתקן. ערך queue depth גבוה יכול לשפר ניצול התקן, אבל גם להגדיל זמן המתנה של בקשה בודדת.

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

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

לצורך השוואת ext4 ו־Btrfs נשתמש בדיסקים וירטואליים ייעודיים במעבדה, עם אותו גודל ופרופיל עומס. לא נפרמט מחיצה של מחשב העבודה. נשמור קונפיגורציה, אפשרויות mount, מצב snapshots, כמות מקום פנוי וגרסת קרנל, משום שכולם יכולים להשפיע על העלות.

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

בקריאת מקור נבחר פעולה אחת, למשל סנכרון קובץ קטן, ונעקוב מהכניסה הציבורית אל מימוש filesystem ואל השלמת בקשות. נרשום את ה־commit ואת הנקודות שבהן נדרש סדר. המטרה היא להבין מי מעכב את אישור הפעולה, ולא לאסוף רשימה ארוכה של שמות פונקציות ללא קשר סיבתי.

פרוטוקול ext4 מתואר בתיעוד journal של ext4, ושכבת התורים בתיעוד blk-mq. המונים מוסברים בתיעוד סטטיסטיקות I/O. נחבר את המסמכים לראיות מהעומס שבחרנו ולא נציג תוצאה אחת כדירוג קבוע של מערכות קבצים.