《Linux原理與結(jié)構(gòu)》課件第11章_第1頁
《Linux原理與結(jié)構(gòu)》課件第11章_第2頁
《Linux原理與結(jié)構(gòu)》課件第11章_第3頁
《Linux原理與結(jié)構(gòu)》課件第11章_第4頁
《Linux原理與結(jié)構(gòu)》課件第11章_第5頁
已閱讀5頁,還剩167頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第十一章虛擬文件系統(tǒng)11.1虛擬文件系統(tǒng)管理結(jié)構(gòu)11.2文件系統(tǒng)管理11.3文件管理11.4文件I/O操作11.5文件緩存管理即使提供了虛擬內(nèi)存、互斥與同步、進(jìn)程間通信等支持機(jī)制,進(jìn)程仍然無法正常工作,原因是還未為其提供與外界交互的手段,即I/O機(jī)制。離開了I/O機(jī)制的支持,進(jìn)程既無法接收外界信息,也無法輸出處理結(jié)果,就會(huì)失去存在的意義。

事實(shí)上,計(jì)算機(jī)系統(tǒng)中除了處理器、內(nèi)存、中斷、時(shí)鐘等核心硬件資源之外,通常還配置有多種外部設(shè)備,如磁盤、光盤等存儲(chǔ)設(shè)備,網(wǎng)卡等通信設(shè)備,鍵盤、鼠標(biāo)等輸入設(shè)備,顯示器、打印機(jī)等輸出設(shè)備。如果說處理器、內(nèi)存等是大腦的話,那么外部設(shè)備就是計(jì)算機(jī)系統(tǒng)的五官和四肢。顯然,外部設(shè)備管理是操作系統(tǒng)的核心任務(wù)之一。在所有的外部設(shè)備中,外部存儲(chǔ)設(shè)備是最重要的一類,操作系統(tǒng)對(duì)其進(jìn)行了一系列的抽象。外存設(shè)備上的存儲(chǔ)空間被抽象成了邏輯塊的數(shù)組,用戶可以以塊為單位對(duì)其進(jìn)行隨機(jī)訪問,因此外存設(shè)備又被稱為塊設(shè)備。塊設(shè)備上存儲(chǔ)的信息被抽象成了文件,一個(gè)塊設(shè)備上的所有文件被組織在一個(gè)目錄結(jié)構(gòu)中,因此單個(gè)塊設(shè)備上的信息管理系統(tǒng)又被稱為物理文件系統(tǒng)。不同塊設(shè)備上的物理文件系統(tǒng)被統(tǒng)一組織起來,形成了單一的虛擬文件系統(tǒng)(VirtualFileSystem,VFS)。塊設(shè)備驅(qū)動(dòng)程序負(fù)責(zé)物理塊設(shè)備操作的實(shí)施,塊設(shè)備管理層負(fù)責(zé)邏輯塊數(shù)組的抽象,物理文件系統(tǒng)負(fù)責(zé)單個(gè)塊設(shè)備中的存儲(chǔ)空間與文件的管理,虛擬文件系統(tǒng)負(fù)責(zé)物理文件系統(tǒng)的管理。進(jìn)一步地,Linux將系統(tǒng)中所有的外部設(shè)備全都抽象成了文件(稱為設(shè)備特殊文件或設(shè)備文件),用普通的文件操作統(tǒng)一了千差萬別的設(shè)備操作,從而統(tǒng)一了外部設(shè)備的管理。因此,虛擬文件系統(tǒng)是I/O系統(tǒng)的總接口,是現(xiàn)代操作系統(tǒng)的核心之一。

虛擬文件系統(tǒng)是由SUN公司首先提出的,最初的設(shè)計(jì)目標(biāo)有四個(gè),分別是可同時(shí)支持多種類型的物理文件系統(tǒng);可屏蔽物理文件系統(tǒng)之間的差別,統(tǒng)一物理文件系統(tǒng)的使用;可為在網(wǎng)絡(luò)上共享文件提供支持;允許用戶開發(fā)并以模塊方式動(dòng)態(tài)加載自己的物理文件系統(tǒng)。11.1虛擬文件系統(tǒng)管理結(jié)構(gòu)經(jīng)過多年的努力,VFS達(dá)到并超過了自己的設(shè)計(jì)目標(biāo),演變成了Unix系列操作系統(tǒng)的標(biāo)準(zhǔn)輸入/輸出管理系統(tǒng)。事實(shí)上,除了管理物理文件系統(tǒng)之外,VFS還管理著系統(tǒng)中的各類外部設(shè)備。VFS與物理文件系統(tǒng)和塊設(shè)備管理程序合作共同完成了塊設(shè)備的管理,與字符設(shè)備管理程序合作完成了字符設(shè)備的管理,與網(wǎng)絡(luò)協(xié)議和網(wǎng)絡(luò)設(shè)備管理程序合作完成了網(wǎng)絡(luò)設(shè)備的管理。11.1.1虛擬文件系統(tǒng)框架

如果僅從塊設(shè)備管理的角度觀察,VFS與物理文件系統(tǒng)合作主要完成三項(xiàng)管理工作,其中邏輯塊的組織與管理工作主要由物理文件系統(tǒng)負(fù)責(zé),文件的組織與管理工作由物理文件系統(tǒng)與VFS共同負(fù)責(zé),物理文件系統(tǒng)的管理工作主要由虛擬文件系統(tǒng)負(fù)責(zé)。塊設(shè)備管理程序、物理文件系統(tǒng)與VFS之間的關(guān)系如圖11.1所示。

圖11.1VFS與物理文件系統(tǒng)和塊設(shè)備管理程序間的關(guān)系然而,VFS不是真實(shí)的文件系統(tǒng),它僅存在于內(nèi)存之中,在外存上并沒有對(duì)應(yīng)的實(shí)體(所以稱為虛擬文件系統(tǒng))。VFS中的實(shí)體和管理結(jié)構(gòu)都是在使用過程中動(dòng)態(tài)生成的,會(huì)在系統(tǒng)關(guān)閉時(shí)自動(dòng)消亡。

事實(shí)上,VFS僅是一個(gè)管理框架,它定義上下兩個(gè)層次的接口。物理文件系統(tǒng)通過下層接口被插入到VFS框架中。只要實(shí)現(xiàn)了下層接口,VFS就認(rèn)為它是一個(gè)物理文件系統(tǒng)。用戶通過VFS的上層接口使用I/O系統(tǒng),如安裝、卸載物理文件系統(tǒng),組織與讀寫文件,操作外部設(shè)備等。VFS將用戶請(qǐng)求的文件或設(shè)備操作轉(zhuǎn)交給下層的物理文件系統(tǒng)或設(shè)備管理程序,因此VFS又被稱為虛擬文件交換機(jī)(VirtualFileSwitch),如圖11.2所示。圖11.2虛擬文件系統(tǒng)框架為了實(shí)現(xiàn)對(duì)物理文件系統(tǒng)的管理,實(shí)現(xiàn)上下層接口之間的轉(zhuǎn)接,VFS建立了一整套數(shù)據(jù)結(jié)構(gòu),包括超級(jí)塊結(jié)構(gòu)super_block、索引節(jié)點(diǎn)結(jié)構(gòu)inode、目錄項(xiàng)結(jié)構(gòu)dentry等,每一個(gè)結(jié)構(gòu)中都包含一到多個(gè)操作集。設(shè)計(jì)物理文件系統(tǒng)的核心工作是實(shí)現(xiàn)這些結(jié)構(gòu)中的操作集。11.1.2超級(jí)塊結(jié)構(gòu)

VFS管理的最重要的實(shí)體或?qū)ο笫俏锢砦募到y(tǒng)。為描述物理文件系統(tǒng),VFS專門定義了超級(jí)塊結(jié)構(gòu)super_block,又稱為文件系統(tǒng)類。Linux為它的每個(gè)活動(dòng)的物理文件系統(tǒng)都建立了一個(gè)超級(jí)塊實(shí)例,就像為每個(gè)進(jìn)程都建立一個(gè)task_struct結(jié)構(gòu)一樣。超級(jí)塊結(jié)構(gòu)中記錄著物理文件系統(tǒng)的所有管理信息,大致包括如下幾類:

(1)底層塊設(shè)備。除了一些特殊的偽文件系統(tǒng)之外,大部分的物理文件系統(tǒng)都建立在塊設(shè)備之上。塊設(shè)備的設(shè)備號(hào)記錄在域s_dev中,邏輯塊設(shè)備的描述結(jié)構(gòu)(即結(jié)構(gòu)block_device,見12.1.3節(jié))記錄在域s_bdev中。

(2)塊尺寸。文件系統(tǒng)以塊為單位讀寫底層塊設(shè)備,每次至少一塊。不同物理文件系統(tǒng)可以選用不同的塊尺寸,但一旦選定就不可再更改,除非重建該物理文件系統(tǒng)。物理文件系統(tǒng)所選用的塊尺寸記錄在域s_blocksize中。塊尺寸是一個(gè)邏輯單位,必須是物理單位(扇區(qū)尺寸,512字節(jié))的整倍數(shù),通常與頁的尺寸相同,如4096字節(jié)。

(3)文件的最大尺寸。理論上說,文件的尺寸可以無限大,只要塊設(shè)備能夠存儲(chǔ)它。然而實(shí)際上,由于受到管理結(jié)構(gòu)的限制,各個(gè)物理文件系統(tǒng)都限制了它的最大文件尺寸。物理文件系統(tǒng)允許存儲(chǔ)的最大文件尺寸記錄在域s_maxbytes中。

(4)類型。文件系統(tǒng)類型(由結(jié)構(gòu)file_system_type描述)中記錄著獲取物理文件系統(tǒng)管理信息(即超級(jí)塊)的方法,每類物理文件系統(tǒng)一個(gè)。物理文件系統(tǒng)所屬的類型記錄在域s_type中。

(5)狀態(tài)。在域s_flags中記錄著物理文件系統(tǒng)的當(dāng)前狀態(tài),如是否已安裝就緒、是否為只讀安裝、是否可被用戶使用、是否允許訪問其中的塊設(shè)備特殊文件、是否允許執(zhí)行其中的程序、是否限制更新文件中的最近存取時(shí)間等。另外,域s_dirt是一個(gè)臟標(biāo)志,表示超級(jí)塊中的信息是否曾被修改過。

(6)根目錄。每個(gè)物理文件系統(tǒng)都將自己的文件組織成一棵目錄樹(實(shí)際是一個(gè)非循環(huán)圖),樹根稱為根目錄。域s_root指向物理文件系統(tǒng)的根目錄。

