圖解Linux虛擬文件系統(tǒng)(VFS)之關(guān)系篇
大家好,今天和大家探討一下Linux虛擬文件系統(tǒng),虛擬文件系統(tǒng)是我一直想要去聊的一個(gè)知識(shí)點(diǎn),如果你想從事Linux開發(fā)相關(guān)的工作,一定要了解虛擬文件系統(tǒng)。
1.什么是虛擬文件系統(tǒng)?
Linux虛擬文件系統(tǒng)(Virtual File System,VFS)是Linux操作系統(tǒng)中的一個(gè)重要組成部分,它提供了一個(gè)統(tǒng)一的接口,使得用戶和應(yīng)用程序可以通過相同的方式訪問不同類型的文件系統(tǒng)。
圖片
VFS的設(shè)計(jì)目標(biāo)是將不同類型的文件系統(tǒng)抽象為一個(gè)統(tǒng)一的接口,使得用戶和應(yīng)用程序無(wú)需關(guān)心底層文件系統(tǒng)的具體實(shí)現(xiàn)細(xì)節(jié)。通過VFS用戶可以使用相同的系統(tǒng)調(diào)用(如open、read、write等)來(lái)訪問不同類型的文件系統(tǒng),包括本地文件系統(tǒng)(如ext4、XFS等)、網(wǎng)絡(luò)文件系統(tǒng)(如NFS、CIFS等)以及虛擬文件系統(tǒng)(如procfs、sysfs等)。
VFS由以下幾個(gè)主要組件組成:
- 虛擬文件系統(tǒng)接口:VFS定義了一組通用的文件系統(tǒng)操作接口。
- 超級(jí)塊(super_block):每個(gè)文件系統(tǒng)都有一個(gè)超級(jí)塊,它包含了文件系統(tǒng)的元數(shù)據(jù)信息,如文件系統(tǒng)類型、塊大小、inode表等,超級(jí)塊提供了對(duì)文件系統(tǒng)的整體描述和管理。
- 目錄項(xiàng)(dentry):dentry是目錄項(xiàng)的縮寫,用于表示文件系統(tǒng)中的目錄和文件,dentry包含了目錄和文件對(duì)應(yīng)的inode指針,通過它可以快速定位到目錄下的文件或子目錄。
- 文件節(jié)點(diǎn)(inode):inode是文件系統(tǒng)中的一個(gè)數(shù)據(jù)結(jié)構(gòu),用于存儲(chǔ)文件或目錄的元數(shù)據(jù)信息,如文件大小、權(quán)限、所有者等,每個(gè)文件或目錄都對(duì)應(yīng)一個(gè)唯一的inode。
- 文件對(duì)象(file):file是表示打開文件的數(shù)據(jù)結(jié)構(gòu),它包含了對(duì)應(yīng)的inode指針、當(dāng)前讀寫位置等信息,通過file可以進(jìn)行文件的讀寫操作。
2.Linux系統(tǒng)文件樹
對(duì)于一個(gè)普通的Linux用戶或者運(yùn)維人員,Linux系統(tǒng)文件樹通常的樣子如下圖,以根文件系統(tǒng)根目錄為起點(diǎn),通過根目錄遍歷整個(gè)文件樹。
圖片
而在系統(tǒng)開發(fā)人員眼中,Linux系統(tǒng)文件樹則變成這樣一個(gè)結(jié)構(gòu),每個(gè)文件和目錄都對(duì)應(yīng)一個(gè)dentry結(jié)構(gòu)體。
圖片
dentry到底是什么?
dentry結(jié)構(gòu)體的主要作用是提供文件系統(tǒng)層次結(jié)構(gòu)的表示,它們通過形成一個(gè)樹狀結(jié)構(gòu)來(lái)組織目錄和文件,每個(gè)dentry都有一個(gè)唯一的路徑名,可以通過遍歷dentry樹來(lái)找到特定文件或目錄。
struct dentry結(jié)構(gòu)體定義:
struct dentry {
struct dentry *d_parent;
struct qstr d_name;
struct inode *d_inode;
const struct dentry_operations *d_op;
struct super_block *d_sb;
struct list_head d_child;
struct list_head d_subdirs;
....
};
struct dentry結(jié)構(gòu)體通過d_parent,d_child,d_subdirs等成員將文件系統(tǒng)組成一顆文件樹,要了解Linux文件系統(tǒng),我們得學(xué)會(huì)運(yùn)用dentry。
小節(jié):dentry是VFS重要的組成部分,要理解VFS先從dentry開始。
3.文件系統(tǒng)注冊(cè)
通過前面的學(xué)習(xí),我們了解到dentry結(jié)構(gòu)的重要性,接下來(lái)圍繞dentry結(jié)構(gòu)體來(lái)解析文件VFS各組件之間的關(guān)系,我們先來(lái)看一下整體架構(gòu)圖:
圖片
Linux文件系統(tǒng)對(duì)應(yīng)一個(gè)file_system_type結(jié)構(gòu)體對(duì)象,file_system_type結(jié)構(gòu)體定義如下:
struct file_system_type {
const char *name;
int fs_flags;
int (*init_fs_context)(struct fs_context *);
const struct fs_parameter_spec *parameters;
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *);
void (*kill_sb) (struct super_block *);
......
};
ramfs文件系統(tǒng)定義如下,name表示文件系統(tǒng)類型,當(dāng)ramfs文件系統(tǒng)需要實(shí)例化,需要通過name查找全局文件系統(tǒng)鏈表頭找到對(duì)應(yīng)的已注冊(cè)文件系統(tǒng),再通過已注冊(cè)文件系統(tǒng)創(chuàng)建超級(jí)塊(super block)。
static struct file_system_type ramfs_fs_type = {
.name = "ramfs",
.init_fs_context = ramfs_init_fs_context,
.parameters = ramfs_fs_parameters,
.kill_sb = ramfs_kill_sb,
.fs_flags = FS_USERNS_MOUNT,
};
定義好文件系統(tǒng)后,通過register_filesystem函數(shù)將文件系統(tǒng)注冊(cè)至Linux系統(tǒng),注冊(cè)成功的文件系統(tǒng)會(huì)插入全局文件系統(tǒng)鏈表,已注冊(cè)的文件系統(tǒng)能夠用來(lái)創(chuàng)建超級(jí)塊(super block)。
通過cat /proc/filesystems查看系統(tǒng)所有已注冊(cè)文件系統(tǒng)
圖片
4.文件系統(tǒng)掛載
文件系統(tǒng)掛載就是新文件系統(tǒng)生成一個(gè)掛載實(shí)例(struct mount),讓新掛載實(shí)例和父文件系統(tǒng)的掛載實(shí)例建立父子關(guān)系。
一個(gè)新的掛載實(shí)例包括幾個(gè)重要部分:
- 超級(jí)塊(super_block)
超級(jí)塊用來(lái)指示新的文件系統(tǒng)對(duì)應(yīng)的設(shè)備。
- 父掛載實(shí)例(mount)
父掛載實(shí)例表示掛載點(diǎn)所處的文件系統(tǒng)掛載實(shí)例。
- 掛載點(diǎn)(mountpoint)
掛載點(diǎn)是新文件系統(tǒng)和父文件系統(tǒng)之間連接的紐帶。
- 文件系統(tǒng)根目錄(dentry)
每個(gè)文件系統(tǒng)都有一個(gè)根目錄,當(dāng)索引一個(gè)文件路徑進(jìn)入到一個(gè)新的文件系統(tǒng)后,會(huì)從新的文件系統(tǒng)根目錄開始索引。
4.1 索引掛載點(diǎn)
索引掛載點(diǎn)的目的是為了獲取掛載點(diǎn)的struct path記錄信息,掛載點(diǎn)索引的過程就是struct path記錄信息不斷被替換的過程。
圖片
以掛載點(diǎn)/mnt/test/dir為例來(lái)講解:
- 索引/目錄,獲取/目錄的path記錄信息。
- 索引mnt目錄,獲取mnt目錄的path記錄信息,并覆蓋/目錄的path記錄信息。
- 索引test目錄,獲取test目錄的path記錄信息,并覆蓋mnt目錄的path記錄信息。
- 索引dir目錄,獲取dir目錄的path記錄信息,并覆蓋test目錄的path記錄信息。
- 最終獲取到掛載點(diǎn)dir的struct path記錄信息。
struct path結(jié)構(gòu)體定義如下:
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
mnt:記錄掛載點(diǎn)所在文件系統(tǒng)的掛載實(shí)例。
dentry:掛載點(diǎn)目錄dentry。
4.2 創(chuàng)建新文件系統(tǒng)掛載實(shí)例
- 創(chuàng)建超級(jí)塊
要?jiǎng)?chuàng)建超級(jí)塊首先要知道文件系統(tǒng)類型,mount命令通過-t參數(shù)指定文件系統(tǒng)類型,通過mount命令傳入的文件系統(tǒng)類型,可以遍歷全局文件系統(tǒng)鏈表找到已注冊(cè)的文件系統(tǒng),通過已注冊(cè)的文件系統(tǒng)創(chuàng)建超級(jí)塊。
- 創(chuàng)建新文件系統(tǒng)掛載實(shí)例
創(chuàng)建超級(jí)塊后,通過超級(jí)塊的信息創(chuàng)建新文件系統(tǒng)掛載實(shí)例。
- 創(chuàng)建掛載點(diǎn)
通過掛載點(diǎn)dentry創(chuàng)建一個(gè)掛載點(diǎn)。
4.3 新舊掛載實(shí)例對(duì)接
通過前面的過程,我們已經(jīng)具備文件系統(tǒng)掛載三要素:
- 新文件系統(tǒng)掛載實(shí)例。
- 父文件系統(tǒng)掛載實(shí)例。
- 掛載點(diǎn)。
通過掛載三要素,我們就能完成新舊掛載實(shí)例對(duì)接,完成對(duì)接后,新文件系統(tǒng)掛載實(shí)例的mnt_parent指向父掛載實(shí)例,整個(gè)掛載過程就已經(jīng)完成。
新文件系統(tǒng)掛載成功后,Linux系統(tǒng)文件樹將新文件系統(tǒng)嫁接進(jìn)來(lái),如下圖:
圖片
此時(shí)我們想要操作新文件系統(tǒng)中的文件,只需要根據(jù)路徑名層層索引獲取文件path信息,path信息記錄dentry信息,dentry綁定了inode對(duì)象。
最終獲取到inode文件節(jié)點(diǎn)就能操作文件了。