日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

從文件 I/O 看 Linux 的虛擬文件系統(tǒng)

 fort 2009-01-19

2007 年 8 月 23 日

Linux 允許眾多不同的文件系統(tǒng)共存,并支持跨文件系統(tǒng)的文件操作,這是因?yàn)橛刑摂M文件系統(tǒng)的存在。虛擬文件系統(tǒng),即VFS(Virtual File System)是 Linux 內(nèi)核中的一個(gè)軟件抽象層。它通過(guò)一些數(shù)據(jù)結(jié)構(gòu)及其方法向?qū)嶋H的文件系統(tǒng)如 ext2,vfat 提供接口機(jī)制。本文在簡(jiǎn)要介紹 VFS 的相關(guān)數(shù)據(jù)結(jié)構(gòu)后,以文件 I/O 為切入點(diǎn)深入 Linux 內(nèi)核源代碼,追蹤了 sys_open 和 sys_read 兩個(gè)系統(tǒng)調(diào)用的代碼結(jié)構(gòu),并在追蹤的過(guò)程中理清了跨文件系統(tǒng)的文件操作的基本原理和“一切皆是文件”的口號(hào)得以實(shí)現(xiàn)的根本。

1 引言

Linux 中允許眾多不同的文件系統(tǒng)共存,如 ext2, ext3, vfat 等。通過(guò)使用同一套文件 I/O 系統(tǒng) 調(diào)用即可對(duì) Linux 中的任意文件進(jìn)行操作而無(wú)需考慮其所在的具體文件系統(tǒng)格式;更進(jìn)一步,對(duì)文件的 操作可以跨文件系統(tǒng)而執(zhí)行。如圖 1 所示,我們可以使用 cp 命令從 vfat 文件系統(tǒng)格式的硬盤(pán)拷貝數(shù)據(jù)到 ext3 文件系統(tǒng)格式的硬盤(pán);而這樣的操作涉及到兩個(gè)不同的文件系統(tǒng)。


圖 1. 跨文件系統(tǒng)的文件操作
圖片示例_跨文件系統(tǒng)的文件操作

“一切皆是文件”是 Unix/Linux 的基本哲學(xué)之一。不僅普通的文件,目錄、字符設(shè)備、塊設(shè)備、 套接字等在 Unix/Linux 中都是以文件被對(duì)待;它們雖然類(lèi)型不同,但是對(duì)其提供的卻是同一套操作界面。


圖 2. 一切皆是文件
圖片示例_一切皆是文件

而虛擬文件系統(tǒng)正是實(shí)現(xiàn)上述兩點(diǎn) Linux 特性的關(guān)鍵所在。虛擬文件系統(tǒng)(Virtual File System, 簡(jiǎn)稱(chēng) VFS), 是 Linux 內(nèi)核中的一個(gè)軟件層,用于給用戶(hù)空間的程序提供文件系統(tǒng)接口;同時(shí),它也提供了內(nèi)核中的一個(gè) 抽象功能,允許不同的文件系統(tǒng)共存。系統(tǒng)中所有的文件系統(tǒng)不但依賴(lài) VFS 共存,而且也依靠 VFS 協(xié)同工作。

為了能夠支持各種實(shí)際文件系統(tǒng),VFS 定義了所有文件系統(tǒng)都支持的基本的、概念上的接口和數(shù)據(jù) 結(jié)構(gòu);同時(shí)實(shí)際文件系統(tǒng)也提供 VFS 所期望的抽象接口和數(shù)據(jù)結(jié)構(gòu),將自身的諸如文件、目錄等概念在形式 上與VFS的定義保持一致。換句話(huà)說(shuō),一個(gè)實(shí)際的文件系統(tǒng)想要被 Linux 支持,就必須提供一個(gè)符合VFS標(biāo)準(zhǔn) 的接口,才能與 VFS 協(xié)同工作。實(shí)際文件系統(tǒng)在統(tǒng)一的接口和數(shù)據(jù)結(jié)構(gòu)下隱藏了具體的實(shí)現(xiàn)細(xì)節(jié),所以在VFS 層和內(nèi)核的其他部分看來(lái),所有文件系統(tǒng)都是相同的。圖3顯示了VFS在內(nèi)核中與實(shí)際的文件系統(tǒng)的協(xié)同關(guān)系。


圖3. VFS在內(nèi)核中與其他的內(nèi)核模塊的協(xié)同關(guān)系
圖片示例_VFS在內(nèi)核中與其他的內(nèi)核模塊的協(xié)同關(guān)系

我們已經(jīng)知道,正是由于在內(nèi)核中引入了VFS,跨文件系統(tǒng)的文件操作才能實(shí)現(xiàn),“一切皆是文件” 的口號(hào)才能承諾。而為什么引入了VFS,就能實(shí)現(xiàn)這兩個(gè)特性呢?在接下來(lái),我們將以這樣的一個(gè)思路來(lái)切入 文章的正題:我們將先簡(jiǎn)要介紹下用以描述VFS模型的一些數(shù)據(jù)結(jié)構(gòu),總結(jié)出這些數(shù)據(jù)結(jié)構(gòu)相互間的關(guān)系;然后 選擇兩個(gè)具有代表性的文件I/O操作sys_open()和sys_read()來(lái)詳細(xì)說(shuō)明內(nèi)核是如何借助VFS和具體的文件系統(tǒng)打 交道以實(shí)現(xiàn)跨文件系統(tǒng)的文件操作和承諾“一切皆是文件”的口號(hào)。





回頁(yè)首


2 VFS數(shù)據(jù)結(jié)構(gòu)

2.1 一些基本概念

從本質(zhì)上講,文件系統(tǒng)是特殊的數(shù)據(jù)分層存儲(chǔ)結(jié)構(gòu),它包含文件、目錄和相關(guān)的控制信息。為了描述 這個(gè)結(jié)構(gòu),Linux引入了一些基本概念:

文件 一組在邏輯上具有完整意義的信息項(xiàng)的系列。在Linux中,除了普通文件,其他諸如目錄、設(shè)備、套接字等 也以文件被對(duì)待??傊耙磺薪晕募?。

目錄 目錄好比一個(gè)文件夾,用來(lái)容納相關(guān)文件。因?yàn)槟夸浛梢园幽夸?,所以目錄是可以層層嵌套,形? 文件路徑。在Linux中,目錄也是以一種特殊文件被對(duì)待的,所以用于文件的操作同樣也可以用在目錄上。

目錄項(xiàng) 在一個(gè)文件路徑中,路徑中的每一部分都被稱(chēng)為目錄項(xiàng);如路徑/home/source/helloworld.c中,目錄 /, home, source和文件 helloworld.c都是一個(gè)目錄項(xiàng)。

索引節(jié)點(diǎn) 用于存儲(chǔ)文件的元數(shù)據(jù)的一個(gè)數(shù)據(jù)結(jié)構(gòu)。文件的元數(shù)據(jù),也就是文件的相關(guān)信息,和文件本身是兩個(gè)不同 的概念。它包含的是諸如文件的大小、擁有者、創(chuàng)建時(shí)間、磁盤(pán)位置等和文件相關(guān)的信息。

超級(jí)塊 用于存儲(chǔ)文件系統(tǒng)的控制信息的數(shù)據(jù)結(jié)構(gòu)。描述文件系統(tǒng)的狀態(tài)、文件系統(tǒng)類(lèi)型、大小、區(qū)塊數(shù)、索引節(jié) 點(diǎn)數(shù)等,存放于磁盤(pán)的特定扇區(qū)中。

如上的幾個(gè)概念在磁盤(pán)中的位置關(guān)系如圖4所示。


圖4. 磁盤(pán)與文件系統(tǒng)
圖片示例_磁盤(pán)與文件系統(tǒng).jpg

關(guān)于文件系統(tǒng)的三個(gè)易混淆的概念:

創(chuàng)建 以某種方式格式化磁盤(pán)的過(guò)程就是在其之上建立一個(gè)文件系統(tǒng)的過(guò)程。創(chuàng)建文現(xiàn)系統(tǒng)時(shí),會(huì)在磁盤(pán)的特定位置寫(xiě)入 關(guān)于該文件系統(tǒng)的控制信息。

注冊(cè) 向內(nèi)核報(bào)到,聲明自己能被內(nèi)核支持。一般在編譯內(nèi)核的時(shí)侯注冊(cè);也可以加載模塊的方式手動(dòng)注冊(cè)。注冊(cè)過(guò)程實(shí) 際上是將表示各實(shí)際文件系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)struct file_system_type 實(shí)例化。

安裝 也就是我們熟悉的mount操作,將文件系統(tǒng)加入到Linux的根文件系統(tǒng)的目錄樹(shù)結(jié)構(gòu)上;這樣文件系統(tǒng)才能被訪(fǎng)問(wèn)。

2.2 VFS數(shù)據(jù)結(jié)構(gòu)

VFS依靠四個(gè)主要的數(shù)據(jù)結(jié)構(gòu)和一些輔助的數(shù)據(jù)結(jié)構(gòu)來(lái)描述其結(jié)構(gòu)信息,這些數(shù)據(jù)結(jié)構(gòu)表現(xiàn)得就像是對(duì)象; 每個(gè)主要對(duì)象中都包含由操作函數(shù)表構(gòu)成的操作對(duì)象,這些操作對(duì)象描述了內(nèi)核針對(duì)這幾個(gè)主要的對(duì)象可以進(jìn)行的操作。

2.2.1 超級(jí)塊對(duì)象

存儲(chǔ)一個(gè)已安裝的文件系統(tǒng)的控制信息,代表一個(gè)已安裝的文件系統(tǒng);每次一個(gè)實(shí)際的文件系統(tǒng)被安裝時(shí), 內(nèi)核會(huì)從磁盤(pán)的特定位置讀取一些控制信息來(lái)填充內(nèi)存中的超級(jí)塊對(duì)象。一個(gè)安裝實(shí)例和一個(gè)超級(jí)塊對(duì)象一一對(duì)應(yīng)。 超級(jí)塊通過(guò)其結(jié)構(gòu)中的一個(gè)域s_type記錄它所屬的文件系統(tǒng)類(lèi)型。

根據(jù)第三部分追蹤源代碼的需要,以下是對(duì)該超級(jí)塊結(jié)構(gòu)的部分相關(guān)成員域的描述,(如下同):


