深入剖析Linux文件系統(tǒng)之文件系統(tǒng)掛載(一)(超詳細~)
1.前言
環(huán)境:處理器架構(gòu):arm64內(nèi)核源碼:linux-5.11ubuntu版本:20.04.1代碼閱讀工具:vim+ctags+cscope
我們知道,Linux系統(tǒng)中我們經(jīng)常將一個塊設備上的文件系統(tǒng)掛載到某個目錄下才能訪問這個文件系統(tǒng)下的文件,但是你有沒有思考過:為什么塊設備掛載之后才能訪問文件?掛載文件系統(tǒng)Linux內(nèi)核到底為我們做了哪些事情?是否可以不將文件系統(tǒng)掛載到具體的目錄下也能訪問?下面,本文將詳細講解Linxu系統(tǒng)中,文件系統(tǒng)掛載的奧秘。 注:本文主要講解文件系統(tǒng)掛載核心邏輯,暫不涉及掛載命名空間和綁定掛載等內(nèi)容(后面的內(nèi)容可能會涉及),且以ext2磁盤文件系統(tǒng)為例講解掛載。本專題文章分為上下兩篇,上篇主要介紹掛載全貌以及具體文件系統(tǒng)的掛載方法,下篇介紹如何通過掛載實例關(guān)聯(lián)掛載點和超級塊。
2. vfs 幾個重要對象
在這里我們不介紹整個IO棧,只說明和文件系統(tǒng)相關(guān)的vfs和具體文件系統(tǒng)層。我們知道在Linux中通過虛擬文件系統(tǒng)層VFS統(tǒng)一所有具體的文件系統(tǒng),提取所有具體文件系統(tǒng)的共性,屏蔽具體文件系統(tǒng)的差異。VFS既是向下的接口(所有文件系統(tǒng)都必須實現(xiàn)該接口),同時也是向上的接口(用戶進程通過系統(tǒng)調(diào)用最終能夠訪問文件系統(tǒng)功能)。 下面我們來看下,vfs中幾個比較重要的結(jié)構(gòu)體對象:
2.1 file_system_type
這個結(jié)構(gòu)來描述一種文件系統(tǒng)類型,一般具體文件系統(tǒng)會定義這個結(jié)構(gòu),然后注冊到系統(tǒng)中;定義了具體文件系統(tǒng)的掛載和卸載方法,文件系統(tǒng)掛載時調(diào)用其掛載方法構(gòu)建超級塊、跟dentry等實例。
文件系統(tǒng)分為以下幾種:
1)磁盤文件系統(tǒng)
文件在非易失性存儲介質(zhì)上(如硬盤,flash),掉電文件不丟失。 如ext2,ext4,xfs
2)內(nèi)存文件系統(tǒng)
文件在內(nèi)存上,掉電丟失。
如tmpfs
3)偽文件系統(tǒng)
是假的文件系統(tǒng),是利用虛擬文件系統(tǒng)的接口(可以對用戶可見如proc、sysfs,也可以對用戶不可見內(nèi)核可見如sockfs,bdev)。
如proc,sysfs,sockfs,bdev
4)網(wǎng)絡文件系統(tǒng)
這種文件系統(tǒng)允許訪問另一臺計算機上的數(shù)據(jù),該計算機通過網(wǎng)絡連接到本地計算機。
如nfs文件系統(tǒng)
結(jié)構(gòu)體定義源碼路徑:include/linux/fs.h +2226
2.2 super_block
超級塊,用于描述塊設備上的一個文件系統(tǒng)總體信息(如文件塊大小,最大文件大小,文件系統(tǒng)魔數(shù)等),一個塊設備上的文件系統(tǒng)可以被掛載多次,但是內(nèi)存中只能有個super_block來描述(至少對于磁盤文件系統(tǒng)來說)。
結(jié)構(gòu)體定義源碼路徑:include/linux/fs.h +1414
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學習書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)? ? ??
?