(7)管理隊(duì)列。屬于同一物理文件系統(tǒng)的所有inode結(jié)構(gòu)被組織在一個(gè)隊(duì)列中,隊(duì)頭為域s_inodes。屬于同一物理文件系統(tǒng)的所有file結(jié)構(gòu)被組織在一個(gè)隊(duì)列中,隊(duì)頭為域s_files。

(8)超級(jí)塊操作集。域s_op指向超級(jí)塊操作集super_operations的一個(gè)實(shí)例,其中記錄著物理文件系統(tǒng)實(shí)現(xiàn)的超級(jí)塊管理操作(如寫出、釋放等操作)、inode管理操作(如分配、寫出、清理、刪除、銷毀等操作)、文件系統(tǒng)管理操作(如同步、重裝、凍結(jié)、解凍等操作)等。

(9)配額管理。配額用于界定一個(gè)用戶可用的外存空間和inode的上限。域dq_op指向配額操作集dquot_operations,內(nèi)含配額的分配、獲取、寫出、釋放、銷毀等操作。

(10)私有信息。除了上述的公共信息之外,每個(gè)物理文件系統(tǒng)還可以定義一個(gè)私有的結(jié)構(gòu),用于記錄自己特有的管理信息。域s_fs_info指向物理文件系統(tǒng)的私有結(jié)構(gòu)。

系統(tǒng)中所有的超級(jí)塊結(jié)構(gòu)被組織在一個(gè)全局鏈表super_blocks中。屬于同一類型的所有超級(jí)塊結(jié)構(gòu)也被組織在一個(gè)鏈表中,表頭是文件系統(tǒng)類型結(jié)構(gòu)中的fs_supers。11.1.3索引節(jié)點(diǎn)結(jié)構(gòu)

超級(jí)塊用于描述物理文件系統(tǒng),物理文件系統(tǒng)所管理的主要實(shí)體是文件。文件是按一定形式組織起來的一組信息,包含兩方面的內(nèi)容,一是文件數(shù)據(jù),二是元數(shù)據(jù)(用于描述文件的組織與管理信息)。作為管理者,文件系統(tǒng)并不關(guān)心文件數(shù)據(jù)的具體內(nèi)容,所關(guān)心的是文件的元數(shù)據(jù)。文件元數(shù)據(jù)常被組織成文件控制塊(FileControlBlock,F(xiàn)CB),Linux稱為索引節(jié)點(diǎn)(IndexNode),由結(jié)構(gòu)inode定義。VFS的結(jié)構(gòu)inode是對(duì)各類文件控制塊共有特征的抽象,用于統(tǒng)一描述不同物理文件系統(tǒng)中的文件,也可稱之為文件類。VFS將它使用的每一個(gè)文件都看成inode類的一個(gè)實(shí)例。進(jìn)一步地,VFS將它使用的設(shè)備、目錄、管道、符號(hào)鏈接等輸入/輸出實(shí)體(簡(jiǎn)稱VFS實(shí)體)全都看成虛擬的文件,并用inode類統(tǒng)一描述它們,從而統(tǒng)一了Linux的所有輸入/輸出實(shí)體。

結(jié)構(gòu)inode中包含輸入/輸出實(shí)體的所有屬性信息,但不包含文件的名稱和正文。事實(shí)上有些實(shí)體,如設(shè)備、管道等,也沒有正文。結(jié)構(gòu)inode中主要包含如下幾類屬性:

(1)所屬物理文件系統(tǒng)。由inode描述的每個(gè)實(shí)體都屬于一個(gè)物理文件系統(tǒng),其中的域i_sb指向物理文件系統(tǒng)的超級(jí)塊結(jié)構(gòu)。

(2)標(biāo)識(shí)符。物理文件系統(tǒng)用無符號(hào)長整數(shù)i_ino標(biāo)識(shí)自己的實(shí)體。i_ino在一個(gè)物理文件系統(tǒng)內(nèi)是唯一的,但不是全局唯一的。i_ino與i_sb合起來才能夠唯一地標(biāo)識(shí)一個(gè)VFS

實(shí)體。

(3)屬主。每個(gè)VFS實(shí)體都屬于一個(gè)用戶,該用戶就是這一實(shí)體的屬主。屬主由UID、GID標(biāo)識(shí),分別記錄在inode結(jié)構(gòu)的i_uid和i_gid域中。

(4)模式。實(shí)體模式i_mode是一個(gè)無符號(hào)小整數(shù)(16位),其中記錄著實(shí)體的類型和訪問權(quán)限等信息,其格式如圖11.3所示。

圖11.3實(shí)體模式域的格式在i_mode域中,最高4位表示實(shí)體的類型。目前的實(shí)體類型包括命名管道、字符設(shè)備、目錄、塊設(shè)備、普通文件、符號(hào)鏈接、Socket等。

第11位是SUID標(biāo)志,第10位是SGID標(biāo)志,第9位是SVTX標(biāo)志。

第6~8位是文件屬主的訪問權(quán)限(讀、寫、執(zhí)行),第3~5位是同組用戶的訪問權(quán)限(讀、寫、執(zhí)行)、第0~2位是其它用戶的訪問權(quán)限(讀、寫、執(zhí)行)。

(5)大小。域i_size、i_blocks和i_bytes中記錄的都是實(shí)體的大小(如普通文件所占用的外存空間、塊設(shè)備的容量等),關(guān)系是i_size=i_blocks

×

512+i_bytes。

(6)時(shí)間。在inode結(jié)構(gòu)中,i_atime是實(shí)體最近一次被訪問的時(shí)間、i_mtime是實(shí)體正文最近一次被修改的時(shí)間、i_ctime是實(shí)體屬性最近一次被修改的時(shí)間。

(7)狀態(tài)。域i_state中記錄著inode結(jié)構(gòu)的狀態(tài),包括I_NEW(正在創(chuàng)建)、I_DIRTY_SYNC(已被改變但不需要同步)、I_DIRTY_DATASYNC(數(shù)據(jù)部分已被改變)、I_DIRTY_PAGES(有臟的數(shù)據(jù)頁)、I_WILL_FREE(即將被釋放)、I_FREEING(正在被釋放)、I_CLEAR(inode是干凈的)、I_SYNC(正在同步過程中)等。

(8)設(shè)備信息。如果inode描述的是字符或塊設(shè)備,那么它的i_rdev域中記錄的是設(shè)備號(hào),i_bdev或i_cdev域中記錄的是設(shè)備的邏輯描述結(jié)構(gòu)。

(9)硬鏈接信息。一個(gè)inode結(jié)構(gòu)可以被加入到多個(gè)目錄中,從而使實(shí)體擁有多個(gè)路徑名。硬鏈接計(jì)數(shù)(域i_nlink)表示該inode在不同目錄中出現(xiàn)的次數(shù)。

(10)引用計(jì)數(shù)。域i_count中記錄著該inode結(jié)構(gòu)的當(dāng)前用戶數(shù)。

(11)操作集。對(duì)實(shí)體元數(shù)據(jù)的操作方法記錄在操作集inode_operations中。域i_op指向?qū)嶓w自己的inode_operations實(shí)例,其中包含數(shù)十個(gè)操作,如:

文件創(chuàng)建操作create用于在目錄中創(chuàng)建一個(gè)指定名稱的新文件。

查找操作lookup用于獲得目錄中某指定名稱的文件的inode結(jié)構(gòu)。

鏈接操作link用于為文件建立一個(gè)新的硬鏈接(起一個(gè)新的名稱)。

刪除操作unlink用于刪除文件的一個(gè)指定名稱的硬鏈接。目錄創(chuàng)建操作mkdir用于在目錄中創(chuàng)建一個(gè)指定名稱的子目錄。

目錄刪除操作rmdir用于刪除目錄中的一個(gè)指定名稱的子目錄。

符號(hào)鏈接操作symlink用于為實(shí)體建立一個(gè)新的符號(hào)鏈接或軟鏈接。

換名操作rename用于更換一個(gè)實(shí)體的名稱。

設(shè)備文件創(chuàng)建操作mknod用于創(chuàng)建一個(gè)指定名稱的新設(shè)備特殊文件。屬性獲取操作getattr用于獲取實(shí)體的屬性。

屬性設(shè)置操作setattr用于設(shè)置實(shí)體的屬性。

長度重置操作truncate用于設(shè)置文件的長度屬性。

對(duì)實(shí)體正文(如文件、管道、設(shè)備中的數(shù)據(jù))的操作方法記錄在操作集file_operations中。域i_fop指向?qū)嶓w自己的file_operations實(shí)例,其中主要包含如下幾個(gè)操作:

打開操作open用于完成實(shí)體打開時(shí)的特定初始化工作。

釋放操作release用于完成實(shí)體關(guān)閉時(shí)的善后處理工作。

讀寫頭定位操作llseek用于設(shè)置文件讀寫頭的位置。文件同步讀操作read用于讀實(shí)體中的數(shù)據(jù)或從實(shí)體中接收數(shù)據(jù)。

文件同步寫操作write用于向?qū)嶓w中寫數(shù)據(jù)或向?qū)嶓w中輸出數(shù)據(jù)。

文件異步讀操作aio_read用于讀實(shí)體中的數(shù)據(jù)或從實(shí)體中接收數(shù)據(jù)。

文件異步寫操作aio_write用于向?qū)嶓w中寫數(shù)據(jù)或向?qū)嶓w中輸出數(shù)據(jù)。

映射操作mmap用于為映射到該文件的虛擬內(nèi)存區(qū)域指定操作集。目錄讀操作readdir用于讀目錄文件中的內(nèi)容。

控制操作ioctl用于向字符或塊設(shè)備發(fā)布控制命令。

同步操作fsync用于將緩存中的文件內(nèi)容同步到物理文件系統(tǒng)中。

文件鎖操作lock用于使能文件的內(nèi)容鎖。

(12)地址空間。地址空間address_space是文件的頁緩存。指針i_mapping指向文件當(dāng)前使用的地址空間,域i_data是一個(gè)嵌入在inode中的address_space結(jié)構(gòu)。

(13)私有信息。除了上述的公共信息之外,每個(gè)物理文件系統(tǒng)都為它的文件定義了特殊的管理結(jié)構(gòu),用于記錄文件的私有信息,如各文件塊的存儲(chǔ)位置等。域i_private指向文件的私有管理結(jié)構(gòu)。

