分類(lèi): LINUX
虛擬文件系統(VFS,virtual filesystem),是一個(gè)內核軟件層,是物理文件系統與服務(wù)之間的一個(gè)接口層,它對Linux的每個(gè)文件系統的所有細節進(jìn)行抽象,使得不同的文件系統在Linux核心以及系統中運行的其他進(jìn)程看來(lái),都是相同的。嚴格說(shuō)來(lái),VFS并不是一種實(shí)際的文件系統。它只存在于內存中,不存在于任何外存空間。VFS在系統啟動(dòng)時(shí)建立,在系統關(guān)閉時(shí)消亡。
VFS支持文件系統主要有三種類(lèi)型:

文件: 一組在邏輯上具有完整意義的信息項的系列。在Linux中,除了普通文件,其他諸如目錄、設備、套接字等 也以文件被對待??傊?,“一切皆文件”。
目錄: 目錄好比一個(gè)文件夾,用來(lái)容納相關(guān)文件。因為目錄可以包含子目錄,所以目錄是可以層層嵌套,形成文件路徑。在Linux中,目錄也是以一種特殊文件被對待的,所以用于文件的操作同樣也可以用在目錄上。
目錄項: 在一個(gè)文件路徑中,路徑中的每一部分都被稱(chēng)為目錄項;如路徑/home/source/helloworld.c中,目錄 /, home, source和文件 helloworld.c都是一個(gè)目錄項。
索引節點(diǎn): 用于存儲文件的元數據的一個(gè)數據結構。文件的元數據,也就是文件的相關(guān)信息,和文件本身是兩個(gè)不同的概念。它包含的是諸如文件的大小、擁有者、創(chuàng )建時(shí)間、磁盤(pán)位置等和文件相關(guān)的信息。
超級塊: 用于存儲文件系統的控制信息的數據結構。描述文件系統的狀態(tài)、文件系統類(lèi)型、大小、區塊數、索引節 點(diǎn)數等,存放于磁盤(pán)的特定扇區中。
如上的幾個(gè)概念在磁盤(pán)中的位置關(guān)系如下圖所示。