2.3 mount
掛載描述符,用于建立超級塊和掛載點等之間的聯(lián)系,描述文件系統(tǒng)的一次掛載,一個塊設備上的文件系統(tǒng)可以被掛載多次,每次掛載內(nèi)存中有一個mount對象描述。
結(jié)構(gòu)體定義源碼路徑:fs/mount.h +39
2.4 inode
索引節(jié)點對象,描述磁盤上的一個文件元數(shù)據(jù)(文件屬性、位置等),有些文件系統(tǒng)需要從塊設備上讀取磁盤上的索引節(jié)點,然后在內(nèi)存中創(chuàng)建vfs的索引節(jié)點對象,一般在文件第一次打開時創(chuàng)建。
結(jié)構(gòu)體定義源碼路徑:include/linux/fs.h +610
2.5 dentry
目錄項對象,用于描述文件的層次結(jié)構(gòu),從而構(gòu)建文件系統(tǒng)的目錄樹,文件系統(tǒng)將目錄當作文件,目錄的數(shù)據(jù)由目錄項組成,而每個目錄項存儲一個目錄或文件的名稱和索引節(jié)點號等內(nèi)容。每當進程訪問一個目錄項就會在內(nèi)存中創(chuàng)建目錄項對象(如ext2路徑名查找中,通過查找父目錄數(shù)據(jù)塊的目錄項,找到對應文件/目錄的名稱,獲得inode號來找到對應inode)。
結(jié)構(gòu)體定義源碼路徑:include/linux/dcache.h +90
2.6 file
文件對象,描述進程打開的文件,當進程打開文件時會創(chuàng)建文件對象加入到進程的文件打開表,通過文件描述符來索引文件對象,后面讀寫等操作都通過文件描述符進行(一個文件可以被多個進程打開,會由多個文件對象加入到各個進程的文件打開表,但是inode只有一個)。
結(jié)構(gòu)體定義源碼路徑:include/linux/fs.h +915
3. 掛載總體流程
3.1系統(tǒng)調(diào)用處理
用戶執(zhí)行掛載是通過系統(tǒng)調(diào)用路徑進入內(nèi)核處理,拷貝用戶空間傳遞參數(shù)到內(nèi)核,掛載委托do_mount。 //fs/namespace.c SYSCALL_DEFINE5(mount
參數(shù):
3.2 掛載點路徑查找
3.3 參數(shù)合法性檢查
參數(shù)合法性檢查, 新掛載委托do_new_mount path_mount -> 參數(shù)合法性檢查 -> 根據(jù)掛載標志調(diào)用不同函數(shù)處理 這里講解是默認 do_new_mount
3.4 調(diào)用具體文件系統(tǒng)掛載方法
來看下文件系統(tǒng)類型沒有實現(xiàn)init_fs_context接口的情況:
繼續(xù)往下走:
3.5 掛載實例添加到全局文件系統(tǒng)樹
下面主要看下vfs_get_tree和do_new_mount_fc:
4.具體文件系統(tǒng)掛載方法
來看下ext2對掛載的處理:
啟動階段初始化->
掛載時調(diào)用->
ext2_mount通過調(diào)用mount_bdev來執(zhí)行實際文件系統(tǒng)的掛載工作,ext2_fill_super的一個函數(shù)指針作為參數(shù)傳遞給get_sb_bdev。該函數(shù)用于填充一個超級塊對象,如果內(nèi)存中沒有適當?shù)某墘K對象,數(shù)據(jù)就必須從硬盤讀取。
mount_bdev是個公用的函數(shù),一般磁盤文件系統(tǒng)會使用它來根據(jù)具體文件系統(tǒng)的fill_super方法來讀取磁盤上的超級塊并在創(chuàng)建內(nèi)存超級塊。
我們來看下mount_bdev的實現(xiàn)(**它執(zhí)行完成之后會創(chuàng)建vfs的三大數(shù)據(jù)結(jié)構(gòu) super_block、根inode和根dentry **):
2)mount_bdev源碼分析
可以看到mount_bdev主要是:
1.根據(jù)要掛載的塊設備文件名查找到對應的塊設備描述符(內(nèi)核后面操作塊設備都是使用塊設備描述符);
2.首先在文件系統(tǒng)類型的fs_supers鏈表查找是否已經(jīng)讀取過指定的vfs超級塊,會對比每個超級塊的s_bdev塊設備描述符,沒有創(chuàng)建一個vfs超級塊;
3.新創(chuàng)建的vfs超級塊,需要調(diào)用具體文件系統(tǒng)的fill_super方法來讀取填充超級塊。
那么下面主要集中在具體文件系統(tǒng)的fill_super方法,這里是ext2_fill_super:
分析重點代碼如下:
3)ext2_fill_super源碼分析
可以看到ext2_fill_super主要工作為:
1.讀取磁盤上的超級塊;
2.填充并關(guān)聯(lián)vfs超級塊;
3.讀取塊組描述符;
4.讀取磁盤根inode并建立vfs 根inode;
5.創(chuàng)建根dentry關(guān)聯(lián)到根inode。
下面給出ext2_fill_super之后ext2相關(guān)圖解:

有了這些信息,雖然能夠獲得塊設備上的文件系統(tǒng)全貌,內(nèi)核也能通過已經(jīng)建立好的block_device等結(jié)構(gòu)訪問塊設備,但是用戶進程不能真正意義上訪問到,用戶一般會通過open打開一個文件路徑來訪問文件,但是現(xiàn)在并沒有關(guān)聯(lián)掛載目錄的路徑,需要將文件系統(tǒng)關(guān)聯(lián)到掛載點,以至于路徑名查找的時候查找到掛載點后,在轉(zhuǎn)向文件系統(tǒng)的根目錄,而這需要通過do_new_mount_fc來去關(guān)聯(lián)并加入全局的文件系統(tǒng)樹中,下一篇深入剖析Linux文件系統(tǒng)之文件系統(tǒng)掛載(二)(超詳細~) - 知乎 (zhihu.com)我們將做詳細講解。