清單1. 超級(jí)塊
                    struct super_block { //超級(jí)塊數(shù)據(jù)結(jié)構(gòu)
                    struct list_head s_list;                /*指向超級(jí)塊鏈表的指針*/
                    ……
                    struct file_system_type  *s_type;       /*文件系統(tǒng)類(lèi)型*/
                    struct super_operations  *s_op;         /*超級(jí)塊方法*/
                    ……
                    struct list_head         s_instances;   /*該類(lèi)型文件系統(tǒng)*/
                    ……
                    };
                    struct super_operations { //超級(jí)塊方法
                    ……
                    //該函數(shù)在給定的超級(jí)塊下創(chuàng)建并初始化一個(gè)新的索引節(jié)點(diǎn)對(duì)象
                    struct inode *(*alloc_inode)(struct super_block *sb);
                    ……
                    //該函數(shù)從磁盤(pán)上讀取索引節(jié)點(diǎn),并動(dòng)態(tài)填充內(nèi)存中對(duì)應(yīng)的索引節(jié)點(diǎn)對(duì)象的剩余部分
                    void (*read_inode) (struct inode *);
                    ……
                    };
                    

2.2.2 索引節(jié)點(diǎn)對(duì)象

索引節(jié)點(diǎn)對(duì)象存儲(chǔ)了文件的相關(guān)信息,代表了存儲(chǔ)設(shè)備上的一個(gè)實(shí)際的物理文件。當(dāng)一個(gè) 文件首次被訪(fǎng)問(wèn)時(shí),內(nèi)核會(huì)在內(nèi)存中組裝相應(yīng)的索引節(jié)點(diǎn)對(duì)象,以便向內(nèi)核提供對(duì)一個(gè)文件進(jìn)行操 作時(shí)所必需的全部信息;這些信息一部分存儲(chǔ)在磁盤(pán)特定位置,另外一部分是在加載時(shí)動(dòng)態(tài)填充的。


清單2. 索引節(jié)點(diǎn)
                    struct inode {//索引節(jié)點(diǎn)結(jié)構(gòu)
                    ……
                    struct inode_operations  *i_op;     /*索引節(jié)點(diǎn)操作表*/
                    struct file_operations   *i_fop;	 /*該索引節(jié)點(diǎn)對(duì)應(yīng)文件的文件操作集*/
                    struct super_block       *i_sb;     /*相關(guān)的超級(jí)塊*/
                    ……
                    };
                    struct inode_operations { //索引節(jié)點(diǎn)方法
                    ……
                    //該函數(shù)為dentry對(duì)象所對(duì)應(yīng)的文件創(chuàng)建一個(gè)新的索引節(jié)點(diǎn),主要是由open()系統(tǒng)調(diào)用來(lái)調(diào)用
                    int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
                    //在特定目錄中尋找dentry對(duì)象所對(duì)應(yīng)的索引節(jié)點(diǎn)
                    struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
                    ……
                    };
                    

2.2.3 目錄項(xiàng)對(duì)象

引入目錄項(xiàng)的概念主要是出于方便查找文件的目的。一個(gè)路徑的各個(gè)組成部分,不管是目錄還是 普通的文件,都是一個(gè)目錄項(xiàng)對(duì)象。如,在路徑/home/source/test.c中,目錄 /, home, source和文件 test.c都對(duì)應(yīng)一個(gè)目錄項(xiàng)對(duì)象。不同于前面的兩個(gè)對(duì)象,目錄項(xiàng)對(duì)象沒(méi)有對(duì)應(yīng)的磁盤(pán)數(shù)據(jù)結(jié)構(gòu),VFS在遍 歷路徑名的過(guò)程中現(xiàn)場(chǎng)將它們逐個(gè)地解析成目錄項(xiàng)對(duì)象。


清單3. 目錄項(xiàng)
                    struct dentry {//目錄項(xiàng)結(jié)構(gòu)
                    ……
                    struct inode *d_inode;           /*相關(guān)的索引節(jié)點(diǎn)*/
                    struct dentry *d_parent;         /*父目錄的目錄項(xiàng)對(duì)象*/
                    struct qstr d_name;              /*目錄項(xiàng)的名字*/
                    ……
                    struct list_head d_subdirs;      /*子目錄*/
                    ……
                    struct dentry_operations *d_op;  /*目錄項(xiàng)操作表*/
                    struct super_block *d_sb;        /*文件超級(jí)塊*/
                    ……
                    };
                    struct dentry_operations {
                    //判斷目錄項(xiàng)是否有效;
                    int (*d_revalidate)(struct dentry *, struct nameidata *);
                    //為目錄項(xiàng)生成散列值;
                    int (*d_hash) (struct dentry *, struct qstr *);
                    ……
                    };
                    

2.2.4 文件對(duì)象

文件對(duì)象是已打開(kāi)的文件在內(nèi)存中的表示,主要用于建立進(jìn)程和磁盤(pán)上的文件的對(duì)應(yīng)關(guān)系。它由sys_open() 現(xiàn)場(chǎng)創(chuàng)建,由sys_close()銷(xiāo)毀。文件對(duì)象和物理文件的關(guān)系有點(diǎn)像進(jìn)程和程序的關(guān)系一樣。當(dāng)我們站在用戶(hù)空間來(lái)看 待VFS,我們像是只需與文件對(duì)象打交道,而無(wú)須關(guān)心超級(jí)塊,索引節(jié)點(diǎn)或目錄項(xiàng)。因?yàn)槎鄠€(gè)進(jìn)程可以同時(shí)打開(kāi)和操作 同一個(gè)文件,所以同一個(gè)文件也可能存在多個(gè)對(duì)應(yīng)的文件對(duì)象。文件對(duì)象僅僅在進(jìn)程觀(guān)點(diǎn)上代表已經(jīng)打開(kāi)的文件,它 反過(guò)來(lái)指向目錄項(xiàng)對(duì)象(反過(guò)來(lái)指向索引節(jié)點(diǎn))。一個(gè)文件對(duì)應(yīng)的文件對(duì)象可能不是惟一的,但是其對(duì)應(yīng)的索引節(jié)點(diǎn)和 目錄項(xiàng)對(duì)象無(wú)疑是惟一的。