關(guān)于文件系統的三個(gè)易混淆的概念:
創(chuàng )建: 以某種方式格式化磁盤(pán)的過(guò)程就是在其之上建立一個(gè)文件系統的過(guò)程。創(chuàng )建文現系統時(shí),會(huì )在磁盤(pán)的特定位置寫(xiě)入關(guān)于該文件系統的控制信息。
注冊: 向內核報到,聲明自己能被內核支持。一般在編譯內核的時(shí)侯注冊;也可以加載模塊的方式手動(dòng)注冊。注冊過(guò)程實(shí)際上是將表示各實(shí)際文件系統的數據結構struct file_system_type 實(shí)例化。
安裝: 也就是我們熟悉的mount操作,將文件系統加入到Linux的根文件系統的目錄樹(shù)結構上;這樣文件系統才能被訪(fǎng)問(wèn)。
前邊提到,VFS對Linux的每個(gè)文件系統的所有細節進(jìn)行抽象,使得不同的文件系統在Linux核心以及系統中運行的其他進(jìn)程看來(lái),都是相同的。把這個(gè)抽象的結構稱(chēng)為通用文件模型。在通用文件模型中,每個(gè)目錄都被看做是一個(gè)文件,可以包含若干目錄和文件。
通用文件模型由下列對象組成:
存放已安裝文件系統的有關(guān)信息,對基于磁盤(pán)的文件系統,這類(lèi)對象通常對應于存放在磁盤(pán)上的文件系統的控制塊(filesystem contorl block).
存放關(guān)于具體文件的一般信息,對基于磁盤(pán)的文件系統,這類(lèi)對象通常對應于存放在磁盤(pán)上的文件控制塊(file contorl block),每個(gè)索引節點(diǎn),都有一個(gè)索引節點(diǎn)號,這個(gè)節點(diǎn)號,唯一的標識文件系統中的文件。
存放目錄項(文件的特定名稱(chēng))與對應文件進(jìn)行鏈接的有關(guān)信息。每個(gè)磁盤(pán)文件系統都以自己特有的方式將該信息存在磁盤(pán)上。
存放打開(kāi)文件與進(jìn)程之間進(jìn)行的交互的有關(guān)信息,這類(lèi)信息僅當進(jìn)程訪(fǎng)問(wèn)文件期間存在于內核內存中。
它屬于磁盤(pán)高速緩存,用于存放最近最常使用的目錄項對象。以加速從文件路徑名到最后一個(gè)路徑分量的索引節點(diǎn)的轉換過(guò)程。
如下圖,說(shuō)明進(jìn)程怎樣和文件進(jìn)行交互。三個(gè)不同的進(jìn)程,打開(kāi)同一個(gè)文件,其中,兩個(gè)進(jìn)程使用同一個(gè)硬鏈接。在這中情況下,每個(gè)進(jìn)程都使用自己的文件對象,單只需要2個(gè)目錄項對象,每個(gè)硬鏈接對應一個(gè)目錄項對象,這2個(gè)目錄項對象指向同一個(gè)索引節點(diǎn)對象,該索引節點(diǎn)對象標識超級塊對象,以及隨后的普通磁盤(pán)文件。
超級對象塊用來(lái)存儲一個(gè)已安裝的文件系統的控制信息,代表一個(gè)已安裝的文件系統;每次一個(gè)實(shí)際的文件系統被安裝時(shí), 內核會(huì )從磁盤(pán)的特定位置讀取一些控制信息來(lái)填充內存中的超級塊對象。一個(gè)安裝實(shí)例和一個(gè)超級塊對象一一對應。 超級塊通過(guò)其結構中的一個(gè)域s_type記錄它所屬的文件系統類(lèi)型。其結構如下
struct super_block {
struct list_head s_list;/* 指向所有超級塊的鏈表 */
dev_t s_dev;/* 設備標識符 */
unsigned char s_dirt;/* 修改(臟)標志*/
unsigned char s_blocksize_bits;/* 以位為單位的塊的大小 */
unsigned long s_blocksize;/* 以字節為單位的塊的大小 */
loff_t s_maxbytes;/* 文件大小上限 */
struct file_system_type *s_type;/* 文件系統類(lèi)型 */
const struct super_operations *s_op;/* 超級塊方法 */
const struct dquot_operations *dq_op;/* 磁盤(pán)限額方法 */
const struct quotactl_ops *s_qcop;/* 限額控制方法 */
const struct export_operations *s_export_op;/* 導出方法 */
unsigned long s_flags;/* 掛載標志 */
unsigned long s_magic;/* 文件系統的幻數 */
struct dentry *s_root;/* 目錄掛載點(diǎn) */
struct rw_semaphore s_umount;/* 卸載信號量 */
struct mutex s_lock;/* 超級塊信號量 */
int s_count;/* 超級塊引用計數 */
atomic_t s_active;/* 活動(dòng)引用計數 */
void *s_security;/* 安全模塊 */
const struct xattr_handler **s_xattr;/* 擴展的屬性操作 */
struct list_head s_inodes;/* inodes 鏈表 */
struct hlist_head s_anon;/* 匿名目錄項 */
struct list_head s_files;/* 被分配文件列表 */
struct list_head s_dentry_lru;/* 未使用目錄項鏈表 */
int s_nr_dentry_unused;/* 鏈表中目錄項的數目 */
struct block_device *s_bdev;/* 相關(guān)的塊文件 */
struct backing_dev_info *s_bdi;/* */
struct mtd_info *s_mtd;/* 存儲磁盤(pán)信息 */
struct list_head s_instances;/* 該文件類(lèi)型系統 */
struct quota_info s_dquot;/* 限額相關(guān)選項 */
int s_frozen;/* frozen標志位 */
wait_queue_head_t s_wait_unfrozen;/* 凍結的等待隊列 */
char s_id[32];/* 文本名字 */
void *s_fs_info;/* 文件系統特殊信息 */
fmode_t s_mode;/* 安裝權限 */
u32 s_time_gran;/* 時(shí)間戳粒度 */
struct mutex s_vfs_rename_mutex;/* 重命名信號量 */
char *s_subtype;/* 子類(lèi)型名稱(chēng) */
char *s_options;/* 已存安裝選項 */
};
索引節點(diǎn)對象存儲了文件的相關(guān)信息,代表了存儲設備上的一個(gè)實(shí)際的物理文件。當一個(gè) 文件首次被訪(fǎng)問(wèn)時(shí),內核會(huì )在內存中組裝相應的索引節點(diǎn)對象,以便向內核提供對一個(gè)文件進(jìn)行操 作時(shí)所必需的全部信息;這些信息一部分存儲在磁盤(pán)特定位置,另外一部分是在加載時(shí)動(dòng)態(tài)填充的。
struct inode{ //........ unsigned long i_ino; //節點(diǎn)號 atomic_t i_count; //引用計數 //........ uid_t i_uid; //使用者id gid_t i_gid; //使用者id組 //........ struct timespec i_atime; //最后訪(fǎng)問(wèn)時(shí)間 struct timespec i_mtime; //最后修改(modify)時(shí)間 struct timespec i_ctime; //最后改變(change)時(shí)間 unsigned long i_blocks; //文件的塊數 unsigned short i_bytes; //使用的字節數 unsigned long i_state; //狀態(tài)標志 struct list_head i_devices; //塊設備鏈表 unsigned char i_sock; //是否套接字 atomic_t i_writecount; //寫(xiě)者計數 //........};引入目錄項的概念主要是出于方便查找文件的目的。一個(gè)路徑的各個(gè)組成部分,不管是目錄還是普通的文件,都是一個(gè)目錄項對象。如,在路徑/home/source/test.c中,目錄 /, home, source和文件 test.c都對應一個(gè)目錄項對象。不同于前面的兩個(gè)對象,目錄項對象沒(méi)有對應的磁盤(pán)數據結構,VFS在遍歷路徑名的過(guò)程中現場(chǎng)將它們逐個(gè)地解析成目錄項對象。
struct dentry {
atomic_t d_count; //目錄項對象使用計數器,可以有未使用態(tài),使用態(tài)和負狀態(tài)
unsigned int d_flags; //目錄項標志
struct inode *d_inode; //與文件名關(guān)聯(lián)的索引節點(diǎn)
struct dentry *d_parent; //父目錄的目錄項對象
struct list_head d_hash; //散列表表項的指針
struct list_head d_lru; //未使用鏈表的指針
struct list_head d_child; //父目錄中目錄項對象的鏈表的指針
struct list_head d_subdirs; //對目錄而言,表示子目錄目錄項對象的鏈表
struct list_head d_alias; //相關(guān)索引節點(diǎn)(別名)的鏈表
int d_mounted; //對于安裝點(diǎn)而言,表示被安裝文件系統根項
struct qstr d_name; //文件名
unsigned long d_time; /* used by d_revalidate */
struct dentry_operations *d_op; //目錄項方法
struct super_block *d_sb; //文件的超級塊對象
vunsigned long d_vfs_flags;
void *d_fsdata; //與文件系統相關(guān)的數據
unsigned char d_iname [DNAME_INLINE_LEN]; //存放短文件名
};
文件對象是已打開(kāi)的文件在內存中的表示,主要用于建立進(jìn)程和磁盤(pán)上的文件的對應關(guān)系。它由sys_open() 現場(chǎng)創(chuàng )建,由sys_close()銷(xiāo)毀。文件對象和物理文件的關(guān)系有點(diǎn)像進(jìn)程和程序的關(guān)系一樣。當我們站在用戶(hù)空間來(lái)看 待VFS,我們像是只需與文件對象打交道,而無(wú)須關(guān)心超級塊,索引節點(diǎn)或目錄項。因為多個(gè)進(jìn)程可以同時(shí)打開(kāi)和操作 同一個(gè)文件,所以同一個(gè)文件也可能存在多個(gè)對應的文件對象。文件對象僅僅在進(jìn)程觀(guān)點(diǎn)上代表已經(jīng)打開(kāi)的文件,它 反過(guò)來(lái)指向目錄項對象(反過(guò)來(lái)指向索引節點(diǎn))。一個(gè)文件對應的文件對象可能不是惟一的,但是其對應的索引節點(diǎn)和 目錄項對象無(wú)疑是惟一的。
struct file { union { struct list_head fu_list; //文件對象鏈表指針linux/include/linux/list.h struct rcu_head fu_rcuhead; //RCU(Read-Copy Update)是Linux 2.6內核中新的鎖機制 } f_u; struct path f_path; //包含dentry和mnt兩個(gè)成員,用于確定文件路徑 #define f_dentry f_path.dentry //f_path的成員之一,當前文件的dentry結構 #define f_vfsmnt f_path.mnt //表示當前文件所在文件系統的掛載根目錄 const struct file_operations *f_op; //與該文件相關(guān)聯(lián)的操作函數 atomic_t f_count; //文件的引用計數(有多少進(jìn)程打開(kāi)該文件) unsigned int f_flags; //對應于open時(shí)指定的flag mode_t f_mode; //讀寫(xiě)模式:open的mod_t mode參數 off_t f_pos; //該文件在當前進(jìn)程中的文件偏移量 struct fown_struct f_owner; //該結構的作用是通過(guò)信號進(jìn)行I/O時(shí)間通知的數據。 unsigned int f_uid, f_gid; //文件所有者id,所有者組id struct file_ra_state f_ra; //在linux/include/linux/fs.h中定義,文件預讀相關(guān) unsigned long f_version; //記錄文件的版本號,每次使用后都自動(dòng)遞增。 #ifdef CONFIG_SECURITY void *f_security; //用來(lái)描述安全措施或者是記錄與安全有關(guān)的信息。 #endif /* needed for tty driver, and maybe others */ void *private_data; //可以用字段指向已分配的數據 #ifdef CONFIG_EPOLL /* Used by fs/eventpoll.c to link all the hooks to this file */ struct list_head f_ep_links; 文件的事件輪詢(xún)等待者鏈表的頭, spinlock_t f_ep_lock; f_ep_lock是保護f_ep_links鏈表的自旋鎖。 #endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping; 文件地址空間的指針};struct files_struct { atomic_t count; /* 共享該表的進(jìn)程數 */ rwlock_t file_lock; /* 保護該結構體的鎖*/ int max_fds; /*當前文件對象的最大數*/ int max_fdset; /*當前文件描述符的最大數*/ int next_fd; /*已分配的文件描述符加1*/ struct file ** fd; /* 指向文件對象指針數組的指針 */ fd_set *close_on_exec; /*指向執行exec()時(shí)需要關(guān)閉的文件描述符*/ fd_set *open_fds; /*指向打開(kāi)文件描述符的指針*/ fd_set close_on_exec_init; /* 執行exec()時(shí)關(guān)閉的初始文件*/ fd_set open_fds_init; /*文件描述符的初值集合*/ struct file * fd_array[32]; /* 文件對象指針的初始化數組*/};聯(lián)系客服