VFSinode是輸入/輸出實(shí)體在內(nèi)存中的表示,只有即將使用的實(shí)體才需建立inode結(jié)構(gòu)。但由于inode結(jié)構(gòu)的建立比較費(fèi)時(shí),所以應(yīng)該將經(jīng)常使用的inode結(jié)構(gòu)緩存起來。為了緩存系統(tǒng)中的inode結(jié)構(gòu),Linux定義了一個(gè)名為inode_hashtable的Hash表、一個(gè)名為inode_unused的空閑inode隊(duì)列、一個(gè)名為inode_in_use的在用inode隊(duì)列和一個(gè)名為b_dirty的臟inode隊(duì)列。域i_hash用于將inode結(jié)構(gòu)插入Hash表,域i_list用于將inode結(jié)構(gòu)插入其它三個(gè)隊(duì)列之一。在圖11.4中,inode0處于非活動(dòng)狀態(tài)(結(jié)構(gòu)有效但已無用戶),inode1和inode2都處于活動(dòng)狀態(tài),但inode1是干凈的而inode2是臟的(修改后的內(nèi)容還未被寫回塊設(shè)備)。臟inode應(yīng)該也是活動(dòng)的inode。另外,inode1和inode2屬于同一個(gè)物理文件系統(tǒng)。

圖11.4inode結(jié)構(gòu)隊(duì)列11.1.4目錄項(xiàng)結(jié)構(gòu)

雖然inode結(jié)構(gòu)中包含了文件實(shí)體的主要描述信息,但其中缺少了實(shí)體名稱和實(shí)體間的組織關(guān)系,因此還不夠完整。在物理文件系統(tǒng)中,用于描述實(shí)體間組織關(guān)系的常用手段是目錄。一個(gè)目錄通常就是一張表,其中的一個(gè)表項(xiàng)(稱為目錄項(xiàng))記錄著一個(gè)實(shí)體名稱與一個(gè)FCB的對(duì)應(yīng)關(guān)系。除最上層的目錄(稱為根目錄)之外,每個(gè)目錄又都包含在其它目錄之中,因而目錄之間形成了一種自然的樹狀或網(wǎng)狀組織關(guān)系。從根目錄開始,按名稱逐層搜索各個(gè)目錄表,可以找到物理文件系統(tǒng)中任意一個(gè)實(shí)體的FCB結(jié)構(gòu)。將實(shí)體名稱與FCB分開的好處是可以給一個(gè)實(shí)體起多個(gè)名稱,因而可以從多條路徑搜索到同一個(gè)實(shí)體。

為了在內(nèi)存中描述實(shí)體間的組織關(guān)系,VFS對(duì)各物理文件系統(tǒng)的目錄項(xiàng)進(jìn)行了抽象,定義了虛擬目錄項(xiàng)結(jié)構(gòu)dentry,其中的主要內(nèi)容如下:

(1)實(shí)體名稱。實(shí)體名稱就是文件或目錄名,記錄在域d_name中。

(2)實(shí)體描述結(jié)構(gòu)。實(shí)體描述結(jié)構(gòu)即實(shí)體的inode結(jié)構(gòu),記錄在域d_inode中。域d_name和d_inode描述了實(shí)體名稱與inode之間的一個(gè)對(duì)應(yīng)關(guān)系。如果一個(gè)實(shí)體有多個(gè)名稱,VFS會(huì)為其創(chuàng)建多個(gè)dentry結(jié)構(gòu),它們指向同一個(gè)inode結(jié)構(gòu),并被域d_alias串成一個(gè)鏈表,表頭為inode中的i_dentry。

(3)父目錄。域d_parent指向父目錄項(xiàng)。根目錄的父目錄就是自己。

(4)目錄樹。屬于一個(gè)目錄的所有實(shí)體的dentry結(jié)構(gòu)被它們的d_child域串成一個(gè)隊(duì)列,隊(duì)頭為父目錄dentry結(jié)構(gòu)中的d_subdirs域。各目錄項(xiàng)的d_subdirs隊(duì)列組合起來構(gòu)成一棵目錄樹,如圖11.5所示。

(5)

Hash表。為了加快dentry的查找速度,除目錄樹之外,Linux還為dentry結(jié)構(gòu)建立了一個(gè)Hash表dentry_hashtable。域d_hash用于將目錄項(xiàng)插入到Hash表中。

(6)安裝點(diǎn)標(biāo)志。域d_mounted非0表示該目錄上安裝了物理文件系統(tǒng)。

(7)操作集。目錄項(xiàng)操作集dentry_operations中記錄著目錄項(xiàng)特有的操作方法,如Hash值計(jì)算方法d_hash、名字比較方法d_compare、inode釋放方法d_iput、目錄項(xiàng)釋放方法d_release、目錄項(xiàng)刪除方法d_delete、目錄項(xiàng)生效方法d_revalidate等。

由此可見,inode是對(duì)實(shí)體本身的抽象,dentry是對(duì)實(shí)體間組織關(guān)系的抽象。VFS為它的每個(gè)實(shí)體都定義了一個(gè)inode結(jié)構(gòu),同時(shí)還會(huì)為其定義至少一個(gè)dentry結(jié)構(gòu)。與inode結(jié)構(gòu)相似,只有即將使用的實(shí)體才需建立dentry結(jié)構(gòu)。由于目錄項(xiàng)的建立比較耗時(shí)且使用頻繁,因而應(yīng)將已建立的dentry結(jié)構(gòu)緩存起來。Linux用兩種方式管理dentry結(jié)構(gòu)的緩存,一是目錄樹,二是Hash表,如圖11.5所示。

圖11.5目錄項(xiàng)結(jié)構(gòu)之間的關(guān)系

常見的計(jì)算機(jī)系統(tǒng)中通常配有多種塊設(shè)備,如磁盤、光盤、U盤等,每一種塊設(shè)備上都可能安裝著物理文件系統(tǒng),如EXT3、NTFS、FAT、ISO9660等。出于某些特殊的目的,Linux還會(huì)建立不需要塊設(shè)備的虛文件系統(tǒng),11.2文件系統(tǒng)管理如sysfs、proc、pipefs、mqueue、shm等,因而運(yùn)行著的Linux系統(tǒng)中常會(huì)包含多種物理文件系統(tǒng),每一種物理文件系統(tǒng)都有自己獨(dú)特的文件組織方法與操作方法。為了統(tǒng)一管理各種不同類型的物理文件系統(tǒng),屏蔽它們之間的差別,VFS提供了一系列的文件系統(tǒng)管理手段,如物理文件系統(tǒng)的注冊(cè)、安裝、重裝、卸載等。按照VFS的約定,只有注冊(cè)過的物理文件系統(tǒng)才能夠安裝,只有安裝后的物理文件系統(tǒng)才能夠使用。11.2.1文件系統(tǒng)注冊(cè)

文件系統(tǒng)管理的首要工作是為即將使用的物理文件系統(tǒng)建立超級(jí)塊結(jié)構(gòu),大致可分為兩步,一是申請(qǐng)一塊能容納super_block結(jié)構(gòu)的物理內(nèi)存,二是填寫其中的管理信息,難點(diǎn)在第二步。由于不同類型的物理文件系統(tǒng)有不同的信息組織方式,因而VFS不可能理解所有的物理文件系統(tǒng),無法直接讀取其管理信息,更何況有些物理文件系統(tǒng)(如sysfs、proc等)并非建立在塊設(shè)備之上,也無處讀取其管理信息。能夠?yàn)閂FS提供管理信息的只有物理文件系統(tǒng)自己。因而,VFS要求即將使用的每種物理文件系統(tǒng)都必須注冊(cè)一個(gè)結(jié)構(gòu)file_system_type,在其中聲明一個(gè)獲取管理信息、填寫超級(jí)塊結(jié)構(gòu)的方法。

結(jié)構(gòu)file_system_type又稱為物理文件系統(tǒng)類型,其中的主要內(nèi)容如下:

(1)名稱name是該文件系統(tǒng)的類型名,如“ext3”、“vfat”、“ntfs”等。

(2)標(biāo)志fs_flags是文件系統(tǒng)的特殊要求,如是否需要塊設(shè)備等。

(3)獲取操作get_sb用于收集物理文件系統(tǒng)的管理信息并將它們填寫到超級(jí)塊結(jié)構(gòu)中,從而為物理文件系統(tǒng)創(chuàng)建一個(gè)super_block結(jié)構(gòu)的實(shí)例。

(4)清理操作kill_sb用于物理文件系統(tǒng)卸載時(shí)的善后處理。

(5)超級(jí)塊隊(duì)列fs_supers中排列著屬于該類型的所有超級(jí)塊結(jié)構(gòu)。

系統(tǒng)中已注冊(cè)的file_system_type結(jié)構(gòu)被其中的next指針串成一個(gè)單向鏈表,表頭為file_systems,如圖11.6所示。

圖11.6文件系統(tǒng)類型注冊(cè)表系統(tǒng)將要使用的每類物理文件系統(tǒng)都要在file_systems中注冊(cè)一個(gè)file_system_type結(jié)構(gòu),但不允許重復(fù)注冊(cè)。沒有注冊(cè)的文件系統(tǒng)無法安裝,因而也無法使用。如果物理文件系統(tǒng)的實(shí)現(xiàn)代碼被編譯在內(nèi)核中,注冊(cè)工作已在系統(tǒng)初始化時(shí)完成;如果物理文件系統(tǒng)的實(shí)現(xiàn)代碼未編譯在內(nèi)核中,注冊(cè)工作將在安裝(模塊插入)時(shí)進(jìn)行。11.2.2文件系統(tǒng)安裝