清單4. 文件對(duì)象
                    struct file {
                    ……
                    struct list_head        f_list;        /*文件對(duì)象鏈表*/
                    struct dentry          *f_dentry;       /*相關(guān)目錄項(xiàng)對(duì)象*/
                    struct vfsmount        *f_vfsmnt;       /*相關(guān)的安裝文件系統(tǒng)*/
                    struct file_operations *f_op;           /*文件操作表*/
                    ……
                    };
                    struct file_operations {
                    ……
                    //文件讀操作
                    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
                    ……
                    //文件寫(xiě)操作
                    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
                    ……
                    int (*readdir) (struct file *, void *, filldir_t);
                    ……
                    //文件打開(kāi)操作
                    int (*open) (struct inode *, struct file *);
                    ……
                    };
                    

2.2.5 其他VFS對(duì)象

2.2.5.1 和文件系統(tǒng)相關(guān)

根據(jù)文件系統(tǒng)所在的物理介質(zhì)和數(shù)據(jù)在物理介質(zhì)上的組織方式來(lái)區(qū)分不同的文件系統(tǒng)類(lèi)型的。 file_system_type結(jié)構(gòu)用于描述具體的文件系統(tǒng)的類(lèi)型信息。被Linux支持的文件系統(tǒng),都有且僅有一 個(gè)file_system_type結(jié)構(gòu)而不管它有零個(gè)或多個(gè)實(shí)例被安裝到系統(tǒng)中。

而與此對(duì)應(yīng)的是每當(dāng)一個(gè)文件系統(tǒng)被實(shí)際安裝,就有一個(gè)vfsmount結(jié)構(gòu)體被創(chuàng)建,這個(gè)結(jié)構(gòu)體對(duì)應(yīng)一個(gè)安裝點(diǎn)。


清單5. 和文件系統(tǒng)相關(guān)
                    struct file_system_type {
                    const char *name;                /*文件系統(tǒng)的名字*/
                    struct subsystem subsys;         /*sysfs子系統(tǒng)對(duì)象*/
                    int fs_flags;                    /*文件系統(tǒng)類(lèi)型標(biāo)志*/
                    /*在文件系統(tǒng)被安裝時(shí),從磁盤(pán)中讀取超級(jí)塊,在內(nèi)存中組裝超級(jí)塊對(duì)象*/
                    struct super_block *(*get_sb) (struct file_system_type*,
                    int, const char*, void *);
                    void (*kill_sb) (struct super_block *);  /*終止訪(fǎng)問(wèn)超級(jí)塊*/
                    struct module *owner;                    /*文件系統(tǒng)模塊*/
                    struct file_system_type * next;          /*鏈表中的下一個(gè)文件系統(tǒng)類(lèi)型*/
                    struct list_head fs_supers;              /*具有同一種文件系統(tǒng)類(lèi)型的超級(jí)塊對(duì)象鏈表*/
                    };
                    struct vfsmount
                    {
                    struct list_head mnt_hash;               /*散列表*/
                    struct vfsmount *mnt_parent;             /*父文件系統(tǒng)*/
                    struct dentry *mnt_mountpoint;           /*安裝點(diǎn)的目錄項(xiàng)對(duì)象*/
                    struct dentry *mnt_root;                 /*該文件系統(tǒng)的根目錄項(xiàng)對(duì)象*/
                    struct super_block *mnt_sb;              /*該文件系統(tǒng)的超級(jí)塊*/
                    struct list_head mnt_mounts;             /*子文件系統(tǒng)鏈表*/
                    struct list_head mnt_child;              /*子文件系統(tǒng)鏈表*/
                    atomic_t mnt_count;                      /*使用計(jì)數(shù)*/
                    int mnt_flags;                           /*安裝標(biāo)志*/
                    char *mnt_devname;                       /*設(shè)備文件名*/
                    struct list_head mnt_list;               /*描述符鏈表*/
                    struct list_head mnt_fslink;             /*具體文件系統(tǒng)的到期列表*/
                    struct namespace *mnt_namespace;         /*相關(guān)的名字空間*/
                    };
                    

2.2.5.2 和進(jìn)程相關(guān)


