TShopping

 找回密碼
 註冊
搜索
查看: 965|回復: 3

[分享] Linux 當中的 VFS 虛擬檔案系統

[複製鏈接]
發表於 2013-10-24 12:59:57 | 顯示全部樓層 |閱讀模式
 
Push to Facebook Push to Plurk Push to Twitter 
在傳統的UNIX系統中,檔案系統是固定的實體檔案系統。但在Linux當中,為了統合各個檔案系統,因而加上了一層虛擬檔案系統 (Virtual File System: VFS) ,VFS 是一組檔案操作的抽象介面,我們可以將任何的真實檔案系統,透過 VFS 掛載到 Linux 中。
由於 VFS只是個虛擬的檔案系統,並不負責組織磁碟結構,因此,所有的組織動作都交由真實的檔案系統處理。VFS 所負責的操作都是在記憶體當中的部分,包含目錄結構的快取等功能,這樣就能增快存取的速度。
VFS 是一個軟體層的介面,負責處理檔案的處理請求,像是讀取、寫入、複製、刪除等。由於各種檔案系統的格式並不相同,所以 VFS 並不直接處理檔案格式,而是規定這些處理請求的介面及操作語意,然後交由真實的檔案系統 (像是 EXT2) 去處理。圖 5 顯示了 Linux 核心、VFS 與真實檔案系統之間的架構關係。

