從檔案 I/O 看 Linux 的虛擬檔案系統
吳 美清 (yolanda_wumq@hotmail.com),
研究生, 北京大學 軟體與微電子學院
簡介: Linux 允許眾多不同的檔案系統共存,並支援跨檔案系統的檔案操作,這是因為有虛擬檔案系統的存在。虛擬檔案系統,即VFS(Virtual File System)是 Linux 核心中的一個軟體抽象層。它通過一些資料結構及其方法向實際的檔案系統如 ext2,vfat 提供介面機制。本文在簡要介紹 VFS 的相關資料結構後,以檔案 I/O 為切入點深入 Linux 核心原始碼,追蹤了 sys_open 和 sys_read 兩個系統調用的代碼結構,並在追蹤的過程中理清了跨檔案系統的檔案操作的基本原理和“一切皆是檔案”的口號得以實現的根本。
本文的標籤: io, vfs
標記本文!
發布日期: 2007 年 8 月 23 日
層級: 中級
訪問情況 : 8750 次瀏覽
評論: 0 (查看 | 添加評論 - 登入)
平均分 (34個評分)
為本文評分
1 引言
Linux 中允許眾多不同的檔案系統共存,如 ext2, ext3, vfat 等。通過使用同一套檔案 I/O 系統調用即可對 Linux 中的任意檔案進行操作而無需考慮其所在的具體檔案系統格式;更進一步,對檔案的操作可以跨檔案系統而執行。 1 所示,我們可以使用 cp 命令從 vfat 檔案系統格式的硬碟拷貝資料到 ext3 檔案系統格式的硬碟;而這樣的操作涉及到兩個不同的檔案系統。
圖 1. 跨檔案系統的檔案操作
“一切皆是檔案”是 Unix/Linux 的基本哲學之一。不僅普通的檔案,目錄、字元裝置、塊裝置、通訊端等在 Unix/Linux 中都是以檔案被對待;它們雖然類型不同,但是對其提供的卻是同一套操作介面。
圖 2. 一切皆是檔案
而虛擬檔案系統正是實現上述兩點 Linux 特性的關鍵所在。虛擬檔案系統(Virtual File System, 簡稱 VFS),是 Linux 核心中的一個軟體層,用於給使用者空間的程式提供檔案系統介面;同時,它也提供了核心中的一個抽象功能,允許不同的檔案系統共存。系統中所有的檔案系統不但依賴 VFS 共存,而且也依靠 VFS 協同工作。
為了能夠支援各種實際檔案系統,VFS 定義了所有檔案系統都支援的基本的、概念上的介面和資料結構;同時實際檔案系統也提供 VFS 所期望的抽象介面和資料結構,將自身的諸如檔案、目錄等概念在形式上與VFS的定義保持一致。換句話說,一個實際的檔案系統想要被 Linux 支援,就必須提供一個符合VFS標準的介面,才能與 VFS 協同工作。實際檔案系統在統一的介面和資料結構下隱藏了具體的實現細節,所以在VFS層和核心的其他部分看來,所有檔案系統都是相同的。圖3顯示了VFS在核心中與實際的檔案系統的協同關係。
圖3. VFS在核心中與其他的核心模組的協同關係
我們已經知道,正是由於在核心中引入了VFS,跨檔案系統的檔案操作才能實現,“一切皆是檔案”的口號才能承諾。而為什麼引入了VFS,就能實現這兩個特性呢?在接下來,我們將以這樣的一個思路來切入文章的正題:我們將先簡要介紹下用以描述VFS模型的一些資料結構,總結出這些資料結構相互間的關係;然後選擇兩個具有代表性的檔案I/O操作sys_open()和sys_read()來詳細說明核心是如何藉助VFS和具體的檔案系統打交道以實現跨檔案系統的檔案操作和承諾“一切皆是檔案”的口號。
回頁首
2 VFS資料結構
2.1 一些基本概念
從本質上講,檔案系統是特殊的資料階層式存放區結構,它包含檔案、目錄和相關的控制資訊。為了描述這個結構,Linux引入了一些基本概念:
檔案 一組在邏輯上具有完整意義的資訊項的系列。在Linux中,除了普通檔案,其他諸如目錄、裝置、通訊端等也以檔案被對待。總之,“一切皆檔案”。
目錄 目錄好比一個檔案夾,用來容納相關檔案。因為目錄可以包含子目錄,所以目錄是可以層層嵌套,形成檔案路徑。在Linux中,目錄也是以一種特殊檔案被對待的,所以用於檔案的操作同樣也可以用在目錄上。
目錄項 在一個檔案路徑中,路徑中的每一部分都被稱為目錄項;如路徑/home/source/helloworld.c中,目錄 /, home, source和檔案 helloworld.c都是一個目錄項。
索引節點 用於隱藏檔的中繼資料的一個資料結構。檔案的中繼資料,也就是檔案的相關資訊,和檔案本身是兩個不同的概念。它包含的是諸如檔案的大小、擁有者、建立時間、磁碟位置等和檔案相關的資訊。
超級塊 用於隱藏檔系統的控制資訊的資料結構。描述檔案系統的狀態、檔案系統類型、大小、區塊數、索引節點數等,存放於磁碟的特定扇區中。
如上的幾個概念在磁碟中的位置關係4所示。
圖4. 磁碟與檔案系統
關於檔案系統的三個易混淆的概念:
建立 以某種方式格式化磁碟的過程就是在其之上建立一個檔案系統的過程。建立文現系統時,會在磁碟的特定位置寫入關於該檔案系統的控制資訊。
註冊 向核心報到,聲明自己能被核心支援。一般在編譯核心的時侯註冊;也可以載入模組的方式手動註冊。註冊過程實際上是將表示各實際檔案系統的資料結構struct file_system_type 執行個體化。
安裝 也就是我們熟悉的mount操作,將檔案系統加入到Linux的根檔案系統的分類樹結構上;這樣檔案系統才能被訪問。
2.2 VFS資料結構
VFS依靠四個主要的資料結構和一些輔助的資料結構來描述其結構資訊,這些資料結構表現得就像是對象;每個主要對象中都包含由操作函數表構成的操作對象,這些操作對象描述了核心針對這幾個主要的對象可以進行的操作。
2.2.1 超級塊對象
儲存一個已安裝的檔案系統的控制資訊,代表一個已安裝的檔案系統;每次一個實際的檔案系統被安裝時,核心會從磁碟的特定位置讀取一些控制資訊來填充記憶體中的超級塊對象。一個安裝執行個體和一個超級塊對象一一對應。超級塊通過其結構中的一個域s_type記錄它所屬的檔案系統類型。
根據第三部分追蹤原始碼的需要,以下是對該超級塊結構的部分相關成員域的描述,(如下同):
清單1. 超級塊
struct super_block { //超級塊資料結構 struct list_head s_list; /*指向超級塊鏈表的指標*/ …… struct file_system_type *s_type; /*檔案系統類型*/ struct super_operations *s_op; /*超級塊方法*/ …… struct list_head s_instances; /*該類型檔案系統*/ ……};struct super_operations { //超級塊方法 …… //該函數在給定的超級塊下建立並初始化一個新的索引節點對象 struct inode *(*alloc_inode)(struct super_block *sb); …… //該函數從磁碟上讀取索引節點,並動態填充記憶體中對應的索引節點對象的剩餘部分 void (*read_inode) (struct inode *); ……}; |
2.2.2 索引節點對象
索引節點Object Storage Service了檔案的相關資訊,代表了存放裝置上的一個實際的物理檔案。當一個檔案首次被訪問時,核心會在記憶體中組裝相應的索引節點對象,以便向核心提供對一個檔案進行操作時所必需的全部資訊;這些資訊一部分儲存在磁碟特定位置,另外一部分是在載入時動態填充的。
清單2. 索引節點
struct inode {//索引節點結構 …… struct inode_operations *i_op; /*索引節點動作表*/ struct file_operations *i_fop; /*該索引節點對應檔案的檔案操作集*/ struct super_block *i_sb; /*相關的超級塊*/ ……};struct inode_operations { //索引節點方法 …… //該函數為dentry對象所對應的檔案建立一個新的索引節點,主要是由open()系統調用來調用 int (*create) (struct inode *,struct dentry *,int, struct nameidata *); //在特定目錄中尋找dentry對象所對應的索引節點 struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *); ……}; |
2.2.3 目錄項對象
引入目錄項的概念主要是出於方便尋找檔案的目的。一個路徑的各個組成部分,不管是目錄還是普通的檔案,都是一個目錄項對象。如,在路徑/home/source/test.c中,目錄 /, home, source和檔案test.c都對應一個目錄項對象。不同於前面的兩個對象,目錄項對象沒有對應的磁碟資料結構,VFS在遍曆路徑名的過程中現場將它們逐個地解析成目錄項對象。
清單3. 目錄項
struct dentry {//目錄項結構 …… struct inode *d_inode; /*相關的索引節點*/ struct dentry *d_parent; /*父目錄的目錄項對象*/ struct qstr d_name; /*目錄項的名字*/ …… struct list_head d_subdirs; /*子目錄*/ …… struct dentry_operations *d_op; /*目錄項動作表*/ struct super_block *d_sb; /*檔案超級塊*/ ……};struct dentry_operations { //判斷目錄項是否有效; int (*d_revalidate)(struct dentry *, struct nameidata *); //為目錄項產生散列值; int (*d_hash) (struct dentry *, struct qstr *); ……}; |
2.2.4 檔案對象
檔案對象是已開啟的檔案在記憶體中的表示,主要用於建立進程和磁碟上的檔案的對應關係。它由sys_open()現場建立,由sys_close()銷毀。檔案對象和物理檔案的關係有點像進程和程式的關係一樣。當我們站在使用者空間來看待VFS,我們像是只需與檔案對象打交道,而無須關心超級塊,索引節點或目錄項。因為多個進程可以同時開啟和操作同一個檔案,所以同一個檔案也可能存在多個對應的檔案對象。檔案對象僅僅在進程觀點上代表已經開啟的檔案,它反過來指向目錄項對象(反過來指向索引節點)。一個檔案對應的檔案對象可能不是惟一的,但是其對應的索引節點和目錄項對象無疑是惟一的。
清單4. 檔案對象
struct file { …… struct list_head f_list; /*檔案對象鏈表*/ struct dentry *f_dentry; /*相關目錄項對象*/ struct vfsmount *f_vfsmnt; /*相關的安裝檔案系統*/ struct file_operations *f_op; /*檔案動作表*/ ……};struct file_operations { …… //檔案讀操作 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); …… //檔案寫操作 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); …… int (*readdir) (struct file *, void *, filldir_t); …… //檔案開啟操作 int (*open) (struct inode *, struct file *); ……}; |
2.2.5 其他VFS對象
2.2.5.1 和檔案系統相關
根據檔案系統所在的物理介質和資料在物理介質上的組織方式來區分不同的檔案系統類型的。file_system_type結構用於描述具體的檔案系統的類型資訊。被Linux支援的檔案系統,都有且僅有一個file_system_type結構而不管它有零個或多個執行個體被安裝到系統中。
而與此對應的是每當一個檔案系統被實際安裝,就有一個vfsmount結構體被建立,這個結構體對應一個安裝點。
清單5. 和檔案系統相關
struct file_system_type { const char *name; /*檔案系統的名字*/ struct subsystem subsys; /*sysfs子系統對象*/ int fs_flags; /*檔案系統類型標誌*/ /*在檔案系統被安裝時,從磁碟中讀取超級塊,在記憶體中組裝超級塊對象*/ struct super_block *(*get_sb) (struct file_system_type*, int, const char*, void *); void (*kill_sb) (struct super_block *); /*終止訪問超級塊*/ struct module *owner; /*檔案系統模組*/ struct file_system_type * next; /*鏈表中的下一個檔案系統類型*/ struct list_head fs_supers; /*具有同一種檔案系統類型的超級塊對象鏈表*/};struct vfsmount{ struct list_head mnt_hash; /*散列表*/ struct vfsmount *mnt_parent; /*父檔案系統*/ struct dentry *mnt_mountpoint; /*安裝點的目錄項對象*/ struct dentry *mnt_root; /*該檔案系統的根目錄項對象*/ struct super_block *mnt_sb; /*該檔案系統的超級塊*/ struct list_head mnt_mounts; /*子檔案系統鏈表*/ struct list_head mnt_child; /*子檔案系統鏈表*/ atomic_t mnt_count; /*使用計數*/ int mnt_flags; /*安裝標誌*/ char *mnt_devname; /*裝置檔案名稱*/ struct list_head mnt_list; /*描述符鏈表*/ struct list_head mnt_fslink; /*具體檔案系統的到期列表*/ struct namespace *mnt_namespace; /*相關的名字空間*/}; |
2.2.5.2 和進程相關
清單6. 開啟的檔案集
struct files_struct {//開啟的檔案集 atomic_t count; /*結構的使用計數*/ …… int max_fds; /*檔案對象數的上限*/ int max_fdset; /*檔案描述符的上限*/ int next_fd; /*下一個檔案描述符*/ struct file ** fd; /*全部檔案對象數組*/ …… };struct fs_struct {//建立進程與檔案系統的關係 atomic_t count; /*結構的使用計數*/ rwlock_t lock; /*保護該結構體的鎖*/ int umask; /*預設的檔案存取權限*/ struct dentry * root; /*根目錄的目錄項對象*/ struct dentry * pwd; /*當前工作目錄的目錄項對象*/ struct dentry * altroot; /*可供選擇的根目錄的目錄項對象*/ struct vfsmount * rootmnt; /*根目錄的安裝點對象*/ struct vfsmount * pwdmnt; /*pwd的安裝點對象*/ struct vfsmount * altrootmnt;/*可供選擇的根目錄的安裝點對象*/}; |
2.2.5.3 和路徑尋找相關
清單7. 輔助尋找
struct nameidata { struct dentry *dentry; /*目錄項對象的地址*/ struct vfsmount *mnt; /*安裝點的資料*/ struct qstr last; /*路徑中的最後一個component*/ unsigned int flags; /*尋找標識*/ int last_type; /*路徑中的最後一個component的類型*/ unsigned depth; /*當前symbolic link的嵌套深度,不能大於6*/ char *saved_names[MAX_NESTED_LINKS + 1];/ /*和嵌套symbolic link 相關的pathname*/ union { struct open_intent open; /*說明檔案該如何訪問*/ } intent; /*專用資料*/}; |
2.2.6 對象間的聯絡
如上的資料結構並不是孤立存在的。正是通過它們的有機聯絡,VFS才能正常工作。如下的幾張圖是對它們之間的聯絡的描述。
5所示,被Linux支援的檔案系統,都有且僅有一個file_system_type結構而不管它有零個或多個執行個體被安裝到系統中。每安裝一個檔案系統,就對應有一個超級塊和安裝點。超級塊通過它的一個域s_type指向其對應的具體的檔案系統類型。具體的檔案系統通過file_system_type中的一個域fs_supers連結具有同一種檔案類型的超級塊。同一種檔案系統類型的超級塊通過域s_instances連結。
圖5. 超級塊、安裝點和具體的檔案系統的關係
從圖6可知:進程通過task_struct中的一個域files_struct files來瞭解它當前所開啟的檔案對象;而我們通常所說的檔案描述符其實是進程開啟的檔案對象數組的索引值。檔案對象通過域f_dentry找到它對應的dentry對象,再由dentry對象的域d_inode找到它對應的索引結點,這樣就建立了檔案對象與實際的物理檔案的關聯。最後,還有一點很重要的是, 檔案對象所對應的檔案操作函數列表是通過索引結點的域i_fop得到的。圖6對第三部分源碼的理解起到很大的作用。
圖6. 進程與超級塊、檔案、索引結點、目錄項的關係
回頁首
3 基於VFS的檔案I/O
到目前為止,文章主要都是從理論上來講述VFS的運行機制;接下來我們將深入原始碼層中,通過闡述兩個具有代表性的系統調用sys_open()和sys_read()來更好地理解VFS向具體檔案系統提供的介面機制。由於本文更關注的是檔案操作的整個流程體制,所以我們在追蹤原始碼時,對一些細節性的處理不予關心。又由於篇幅所限,只列出相關代碼。本文中的原始碼來自於linux-2.6.17核心版本。
在深入sys_open()和sys_read()之前,我們先概覽下調用sys_read()的上下文。圖7描述了從使用者空間的read()調用到資料從磁碟讀出的整個流程。當在使用者應用程式調用檔案I/O read()操作時,系統調用sys_read()被激發,sys_read()找到檔案所在的具體檔案系統,把控制權傳給該檔案系統,最後由具體檔案系統與物理介質互動,從介質中讀出資料。
圖7. 從物理介質讀資料的過程
3.1 sys_open()
sys_open()系統調用開啟或建立一個檔案,成功返回該檔案的檔案描述符。圖8是sys_open()實現代碼中主要的函數呼叫歷程圖。
圖8. sys_open函數呼叫歷程圖
由於sys_open()的代碼量大,函數調用關係複雜,以下主要是對該函數做整體的解析;而對其中的一些關鍵點,則列出其關鍵代碼。
a. 從sys_open()的函數呼叫歷程圖可以看到,sys_open()在做了一些簡單的參數檢驗後,就把接力棒傳給do_sys_open():
1)、首先,get_unused_fd()得到一個可用的檔案描述符;通過該函數,可知檔案描述符實質是進程開啟檔案清單中對應某個檔案對象的索引值;
2)、接著,do_filp_open()開啟檔案,返回一個file對象,代表由該進程開啟的一個檔案;進程通過這樣的一個資料結構對物理檔案進行讀寫操作。
3)、最後,fd_install()建立檔案描述符與file對象的聯絡,以後進程對檔案的讀寫都是通過操縱該檔案描述符而進行。
b. do_filp_open()用於開啟檔案,返回一個file對象;而開啟之前需要先找到該檔案:
1)、open_namei()用於根據檔案路徑名尋找檔案,藉助一個持有路徑資訊的資料結構nameidata而進行;
2)、尋找結束後將填充有路徑資訊的nameidata返回給接下來的函數nameidata_to_filp()從而得到最終的file對象;當達到目的後,nameidata這個資料結構將會馬上被釋放。
c.open_namei()用於尋找一個檔案:
1)、path_lookup_open()實現檔案的尋找功能;要開啟的檔案若不存在,還需要有一個建立的過程,則調用path_lookup_create(),後者和前者封裝的是同一個實際的路徑尋找函數,只是參數不一樣,使它們在處理細節上有所偏差;
2)、當是以建立檔案的方式開啟檔案時,即設定了O_CREAT標識時需要建立一個新的索引節點,代表建立一個檔案。在vfs_create()裡的一句核心語句dir->i_op->create(dir, dentry, mode, nd)可知它調用了具體的檔案系統所提供的建立索引節點的方法。注意:這邊的索引節點的概念,還只是位於記憶體之中,它和磁碟上的物理的索引節點的關係就像位於記憶體中和位於磁碟中的檔案一樣。此時建立的索引節點還不能完全標誌一個物理檔案的成功建立,只有當把索引節點回寫到磁碟上才是一個物理檔案的真正建立。想想我們以建立的方式開啟一個檔案,對其讀寫但最終沒有儲存而關閉,則位於記憶體中的索引節點會經曆從建立到消失的過程,而磁碟卻始終不知道有人曾經想過建立一個檔案,這是因為索引節點沒有回寫的緣故。
3)、path_to_nameidata()填充nameidata資料結構;
4)、may_open()檢查是否可以開啟該檔案;一些檔案如連結檔案和只有寫入權限的目錄是不能被開啟的,先檢查nd->dentry->inode所指的檔案是否是這一類檔案,是的話則錯誤返回。還有一些檔案是不能以TRUNC的方式開啟的,若nd->dentry->inode所指的檔案屬於這一類,則顯式地關閉TRUNC標誌位。接著如果有以TRUNC方式開啟檔案的,則更新nd->dentry->inode的資訊
3.1.1__path_lookup_intent_open()
不管是path_lookup_open()還是path_lookup_create()最終都是調用__path_lookup_intent_open()來實現尋找檔案的功能。尋找時,在遍曆路徑的過程中,會逐層地將各個路徑組成部分解析成目錄項對象,如果此目錄項對象在目錄項緩衝中,則直接從緩衝中獲得;如果該目錄項在緩衝中不存在,則進行一次實際的讀盤操作,從磁碟中讀取該目錄項所對應的索引節點。得到索引節點後,則建立索引節點與該目錄項的聯絡。如此迴圈,直到最終找到目標檔案對應的目錄項,也就找到了索引節點,而由索引節點找到對應的超級塊對象就可知道該檔案所在的檔案系統的類型。從磁碟中讀取該目錄項所對應的索引節點;這將引發VFS和實際的檔案系統的一次互動。從前面的VFS理論介紹可知,讀索引節點方法是由超級塊來提供的。而當安裝一個實際的檔案系統時,在記憶體中建立的超級塊的資訊是由一個實際檔案系統的相關資訊來填充的,這裡的相關資訊就包括了實際檔案系統所定義的超級塊的操作函數列表,當然也就包括了讀索引節點的具體執行方式。當繼續追蹤一個實際檔案系統ext3的ext3_read_inode()時,可發現這個函數很重要的一個工作就是為不同的檔案類型設定不同的索引節點操作函數表和檔案操作函數表。
清單8. ext3_read_inode
void ext3_read_inode(struct inode * inode) { …… //是普通檔案 if (S_ISREG(inode->i_mode)) { inode->i_op = &ext3_file_inode_operations; inode->i_fop = &ext3_file_operations; ext3_set_aops(inode); } else if (S_ISDIR(inode->i_mode)) { //是目錄檔案 inode->i_op = &ext3_dir_inode_operations; inode->i_fop = &ext3_dir_operations; } else if (S_ISLNK(inode->i_mode)) { // 是串連檔案 …… } else { // 如果以上三種情況都排除了,則是裝置驅動 //這裡的裝置還包括套結字、FIFO等偽裝置 …… } |
3.1.2 nameidata_to_filp子函數:__dentry_open
這是VFS與實際的檔案系統聯絡的一個關鍵點。從3.1.1小節分析中可知,調用實際檔案系統讀取索引節點的方法讀取索引節點時,實際檔案系統會根據檔案的不同類型賦予索引節點不同的檔案操作函數集,如普通檔案有普通檔案對應的一套操作函數,裝置檔案有裝置檔案對應的一套操作函數。這樣當把對應的索引節點的檔案操作函數集賦予檔案對象,以後對該檔案進行操作時,比如讀操作,VFS雖然對各種不同檔案都是執行同一個read()操作介面,但是真正讀時,核心卻知道怎麼區分對待不同的檔案類型。
清單9. __dentry_open
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,int flags, struct file *f,int (*open)(struct inode *, struct file *)) { struct inode *inode; …… //整個函數的工作在於填充一個file對象 …… f->f_mapping = inode->i_mapping; f->f_dentry = dentry; f->f_vfsmnt = mnt; f->f_pos = 0; //將對應的索引節點的檔案操作函數集賦予檔案對象的巨集指令清單 f->f_op = fops_get(inode->i_fop); …… //若檔案自己定義了open操作,則執行這個特定的open操作。 if (!open && f->f_op) open = f->f_op->open; if (open) { error = open(inode, f); if (error) goto cleanup_all; …… return f;} |
3.2 sys_read()
sys_read()系統調用用於從已開啟的檔案讀取資料。如read成功,則返回讀到的位元組數。如已到達檔案的尾端,則返回0。圖9是sys_read()實現代碼中的函數呼叫歷程圖。
圖9. sys_read函數呼叫歷程圖
對檔案進行讀操作時,需要先開啟它。從3.1小結可知,開啟一個檔案時,會在記憶體組裝一個檔案對象,希望對該檔案執行的操作方法已在檔案對象設定好。所以對檔案進行讀操作時,VFS在做了一些簡單的轉換後(由檔案描述符得到其對應的檔案對象;其核心思想是返回current->files->fd[fd]所指向的檔案對象),就可以通過語句file->f_op->read(file, buf, count, pos)輕鬆調用實際檔案系統的相應方法對檔案進行讀操作了。
回頁首
4 解決問題
4.1 跨檔案系統的檔案操作的基本原理
到此,我們也就能夠解釋在Linux中為什麼能夠跨檔案系統地操作檔案了。舉個例子,將vfat格式的磁碟上的一個檔案a.txt拷貝到ext3格式的磁碟上,命名為b.txt。這包含兩個過程,對a.txt進行讀操作,對b.txt進行寫操作。讀寫操作前,需要先開啟檔案。由前面的分析可知,開啟檔案時,VFS會知道該檔案對應的檔案系統格式,以後操作該檔案時,VFS會調用其對應的實際檔案系統的操作方法。所以,VFS調用vfat的讀檔案方法將a.txt的資料讀入記憶體;在將a.txt在記憶體中的資料對應到b.txt對應的記憶體空間後,VFS調用ext3的寫檔案方法將b.txt寫入磁碟;從而實現了最終的跨檔案系統的複製操作。
4.2“一切皆是檔案”的實現根本
不論是普通的檔案,還是特殊的目錄、裝置等,VFS都將它們同等看待成檔案,通過同一套檔案操作介面來對它們進行操作。操作檔案時需先開啟;開啟檔案時,VFS會知道該檔案對應的檔案系統格式;當VFS把控制權傳給實際的檔案系統時,實際的檔案系統再做出具體區分,對不同的檔案類型執行不同的操作。這也就是“一切皆是檔案”的根本所在。
回頁首
5 總結
VFS即虛擬檔案系統是Linux檔案系統中的一個抽象軟體層;因為它的支援,眾多不同的實際檔案系統才能在Linux中共存,跨檔案系統操作才能實現。VFS藉助它四個主要的資料結構即超級塊、索引節點、目錄項和檔案對象以及一些輔助的資料結構,向Linux中不管是普通的檔案還是目錄、裝置、通訊端等都提供同樣的操作介面,如開啟、讀寫、關閉等。只有當把控制權傳給實際的檔案系統時,實際的檔案系統才會做出區分,對不同的檔案類型執行不同的操作。由此可見,正是有了VFS的存在,跨檔案系統操作才能執行,Unix/Linux中的“一切皆是檔案”的口號才能夠得以實現。
回頁首
6 參考文獻
[1].Claudia Salzberg Rodriguez, Gordon Fischer, Steven Smolski. The Linux Kernel Primer.機械工業出版社.2006.7
[2].Robert Love.Linux核心設計與實現(第二版).機械工業出版社.2007.1
[3].Stevens W.Richard.Unix環境進階編程(第二版).人民郵電出版社.2006
[4].楊芙清,陳向群.作業系統教程.北京大學出版社.2005.7
[5].Linux-2.6.17.13核心原始碼
關於作者
吳美清,北京大學 軟體與微電子學院研究生
轉載自http://www.ibm.com/developerworks/cn/linux/l-cn-vfs/