注冊(cè)操作僅僅向VFS聲明了一個(gè)file_system_type結(jié)構(gòu),并未建立起物理文件系統(tǒng)的管理結(jié)構(gòu),因而注冊(cè)后的物理文件系統(tǒng)還不能使用。為物理文件系統(tǒng)建立管理結(jié)構(gòu)的工作由安裝程序負(fù)責(zé)。在被安裝之后,物理文件系統(tǒng)的超級(jí)塊結(jié)構(gòu)super_block被建立,其目錄樹也被嫁接在VFS的某個(gè)目錄(稱為安裝點(diǎn))之上,形成了統(tǒng)一的全局視圖,如圖11.7所示。只有在安裝工作完成之后,物理文件系統(tǒng)才能夠使用。圖11.7文件系統(tǒng)的目錄樹嫁接在嫁接之前,每個(gè)物理文件系統(tǒng)都有自己獨(dú)立的目錄樹,多棵目錄樹放在一起形成的是目錄樹森林。將目錄樹森林嫁接在一起的方法是用各目錄樹的根去覆蓋它的安裝點(diǎn)目錄。在圖11.7中,塊設(shè)備dsk0和dsk1上分別安裝著不同的物理文件系統(tǒng),兩個(gè)物理文件系統(tǒng)的目錄樹相互獨(dú)立,D3是dsk0的一個(gè)目錄。當(dāng)把dsk1中的物理文件系統(tǒng)安裝在目錄D3上之后,兩棵目錄樹就拼接在了一起,dsk1的根目錄覆蓋了dsk0的D3目錄,或者說目錄/D1/D3變成了dsk1的文件系統(tǒng)的根,文件f6的路徑名變成了/D1/D3/f6。如果文件系統(tǒng)B被安裝在文件系統(tǒng)A的某個(gè)目錄之上,那么B是A的子文件系統(tǒng),A是B的父文件系統(tǒng)。位于最頂層的文件系統(tǒng)是所有其它文件系統(tǒng)的祖先,稱為根文件系統(tǒng)。顯然,根文件系統(tǒng)必須預(yù)先建立起來。在早期的Linux版本中,通常將來自根塊設(shè)備的文件系統(tǒng)預(yù)裝成根文件系統(tǒng),并通過dentry中的指針在子文件系統(tǒng)的根目錄與父文件系統(tǒng)的安裝點(diǎn)目錄之間建立連接關(guān)系。隨著Linux的發(fā)展,人們發(fā)現(xiàn)這種安裝方法存在許多問題,如難以更換根文件系統(tǒng)、一個(gè)文件系統(tǒng)僅能安裝一次而且僅能安裝在一個(gè)點(diǎn)上、一個(gè)安裝點(diǎn)上僅能安裝一個(gè)文件系統(tǒng)、每個(gè)進(jìn)程都可以看到整棵目錄樹等等。為解決上述問題,新版本的Linux采取了如下改進(jìn)措施:

(1)在系統(tǒng)初始化時(shí),預(yù)先建立了一個(gè)偽文件系統(tǒng)用做整個(gè)系統(tǒng)的根文件系統(tǒng),其類型為rootfs。偽根文件系統(tǒng)是一種內(nèi)存文件系統(tǒng)(Ramfs),僅有一個(gè)空的根目錄,用于安裝實(shí)際的根文件系統(tǒng)。

(2)在子文件系統(tǒng)的根目錄與父文件系統(tǒng)的安裝點(diǎn)目錄之間不再建立直接的連接關(guān)系,各文件系統(tǒng)通過安裝點(diǎn)結(jié)構(gòu)vfsmount間接地連接起來。

(3)分割文件系統(tǒng)名字空間,允許為每個(gè)名字空間建立一棵全局目錄樹,使進(jìn)程僅能看到自己名字空間中的目錄樹,而不再是全系統(tǒng)的目錄樹。

安裝點(diǎn)結(jié)構(gòu)vfsmount描述文件系統(tǒng)之間的安裝關(guān)系,每個(gè)已安裝的文件系統(tǒng)都至少有一個(gè)安裝點(diǎn)結(jié)構(gòu)。若一個(gè)文件系統(tǒng)被安裝多次,Linux會(huì)為其建立多個(gè)vfsmount結(jié)構(gòu)。在安裝點(diǎn)vfsmount中,mnt_sb指向子文件系統(tǒng)的超級(jí)塊、mnt_root指向子文件系統(tǒng)的根目錄,mnt_parent指向父文件系統(tǒng)的安裝點(diǎn)結(jié)構(gòu)、mnt_mountpoint指向位于父文件系統(tǒng)中的安裝點(diǎn)目錄,這四個(gè)域合起來表明子文件系統(tǒng)mnt_sb的根目錄mnt_root被嫁接在父文件系統(tǒng)mnt_parent的目錄mnt_mountpoint上。子文件系統(tǒng)的特殊安裝需求記錄在域mnt_flags中。一個(gè)文件系統(tǒng)上可以安裝多個(gè)子文件系統(tǒng),子文件系統(tǒng)上還可以再安裝子文件系統(tǒng),因而系統(tǒng)中的vfsmount自然地形成了一種樹形組織結(jié)構(gòu)。域mnt_child將各兄弟文件系統(tǒng)的vfsmount串成一個(gè)隊(duì)列,隊(duì)頭是父vfsmount結(jié)構(gòu)中的域mnt_mounts。從根文件系統(tǒng)的vfsmount開始,搜索vfsmount樹,可以找到已安裝的任意一個(gè)子文件系統(tǒng),但速度較慢。為了加快文件系統(tǒng)的查找速度,Linux另創(chuàng)建了一個(gè)vfsmount結(jié)構(gòu)的Hash表mount_hashtable,其Hash值由父文件系統(tǒng)的vfsmount和安裝點(diǎn)目錄的dentry算出。給出一個(gè)安裝點(diǎn)目錄,查mount_hashtable,可以找到安裝在其上的第一層子文件系統(tǒng)。安裝點(diǎn)結(jié)構(gòu)vfsmount中的域mnt_ns指向文件系統(tǒng)所屬的名字空間mnt_namespace。安裝在同一名字空間中的所有文件系統(tǒng)的vfsmount結(jié)構(gòu)被它們的mnt_list域串成一個(gè)鏈表,表頭為mnt_namespace結(jié)構(gòu)中的list。文件系統(tǒng)安裝點(diǎn)結(jié)構(gòu)的組織形式與物理文件系統(tǒng)的實(shí)際安裝方式一一對(duì)應(yīng),如圖11.8所示。

在圖11.8中,文件系統(tǒng)1安裝在文件系統(tǒng)0的A目錄上,文件系統(tǒng)2安裝在文件系統(tǒng)0的B目錄上。B目錄的內(nèi)容被文件系統(tǒng)2的根目錄覆蓋,其中的文件f2和目錄C被隱藏,在B目錄中看到的是文件系統(tǒng)2的F和G子目錄。文件系統(tǒng)3安裝在文件系統(tǒng)1的D目錄和文件系統(tǒng)2的根目錄上,覆蓋掉了D目錄和整個(gè)文件系統(tǒng)2的內(nèi)容,在D和B目錄中看到的都是文件系統(tǒng)3的H和I子目錄。文件系統(tǒng)1和2是文件系統(tǒng)0的子文件系統(tǒng),文件系統(tǒng)3是文件系統(tǒng)1和2的子文件系統(tǒng),文件系統(tǒng)1和2是兄弟文件系統(tǒng)。

圖11.8文件系統(tǒng)安裝結(jié)構(gòu)物理文件系統(tǒng)只能由超級(jí)用戶安裝,安裝時(shí)需提供多個(gè)參數(shù),如物理文件系統(tǒng)所在塊設(shè)備的特殊文件名dev_name、安裝點(diǎn)目錄的路徑名dir_name、文件系統(tǒng)類型名type、特殊的安裝要求flags等。文件系統(tǒng)的安裝過程大致如下:

(1)解析安裝點(diǎn)目錄的路徑名,獲得它的目錄項(xiàng)結(jié)構(gòu)dentry和所在文件系統(tǒng)的安裝點(diǎn)結(jié)構(gòu)vfsmount。新文件系統(tǒng)的安裝點(diǎn)目錄必須在已安裝的目錄樹上,且必須是一個(gè)目錄。如果所給的安裝點(diǎn)目錄上已安裝了其它文件系統(tǒng),那么此處獲得的vfsmount描述的應(yīng)該是在該目錄上的最新一次安裝,或者說是覆蓋在安裝點(diǎn)之上的最上一層文件系統(tǒng),dentry應(yīng)該是覆蓋在安裝點(diǎn)目錄上的最上一層文件系統(tǒng)的根目錄。如果安裝點(diǎn)目錄是圖11.8中的目錄/B,那么解析所得的安裝點(diǎn)應(yīng)該是文件系統(tǒng)3的根目錄。

(2)搜索隊(duì)列file_systems隊(duì)列,找到名為type的file_system_type結(jié)構(gòu)。如果名為type的文件系統(tǒng)類型還未注冊(cè),則請(qǐng)求模塊加載程序?qū)⒃擃愋偷奈锢砦募到y(tǒng)實(shí)現(xiàn)模塊插入到內(nèi)核中,并注冊(cè)其文件系統(tǒng)類型。

(3)申請(qǐng)一個(gè)空的安裝點(diǎn)結(jié)構(gòu)vfsmount。

(4)執(zhí)行file_system_type結(jié)構(gòu)中的get_sb操作,為新安裝的物理文件系統(tǒng)創(chuàng)建超級(jí)塊結(jié)構(gòu)super_block,并為其根目錄創(chuàng)建dentry結(jié)構(gòu)和inode結(jié)構(gòu)。

(5)為新建的vfsmount結(jié)構(gòu)建立社會(huì)關(guān)系,如圖11.9所示。圖11.9新安裝結(jié)構(gòu)的社會(huì)關(guān)系特別地,類型為rootfs的根文件系統(tǒng)是在系統(tǒng)初始化時(shí)由函數(shù)init_mount_tree()安裝的,是系統(tǒng)中的第一個(gè)文件系統(tǒng)。根文件系統(tǒng)是一種Ramfs,其管理信息都是動(dòng)態(tài)生成的。由于根文件系統(tǒng)不需要嫁接在其它文件系統(tǒng)之上,因而其安裝過程是從第(2)步開始的,其中的參數(shù)type是“rootfs”,與之對(duì)應(yīng)的file_system_type結(jié)構(gòu)中的get_sb操作是rootfs_get_sb(),完成的工作如下:

(1)創(chuàng)建一個(gè)超級(jí)塊結(jié)構(gòu),對(duì)其作如下設(shè)置:

標(biāo)志s_flags中包含MS_NOUSER,表示該文件系統(tǒng)不為用戶所見;設(shè)備號(hào)s_dev是動(dòng)態(tài)生成的,主設(shè)備號(hào)為0,域s_bdev為空;

塊尺寸s_blocksize是4096字節(jié),文件的最大尺寸s_maxbytes是文件頁緩存的最大尺寸(在32位機(jī)器上為243-1,在64位機(jī)器上為263-1);

超級(jí)塊操作集s_op是ramfs_ops。

(2)創(chuàng)建一個(gè)根inode結(jié)構(gòu),對(duì)其作如下設(shè)置:

i_ino為0;

inode操作集是ramfs_dir_inode_operations;

文件操作集是simple_dir_operations;

地址空間i_mapping中的操作集a_ops是ramfs_aops。

(3)創(chuàng)建一個(gè)根目錄項(xiàng)結(jié)構(gòu)dentry,對(duì)其作如下設(shè)置:

名稱為“/”,操作集d_op為空,d_parent指向自己;

標(biāo)志d_flags中包含DCACHE_UNHASHED,表示該目錄項(xiàng)不在Hash表dentry_hashtable中。

在根文件系統(tǒng)的安裝點(diǎn)結(jié)構(gòu)vfsmount中,mnt_parent指向自身,mnt_mountpoint指向自己的根目錄項(xiàng),mnt_child等隊(duì)列都是空的。值得一提的是,根文件系統(tǒng)的vfsmount結(jié)構(gòu)并未插入到Hash表mount_hashtable中。事實(shí)上,由于根文件系統(tǒng)是整個(gè)名字空間的根(mnt_namespace結(jié)構(gòu)中的root指向根文件系統(tǒng)的vfsmount),未嫁接在任何文件系統(tǒng)之上,也沒有查找其vfsmount結(jié)構(gòu)的必要。

在安裝點(diǎn)結(jié)構(gòu)的社會(huì)關(guān)系中,Hash表mount_hashtable的主要作用是方便vfsmount的查找,以便快速確定嫁接在某一安裝點(diǎn)上的子文件系統(tǒng)。在早期的Linux中,安裝點(diǎn)與子文件系統(tǒng)的根目錄是綁定在一起的,從安裝點(diǎn)目錄可以直接找到子文件系統(tǒng)的根目錄,如圖11.7所示。這種綁定安裝方式方便了路徑名的解析,但卻降低了安裝的靈活性,如無法將一個(gè)文件系統(tǒng)同時(shí)嫁接在多個(gè)安裝點(diǎn)上。Hash表mount_hashtable的引入解除了安裝點(diǎn)與根目錄之間的綁定關(guān)系,極大地提高了安裝的靈活性,如:

(1)為一個(gè)物理文件系統(tǒng)建立多個(gè)vfsmount結(jié)構(gòu)之后,可將其同時(shí)嫁接在多個(gè)安裝點(diǎn)上,如圖11.8中的文件系統(tǒng)3就被同時(shí)嫁接在兩個(gè)安裝點(diǎn)上。

(2)修改安裝結(jié)構(gòu)和它所處的社會(huì)關(guān)系,即可將已安裝的文件系統(tǒng)從一個(gè)安裝點(diǎn)移到另一個(gè)安裝點(diǎn)。

(3)新建一個(gè)vfsmount結(jié)構(gòu),即可將已安裝文件系統(tǒng)的某個(gè)子目錄樹嫁接到一個(gè)新的安裝點(diǎn)上,使其可在多個(gè)位置同時(shí)被訪問到。

(4)修改安裝結(jié)構(gòu)中的標(biāo)志mnt_flags即可改變文件系統(tǒng)的安裝方式(稱為重裝remount),如由只讀安裝到讀寫安裝等。

(5)能夠提供滿足特殊需求的新式安裝,如共享安裝、主從安裝等。11.2.3文件系統(tǒng)卸載

除根文件系統(tǒng)rootfs之外,其它文件系統(tǒng)都是動(dòng)態(tài)安裝的,也可以被動(dòng)態(tài)卸載。在從計(jì)算機(jī)系統(tǒng)上將可移動(dòng)介質(zhì)(如U盤)拔下之前應(yīng)該先卸載其上的文件系統(tǒng)。在卸載文件系統(tǒng)時(shí),用戶需提供文件系統(tǒng)的安裝點(diǎn)目錄和特殊的卸載需求。如果用戶提供的是塊設(shè)備特殊文件名,需先查出其安裝點(diǎn)目錄。如果一個(gè)塊設(shè)備上的文件系統(tǒng)被同時(shí)安裝在多個(gè)目錄上,用塊設(shè)備特殊文件名卸載文件系統(tǒng)會(huì)引起歧義。

正在使用的文件系統(tǒng)(如其上有未關(guān)閉的文件、有作為進(jìn)程pwd的目錄、有未卸載的子文件系統(tǒng)等)稱為忙文件系統(tǒng),不能卸載。

文件系統(tǒng)卸載操作umount大致完成如下幾件工作:

(1)解析安裝點(diǎn)目錄的路徑名,做必要的合法性檢查。

(2)如果用戶要強(qiáng)行卸載文件系統(tǒng)且超級(jí)塊操作集中提供了umount_begin操作,則執(zhí)行該操作。

(3)如果要卸載的文件系統(tǒng)是當(dāng)前進(jìn)程的根,則將其改裝成只讀文件系統(tǒng)。

(4)斷開文件系統(tǒng)的安裝點(diǎn)結(jié)構(gòu)vfsmount的社會(huì)關(guān)系,并將其釋放。(5)如果文件系統(tǒng)已無其它安裝位置,則將其從系統(tǒng)中清除,包括執(zhí)行文件系統(tǒng)類型中的kill_sb操作、釋放它的超級(jí)塊結(jié)構(gòu)等。

卸載過程中的許多實(shí)質(zhì)性、事務(wù)性的工作都在kill_sb操作中完成,如將對(duì)該文件系統(tǒng)的所有修改全部寫回到底層塊設(shè)備中(稱為文件系統(tǒng)同步)、釋放屬于該文件系統(tǒng)的所有目錄項(xiàng)和inode、執(zhí)行超級(jí)塊操作集中的put_super操作、關(guān)閉底層塊設(shè)備等。

VFS的文件系統(tǒng)管理子系統(tǒng)提供的是對(duì)全局目錄樹的粗粒度管理,如其中的安裝操作用于將新的目錄子樹拼接在全局目錄樹上,卸載操作用于從全局目錄樹中刪除不再使用的目錄子樹,所管理的單位是子樹,無法對(duì)單個(gè)節(jié)點(diǎn)(如文件、目錄等)實(shí)施管理。負(fù)責(zé)全局目錄樹細(xì)粒度管理的子系統(tǒng)稱為文件管理子系統(tǒng),其主要工作包括:11.3文件管理

(1)與物理文件系統(tǒng)合作,解析節(jié)點(diǎn)的路徑名,獲得文件或目錄的物理描述信息,從中抽取出VFS關(guān)心的元數(shù)據(jù),建立inode和dentry結(jié)構(gòu)。

(2)將最近使用的文件與目錄的路徑信息緩存起來,形成統(tǒng)一的目錄項(xiàng)緩存,以加快路徑名的解析速度,并維護(hù)緩存信息與物理文件系統(tǒng)的一致性。

(3)與物理文件系統(tǒng)合作,向用戶提供文件與目錄的創(chuàng)建、刪除、移動(dòng)等服務(wù)。

VFS通過全局目錄樹來組織、管理系統(tǒng)中的文件,不管它們來自哪個(gè)物理文件系統(tǒng)。然而作為最高一級(jí)的管理機(jī)構(gòu),VFS并不管理文件的實(shí)現(xiàn)細(xì)節(jié),如文件在塊設(shè)備中的存儲(chǔ)位置等。事實(shí)上,文件的真正管理者仍然是物理文件系統(tǒng),VFS的作用僅僅是屏蔽各類物理文件系統(tǒng)的管理差異,為用戶提供統(tǒng)一的文件管理接口。11.3.1路徑名解析

VFS實(shí)現(xiàn)文件管理的主要依據(jù)是全局目錄樹。在全局目錄樹中,任何一個(gè)節(jié)點(diǎn),不管它屬于哪個(gè)物理文件系統(tǒng),都有一個(gè)路徑名。從根目錄到任一特定節(jié)點(diǎn)的路徑稱為絕對(duì)路徑,將絕對(duì)路徑上的所有目錄名串連起來(用‘/’隔開)就構(gòu)成了節(jié)點(diǎn)的絕對(duì)路徑名。從當(dāng)前工作目錄到特定節(jié)點(diǎn)的路徑稱為相對(duì)路徑,將相對(duì)路徑上所有的目錄名串連起來就構(gòu)成了節(jié)點(diǎn)的相對(duì)路徑名。絕對(duì)路徑名和相對(duì)路徑名都可用于標(biāo)識(shí)文件和目錄。

當(dāng)要訪問一個(gè)文件或目錄時(shí),進(jìn)程可以提供絕對(duì)路徑名,也可以提供相對(duì)路徑名。對(duì)一個(gè)進(jìn)程來說,絕對(duì)路徑名是相對(duì)于其主目錄(home目錄)的路徑名,相對(duì)路徑名是相對(duì)于其當(dāng)前工作目錄的路徑名。在進(jìn)程的管理結(jié)構(gòu)task_struct中,fs域總是指向一個(gè)fs_struct結(jié)構(gòu),其中的root是進(jìn)程的主目錄,pwd是進(jìn)程的當(dāng)前工作目錄。在圖11.10中,B是進(jìn)程的主目錄,H是進(jìn)程的當(dāng)前工作目錄。雖然全局目錄樹很大,但進(jìn)程所能看到的僅有其中的一部分,即以B為根的目錄子樹。在進(jìn)程運(yùn)行過程中,它的主目錄和當(dāng)前工作目錄都可以改變,但新目錄必須在全局目錄樹中。系統(tǒng)調(diào)用chroot用于改變進(jìn)程的主目錄,chdir用于改變進(jìn)程的當(dāng)前工作目錄。

圖11.10進(jìn)程的主目錄和當(dāng)前工作目錄在訪問一個(gè)文件或目錄之前,首要的任務(wù)是找到路徑名所標(biāo)識(shí)的實(shí)體,并為其建立inode和dentry結(jié)構(gòu),這一過程稱為路徑名解析。Linux用path結(jié)構(gòu)描述實(shí)體的路徑信息,其中包含一個(gè)vfsmount和一個(gè)dentry結(jié)構(gòu),定義如下:

structpath{

structvfsmount *mnt; //實(shí)體所在文件系統(tǒng)的安裝點(diǎn)結(jié)構(gòu)

structdentry *dentry; //實(shí)體本身的目錄項(xiàng)結(jié)構(gòu)

};

Linux用path_lookup()系列的函數(shù)實(shí)現(xiàn)路徑名解析。要解析的路徑名是一個(gè)由‘/’分隔的字符串,如“/./usr/bin/../local/././bin//emacs”(等價(jià)于“/usr/local/bin/emacs”)。除最后一個(gè)子串之外,其余各子串都是路徑上的目錄名。最后一個(gè)子串可能是文件名,也可能是目錄名。路徑名中的一個(gè)子串稱為一個(gè)子路徑名。

由于進(jìn)程給出的路徑名都是相對(duì)路徑名,因而在解析之前需要先確定此次解析的參考點(diǎn)或出發(fā)點(diǎn)。一般情況下,如果路徑名的首字符是‘/’,參考點(diǎn)應(yīng)該是進(jìn)程的主目錄;如果路徑名的首字符不是‘/’,參考點(diǎn)應(yīng)該是進(jìn)程的當(dāng)前工作目錄。在新版本中,Linux還允許進(jìn)程自己指定解析的參考點(diǎn)。所謂路徑名解析實(shí)際就是從參考點(diǎn)出發(fā),依照各子路徑名的指示,沿著全局目錄樹逐步前行,直到最后一個(gè)子路徑名所指示的節(jié)點(diǎn)。所以路徑名的解析過程實(shí)際就是對(duì)各子路徑名進(jìn)行順序分析的過程。

為記錄路徑名解析的結(jié)果,需定義一個(gè)path結(jié)構(gòu),將其初值設(shè)為參考點(diǎn)目錄。此后,每解析一個(gè)子路徑名,就對(duì)path結(jié)構(gòu)做一次調(diào)整,將已解析出的中間實(shí)體記錄在其中。在解析過程中,path總是指向當(dāng)前目錄;在解析完成后,path指向解析的結(jié)果(整個(gè)路徑名所標(biāo)識(shí)的實(shí)體)。設(shè)name是下一個(gè)要解析的子路徑名,解析的過程如下:

(1)檢查當(dāng)前目錄的權(quán)限。當(dāng)前進(jìn)程必須擁有當(dāng)前目錄的執(zhí)行權(quán)限。

(2)在當(dāng)前目錄附近找名為name的dentry。name的值可能是下列三種之一:

①“.”表示當(dāng)前目錄,不需要前行,也不需要調(diào)整path結(jié)構(gòu)。②“..”表示當(dāng)前目錄的父目錄,需調(diào)整path結(jié)構(gòu),讓它指向父目錄。一般情況下,dentry結(jié)構(gòu)中的d_parent就是其父目錄,但也有特例:

●如果當(dāng)前目錄是當(dāng)前進(jìn)程的主目錄,那么其父目錄仍是自己。

●如果當(dāng)前目錄是某個(gè)子文件系統(tǒng)的根目錄,則需向上跨越安裝點(diǎn)。

●如果父目錄是一個(gè)安裝點(diǎn)目錄,則需向下跨越安裝點(diǎn)。③其余格式的子路徑名表示當(dāng)前目錄中的一個(gè)子目錄或文件。由于該子目錄或文件可能已被解析過,其dernty結(jié)構(gòu)可能已在目錄項(xiàng)緩存中,因而應(yīng)先根據(jù)name的Hash值查目錄項(xiàng)的Hash表dentry_hashtable,找父目錄為path.dentry、名字為name的dentry結(jié)構(gòu),結(jié)果有二:

●在緩存中。執(zhí)行目錄項(xiàng)操作集中的d_revalidate操作,以確保該dentry結(jié)構(gòu)的有效性,而后調(diào)整path結(jié)構(gòu)。

●不在緩存中。創(chuàng)建一個(gè)新的dentry結(jié)構(gòu),執(zhí)行當(dāng)前目錄inode操作集中的lookup操作,請(qǐng)求物理文件系統(tǒng)在當(dāng)前目錄中查找名為name的實(shí)體、讀入其管理信息并創(chuàng)建inode結(jié)構(gòu),而后調(diào)整path結(jié)構(gòu)。

(3)跨越安裝點(diǎn)。如果找到的dentry是一個(gè)安裝點(diǎn)目錄,則需向下跨越安裝點(diǎn)。

(4)跨越符號(hào)鏈接。如果找到的dentry是一個(gè)符號(hào)鏈接,則需跨越該符號(hào)鏈接。

(5)讓name指向下一個(gè)子路徑名。如果name不空,則從(1)開始解析下一段子路徑名。如果name為空,path中記錄的就是解析結(jié)果。

判斷目錄是否為安裝點(diǎn)的依據(jù)是其dentry結(jié)構(gòu)中的d_mounted標(biāo)志。若d_mounted的值大于0,說明該目錄上安裝有文件系統(tǒng),其內(nèi)容已被覆蓋,不能作為路徑中的一個(gè)節(jié)點(diǎn),也不應(yīng)該在其上停留,而應(yīng)向下跨越該安裝點(diǎn)目錄,走到安裝在其上的文件系統(tǒng)的根目錄上。根據(jù)已獲得的安裝點(diǎn)目錄的path結(jié)構(gòu),查Hash表mount_hashtable,可以找到安裝在其上的文件系統(tǒng)及其根目錄,path應(yīng)指向該根目錄。由于新找到的根目錄仍然可能是安裝點(diǎn),因而這種向下跨越可能需要多次,如圖11.11所示。

圖11.11安裝點(diǎn)跨越在圖11.11中,在目錄B上安裝著文件系統(tǒng)2,在目錄C上安裝著文件系統(tǒng)3。當(dāng)解析A目錄下的B子目錄時(shí),解析程序發(fā)現(xiàn)B是安裝點(diǎn),因而需查mount_hashtable獲得安裝在B上的子文件系統(tǒng)的根C。由于C也是安裝點(diǎn),因而需再查mount_hashtable獲得安裝在C上的子文件系統(tǒng)的根D。D才是對(duì)A目錄下B子目錄的解析結(jié)果。

當(dāng)需要查找當(dāng)前目錄的父目錄時(shí),有可能需要向上跨越安裝點(diǎn),原因是當(dāng)前目錄可能為某個(gè)子文件系統(tǒng)的根目錄。由于根目錄項(xiàng)的d_parent總是指向自身,無法通過它找到其父目錄,只能向上跨越安裝點(diǎn)。若path是已解析出的當(dāng)前目錄,且該目錄是某子文件系統(tǒng)的根,則path.mnt->mnt_parent指向父安裝點(diǎn)結(jié)構(gòu),path.mnt->mnt_mountpoint指向安裝點(diǎn)目錄。由于新找到的安裝點(diǎn)目錄仍然可能是某子文件系統(tǒng)的根目錄,因而這種向上跨越有可能需要多次。向上跨越之后,path應(yīng)指向最底層安裝點(diǎn)目錄的父目錄。在圖11.11中,目錄D的父目錄是A,它跨越了C和B。

符號(hào)鏈接是一種特殊文件,其內(nèi)容是另一個(gè)文件或目錄的路徑名,因而更像是一個(gè)指針。如果要解析的路徑名中包含符號(hào)鏈接,則整個(gè)路徑名的解析過程應(yīng)被分成三步:

(1)解析符號(hào)鏈接之前的路徑名,讓path指向符號(hào)鏈接自身。

(2)跨越符號(hào)鏈接,過程是先讀出符號(hào)鏈接文件的內(nèi)容,再從頭開始解析其中的路徑名,讓path指向目標(biāo)實(shí)體,如圖11.12所示。

(3)解析符號(hào)鏈接之后的路徑名,讓path指向最終的實(shí)體。

圖11.12符號(hào)鏈接跨越符號(hào)鏈接實(shí)體的inode操作集中肯定包含一個(gè)follow_link操作,用于讀出其中的路徑名。如果符號(hào)鏈接的目標(biāo)實(shí)體仍然是符號(hào)鏈接,則需要再次跨越。因此對(duì)符號(hào)鏈接的跨越是一個(gè)遞歸的過程,Linux限制了遞歸的深度,如8層。

在跨越符號(hào)鏈接時(shí),path結(jié)構(gòu)被重新初始化,其中的前期解析結(jié)果被丟棄,因而符號(hào)鏈接只能向前跨越,不能回溯。如路徑名“/B/C/..”的解析結(jié)果是目錄“F”而不是“B”。

若路徑名的最終解析結(jié)果是符號(hào)鏈接,則可跨越也可不跨越,由參數(shù)聲明。如果在目錄項(xiàng)緩存中找不到需要的dentry結(jié)構(gòu),說明該目錄項(xiàng)在近期內(nèi)未被訪問過,因而需要為其新建dentry和inode結(jié)構(gòu)。當(dāng)然,新建的dentry結(jié)構(gòu)要被插入到目錄項(xiàng)緩存中,包括Hash表dentry_hashtable和目錄項(xiàng)樹,如圖11.5所示;新建的inode結(jié)構(gòu)要被插入到inode緩存(Hash表inode_hashtable)中。新目錄項(xiàng)及其inode結(jié)構(gòu)中的信息來源于物理文件系統(tǒng)。通過父目錄的inode操作集中的lookup操作可請(qǐng)求物理文件系統(tǒng)從該父目錄中獲取指定名稱的物理實(shí)體,并獲取其管理信息。如果要解析的實(shí)體也不在物理文件系統(tǒng)之中,說明該實(shí)體還未被創(chuàng)建,其管理結(jié)構(gòu)還不存在,那么新目錄項(xiàng)的d_inode域應(yīng)該為空,但新目錄項(xiàng)仍要被插入到目錄項(xiàng)緩存中。由路徑名的解析過程還可以看出,進(jìn)程無法向上跨越自己的主目錄。因而,只要給不同的進(jìn)程設(shè)定不同的主目錄,就可以使它們僅看到自己的目錄子樹,從而區(qū)分它們的文件系統(tǒng)名字空間,避免進(jìn)程之間的相互干擾。

當(dāng)不再需要某個(gè)dentry或inode結(jié)構(gòu)時(shí),可通過函數(shù)dput()或iput()將其釋放。釋放操作遞減dentry或inde結(jié)構(gòu)上的引用計(jì)數(shù),只有當(dāng)引用計(jì)數(shù)被減到0時(shí)才真正將它們銷毀。當(dāng)然,在銷毀之前需要將結(jié)構(gòu)中的內(nèi)容同步到物理文件系統(tǒng)中。值得注意的是,銷毀dentry或inode結(jié)構(gòu)僅僅意味著內(nèi)核不再緩存與之對(duì)應(yīng)的實(shí)體信息,并非銷毀物理實(shí)體本身。11.3.2文件管理操作

以全局目錄樹和路徑名解析為基礎(chǔ),VFS提供了多種文件管理服務(wù),如子目錄的創(chuàng)建與刪除、硬鏈接的創(chuàng)建與刪除、文件與符號(hào)鏈接的創(chuàng)建、實(shí)體的移動(dòng)、實(shí)體屬性的設(shè)置等。Linux用戶可以利用這些服務(wù)來組織、管理自己的VFS實(shí)體,包括目錄、普通文件、設(shè)備特殊文件、符號(hào)鏈接、命名管道等。VFS按大致相同的方式實(shí)現(xiàn)文件管理服務(wù),其操作過程大致分為三步,一是解析路徑名以獲得實(shí)體所在父目錄及實(shí)體本身的dentry和inode結(jié)構(gòu),并進(jìn)行權(quán)限檢查;二是執(zhí)行父目錄的inode操作集中的相應(yīng)操作,請(qǐng)求物理文件系統(tǒng)完成實(shí)體的物理管理操作;三是調(diào)整相關(guān)實(shí)體的dentry結(jié)構(gòu),以便在目錄項(xiàng)緩存和全局目錄樹中反映實(shí)體的變化。文件管理操作的流程如圖11.13所示。