圖 5 Linux 的檔案系統結構
真實檔案系統是在檔案結構的組織下,利用區塊裝置驅動模組所建構出來的。在Linux 作業系統中,允許安裝各式各樣的檔案系統,像是 BSD、FAT32、NTFS、EXT2、EXT3、JFS、JFS2、ReiserFS 等,這些檔案系統透過統一的虛擬檔案系統介面 (Virtual File System : VFS),就能被掛載到 Linux 作業系統中。
甚至,有些非實體檔案系統,也可以透過 VFS 掛載進來,像是根檔案系統 (rootfs), 記憶體對應檔案系統 (proc), 網路檔案系統 (sockfs) 等,都可以透過 VFS掛載進來,由 VFS 進行管理與操控。
為了要將檔案系統掛載到 Linux 當中,Linux 仍然利用『註冊與反向呼叫機制』 (Register and Callback) 作為 VFS 的主要設計方式。實體檔案系統 (像是Ext2) 會向 VFS 進行註冊,以便將反向呼叫用的函數指標傳遞給 Linux 系統,接著 Linux 系統就可以在是當的時機,透過這些函數指標呼叫實體檔案系統的函數。
為了組織 VFS 的這些註冊函數,Linux 採用了一個類似物件導向的方式,將函數以物件的方式組織起來。由於VFS 的設計深受UNIX與 Ext2 的影響,因此在使用的術語及結構安排上都使用由UNIX/Ext2 所遺留下來的檔案系統之術語。
受到 UNIX/Ext2 的影’響,VFS系統也是由超級區塊與 inode 物件所構成的,另外還有目錄項 (dentry) 與檔案 (file) 等物件,分別對應到目錄中的子項與檔案等物件 。
超級區塊 (Superblock) 在邏輯上對應到一個檔案系統分割區 (Partition),而索引節點 (inode) 則對應到目錄結構 (directory structure),目錄中包含很多子項目,可能是檔案或資料夾,這些子項目稱為目錄項 (dentry),這些項目其中有些代表檔案 (file),透過 inode 與 dentry 就可以取得磁碟中的資料區塊 (data block),這些區塊就是檔案的內容。
於是,VFS 可以再 inode 中新增、修改、刪除、查詢 dentry (mkdir, rmdir, …),然後取得或設定 dentry 的屬性 (chmod, chowner, …),或者利用inode找到檔案 (open),然後再對檔案進行操作 (read, write, …)。表格 5 顯示了 VFS 系統中的重要函數原型,讀者應可看出這些物件間的關係。
表格 5. VFS 中的代表性函數原型
操作
函數原型
開檔
int (*open) (struct inode *, struct file *);
建立資料夾
int (*mkdir) (struct inode *,struct dentry *,int);
設定屬性
int (*setattr) (struct dentry *, struct iattr *);
追蹤連結
void * (*follow_link) (struct dentry *, struct nameidata *);
所有掛載後的分割都會被放在 vfsmntlist 這個 Linux Kernel 的串列變數中,串列中的每一個元素為 vfsmount結構,該結構代表一個分割,其中的 mnt_sb 欄即是該分割的超級區塊 (super_block),超級區塊中有個 s_inodes 欄位指向 inode 節點串列,inode 也透過 i_sb 欄位指回超級區塊。
當檔案系統想要掛載到 Linux 當中時,會將一個稱為 read_super 的函數,傳遞給 VFS,於是 VFS 就可以透過下列的結構,將檔案系統串接起來,形成檔案系統串列。在需要使用該檔案系統時,再透過read_super 將 super_block載入記憶體中 ,
super_block, inode, file, dentry 結構中都有個 op 欄位,該欄位儲存了一堆反向呼叫函數,可以用來呼叫真實檔案系統 (像是 Ext2) 中的操作函數,以便取得硬碟中的資料,或者將資料寫回硬碟當中。範例 1 顯示了 Linux 原始碼當中這些物件的結構與操作函數,詳細閱讀有助於進一步理解 VFS 檔案系統的設計方式。
範例 1. 虛擬檔案系統VFS的Linux 原始碼節錄
  1. 檔案:Linux 2.6.29.4 原始檔 include/linux/fs.h 649 struct inode { 索引節點inode結構 650 struct hlist_node i_hash; 雜湊表格 651 struct list_head i_list; inode 串列 652 struct list_head i_sb_list; 超級區塊串列 653 struct list_head i_dentry; 目錄項串列 … … 675 const struct inode_operations *i_op; inode 的操作函數 676 const struct file_operations *i_fop; 檔案的操作函數 677 struct super_block *i_sb; 指向超級區塊 … … 714 }; … … 839 struct file { 檔案物件的結構 … … 847 struct path f_path; 路徑 848 #define f_dentry f_path.dentry 849 #define f_vfsmnt f_path.mnt 850 const struct file_operations *f_op; 檔案的操作函數 … … 875 }; … … 1132 struct super_block { 超級區塊的結構 1133 struct list_head s_list; 區塊串列 1134 dev_t s_dev; 設備代號 1135 unsigned long s_blocksize; 區塊大小 … … … 1139 struct file_system_type *s_type; 真實檔案系統 1140 const struct super_operations *s_op; 超級區塊操作函數 … … … 1157 struct list_head s_inodes; 整個inode串列 1158 struct list_head s_dirty; 修改過的inode串列 … … 1206 }; … … 1310 struct file_operations { 檔案的操作函數 1311 struct module *owner; 檔案擁有者 … …包含一群檔案操作的函數指標,列舉如下… … llseek(), read(), write(), aio_read(), aio_write(), … readdir(), poll(), ioctl(), unlocked_ioctl(), … compat_ioctl(), mmap(), open(), flush(), release(), … fsync(), aio_fsync(), lock(), sendpage(), … get_unmapped_area(), check_flags(), flock(), … splice_write(), splice_read(), setlease() 1337 }; 1338 1339 struct inode_operations { inode 的操作函數 … …包含一群inode操作的函數指標,列舉如下… … … create(), lookup(), link(), unlink(), symlink(), mkdir(), … rmdir(), mknod(), rename(), readlink(), follow_link(), … put_link(), truncate(), permission(), setattr(), … setxattr(), getxattr(), listxattr(), removexattr(), … trunctate_range(), fallocate(), filemap() 1366 }; 1382 struct super_operations { 超級區塊操作函數 … …包含一群超級區塊操作的函數指標,列舉如下… … alloc_inode(), destroy_inode(), dirty_inode(), … write_inode(), drop_inode(), delete_inode(), … put_super(), write_super(), sunc_fs(), freeze_fs(), … unfreeze_fs(), statfs(), remount_fs(), clear_inode(), … unmount_begin(), show_options(), show_stats(), … quota_read(),quota_write(),bdev_try_to_free_page() 1407 }; … …. 1565 struct file_system_type { 檔案系統物件 1566 const char *name; 檔案系統名稱 1567 int fs_flags; 旗標 1568 int (*get_sb) (struct file_system_type *, int, 超級區塊取得函數 1569 const char *, void *, struct vfsmount *); 1570 void (*kill_sb) (struct super_block *); 超級區塊刪除函數 1571 struct module *owner; 1572 struct file_system_type * next; 下一個檔案系統 1573 struct list_head fs_supers; 超級區塊串列 … … 1582 };
複製代碼

以下顯示了 dentry 的操作函數的相關檔案結構
  1. … 檔案:Linux 2.6.29.4 原始檔 include/linux/fs.h … 89 struct dentry { 目錄項dentry物件 … … 94 struct inode *d_inode; 指向 inode … … 100 struct hlist_node d_hash; 雜湊表 101 struct dentry *d_parent; 父目錄 102 struct qstr d_name; 目錄項名稱 103 104 struct list_head d_lru; /* LRU list */ 最近最少使用串列 … … (快取的取代策略) 115 struct dentry_operations *d_op; dentry 的操作函數 116 struct super_block *d_sb; 指向超級區塊 … … 120 }; … … 134 struct dentry_operations { dentry的操作函數 … …包含一群 dentry 操作的函數指標,列舉如下… … d_revalidate(), d_hash(), d_compare(), d_delete(), … d_release(), d_iput(), ddname() 142 };
複製代碼

VFS 如何接上真實檔案系統
由於 VFS 的設計與 Ext2 相當類似,因此在實作對應上相當容易,像是在 ext2 檔案系統中,就直接將超級區塊相關的所有函數指標,全數塞入到 ext2_sops 這個型態為 super_operations 的變數中。然後在ext2_fill_super 這個函數中,將些函數塞入到 Linux VFS 超級區塊的 s_op 欄位中,就完成了連接的動作,於是 Linux 核心就可以透過超級區塊中的這些函數指標,操作 Ext2 檔案系統的超級區塊了。
範例 2. 將 Ext2 連接到 VFS 上的程式原始碼片段
  1.     檔案:Linux 2.6.29.4 原始檔 fs/ext2/super.c
  2. …    …
  3. 300    static const struct super_operations ext2_sops = {
  4. 301     .alloc_inode = ext2_alloc_inode,
  5. 302     .destroy_inode = ext2_destroy_inode,
  6. 303     .write_inode = ext2_write_inode,
  7. 304     .delete_inode = ext2_delete_inode,
  8. 305     .put_super = ext2_put_super,
  9. 306     .write_super = ext2_write_super,
  10. 307     .statfs  = ext2_statfs,
  11. 308     .remount_fs = ext2_remount,
  12. 309     .clear_inode = ext2_clear_inode,
  13. 310     .show_options = ext2_show_options,
  14. 311    #ifdef CONFIG_QUOTA
  15. 312     .quota_read = ext2_quota_read,
  16. 313     .quota_write = ext2_quota_write,
  17. 314    #endif
  18. 315    };
  19. …    …
  20. 739    static int ext2_fill_super(struct super_block *sb, void *data, int silent)
  21. …    …
  22. …     sb->s_op = &ext2_sops;
  23. 1050
複製代碼

於是,Linux 就可以利用 VFS 中的物件結構,對任何的檔案系統 (像是 Ext2, NTFS等) 進行操作。必須注意的是,掛載到 Linux VFS 上的檔案系統未必像 Ext2 這樣與 VFS 在設計理念上完全吻合,但是只要經過適當的封裝之後,仍然可以順利的掛載上去。像是 NTFS 就使用了 B+ Tree 的結構,並沒有使用 inode 結構,但是仍然可以被掛載到 VFS 中。
透過VFS中的物件,Linux 可以呼叫真實檔案系統 (像是 Ext2) 的 VFS 介面函數,以便完成檔案操作的功能。舉例而言,Linux 中的系統呼叫 sys_stat() 函數,其實作方法如範例 3 所示,但是為了簡短起見,該函數的資料宣告部分已被去除,程式也被簡化了。
範例 3. Linux 使用 VFS 操作檔案系統的範例
  1. sys_stat(path, buf) {                              取得檔案屬性
  2.   dentry = namei(path);                            透過路徑取得目錄項
  3.   if ( dentry == NULL ) return -ENOENT;            

  4.   inode = dentry->d_inode;                         透過目錄項取得 inode
  5.   rc =inode->i_op->i_permission(inode);            執行i_permission() 操作
  6.   if  ( rc ) return -EPERM;                        取得操作權

  7.   rc = inode->i_op->i_getattr(inode, buf);         執行 i_getattr()
  8.   dput(dentry);                                    取得目錄項屬性傳回
  9.   return rc;                                       
  10. }
複製代碼


 

臉書網友討論
發表於 2013-11-9 00:59:43 | 顯示全部樓層
說的真有道理啊!

版主招募中

發表於 2013-11-9 00:59:43 | 顯示全部樓層
支持你加分  


發表於 2013-11-9 00:59:43 | 顯示全部樓層
(*^__^*) 嘻嘻……   


您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2016-12-8 08:21 , Processed in 0.070560 second(s), 23 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表