清單6. 打開(kāi)的文件集
                    struct files_struct {//打開(kāi)的文件集
                    atomic_t count;              /*結(jié)構(gòu)的使用計(jì)數(shù)*/
                    ……
                    int max_fds;                 /*文件對(duì)象數(shù)的上限*/
                    int max_fdset;               /*文件描述符的上限*/
                    int next_fd;                 /*下一個(gè)文件描述符*/
                    struct file ** fd;           /*全部文件對(duì)象數(shù)組*/
                    ……
                    };
                    struct fs_struct {//建立進(jìn)程與文件系統(tǒng)的關(guān)系
                    atomic_t count;              /*結(jié)構(gòu)的使用計(jì)數(shù)*/
                    rwlock_t lock;               /*保護(hù)該結(jié)構(gòu)體的鎖*/
                    int umask;                  /*默認(rèn)的文件訪(fǎng)問(wèn)權(quán)限*/
                    struct dentry * root;        /*根目錄的目錄項(xiàng)對(duì)象*/
                    struct dentry * pwd;         /*當(dāng)前工作目錄的目錄項(xiàng)對(duì)象*/
                    struct dentry * altroot;    /*可供選擇的根目錄的目錄項(xiàng)對(duì)象*/
                    struct vfsmount * rootmnt;   /*根目錄的安裝點(diǎn)對(duì)象*/
                    struct vfsmount * pwdmnt;    /*pwd的安裝點(diǎn)對(duì)象*/
                    struct vfsmount * altrootmnt;/*可供選擇的根目錄的安裝點(diǎn)對(duì)象*/
                    };
                    

2.2.5.3 和路徑查找相關(guān)


清單7. 輔助查找
                    struct nameidata {
                    struct dentry  *dentry;     /*目錄項(xiàng)對(duì)象的地址*/
                    struct vfsmount  *mnt;      /*安裝點(diǎn)的數(shù)據(jù)*/
                    struct qstr  last;          /*路徑中的最后一個(gè)component*/
                    unsigned int  flags;        /*查找標(biāo)識(shí)*/
                    int  last_type;             /*路徑中的最后一個(gè)component的類(lèi)型*/
                    unsigned  depth;            /*當(dāng)前symbolic link的嵌套深度,不能大于6*/
                    char   *saved_names[MAX_NESTED_LINKS + 1];/
                    /*和嵌套symbolic link 相關(guān)的pathname*/
                    union {
                    struct open_intent open; /*說(shuō)明文件該如何訪(fǎng)問(wèn)*/
                    } intent;   /*專(zhuān)用數(shù)據(jù)*/
                    };
                    

2.2.6 對(duì)象間的聯(lián)系

如上的數(shù)據(jù)結(jié)構(gòu)并不是孤立存在的。正是通過(guò)它們的有機(jī)聯(lián)系,VFS才能正常工作。如下的幾張圖是對(duì)它們之間的聯(lián)系的描述。

如圖5所示,被Linux支持的文件系統(tǒng),都有且僅有一個(gè)file_system_type結(jié)構(gòu)而不管它有零個(gè)或多個(gè)實(shí)例被安裝到系統(tǒng) 中。每安裝一個(gè)文件系統(tǒng),就對(duì)應(yīng)有一個(gè)超級(jí)塊和安裝點(diǎn)。超級(jí)塊通過(guò)它的一個(gè)域s_type指向其對(duì)應(yīng)的具體的文件系統(tǒng)類(lèi)型。具體的 文件系統(tǒng)通過(guò)file_system_type中的一個(gè)域fs_supers鏈接具有同一種文件類(lèi)型的超級(jí)塊。同一種文件系統(tǒng)類(lèi)型的超級(jí)塊通過(guò)域s_instances鏈 接。


圖5. 超級(jí)塊、安裝點(diǎn)和具體的文件系統(tǒng)的關(guān)系
圖片示例_超級(jí)塊、安裝點(diǎn)和具體的文件系統(tǒng)的關(guān)系

從圖6可知:進(jìn)程通過(guò)task_struct中的一個(gè)域files_struct files來(lái)了解它當(dāng)前所打開(kāi)的文件對(duì)象;而我們通常所說(shuō)的文件 描述符其實(shí)是進(jìn)程打開(kāi)的文件對(duì)象數(shù)組的索引值。文件對(duì)象通過(guò)域f_dentry找到它對(duì)應(yīng)的dentry對(duì)象,再由dentry對(duì)象的域d_inode找 到它對(duì)應(yīng)的索引結(jié)點(diǎn),這樣就建立了文件對(duì)象與實(shí)際的物理文件的關(guān)聯(lián)。最后,還有一點(diǎn)很重要的是, 文件對(duì)象所對(duì)應(yīng)的文件操作函數(shù) 列表是通過(guò)索引結(jié)點(diǎn)的域i_fop得到的。圖6對(duì)第三部分源碼的理解起到很大的作用。


圖6. 進(jìn)程與超級(jí)塊、文件、索引結(jié)點(diǎn)、目錄項(xiàng)的關(guān)系
圖片示例_進(jìn)程與超級(jí)塊、文件、索引結(jié)點(diǎn)、目錄項(xiàng)的關(guān)系




回頁(yè)首


3 基于VFS的文件I/O

到目前為止,文章主要都是從理論上來(lái)講述VFS的運(yùn)行機(jī)制;接下來(lái)我們將深入源代碼層中,通過(guò)闡述兩個(gè)具有代表性的系統(tǒng) 調(diào)用sys_open()和sys_read()來(lái)更好地理解VFS向具體文件系統(tǒng)提供的接口機(jī)制。由于本文更關(guān)注的是文件操作的整個(gè)流程體制,所以我 們?cè)谧粉櫾创a時(shí),對(duì)一些細(xì)節(jié)性的處理不予關(guān)心。又由于篇幅所限,只列出相關(guān)代碼。本文中的源代碼來(lái)自于linux-2.6.17內(nèi)核版本。

