VFS采用的是面向對象的設計思想,使用一簇數據結構來代表通用文件對象。所有內核中的數據結構都使用C結構體實現。
保存一個掛在的文件系統的相關信息(Stores information concerning a mounted filesystem. For disk-based filesystems, this object usually corresponds to a filesystem control block stored on disk.)
(1)超級塊用來描述特定文件系統的信息。它存放在磁盤特定的扇區中 ,它在使用的時候將信息存在於內存中。
(2)當內核對一個文件系統進行初始化和注冊時在內存為其分配一個超級塊,這就是VFS超級塊。即,VFS超級塊是各種具體文件系統在安裝時建立的,並在這些文件系統卸載時被自動刪除 。
(3)超級塊對象由結構體 super_block來體現。VFS超級塊的數據結構為 super_block在include/linux/fs.h中可以查看
struct super_block {
…….
};
我們先來看一個圖,再來具體解釋:
其中主要的數據成員和解釋如下:
(1) s_list :所有的超級塊形成一個雙聯表,s_list.prev和s_list.next分別指向與當前超級塊相鄰的前一個元素和後一個元素。
(2) s_lock :保護鏈表免受多處理器系統上的同時訪問。
(3) s_fs_info: 字段指向具體文件系統的超級塊。
例如:超級塊對象指的是Ext2文件系統,該字段就指向ext2_sb_info數據結構。
(4) s_dirt :來表示該超級塊是否是髒的,也就是說,磁盤上的數據是否必須要更新。
(5) 超級塊對象是通過函數alloc_super()創建並初始化的。在文件系統安裝時,內核會調用該函數以便從磁盤讀取文件系統超級塊,並且將其信息填充到內存中的超級塊對象中 。
超級對象中最重要的就是s_op,每一種文件系統都應該有自己的super_operations操作實例。它指向超級塊的操作函數表, 它由struct super_operations結構體來表示。
現在來看一下它的定義:它的定義在 include/linux/fs.h頭文件中可以看到
1560struct super_operations {
1561 struct inode *(*alloc_inode)(struct super_block *sb);
1562 void (*destroy_inode)(struct inode *);
1563
1564 void (*dirty_inode) (struct inode *);
1565 int (*write_inode) (struct inode *, struct writeback_control *wbc);
1566 int (*drop_inode) (struct inode *);
1567 void (*evict_inode) (struct inode *);
1568 void (*put_super) (struct super_block *);
1569 void (*write_super) (struct super_block *);
1570 int (*sync_fs)(struct super_block *sb, int wait);
1571 int (*freeze_fs) (struct super_block *);
1572 int (*unfreeze_fs) (struct super_block *);
1573 int (*statfs) (struct dentry *, struct kstatfs *);
1574 int (*remount_fs) (struct super_block *, int *, char *);
1575 void (*umount_begin) (struct super_block *);
1576
1577 int (*show_options)(struct seq_file *, struct vfsmount *);
1578 int (*show_stats)(struct seq_file *, struct vfsmount *);
1579#ifdef CONFIG_QUOTA
1580 ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
1581 ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
1582#endif
1583 int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
1584};
(1) 可以看到該結構體中的每一項都是一個指向超級塊操作函數的指針,超級塊操作函數執行文件系統和索引節點的底層操作。
(2)當文件系統需要對超級塊執行操作時,要在超級塊對象中尋找需要的操作方法。
例如:一個文件系統要寫自己的超級塊,需要調用:
sturct super_block * sb;
sb->s_op->write_super(sb);
sb是指向文件系統超級塊的指針,沿著該指針進入超級塊操作函數表,並從表中取得writ_super()函數,該函數執行寫入超級塊的實際操作。
說明:
盡管writ_super()方法來自超級塊,但是在調用時,還是要把超級塊作為參數傳遞給它。因為沒有C++的this指針。
(3)具體操作說明
struct inode * alloc_inode(struct super_block * sb) :創建和初始化一個新的索引結點。
void destroy_inode(struct super_block *sb) :釋放指定的索引結點 。
void dirty_inode(struct inode *inode) :VFS在索引節點被修改時會調用此函數。
void write_inode(struct inode *inode, struct writeback_control *wbc) 將指定的inode寫回磁盤。
void drop_inode( struct inode * inode):刪除索引節點。
void put_super(struct super_block *sb) :用來釋放超級塊。
void write_super(struct super_block *sb):更新磁盤上的超級塊。
void sync_fs(struct super_block *sb,in wait):使文件系統的數據元素與磁盤上的文件系統同步,wait參數指定操作是否同步。
int statfs(struct super_block *sb,struct statfs *statfs):獲取文件系統狀態。把文件系統相關的統計信息放在statfs中。
(文件或目錄的靜態描述信息,不隨進程不同而變化)
(1) 保存一個文件的通用信息,每個inode有一個inode number,在文件系統中,一個inode number能夠唯一地標識一個文件(Stores general information about a specific file. For disk-based filesystems, this object usually corresponds to a file control block stored on disk. Each inode object is associated with an inode number, which uniquely identifies the file within the filesystem.)
(2) 文件系統處理文件或目錄時的所有信息都存放在稱為索引節點的數據結構中。文件名可以隨時改,但是索引節點對文件是唯一的(它是隨文件的存在而存在)。
(3) 具體文件系統的索引節點是存放在磁盤上的,是一種靜態結構,要使用它,必須將其調入內存,填寫 VFS的索引節點。VFS索引節點也稱為動態節點。(即索引節點僅當文件被訪問時才在內存中創建)
它的定義在 /include/linux/fs.h中有這個結構體的定義
struct inode {
……
};
還記的我們在終端下輸入命令:ls 命令後可以看到文件的信息,這些信息就是記錄在這裡的。這是為什麼呢?
我們知道,文件是由FCB(文件控制塊控制的),而具體到Linux下,文件是有索引節點結構控制的。所以在struct inode 裡存放了文件的基本信息。大家有沒有發現在怎麼在索引節點裡面會包含超級塊的對象呢,有些人可能不明白了,先看看下面的圖,再來解釋把。
從上面對的圖我們可以看出索引節點 對象靠i_sb指回到了超級塊對象。
成員說明:
i_hash :為了提高查找inode的效率,每一個inode都會有一個hash值。該字段指向hash值相同的inode所形成的雙鏈表該字段包含prev和next兩個指針,分別指向上述鏈表的前一個元素和後一個元素;
i_list :所有索引結點形成的雙聯表,(從圖上可以看出,索引節點對象是靠它來鏈接的)
i_dentry :所有引用該inode的目錄項將形成一個雙聯表,該字段即為這個雙聯表的頭結點
i_ino :索引結點號。通過ls -i命令可以查看文件的索引節點號;
i_count :引用計數;
i_nlink :硬鏈接數。當該inode描述一個目錄時,這個值至少為2,代表.和..的數目;
(注:索引節點沒有軟連接數,軟連接會對應單獨的索引節點)
i_uid :inode所屬文件的擁有者的id,通過ls -n可查看擁有者id;
i_gid :inode所屬文件所在組的id,通過ls -n可查看組id;
i_rdev :如果該inode描述的是一個設備文件,此值為設備號;
i_blkbits :以位為單位的塊大小;
i_atime :文件最近一次被訪問的時間。通過ls -lu可查看該時間;
i_mtime :文件最近一次被修改的時間,這裡的修改只文件內容被修改。通過ls -l可查看該時間;
i_ctime :文件最近一次被修改的時間,這裡的修改除了指文件內容被修改外,更強調的是文件的屬性被修改。通過ls -lc可查看該時間;
i_blocks :文件使用塊的個數,通過ls -s可以查看該某個文件的塊使用數目;
i_mode :文件的訪問權限;
i_op : 指向索引結點操作結構體的指針;
i_fop : 指向文件操作街頭體的指針;
i_sb : 指向inode所屬文件系統的超級塊的指針;
i_pipe :如果inode所代表的文件是一個管道,則使用該字段;
i_bdev :如果inode所代表的文件是一個塊設備,則使用該字段;
i_cdev :如果inode所代表的文件是一個字符設備,則使用該字段;
i_state : 索引節點的狀態信息。
說明:
(1) 在同一個文件系統中,每個索引節點號都是唯一的,內核可以根據索引節點號的散列值來查找其inode結構。
(2) inode中有兩個設備號i_dev和i_rdev。
a. 除特別文件外,每個節點都存儲在某個設備上,這就是i_dev。
b. 如果索引節點所代表的並不是常規文件,而是某個設備,則需要另一個設備號,這就是i_rdev。
(3) 對i_state的說明:
每個VFS索引節點都會復制磁盤索引節點包含的一些數據,比如文件占有的磁盤數。如果i_state 的值等於I_DIR,該索引節點就是“髒“的。也就是說,對應的磁盤索引節點必須被更新。
(4) 三個重要的雙向鏈表:
a.未用索引節點鏈表,正在使用索引節點鏈表和髒索引節點鏈表。每個索引節點對象總是出現在上面三種的一個。
b.這3個鏈表都是通過索引節點的i_list 域鏈接在一起的。
c.屬於“正在使用“或“髒“鏈表的索引節點對象也同時存放在一個散列表中。
(4) 一個索引節點代表文件系統中的一個文件,它也可以是設備或管道這樣的特殊文件。所以在索引節點結構體中有一些和特殊文件相關的項。
(6) 有時候某些文件系統並不能完整地包含索引節點結構體要求的所有信息。那麼此時剛怎麼辦呢?
此時,可以給它賦一些其它的值。例如:一個文件系統可能並不記錄文件的訪問時間,這時就可以在i_atime中存儲0。
(7) i_list和i_sb_list的區別
a.i_list:VFS中使用四個鏈表來管理不同狀態的inode結點。inode_unused將當前未使用的inode鏈接起來,inode_in_use將當前正在被使用的inode鏈接起來,超級塊中的s_dirty將所有髒inode鏈接起來,i_hash將所有hash值相同的inode鏈接起來。i_list中包含prev和next兩個指針,分別指向與當前inode處於同一個狀態鏈表的前後兩個元素
b.i_sb_list:每個文件系統中的inode都會形成一個雙聯表,這個雙鏈表的頭結點存放在超級塊的s_inodes中。而該字段中的prev和next指針分別指向在雙鏈表中與其相鄰的前後兩個元素
c.索引結點中i_sb_list鏈表是鏈接一個文件系統中所有inode的鏈表,因此相鄰的inode之間均會由此鏈表鏈接;而i_list鏈接的是處於同一個狀態的所有inode。所以,相鄰inode之間並不一定鏈接在一起。
與索引節點關聯的方法叫索引節點操作表,它是在 struct inode_operations這個結構體中具體描述的。它的定義在 include/linux/fs.h頭文件中定義。
1516struct inode_operations {
1517 int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
1518 struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
1519 int (*link) (struct dentry *,struct inode *,struct dentry *);
1520 int (*unlink) (struct inode *,struct dentry *);
1521 int (*symlink) (struct inode *,struct dentry *,const char *);
1522 int (*mkdir) (struct inode *,struct dentry *,int);
1523 int (*rmdir) (struct inode *,struct dentry *);
1524 int (*mknod) (struct inode *,struct dentry *,int,dev_t);
1525 int (*rename) (struct inode *, struct dentry *,
1526 struct inode *, struct dentry *);
1527 int (*readlink) (struct dentry *, char __user *,int);
1528 void * (*follow_link) (struct dentry *, struct nameidata *);
1529 void (*put_link) (struct dentry *, struct nameidata *, void *);
1530 void (*truncate) (struct inode *);
1531 int (*permission) (struct inode *, int);
1532 int (*check_acl)(struct inode *, int);
1533 int (*setattr) (struct dentry *, struct iattr *);
1534 int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
1535 int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
1536 ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
1537 ssize_t (*listxattr) (struct dentry *, char *, size_t);
1538 int (*removexattr) (struct dentry *, const char *);
1539 void (*truncate_range)(struct inode *, loff_t, loff_t);
1540 long (*fallocate)(struct inode *inode, int mode, loff_t offset,
1541 loff_t len);
1542 int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
1543 u64 len);
1544};
現在我們對其中一些重要的結果進行分析: