我們在進程中要怎樣去描述一個文件呢?我們用目錄項(dentry)和索引節點(inode)。它們的定義如下:
struct dentry {
struct inode *d_inode; /* Where the name belongs to - NULL is
struct dentry *d_parent; /* parent directory */
struct list_head d_child; /* child of parent list */
struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
void *d_fsdata; /* fs-specific data */
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
......
};
struct inode {
unsigned long i_ino;
atomic_t i_count;
umode_t i_mode;
unsigned int i_nlink;
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev;
loff_t i_size;
struct timespec i_atime;
unsigned long i_blocks;
unsigned short i_bytes;
unsigned char _sock;
12
struct inode_operations *i_op;
struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct super_block *i_sb;
......
};
所謂"文件", 就是按一定的形式存儲在介質上的信息,所以一個文件其實包含了兩方面的信息,一是存儲的數據本身,二是有關該文件的組織和管理的信息。在內存中, 每個文件都有一個dentry(目錄項)和inode(索引節點)結構,dentry記錄著文件名,上級目錄等信息,正是它形成了我們所看到的樹狀結構;而有關該文件的組織和管理的信息主要存放inode裡面,它記錄著文件在存儲介質上的位置與分布。同時dentry->d_inode指向相應的inode結構。dentry與inode是多對一的關系,因為有可能一個文件有好幾個文件名(硬鏈接,
hard link, 可以參考這個網頁 http://www.ugrad.cs.ubc.ca/~cs219/CourseNotes/Unix/commands-links.html)。
所有的dentry用d_parent和d_child連接起來,就形成了我們熟悉的樹狀結構。
inode代表的是物理意義上的文件,通過inode可以得到一個數組,這個數組記錄了文件內容的位置,如該文件位於硬盤的第3,8,10塊,那麼這個數組的內容就是3,8,10。其索引節點號inode->i_ino,在同一個文件系統中是唯一的,內核只要根據i_ino,就可以計算出它對應的inode在介質上的位置。就硬盤來說,根據i_ino就可以計算出它對應的inode屬於哪個塊(block),從而找到相應的inode結構。但僅僅用inode還是無法描述出所有的文件系統,對於某一種特定的文件系統而言,比如ext3,在內存中用ext3_inode_info描述。他是一個包含inode的"容器"。
struct ext3_inode_info {
__le32 i_data[15];
......
struct inode vfs_inode;
};
le32 i data[15]這個數組就是上一段中所提到的那個數組。
注意,在遙遠的2.4的古代,不同文件系統索引節點的內存映像(ext3_inode_info,reiserfs_inode_info,msdos_inode_info ...)都是用一個union內嵌在inode數據結構中的.
但inode作為一種非常基本的數據結構而言,這樣搞太大了,不利於快速的分配和回收。但是後來發明了container_of(...)這種方法後,就把union移到了外部,我們可以用類似Container of(inode,
struct ext3_inode_info, vfs_inode),從inode出發,得到其的"容器"。
dentry和inode終究都是在內存中的,它們的原始信息必須要有一個載體。否則斷電之後豈不是玩完了?且聽我慢慢道來。
文件可以分為磁盤文件,設備文件,和特殊文件三種。設備文件暫且不表。
磁盤文件就磁盤文件而言,dentry和inode的載體在存儲介質(磁盤)上。對於像ext3這樣的磁盤文件來說,存儲介質中的目錄項和索引節點載體如下,
struct ext3_inode {
__le16 i_mode; /* File mode */
__le16 i_uid; /* Low 16 bits of Owner Uid */
__le32 i_size; /* Size in bytes */
__le32 i_atime; /* Access time */
__le32 i_ctime; /* Creation time */
__le32 i_mtime; /* Modification time */
__le32 i_dtime; /* Deletion Time */
__le16 i_gid; /* Low 16 bits of Group Id */
__le16 i_links_count; /* Links count */
......
__le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
......
}
struct ext3_dir_entry_2 {
__u32 inode; /* Inode number */
__u16 rec_len; /* Directory entry length */
__u8 name_len; /* Name length */
__u8 file_type;
char name[EXT3_NAME_LEN]; /* File name */
};
le32 i block[EXT2 N BLOCKS];/* Pointers to blocks */
i_block數組指示了文件的內容所存放的地點(在硬盤上的位置)。
ext3_inode是放在索引節點區,而ext3_dir_entry_2是以文件內容的形式存放在數據區。我們只要知道了ino,由於ext3_inode大小已知,我們就可以計算出ext3_inode在索引節點區的位置( ino * sizeof(ext3_inode) ),而得到了ext3_inode,我們根據i_block就可以知道這個文件的數據存放的地點。將磁盤上ext3_inode的內容讀入到ext3_inode_info中的函數是ext3_read_inode()。以一個有100
block的硬盤為例,一個文件系統的組織布局大致如下圖。位圖區中的每一位表示每一個相應的對象有沒有被使用。
特殊文件特殊文件在內存中有inode和dentry數據結構,但是不一定在存儲介質上有"索引節點",它斷電之後的確就玩完了,所以不需要什麼載體。當從一個特殊文件讀時,所讀出的數據是由系統內部按一定的規則臨時生成的,或從內存中收集,加工出來的。sysfs裡面就是典型的特殊文件。它存儲的信息都是由系統動態的生成的,它動態的包含了整個機器的硬件資源情況。從sysfs讀寫就相當於向kobject層次結構提取數據。
還請注意, 我們談到目錄項和索引節點時,有兩種含義。一種是在存儲介質(硬盤)中的(如ext3_inode),一種是在內存中的,後者是根據在前者生成的。內存中的表示就是dentry和inode,它是VFS中的一層,不管什麼樣的文件系統,最後在內存中描述它的都是dentry和inode結構。我們使用不同的文件系統,就是將它們各自的文件信息都抽象到dentry和inode中去。這樣對於高層來說,我們就可以不關心底層的實現,我們使用的都是一系列標准的函數調用。這就是VFS的精髓,實際上就是面向對象。
我們在進程中打開一個文件F,實際上就是要在內存中建立F的dentry,和inode結構,並讓它們與進程結構聯系來,把VFS中定義的接口給接起來。我們來看一看這個經典的圖。這張圖之於文件系統,就像每天愛你多一些之於張學友,番茄炒蛋之於復旦南區食堂,刻骨銘心。