c

圖11.13文件管理操作流程

(1)硬鏈接創(chuàng)建link或linkat。創(chuàng)建一個(gè)硬鏈接就是為實(shí)體起一個(gè)別名??梢詾橐粋€(gè)實(shí)體創(chuàng)建多個(gè)硬鏈接,它們可以位于不同的目錄下,但必須在同一個(gè)物理文件系統(tǒng)中。硬鏈接創(chuàng)建操作需要兩個(gè)路徑名,其中老路徑名標(biāo)識(shí)目標(biāo)實(shí)體,必須存在且不能是目錄,新路徑名是給實(shí)體起的新名稱。VFS為新鏈接創(chuàng)建一個(gè)新的目錄項(xiàng),將其插入目錄項(xiàng)緩存中,并執(zhí)行其父目錄的inode操作集中的link操作,以請(qǐng)求物理文件系統(tǒng)完成硬鏈接的物理創(chuàng)建。如果老路徑名所標(biāo)識(shí)的是一個(gè)符號(hào)鏈接,那么新硬鏈接所指向的是符號(hào)鏈接本身而不是符號(hào)鏈接的目標(biāo)實(shí)體。

(2)硬鏈接刪除unlink或unlinkat。刪除一個(gè)硬鏈接就是刪除實(shí)體的一個(gè)路徑名。工作有二,一是釋放實(shí)體的dentry結(jié)構(gòu);二是執(zhí)行父目錄的inode操作集中的unlink操作,請(qǐng)求物理文件系統(tǒng)刪除目錄項(xiàng),并遞減實(shí)體的硬鏈接計(jì)數(shù)i_nlink。如果實(shí)體的i_nlink已被減到0,說明實(shí)體的最后一個(gè)訪問路徑已被刪除,應(yīng)釋放它的inode結(jié)構(gòu)。如果實(shí)體的inode結(jié)構(gòu)已沒有用戶,應(yīng)執(zhí)行超級(jí)塊操作集中的delete_inode操作,請(qǐng)求物理文件系統(tǒng)釋放它所占用的外存空間和管理結(jié)構(gòu),以完成實(shí)體的物理刪除。如果路徑名所標(biāo)識(shí)的是一個(gè)符號(hào)鏈接,該操作僅刪除符號(hào)鏈接本身而不影響鏈接的目標(biāo)實(shí)體。

(3)子目錄創(chuàng)建mkdir或mkdirat。Linux用戶可以創(chuàng)建新的子目錄來更好地組織自己的文件。創(chuàng)建子目錄操作需要兩個(gè)參數(shù),一是新子目錄的路徑名,二是新子目錄的訪問權(quán)限。真正的創(chuàng)建操作由父目錄的inode操作集中的mkdir完成,該操作請(qǐng)求物理文件系統(tǒng)完成子目錄的物理創(chuàng)建。新子目錄的dentry應(yīng)出現(xiàn)在目錄樹中。

(4)子目錄刪除rmdir。要?jiǎng)h除的子目錄應(yīng)該是空的,而且不能是安裝點(diǎn),也不能是進(jìn)程的主目錄或當(dāng)前工作目錄。父目錄的inode操作集中的rmdir操作會(huì)請(qǐng)求物理文件系統(tǒng)完成子目錄的物理刪除。被刪除之后,子目錄的dentry和inode結(jié)構(gòu)也要釋放。

(5)文件創(chuàng)建creat。要?jiǎng)?chuàng)建一個(gè)新文件至少需要兩個(gè)參數(shù),一是文件的路徑名,二是文件的訪問權(quán)限。父目錄的inode操作集中的create操作會(huì)請(qǐng)求物理文件系統(tǒng)完成新文件的物理創(chuàng)建。新文件的dentry應(yīng)出現(xiàn)在目錄樹中。

(6)特殊文件創(chuàng)建mknod或mknodat。特殊文件包括字符設(shè)備特殊文件、塊設(shè)備特殊文件、命名管道、Socket等,其作用是借助文件的描述與組織方式來管理這些輸入/輸出實(shí)體,并非為了在其中存儲(chǔ)數(shù)據(jù)。在創(chuàng)建設(shè)備特殊文件時(shí)需要提供路徑名、訪問權(quán)限和設(shè)備號(hào)。父目錄的inode操作集中的mknod操作會(huì)請(qǐng)求物理文件系統(tǒng)完成新文件的物理創(chuàng)建。新文件的dentry應(yīng)出現(xiàn)在目錄樹。

(7)符號(hào)鏈接創(chuàng)建symlink或symlinkat。符號(hào)鏈接是一種特殊文件,其正文中保存的不是數(shù)據(jù)而是全局目錄樹中另一個(gè)實(shí)體的路徑名。符號(hào)鏈接所指實(shí)體可以不存在(稱為虛懸鏈接)。創(chuàng)建符號(hào)鏈接就是創(chuàng)建一個(gè)符號(hào)鏈接類型的新文件。父目錄的inode操作集中的symlink操作會(huì)請(qǐng)求物理文件系統(tǒng)完成符號(hào)鏈接文件的物理創(chuàng)建。

符號(hào)鏈接的內(nèi)容可以通過readlink操作讀出,符號(hào)鏈接的刪除由unlink操作完成。

(8)實(shí)體移動(dòng)rename或renameat。移動(dòng)實(shí)體就是將文件系統(tǒng)中的某個(gè)指定實(shí)體從當(dāng)前位置移動(dòng)到新的位置,或者說改變實(shí)體的路徑名。實(shí)體的新老位置必須在同一個(gè)物理文件系統(tǒng)中。如果老路徑名是目錄,那么新路徑名要么不存在,要么必須是空目錄。如果新路徑名已存在,它將被覆蓋。如果老路徑名是符號(hào)鏈接,那么移動(dòng)的是符號(hào)鏈接本身而不是它所指的實(shí)體。移動(dòng)操作僅修改實(shí)體的目錄項(xiàng),不改變實(shí)體本身。移動(dòng)工作分三步進(jìn)行,一是執(zhí)行老路徑中父目錄的inode操作集中的rename操作,請(qǐng)求物理文件系統(tǒng)刪除老目錄項(xiàng)、添加新目錄項(xiàng),完成實(shí)體的物理移動(dòng);二是更換老目錄項(xiàng)的名稱,并按新名稱和新位置將其重新插入到目錄項(xiàng)緩存和目錄項(xiàng)樹中;三是釋放新目錄項(xiàng)(可能會(huì)將其刪除)。

(9)實(shí)體屬性設(shè)置。文件系統(tǒng)實(shí)體的屬性包括屬主(uid、gid)、訪問權(quán)限(讀、寫、執(zhí)行)等。當(dāng)實(shí)體被創(chuàng)建時(shí),其uid被設(shè)為進(jìn)程的fsuid,gid被設(shè)為進(jìn)程的fsgid或父目錄的gid,訪問權(quán)限由創(chuàng)建者提供。在實(shí)體被創(chuàng)建出來之后,其屬性還可以改變。Linux提供了多個(gè)系統(tǒng)調(diào)用用于設(shè)置實(shí)體的屬性,其中chown類操作用于設(shè)置實(shí)體的屬主,chmod類操作用于設(shè)置實(shí)體的訪問權(quán)限。當(dāng)然,設(shè)置屬性操作也需要一定的權(quán)限,通常只能由屬主或超級(jí)用戶執(zhí)行。如果父目錄的inode操作集中提供了setattr操作,則由該操作完成屬性的物理設(shè)置;否則,Linux僅修改實(shí)體inode結(jié)構(gòu)中的屬性并將其標(biāo)記為臟的,實(shí)體物理屬性的改變要等到inode被同步時(shí)才能完成。

管理文件的目的是為了更方便、更高效地使用文件。從用戶的角度看,文件的內(nèi)容千差萬別,其使用方式也各種各樣。但從VFS的角度看,對(duì)文件的使用方式只要兩種,即向文件中寫數(shù)據(jù)和從文件中讀數(shù)據(jù)。因此,VFS的核心任務(wù)是提供文件I/O服務(wù),以便更加高效地讀寫文件中的數(shù)據(jù)??梢哉f,VFS的其它子系統(tǒng),如文件系統(tǒng)管理、文件管理等,都是為文件I/O操作服務(wù)的。11.4文件I/O操作要讀寫文件中的數(shù)據(jù)至少需要知道文件的路徑名、數(shù)據(jù)在文件中的開始位置、數(shù)據(jù)在內(nèi)存中的開始位置、數(shù)據(jù)長度等,所以每次I/O操作都需要路徑名解析和權(quán)限檢查。由于經(jīng)常需要反復(fù)讀寫同一個(gè)文件,因而重復(fù)的路徑名解析和權(quán)限檢查會(huì)嚴(yán)重影響文件I/O操作的性能。為了減少路徑名解析的次數(shù),VFS在讀寫操作之外又提供了文件打開和關(guān)閉操作,由打開操作專門負(fù)責(zé)文件路徑名的解析和權(quán)限檢查。11.4.1文件描述符表

打開與關(guān)閉操作的核心工作是維護(hù)進(jìn)程的文件描述符表。進(jìn)程對(duì)文件的所有讀寫操作都必須經(jīng)過其文件描述符表。

有了打開和關(guān)閉操作之后,文件I/O操作一般分三步完成,即打開文件、反復(fù)讀寫文件、關(guān)閉文件。在對(duì)文件進(jìn)行讀寫操作之前,進(jìn)程通過打開操作聲明自己要操作的文件(路徑名)及要操作的方式。VFS在收到進(jìn)程的打開請(qǐng)求之后,解析文件的路徑名,找到文件的inode結(jié)構(gòu),對(duì)其進(jìn)行權(quán)限檢查。一旦通過權(quán)限檢查,VFS就為進(jìn)程創(chuàng)建一個(gè)file結(jié)構(gòu),在其中記錄此次路徑名解析的結(jié)果,并將其插入到進(jìn)程的文件描述符表中。file結(jié)構(gòu)在文件描述符表中的索引稱為文件描述符(filedescriptor)。此后,當(dāng)需要讀寫已打開的文件時(shí),進(jìn)程只需在讀寫操作中提供文件的描述符即可,不需要再提供文件的路徑名。VFS根據(jù)文件描述符查進(jìn)程的文件描述符表,即可找到與之對(duì)應(yīng)的file結(jié)構(gòu),利用file結(jié)構(gòu)即可完成進(jìn)程請(qǐng)求的所有文件I/O操作。在完成文件I/O操作之后,進(jìn)程再通過關(guān)閉操作釋放與之對(duì)應(yīng)的file結(jié)構(gòu)。因此,增加了打開與關(guān)閉操作之后,不管進(jìn)程對(duì)文件進(jìn)行多少次讀寫操作,系統(tǒng)僅需解析一次路徑名。由于文件描述符表的存在,繁瑣的路徑名解析工作變成了簡(jiǎn)單的查表工作,極大地提高了文件I/O操作的性能。

