linux文件系統(tǒng)之總結(jié)
由于前階段一直在搞文件系統(tǒng),所以我是先讀該書的文件系統(tǒng)部分?;緦⒌?font face="AR PL UMing CN, serif">5章讀了一遍。下面做一些總結(jié)。
1、概述
對(duì)于普通文件,文件系統(tǒng)層最終要通過(guò)磁盤或其他存儲(chǔ)設(shè)備的驅(qū)動(dòng)程序從存儲(chǔ)介質(zhì)上讀或?qū)?。從磁盤文件的角度來(lái)看,對(duì)存儲(chǔ)介質(zhì)的訪問(wèn)可以涉及到四種不同的目標(biāo)那就是:
(1)、文件中的數(shù)據(jù),包括目錄的內(nèi)容,即目錄項(xiàng)數(shù)據(jù)結(jié)構(gòu);
(2)、文件的組織與管理信息,即索引節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu);
(3)、磁盤的超級(jí)塊。如果物理的磁盤被劃分成若干分區(qū),那就包括每個(gè)‘邏輯磁盤’的超級(jí)塊。
(4)、引導(dǎo)塊。
涉及的有關(guān)數(shù)據(jù)結(jié)構(gòu)有:
(1)、文件操作跳轉(zhuǎn)表,即file_operations數(shù)據(jù)結(jié)構(gòu):file結(jié)構(gòu)中的指針f_op指向具體的file_operations結(jié)構(gòu),如ext2就有這樣的數(shù)據(jù)結(jié)構(gòu),分別用于普通文件和目錄文件。
(2)、目錄項(xiàng)操作跳轉(zhuǎn)表,即dentry_operations數(shù)據(jù)結(jié)構(gòu):dentry結(jié)構(gòu)中的指針d_op指向具體的dentry_operations數(shù)據(jù)結(jié)構(gòu),這是內(nèi)核中hash()、compare()等內(nèi)部操作的跳轉(zhuǎn)表。
(3)、索引節(jié)點(diǎn)跳轉(zhuǎn)表,即inode_operations數(shù)據(jù)結(jié)構(gòu):inode結(jié)構(gòu)中的指針i_op指向具體的inode_operations數(shù)據(jù)結(jié)構(gòu),這是mkdir()、mknod()等文件操作以及lockup()、permission()等內(nèi)部函數(shù)的跳轉(zhuǎn)表。同樣,一種文件系統(tǒng)也并不只限于一個(gè)file_operations結(jié)構(gòu)。
(4)、超級(jí)快操作跳轉(zhuǎn)表,即super_operations數(shù)據(jù)結(jié)構(gòu):super_block結(jié)構(gòu)中的指針s_op指向具體的super_operations數(shù)據(jù)結(jié)構(gòu),這是read_inode()、write_node()、delete_inode()等內(nèi)部操作跳轉(zhuǎn)表。
(5)、超級(jí)快本身也因?yàn)槲募到y(tǒng)而異。
雖然每個(gè)文件都有目錄項(xiàng)和索引節(jié)點(diǎn)在磁盤上,但是只有在需要時(shí)才在內(nèi)存中為之建立起相應(yīng)的denty和inode數(shù)據(jù)結(jié)構(gòu)。
2、從路徑名到目標(biāo)節(jié)點(diǎn)
主要涉及兩個(gè)函數(shù),即path_init()和path_walk()以及它們下面的一些底層函數(shù)。二者合在一起就可以根據(jù)給定的文件路徑名在內(nèi)存中找到或建立代表著目標(biāo)文件或目錄的dentry結(jié)構(gòu)和inode結(jié)構(gòu)。
path_init(),內(nèi)部涉及到替換根目錄的一些判斷,成功返回后nameidata結(jié)構(gòu)中的指針dentry指向路徑搜索的起點(diǎn),接著就是通過(guò)path_walk()順著路徑名的指引進(jìn)行搜索了。其中path_walk涉及到了,如路徑中包含“.””..”和一些link文件的判斷,根據(jù)其中的標(biāo)記進(jìn)行尋找等。
結(jié)構(gòu)ext2_inode_info中的i_data[]是一塊很重要的數(shù)據(jù)。對(duì)于有存儲(chǔ)內(nèi)容的文件(普通文件和目錄文件),這里存放著一些指針,直接或間接地(takecare)指向磁盤上存儲(chǔ)著該文件的所有記錄塊(在文件的讀寫章節(jié)中將會(huì)詳細(xì)介紹)。所謂索引節(jié)點(diǎn)即因此而得名。至于代表著符合連接的節(jié)點(diǎn),則并沒(méi)有文件內(nèi)容(數(shù)據(jù)),所以正好用這塊空間來(lái)存儲(chǔ)連接目標(biāo)的路徑名。
在找到了或建立了所需的inode結(jié)構(gòu)后,就返回到ext2_lookup,在那里還要通過(guò)d_add()將inode結(jié)構(gòu)與dentry結(jié)構(gòu)掛上鉤,并將dentry結(jié)構(gòu)掛入雜湊表的某個(gè)隊(duì)列。兩個(gè)數(shù)據(jù)結(jié)構(gòu)之間的聯(lián)系是雙向的。一方面是dentry結(jié)構(gòu)的指針d_inode指向inode結(jié)構(gòu),這是一對(duì)一的關(guān)系,因?yàn)橐粋€(gè)目錄項(xiàng)只代表著一個(gè)文件。可是,反過(guò)來(lái)就不一樣了,同一個(gè)文件可以有多個(gè)不同的文件名或路徑(link),所以從inode結(jié)構(gòu)到dentry結(jié)構(gòu)的方向可以是一對(duì)多的關(guān)系。因此,inode結(jié)構(gòu)中的i_dentry是個(gè)隊(duì)列,dentry結(jié)構(gòu)通過(guò)其隊(duì)列頭部d_alias掛入相應(yīng)inode結(jié)構(gòu)的隊(duì)列中。
從path_walk()返回時(shí),函數(shù)值為0表示搜索成功,此時(shí)nameidata結(jié)構(gòu)中的指針dentry指向目標(biāo)節(jié)點(diǎn)的dentry結(jié)構(gòu),指針mnt指向目標(biāo)節(jié)點(diǎn)所在設(shè)備的安裝結(jié)構(gòu)。同時(shí)這個(gè)結(jié)構(gòu)的last_type表示最后一個(gè)節(jié)點(diǎn)類型,節(jié)點(diǎn)名則在qstr結(jié)構(gòu)last中。如果失敗的話,則函數(shù)值為一負(fù)的出錯(cuò)代碼,而nameidata結(jié)構(gòu)中則提供失敗的節(jié)點(diǎn)名等信息。
根據(jù)給定路徑名找到目標(biāo)節(jié)點(diǎn)的dentry結(jié)構(gòu)(以及inode)的過(guò)程,涉及與文件系統(tǒng)有關(guān)的幾乎所有數(shù)據(jù)結(jié)構(gòu)以及這些數(shù)據(jù)結(jié)構(gòu)間的關(guān)系,搞懂了這個(gè)過(guò)程就對(duì)文件系統(tǒng)有了基本的理解。
3、訪問(wèn)權(quán)限與文件的安全性
內(nèi)核在執(zhí)行用戶進(jìn)程訪問(wèn)文件的請(qǐng)求時(shí)要對(duì)比進(jìn)程(和用戶)的uid、gid以及文件的訪問(wèn)模式,以決定該進(jìn)程是否對(duì)此文件具有所要求的訪問(wèn)權(quán)限。
除訪問(wèn)權(quán)限外,每個(gè)文件可以有另外的一些屬性,“不可更改”(IS_IMMUTABLE)即是其中之一,這些屬性也像訪問(wèn)權(quán)限一樣以標(biāo)記位的形式存儲(chǔ)在文件的索引節(jié)點(diǎn)中,但是不像訪問(wèn)權(quán)限那樣區(qū)分文件主、文件主的同組用戶以及公眾,而是另成體系,并且凌駕于訪問(wèn)權(quán)限之上。而且一旦設(shè)置了這些屬性,即使是特權(quán)用戶也不能在系統(tǒng)還在正常的多用戶環(huán)境下運(yùn)行時(shí)將這些屬性去除,這樣就算黑客偷到了特權(quán)用戶的口令,對(duì)這些文件也就無(wú)能為力了。I_IMMUTABLE要有另外一種授權(quán)(CAP_LINUX_IMMUTABLE)的進(jìn)程才能設(shè)置。
4、文件系統(tǒng)的安裝和拆卸
系統(tǒng)在初始化時(shí)將一個(gè)“根設(shè)備”安裝到節(jié)點(diǎn)“/”上,這個(gè)設(shè)備上的文件系統(tǒng)就成了整個(gè)系統(tǒng)中原始的、基本的文件系統(tǒng)。此后,就可以由超級(jí)用戶進(jìn)程通過(guò)系統(tǒng)調(diào)用mount把其他的子系統(tǒng)安裝到已經(jīng)存在于文件系統(tǒng)中的空閑節(jié)點(diǎn)上,使整個(gè)文件系統(tǒng)得以擴(kuò)展,當(dāng)不再需要使用某個(gè)子系統(tǒng)時(shí),或者在關(guān)閉系統(tǒng)之前,則通過(guò)系統(tǒng)調(diào)用umount把已經(jīng)安裝的設(shè)備逐個(gè)拆卸下來(lái)。
其中提到了lookback,從系統(tǒng)角度來(lái)看,它似乎是一種設(shè)備,但實(shí)際上它只是提供了一條lookback(回接)到某個(gè)可訪問(wèn)普通文件或塊設(shè)備的手段。
安裝過(guò)程:確定安裝點(diǎn)(dentry),讀取待安裝設(shè)備的super_block,然后將super_block與安裝點(diǎn)的dentry數(shù)據(jù)結(jié)構(gòu)聯(lián)系在一起,即所謂的‘安裝’,這是通過(guò)add_vfsmnt()完成。
在拆卸過(guò)程中,為了提高效率,塊設(shè)備的輸入/輸出一般都是有緩沖的,無(wú)論是對(duì)超級(jí)塊的改變還是對(duì)某個(gè)索引節(jié)點(diǎn)的改變,或者對(duì)某個(gè)數(shù)據(jù)塊的改變,都只是對(duì)它們?cè)趦?nèi)存中映像的改變,而不一定馬上就寫回設(shè)備上,現(xiàn)在設(shè)備要卸下來(lái)了,當(dāng)然要先把已經(jīng)改變了但是尚未寫回設(shè)備的內(nèi)容寫回去。這稱為‘同步’。同步完,如果沒(méi)有其它進(jìn)程在使用文件系統(tǒng)就可以卸載了。
5、文件的打開(kāi)與關(guān)閉
sys_open,其中調(diào)用參數(shù)filename實(shí)際上是文件的路徑名(絕對(duì)路徑或相對(duì)路徑);mode表示打開(kāi)的模式,如“只讀”等等;而flag則包含了許多標(biāo)記位,用以表示打開(kāi)模式以外的一些屬性和要求。函數(shù)通過(guò)getname()從用戶空間把文件的路徑名拷貝到系統(tǒng)空間,并通過(guò)get_unused_fd()從當(dāng)前進(jìn)程的“打開(kāi)文件表”中找到一個(gè)空閑的表項(xiàng),該表項(xiàng)的下標(biāo)即為“打開(kāi)文件號(hào)”。然后,根據(jù)文件名通過(guò)file_open()找到或創(chuàng)建一個(gè)連接,或者說(shuō)讀/寫該文件的上下文。文件讀寫的上下文是有file數(shù)據(jù)結(jié)構(gòu)代表和繪制的。
sys_close,先檢查文件號(hào)對(duì)應(yīng)的file結(jié)構(gòu)指針filp是否為0,然后釋放打開(kāi)的文件號(hào),然后調(diào)用filp_close(),把文件中已經(jīng)改變過(guò)的內(nèi)容寫回設(shè)備上即file_operations數(shù)據(jù)結(jié)構(gòu)中提供相應(yīng)的函數(shù)指針flush,同時(shí)移除POSIX鎖,最后調(diào)用fput,遞減file結(jié)構(gòu)中的共享計(jì)數(shù),如果遞減后達(dá)到了0就釋放該file結(jié)構(gòu)。
6、文件的寫與讀
為了提高效率,稍微復(fù)雜一些的操作系統(tǒng)對(duì)文件的讀/寫都是帶緩沖的,linux當(dāng)然也不列外。用戶空間和設(shè)備都可以映射到同一個(gè)緩沖頁(yè)面,這樣文件系統(tǒng)的緩沖機(jī)制和文件的內(nèi)存映射機(jī)制巧妙地結(jié)合在一起了。
本節(jié)主要記錄下關(guān)于文件的記錄塊原理。linux采用了直接和間接相結(jié)合的尋址方法。其方法是把整個(gè)文件的記錄塊尋址分成幾個(gè)部分來(lái)實(shí)現(xiàn)。第一部分是個(gè)以文件內(nèi)塊號(hào)為下標(biāo)的數(shù)組,這是采用直接映射的部分,對(duì)于較小的文件這一部分就夠用了。由于根據(jù)文件的內(nèi)塊號(hào)就可以在inode結(jié)構(gòu)里的數(shù)組中直接找到相應(yīng)的設(shè)備上塊號(hào),所以效率很高。至于較大的文件,其開(kāi)頭那一部分記錄塊號(hào)同樣直接就可以找到,但是當(dāng)文件的大小超出這一部分的容量時(shí),超出的那一部分就要采用間接尋址了。ext2文件系統(tǒng)的大小為12個(gè)記錄塊,即數(shù)組大小為12。當(dāng)記錄塊大小為1K字節(jié)時(shí),相應(yīng)的文件大小為12k字節(jié)。在ext2文件系統(tǒng)的ext2_inode_info結(jié)構(gòu)中,有個(gè)大小為15的整型數(shù)組i_data[],其開(kāi)頭12個(gè)元素即用于此項(xiàng)目。當(dāng)文件大小超過(guò)這一部分的容量時(shí),該數(shù)組中的第13個(gè)元素指向一個(gè)記錄塊,這個(gè)記錄塊的內(nèi)容也是一個(gè)整型數(shù)組,其中的每個(gè)元素都指向一個(gè)設(shè)備上記錄塊。如果記錄塊大小為1k字節(jié),則該數(shù)組的大小為256,也就是說(shuō)間接尋址容量為256個(gè)記錄塊,即256k字節(jié)。這樣,兩個(gè)部分的總?cè)萘繛?font face="AR PL UMing CN, serif">12k+256k??墒?,更大的文件還是容納不下,所以超過(guò)此容量的部分要進(jìn)一步采用雙重(二層)間接尋址。此時(shí)inode結(jié)構(gòu)里i_data[]數(shù)組中的第14個(gè)元素指向另一個(gè)記錄塊,該記錄塊的內(nèi)容也是一個(gè)數(shù)組,但是每個(gè)元素都指向另一個(gè)記錄塊中的數(shù)組,那才是文件內(nèi)塊號(hào)至設(shè)備上的塊號(hào)映射表。這么一來(lái),雙重間接尋址部分的能力為256*256個(gè)記錄塊即64M字節(jié)。以此類推,數(shù)組的第15個(gè)元素用于三重間接尋址,這一部分的容量可達(dá)256*256*256個(gè)記錄塊,也就是16GB,所以,對(duì)于32位結(jié)構(gòu)的系統(tǒng),當(dāng)記錄塊大小為1k字節(jié)時(shí),文件的最大容量為16G+64M+256K+12K。如果設(shè)備的容量大于這個(gè)數(shù)值,就得采用的記錄塊大小了。
7、其它文件操作
如sys_lseek,sys_dup,sys_ioctl
8、特殊文件系統(tǒng)/proc
這個(gè)目錄下的內(nèi)容主要包括如下幾類:
(1)、系統(tǒng)中的每個(gè)進(jìn)程都有一個(gè)以其pid為名的子目錄,而每個(gè)子目錄中則包括關(guān)于該進(jìn)程執(zhí)行的命令行、所有環(huán)境變量、cpu占用時(shí)間、內(nèi)存映射表、已打開(kāi)文件的文件號(hào)以及進(jìn)程狀態(tài)等特殊文件。
(2)、系統(tǒng)中各種資源的管理信息,如/proc/slabinfo就是內(nèi)存管理中關(guān)于各個(gè)slab緩沖塊隊(duì)列的信息,/proc/swaps就是關(guān)于系統(tǒng)的swap設(shè)備信息,/proc/partitions就是關(guān)于各個(gè)磁盤分區(qū)的信息,等等。
(3)、系統(tǒng)中的各種設(shè)備的有關(guān)信息,如/proc/pci就是關(guān)于系統(tǒng)的pci總線上所有設(shè)備的一份清單等等。
(4)、文件系統(tǒng)的信息,如/proc/mounts就是系統(tǒng)中已經(jīng)安裝的各個(gè)文件系統(tǒng)設(shè)備清單,而/proc/filesystems則是系統(tǒng)中已經(jīng)登記的每種文件系統(tǒng)(類型)的清單。
(5)、中斷的使用,/proc/interrupts是一份關(guān)于中斷源和它們的中斷向量編號(hào)的清單。
(6)、與動(dòng)態(tài)安裝模塊有關(guān)的信息,/proc/modules是一份系統(tǒng)中已安裝動(dòng)態(tài)模塊的清單,而/proc/ksyms則是內(nèi)核中供可安裝模塊動(dòng)態(tài)連接的符合名及其地址的清單。
(7)、與前述/dev/mem類似的內(nèi)存訪問(wèn)手段,如/proc/kcore.
(8)、系統(tǒng)的版本號(hào)以及其他各種統(tǒng)計(jì)與狀態(tài)信息。