在深入sys_open()和sys_read()之前,我們先概覽下調(diào)用sys_read()的上下文。圖7描述了從用戶(hù)空間的read()調(diào)用到數(shù)據(jù)從 磁盤(pán)讀出的整個(gè)流程。當(dāng)在用戶(hù)應(yīng)用程序調(diào)用文件I/O read()操作時(shí),系統(tǒng)調(diào)用sys_read()被激發(fā),sys_read()找到文件所在的具體文件 系統(tǒng),把控制權(quán)傳給該文件系統(tǒng),最后由具體文件系統(tǒng)與物理介質(zhì)交互,從介質(zhì)中讀出數(shù)據(jù)。


圖7. 從物理介質(zhì)讀數(shù)據(jù)的過(guò)程
圖片示例_從物理介質(zhì)讀數(shù)據(jù)的過(guò)程

3.1 sys_open()

sys_open()系統(tǒng)調(diào)用打開(kāi)或創(chuàng)建一個(gè)文件,成功返回該文件的文件描述符。圖8是sys_open()實(shí)現(xiàn)代碼中主要的函數(shù)調(diào)用關(guān)系圖。


圖8. sys_open函數(shù)調(diào)用關(guān)系圖
圖片示例_sys_open函數(shù)調(diào)用關(guān)系圖

由于sys_open()的代碼量大,函數(shù)調(diào)用關(guān)系復(fù)雜,以下主要是對(duì)該函數(shù)做整體的解析;而對(duì)其中的一些關(guān)鍵點(diǎn),則列出其關(guān)鍵代碼。

a. 從sys_open()的函數(shù)調(diào)用關(guān)系圖可以看到,sys_open()在做了一些簡(jiǎn)單的參數(shù)檢驗(yàn)后,就把接力棒傳給do_sys_open():

1)、首先,get_unused_fd()得到一個(gè)可用的文件描述符;通過(guò)該函數(shù),可知文件描述符實(shí)質(zhì)是進(jìn)程打開(kāi)文件列表中對(duì)應(yīng)某個(gè)文件對(duì)象的索引值;

2)、接著,do_filp_open()打開(kāi)文件,返回一個(gè)file對(duì)象,代表由該進(jìn)程打開(kāi)的一個(gè)文件;進(jìn)程通過(guò)這樣的一個(gè)數(shù)據(jù)結(jié)構(gòu)對(duì)物理文件進(jìn)行讀寫(xiě)操作。

3)、最后,fd_install()建立文件描述符與file對(duì)象的聯(lián)系,以后進(jìn)程對(duì)文件的讀寫(xiě)都是通過(guò)操縱該文件描述符而進(jìn)行。

b. do_filp_open()用于打開(kāi)文件,返回一個(gè)file對(duì)象;而打開(kāi)之前需要先找到該文件:

1)、open_namei()用于根據(jù)文件路徑名查找文件,借助一個(gè)持有路徑信息的數(shù)據(jù)結(jié)構(gòu)nameidata而進(jìn)行;

2)、查找結(jié)束后將填充有路徑信息的nameidata返回給接下來(lái)的函數(shù)nameidata_to_filp()從而得到最終的file對(duì)象;當(dāng)達(dá)到目的后,nameidata這個(gè)數(shù)據(jù)結(jié)構(gòu)將會(huì)馬上被釋放。

c.open_namei()用于查找一個(gè)文件:

1)、path_lookup_open()實(shí)現(xiàn)文件的查找功能;要打開(kāi)的文件若不存在,還需要有一個(gè)新建的過(guò)程,則調(diào)用 path_lookup_create(),后者和前者封裝的是同一個(gè)實(shí)際的路徑查找函數(shù),只是參數(shù)不一樣,使它們?cè)谔幚砑?xì)節(jié)上有所偏差;

2)、當(dāng)是以新建文件的方式打開(kāi)文件時(shí),即設(shè)置了O_CREAT標(biāo)識(shí)時(shí)需要?jiǎng)?chuàng)建一個(gè)新的索引節(jié)點(diǎn),代表創(chuàng)建一個(gè)文件。在vfs_create()里的一句 核心語(yǔ)句dir->i_op->create(dir, dentry, mode, nd)可知它調(diào)用了具體的文件系統(tǒng)所提供的創(chuàng)建索引節(jié)點(diǎn)的方法。注意:這邊的索引節(jié)點(diǎn)的概念,還只是位于內(nèi)存之中,它和磁盤(pán)上的物理的索引節(jié)點(diǎn)的關(guān)系就像 位于內(nèi)存中和位于磁盤(pán)中的文件一樣。此時(shí)新建的索引節(jié)點(diǎn)還不能完全標(biāo)志一個(gè)物理文件的成功創(chuàng)建,只有當(dāng)把索引節(jié)點(diǎn)回寫(xiě)到磁盤(pán)上才是一個(gè)物理文件的真正創(chuàng) 建。想想我們以新建的方式打開(kāi)一個(gè)文件,對(duì)其讀寫(xiě)但最終沒(méi)有保存而關(guān)閉,則位于內(nèi)存中的索引節(jié)點(diǎn)會(huì)經(jīng)歷從新建到消失的過(guò)程,而磁盤(pán)卻始終不知道有人曾經(jīng)想 過(guò)創(chuàng)建一個(gè)文件,這是因?yàn)樗饕?jié)點(diǎn)沒(méi)有回寫(xiě)的緣故。

3)、path_to_nameidata()填充nameidata數(shù)據(jù)結(jié)構(gòu);

4)、may_open()檢查是否可以打開(kāi)該文件;一些文件如鏈接文件和只有寫(xiě)權(quán)限的目錄是不能被打開(kāi)的,先檢查 nd->dentry->inode所指的文件是否是這一類(lèi)文件,是的話(huà)則錯(cuò)誤返回。還有一些文件是不能以TRUNC的方式打開(kāi)的,若 nd->dentry->inode所指的文件屬于這一類(lèi),則顯式地關(guān)閉TRUNC標(biāo)志位。接著如果有以TRUNC方式打開(kāi)文件的,則更新 nd->dentry->inode的信息

3.1.1__path_lookup_intent_open()

不管是path_lookup_open()還是path_lookup_create()最終都是調(diào)用 __path_lookup_intent_open()來(lái)實(shí)現(xiàn)查找文件的功能。 查找時(shí),在遍歷路徑的過(guò)程中,會(huì)逐層地將各個(gè)路徑組成部分解析成目錄項(xiàng)對(duì)象,如果此目錄項(xiàng)對(duì)象在目錄項(xiàng)緩存中,則直接從緩存中獲得;如果該目錄項(xiàng)在緩存中 不存在,則進(jìn)行一次實(shí)際的讀盤(pán)操作,從磁盤(pán)中讀取該目錄項(xiàng)所對(duì)應(yīng)的索引節(jié)點(diǎn)。得到索引節(jié)點(diǎn)后,則建立索引節(jié)點(diǎn)與該目錄項(xiàng)的聯(lián)系。如此循環(huán),直到最終找到目 標(biāo)文件對(duì)應(yīng)的目錄項(xiàng),也就找到了索引節(jié)點(diǎn),而由索引節(jié)點(diǎn)找到對(duì)應(yīng)的超級(jí)塊對(duì)象就可知道該文件所在的文件系統(tǒng)的類(lèi)型。 從磁盤(pán)中讀取該目錄項(xiàng)所對(duì)應(yīng)的索引節(jié)點(diǎn);這將引發(fā)VFS和實(shí)際的文件系統(tǒng)的一次交互。從前面的VFS理論介紹可知,讀索引節(jié)點(diǎn)方法是由超級(jí)塊來(lái)提供的。而 當(dāng)安裝一個(gè)實(shí)際的文件系統(tǒng)時(shí),在內(nèi)存中創(chuàng)建的超級(jí)塊的信息是由一個(gè)實(shí)際文件系統(tǒng)的相關(guān)信息來(lái)填充的,這里的相關(guān)信息就包括了實(shí)際文件系統(tǒng)所定義的超級(jí)塊的 操作函數(shù)列表,當(dāng)然也就包括了讀索引節(jié)點(diǎn)的具體執(zhí)行方式。 當(dāng)繼續(xù)追蹤一個(gè)實(shí)際文件系統(tǒng)ext3的ext3_read_inode()時(shí),可發(fā)現(xiàn)這個(gè)函數(shù)很重要的一個(gè)工作就是為不同的文件類(lèi)型設(shè)置不同的索引節(jié)點(diǎn)操 作函數(shù)表和文件操作函數(shù)表。


清單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 { 
                              // 如果以上三種情況都排除了,則是設(shè)備驅(qū)動(dòng)
                    //這里的設(shè)備還包括套結(jié)字、FIFO等偽設(shè)備 
                    ……
                    }
                    

3.1.2 nameidata_to_filp子函數(shù):__dentry_open

這是VFS與實(shí)際的文件系統(tǒng)聯(lián)系的一個(gè)關(guān)鍵點(diǎn)。從3.1.1小節(jié)分析中可知,調(diào)用實(shí)際文件系統(tǒng)讀取索引節(jié)點(diǎn)的方法讀取索引節(jié)點(diǎn)時(shí),實(shí)際文件系統(tǒng)會(huì)根據(jù)文件 的不同類(lèi)型賦予索引節(jié)點(diǎn)不同的文件操作函數(shù)集,如普通文件有普通文件對(duì)應(yīng)的一套操作函數(shù),設(shè)備文件有設(shè)備文件對(duì)應(yīng)的一套操作函數(shù)。這樣當(dāng)把對(duì)應(yīng)的索引節(jié)點(diǎn) 的文件操作函數(shù)集賦予文件對(duì)象,以后對(duì)該文件進(jìn)行操作時(shí),比如讀操作,VFS雖然對(duì)各種不同文件都是執(zhí)行同一個(gè)read()操作界面,但是真正讀時(shí),內(nèi)核 卻知道怎么區(qū)分對(duì)待不同的文件類(lèi)型。


清單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;
                    ……
                    //整個(gè)函數(shù)的工作在于填充一個(gè)file對(duì)象
                    ……
                    f->f_mapping = inode->i_mapping;
                    f->f_dentry = dentry;
                    f->f_vfsmnt = mnt;
                    f->f_pos = 0;
                    //將對(duì)應(yīng)的索引節(jié)點(diǎn)的文件操作函數(shù)集賦予文件對(duì)象的操作列表
                    f->f_op = fops_get(inode->i_fop); 
                    ……
                    //若文件自己定義了open操作,則執(zhí)行這個(gè)特定的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()系統(tǒng)調(diào)用用于從已打開(kāi)的文件讀取數(shù)據(jù)。如read成功,則返回讀到的字節(jié)數(shù)。如已到達(dá)文件的尾端,則返回0。圖9是sys_read()實(shí)現(xiàn)代碼中的函數(shù)調(diào)用關(guān)系圖。