由于文件讀寫操作都是由進(jìn)程發(fā)起的,而不同的進(jìn)程可能以不同的方式讀寫不同的文件,因而Linux為每個(gè)進(jìn)程都維護(hù)了一張文件描述符表。在進(jìn)程的task_struct結(jié)構(gòu)中,有一個(gè)指向files_struct結(jié)構(gòu)的指針(見7.1節(jié)),其中的主要內(nèi)容是一個(gè)file結(jié)構(gòu)的指針數(shù)組,也就是進(jìn)程的文件描述符表。換句話說,進(jìn)程的文件描述符表實(shí)際就是一個(gè)file結(jié)構(gòu)的指針數(shù)組,一個(gè)文件描述符就代表著一個(gè)file結(jié)構(gòu)。一個(gè)file結(jié)構(gòu)描述一個(gè)進(jìn)程對(duì)一個(gè)文件的一種I/O操作方式,稱為打開文件對(duì)象。顯然,file結(jié)構(gòu)是VFS用于實(shí)現(xiàn)文件I/O操作的核心。在Linux的發(fā)展過程中,file結(jié)構(gòu)的定義發(fā)生了一些變化,但其主要內(nèi)容被保留了下來,大致有以下幾個(gè):

(1)文件路徑f_path。VFS用結(jié)構(gòu)path描述文件的路徑,其中的域dentry指向文件的目錄項(xiàng),目錄項(xiàng)中的d_inode指向文件的inode。因此,通過f_path可找到目標(biāo)文件的所有描述信息,或者說f_path就是文件路徑名的解析結(jié)果。

(2)讀寫頭位置f_pos。文件的讀寫方式有兩種,一是順序,二是隨機(jī)。缺省情況下,Linux按順序方式讀寫文件,下一次讀寫操作的開始位置記錄在f_pos中。每一次成功的I/O操作之后,f_pos的值都會(huì)被調(diào)整。由于f_pos的存在,用戶省掉了位置記錄工作,并可在讀寫操作中少提供一個(gè)位置參數(shù)。

(3)文件操作模式f_mode。記錄進(jìn)程在打開時(shí)申請(qǐng)并經(jīng)VFS批準(zhǔn)的文件操作方式,如只讀、只寫、既讀又寫等。進(jìn)程對(duì)文件的I/O操作方式必須遵循該模式的約定。

(4)文件操作集f_op。文件操作集是一個(gè)file_operations結(jié)構(gòu)(見11.1.3),其中記錄著各種文件操作方法,如open、release、llseek、read、write、mmap等。文件被打開之后,對(duì)它的所有操作都由該操作集中的對(duì)應(yīng)方法實(shí)現(xiàn)。

(5)地址空間f_mapping。地址空間由結(jié)構(gòu)address_space描述,實(shí)際是一個(gè)文件頁的緩存,用于暫存來自文件或即將寫入文件的數(shù)據(jù)頁,見11.5。

(6)引用計(jì)數(shù)f_count。引用計(jì)數(shù)用于記錄file結(jié)構(gòu)的當(dāng)前用戶數(shù)。當(dāng)f_count為0時(shí),file結(jié)構(gòu)應(yīng)該被釋放或回收。

進(jìn)程每打開一個(gè)文件,VFS都會(huì)為其創(chuàng)建一個(gè)file結(jié)構(gòu)。同一進(jìn)程多次打開同一個(gè)文件會(huì)創(chuàng)建多個(gè)file結(jié)構(gòu),不同進(jìn)程打開同一個(gè)文件也會(huì)創(chuàng)建不同的file結(jié)構(gòu)。為一個(gè)進(jìn)程創(chuàng)建的所有file結(jié)構(gòu)都記錄在它的files_struct結(jié)構(gòu)中。在早期的版本中,Linux在files_struct結(jié)構(gòu)中為每個(gè)進(jìn)程預(yù)建了一個(gè)一頁大小的文件描述符表,可在其中記錄1024個(gè)file結(jié)構(gòu)。通常情況下,進(jìn)程的文件描述符表都會(huì)有一些浪費(fèi),但當(dāng)進(jìn)程同時(shí)打開太多文件時(shí),其描述符表又不夠用。新版本的Linux改進(jìn)了文件描述符表的管理方式,專門定義了一個(gè)fdtable結(jié)構(gòu)來描述進(jìn)程的描述符表。缺省情況下,文件描述符表的大小為32或64。在運(yùn)行過程中,VFS會(huì)根據(jù)需要為進(jìn)程更換fdtable結(jié)構(gòu),從而調(diào)整其文件描述符表的大小。結(jié)構(gòu)fdtable和files_struct的定義如下:

structfdtable{

unsignedint max_fds; //文件描述符表的大小

structfile **fd; //文件描述符表

fd_set *close_on_exec; //需要在加載時(shí)關(guān)閉的文件描述符

fd_set *open_fds; //各文件描述符的狀態(tài)

structrcu_head rcu; //RCU回調(diào)節(jié)點(diǎn)

structfdtable *next;

};

structfiles_struct{

atomic_t count; //引用計(jì)數(shù)

structfdtable *fdt; //當(dāng)前使用的fdtable

structfdtable fdtab; //缺省的fdtable

spinlock_t file_lock; //保護(hù)鎖

int next_fd; //下一個(gè)可用的文件描述符

structembedded_fd_set close_on_exec_init; //缺省的close_on_exec

structembedded_fd_set open_fds_init; //缺省的open_fds

structfile *fd_array[NR_OPEN_DEFAULT];

};

在files_struct中,fd_array是缺省的文件描述符表,大小為NR_OPEN_DEFAULT(一個(gè)長整數(shù)中的位數(shù),如32或64);類型embedded_fd_set和fd_set都是位圖,其中的一位描述一個(gè)文件描述符的使用情況,0表示空閑。

只有第0號(hào)進(jìn)程(即init_struct)的files_struct結(jié)構(gòu)是靜態(tài)建立的,其余各進(jìn)程的files_struct結(jié)構(gòu)都是從創(chuàng)建者進(jìn)程中復(fù)制的。第0號(hào)進(jìn)程的files_struct結(jié)構(gòu)定義如下:

structfiles_struct init_files={

.count =ATOMIC_INIT(1),

.fdt =&init_files.fdtab,

.fdtab ={

.max_fds =NR_OPEN_DEFAULT,

.fd =&init_files.fd_array[0],

.close_on_exec =(fd_set*)&init_files.close_on_exec_init,

.open_fds =(fd_set*)&init_files.open_fds_init,

.rcu =RCU_HEAD_INIT,

},

.file_lock =_

_SPIN_LOCK_UNLOCKED(init_task.file_lock),

};

由此可見,第0號(hào)進(jìn)程使用的是缺省的fdtable結(jié)構(gòu)、缺省的文件描述符表fd_array和缺省的位圖close_on_exec_init與open_fds_init。在創(chuàng)建之初,新進(jìn)程要么與創(chuàng)建者進(jìn)程共用同一個(gè)files_struct結(jié)構(gòu),要么從創(chuàng)建者進(jìn)程中復(fù)制一個(gè)files_struct結(jié)構(gòu)。值得注意的是,所復(fù)制的內(nèi)容不包括file結(jié)構(gòu)。因而在復(fù)制之后,創(chuàng)建者進(jìn)程的各file結(jié)構(gòu)會(huì)同時(shí)出現(xiàn)在新老進(jìn)程的文件描述符表中,兩個(gè)進(jìn)程用同一個(gè)描述符所訪問的file結(jié)構(gòu)是相同的,它們對(duì)文件的操作會(huì)相互影響(如讀寫頭的位置等)。復(fù)制完成之后,不管是創(chuàng)建者進(jìn)程還是被創(chuàng)建者進(jìn)程,新打開的文件都會(huì)使用獨(dú)立的file結(jié)構(gòu),不會(huì)自動(dòng)與其它進(jìn)程共用。缺省情況下,進(jìn)程在加載新程序時(shí)會(huì)保留已打開的文件,如標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯(cuò)誤等,僅有一些特別聲明的文件(在位圖close_on_exec中)會(huì)在加載時(shí)關(guān)閉。

在進(jìn)程運(yùn)行過程中,若它同時(shí)打開的文件數(shù)超過了其文件描述符表的容量,VFS會(huì)采用RCU方式(見9.4節(jié))自動(dòng)為其更換新的、更大的文件描述符表,過程如下:

(1)為進(jìn)程創(chuàng)建新的fdtable結(jié)構(gòu),同時(shí)為其創(chuàng)建新的描述符表fd及新的位圖open_fds和close_on_exec。新描述符表應(yīng)比老表大一倍,新位圖的位數(shù)應(yīng)與新表的長度一致。新的fd、close_on_exec和open_fds的內(nèi)容是從老表中復(fù)制的。

(2)讓進(jìn)程files_struct結(jié)構(gòu)中的fdt指向新的fdtable結(jié)構(gòu),使其成為進(jìn)程新的當(dāng)前描述符表;

(3)如果老的fdtable結(jié)構(gòu)不是缺省的fdtab,則通過call_rcu()向RCU注冊(cè)一個(gè)回調(diào)函數(shù),讓它在寬限期結(jié)束時(shí)釋放老的fdtable結(jié)構(gòu)。圖11.14是進(jìn)程文件描述符表的一個(gè)示意圖,其中的虛線為缺省部分,實(shí)線為當(dāng)前使用部分。在為進(jìn)程更換文件描述符表之后,定義在files_struct結(jié)構(gòu)中的缺省描述符表fdtab以及fd_array、close_on_exec_init、open_fds_init等都會(huì)被擱置不用,是一種小的浪費(fèi)。當(dāng)然可以將缺省的文件描述符表定義在files_struct結(jié)構(gòu)之外,從而節(jié)省這部分內(nèi)存空間。然

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論