לדלג לתוכן

מערכת הקבצים הוירטואלית מוסברת - איך לינוקס מתייחס לכל דבר כאל קובץ

בלינוקס אפשר להריץ open, read ו-close על קובץ שיושב על דיסק מקומי מסוג ext4, על כונן רשת מרוחק, ואפילו על קובץ וירטואלי בתוך /proc שלא קיים על שום דיסק בכלל - וזה עובד באותה צורה מדויקת בכל המקרים. השכבה שמאפשרת את האחידות המרשימה הזאת נקראת VFS, מערכת הקבצים הוירטואלית, ואפשר לחשוב עליה כעל המתרגם הראשי בין הקוד שלכם לבין עשרות מערכות קבצים שונות שרצות מתחתיו.

הבעיה ש-VFS פותרת

בלינוקס יש עשרות סוגי מערכות קבצים שונות - ext4, XFS, Btrfs, מערכות רשת כמו NFS, ואפילו מערכות וירטואליות כמו /proc שדיברנו עליה. לכל אחת מהן יש מבנה נתונים פנימי שונה לגמרי, שיטת אחסון שונה, ולפעמים אפילו ביצועים שונים באופן מהותי. אם כל תוכנית הייתה צריכה לדעת בדיוק איזו מערכת קבצים היא מדברת איתה, כל קוד שקורא ל-open היה צריך להתחשב בעשרות מקרים שונים.

VFS פותר את זה על ידי הגדרת ממשק אחיד - קבוצת פעולות סטנדרטית כמו open, read, write, close, שכל מערכת קבצים חייבת לממש. תוכנית שקוראת ל-open תמיד מדברת עם VFS, ו-VFS הוא זה שמפנה את הבקשה למימוש הספציפי של מערכת הקבצים שבה הקובץ באמת נמצא. התוכנית עצמה לא צריכה לדעת, ולרוב גם לא יודעת, על איזו מערכת קבצים היא בכלל עובדת.

שלושת השחקנים המרכזיים - inode, dentry ו-superblock

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

  • Superblock. מייצג מערכת קבצים שלמה שמורכבת - כמו כונן שחובר, ומכיל מידע כללי כמו גודלה וסוגה.
  • Inode. מייצג קובץ בודד ברמת המטא-דאטה שלו - גודל, הרשאות, זמני שינוי, ורשימת בלוקים פיזיים שמכילים את התוכן שלו. חשוב להבין - inode לא מכיל את שם הקובץ בכלל, רק את המידע עליו.
  • Dentry. מייצג ערך בתוך תיקייה - חיבור בין שם קובץ לבין ה-inode שהוא מצביע עליו. אותו inode יכול להיות מקושר מכמה dentries שונים, וזו בדיוק הסיבה שקישור קשיח - hard link, אפשרי בכלל - שני שמות שונים יכולים להצביע על אותו inode ואותו תוכן בדיוק.

איך זה נראה בפועל כשקוראים לopen

כשתוכנית קוראת ל-open, הקרנל עובר דרך VFS כדי לתרגם את הנתיב, למשל /home/user/file.txt, לרצף של dentries שמובילות ל-inode הסופי. ברגע שה-inode נמצא, VFS מפעיל את פעולת ה-open הספציפית של מערכת הקבצים שבה הקובץ יושב - לגרסה של ext4 יש מימוש אחד, לגרסה של NFS יש מימוש אחר לגמרי, שכולל תקשורת רשת מלאה. מבחינת התוכנית שקראה ל-open, שום דבר מזה לא נראה - היא פשוט מקבלת מתאר קובץ, ומשם ואילך היא עובדת בדיוק כפי שדיברנו עליו כשהסברנו על מתארי קבצים.

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

בזכות ה-VFS, מפתחים שרוצים להוסיף תמיכה במערכת קבצים חדשה לגמרי, לא צריכים לגעת בקוד הקרנל שמטפל ב-open או ב-read. הם רק צריכים לממש קבוצת פעולות בתוך מבנה שנקרא file_operations, שמתאר איך לבצע כל פעולה בסיסית עבור מערכת הקבצים הספציפית שלהם. הקרנל קורא לפעולות האלה דרך מנגנון שדומה מאוד לפולימורפיזם בתכנות מונחה עצמים - ממשק אחיד, מימושים שונים מתחתיו.

למה זה שווה הבנה

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

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

ואם המבנה של inode ו-dentry עדיין קצת מעורפל, יש קהילה שתשמח להבהיר.

הצטרפו לקהילה בדיסקורד

לסיכום

VFS היא שכבת ההפשטה שמאפשרת לאותו קוד, ולאותן קריאות מערכת, לעבוד על כל סוגי מערכות הקבצים בלינוקס בלי הבדל. superblock מייצג את מערכת הקבצים כולה, inode מייצג קובץ ברמת המטא-דאטה, ו-dentry מחבר בין שם לinode. ברגע שמבינים את השלושה האלה, גם דברים כמו קישורים קשיחים או תמיכה במערכת קבצים חדשה מפסיקים להיות מסתוריים.