圖9. sys_read函數(shù)調(diào)用關(guān)系圖
圖片示例_sys_read函數(shù)調(diào)用關(guān)系圖

對(duì)文件進(jìn)行讀操作時(shí),需要先打開(kāi)它。從3.1小結(jié)可知,打開(kāi)一個(gè)文件時(shí),會(huì)在內(nèi)存組裝一個(gè)文件對(duì)象,希望對(duì)該文件執(zhí)行的操作方法已在文件對(duì)象設(shè)置好。所以 對(duì)文件進(jìn)行讀操作時(shí),VFS在做了一些簡(jiǎn)單的轉(zhuǎn)換后(由文件描述符得到其對(duì)應(yīng)的文件對(duì)象;其核心思想是返回 current->files->fd[fd]所指向的文件對(duì)象),就可以通過(guò)語(yǔ)句 file->f_op->read(file, buf, count, pos)輕松調(diào)用實(shí)際文件系統(tǒng)的相應(yīng)方法對(duì)文件進(jìn)行讀操作了。





回頁(yè)首


4 解決問(wèn)題

4.1 跨文件系統(tǒng)的文件操作的基本原理

到此,我們也就能夠解釋在Linux中為什么能夠跨文件系統(tǒng)地操作文件了。舉個(gè)例子,將vfat格式的磁盤(pán)上的一個(gè)文件a.txt拷貝到ext3格式的磁 盤(pán)上,命名為b.txt。這包含兩個(gè)過(guò)程,對(duì)a.txt進(jìn)行讀操作,對(duì)b.txt進(jìn)行寫(xiě)操作。讀寫(xiě)操作前,需要先打開(kāi)文件。由前面的分析可知,打開(kāi)文件 時(shí),VFS會(huì)知道該文件對(duì)應(yīng)的文件系統(tǒng)格式,以后操作該文件時(shí),VFS會(huì)調(diào)用其對(duì)應(yīng)的實(shí)際文件系統(tǒng)的操作方法。所以,VFS調(diào)用vfat的讀文件方法將 a.txt的數(shù)據(jù)讀入內(nèi)存;在將a.txt在內(nèi)存中的數(shù)據(jù)映射到b.txt對(duì)應(yīng)的內(nèi)存空間后,VFS調(diào)用ext3的寫(xiě)文件方法將b.txt寫(xiě)入磁盤(pán);從而 實(shí)現(xiàn)了最終的跨文件系統(tǒng)的復(fù)制操作。

4.2“一切皆是文件”的實(shí)現(xiàn)根本

不論是普通的文件,還是特殊的目錄、設(shè)備等,VFS都將它們同等看待成文件,通過(guò)同一套文件操作界面來(lái)對(duì)它們進(jìn)行操作。操作文件時(shí)需先打開(kāi);打開(kāi)文件 時(shí),VFS會(huì)知道該文件對(duì)應(yīng)的文件系統(tǒng)格式;當(dāng)VFS把控制權(quán)傳給實(shí)際的文件系統(tǒng)時(shí),實(shí)際的文件系統(tǒng)再做出具體區(qū)分,對(duì)不同的文件類(lèi)型執(zhí)行不同的操作。這 也就是“一切皆是文件”的根本所在。





回頁(yè)首


5 總結(jié)

VFS即虛擬文件系統(tǒng)是Linux文件系統(tǒng)中的一個(gè)抽象軟件層;因?yàn)樗闹С?,眾多不同的?shí)際文件系統(tǒng)才能在Linux中共存,跨文件系統(tǒng)操作才能實(shí)現(xiàn)。 VFS借助它四個(gè)主要的數(shù)據(jù)結(jié)構(gòu)即超級(jí)塊、索引節(jié)點(diǎn)、目錄項(xiàng)和文件對(duì)象以及一些輔助的數(shù)據(jù)結(jié)構(gòu),向Linux中不管是普通的文件還是目錄、設(shè)備、套接字等 都提供同樣的操作界面,如打開(kāi)、讀寫(xiě)、關(guān)閉等。只有當(dāng)把控制權(quán)傳給實(shí)際的文件系統(tǒng)時(shí),實(shí)際的文件系統(tǒng)才會(huì)做出區(qū)分,對(duì)不同的文件類(lèi)型執(zhí)行不同的操作。由此 可見(jiàn),正是有了VFS的存在,跨文件系統(tǒng)操作才能執(zhí)行,Unix/Linux中的“一切皆是文件”的口號(hào)才能夠得以實(shí)現(xiàn)。





回頁(yè)首


6 參考文獻(xiàn)

[1].Claudia Salzberg Rodriguez, Gordon Fischer, Steven Smolski. The Linux Kernel Primer.機(jī)械工業(yè)出版社.2006.7

[2].Robert Love.Linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)(第二版).機(jī)械工業(yè)出版社.2007.1

[3].Stevens W.Richard.Unix環(huán)境高級(jí)編程(第二版).人民郵電出版社.2006

[4].楊芙清,陳向群.操作系統(tǒng)教程.北京大學(xué)出版社.2005.7

[5].Linux-2.6.17.13內(nèi)核源代碼

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀(guān)點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多