VFS中的資料結構(superblock、dentry、inode、file)
VFS簡介
VFS的理念是使用統一的資料結構在核心中儲存不同型別檔案系統的資訊(含操作)。
VFS是一個介於使用者程式和檔案系統實現之間的一個抽象層,VFS既給了不同型別的檔案系統支援Linux系統的公共介面,也給使用者程式提供了一個統一的呼叫介面。
VFS背後的核心idea是引入了通用檔案模型(common file model),該模型其實就是Unix原生檔案系統的一個映象(這樣可以在Unix原生檔案系統了具有最小的開銷,最高的效能)。其他非原生檔案系統(比如FAT、MS-DOS filesystem)則需要做一些轉換,從而支援該通用檔案模型
通用檔案模型(common file model)
通用檔案模型由superblock、inode、dentry、file四種資料結構構成,這四種資料結構都是儲存在核心空間中的。
- superblock資料結構儲存了掛載的檔案系統的元資訊,對於基於磁碟的檔案系統,通常在磁碟上有一個對應的superblock(比如ext2的superblock)。
- inode資料結構用於儲存檔案的元資訊,一個檔案對應一個inode,每個inode都有一個inode number唯一標識。inode通常對應磁碟上儲存的檔案控制塊(file control block)
- dentry資料結構用於將inode(表示檔案)和目錄項(表示檔案路徑)關聯起來。inode中是沒有檔案路徑的,所以需要dentry來講檔案路徑和檔案關聯起來。不同的磁碟檔案系統使用各自的方式來儲存dentry
- file資料結構用於儲存程序和開啟的檔案之間互動的資訊,這個資訊只會存在於核心空間中,在磁碟上沒有對應的資訊。
下面將分別介紹這4種資料結構,所列舉的資訊使用的是2.6核心的資料。
superblock資料結構
superblock是檔案系統的metadata,儲存了檔案系統的各種資訊以及可以對其執行的操作。
包含的域(僅列舉部分):
型別 | 域名 | 描述 |
---|---|---|
int | s_type | 檔案系統型別 |
unsigned long | s_blocksize | 塊大小(block size) |
struct dentry * | s_root | 指向檔案系統根目錄對應的dentry |
struct list_head | s_inodes | 檔案系統中所有檔案的inode(使用list_head雙向連結串列儲存) |
void * | s_fs_info | 指向具體檔案系統實現(如ext2)的特有的資料結構 |
struct superblock* | s_op | superblock的操作函式(結構體裡都是函式指標) |
superblock operations(儲存在s_op中,僅列舉部分):
函式 | 功能 |
---|---|
alloc_inode(sb) | 為一個inode物件分配空間 |
destroy_inode(inode) | 銷燬一個inode物件 |
read_inode(inode) | 從磁碟中讀取inode資料,填充作為傳入引數的inode物件 |
write_inode(inode, flag) | 使用記憶體中的inode資訊更新磁碟中的inode資訊 |
delete_inode(inode) | 刪除記憶體中的inode物件同時刪除磁碟上的inode |
inode資料結構
inode資料結構中儲存了檔案系統處理檔案所需要的全部資訊以及可以對其執行的操作。一個inode對應磁碟上一個實際的檔案(目錄是一種特殊的檔案)
inode中沒有直接儲存檔案的每個塊的位置,根據ext2的的Data Blocks Addressing,下表中的i_blocks和i_bytes應該是共同規定了檔案的file block number(從0開始計數)和檔案結束位置。下層的檔案系統(如ext2)負責將file block number轉換為logical block numbe(在磁碟上的block地址)
包含的域(僅列舉部分):
型別 | 域名 | 描述 |
---|---|---|
struct super_block * | i_sb | 指向inode所在的superblock物件 |
struct list_head | i_dentry | 這是一個雙向連結串列的頭節點,連結串列中儲存的是指向該inode的dentry物件 |
unsigned long | i_ino | inode編號 |
umode_t | i_mode | 檔案型別和訪問許可權域 |
unsigned int | i_nlink | 指向該inode的硬連結數量,為0時意味著該inode要銷燬了 |
uid_t | i_uid | inode所有者的id |
struct timespec | i_atime | 上次訪問的時間戳 |
unsigned long | i_blocks | 檔案的塊數目 |
unsigned short | i_bytes | 檔案最後一個塊的位元組大小 |
struct inode_operations * | i_op | inode operations,inode的操作函式 |
inode operations(儲存在i_op中,僅列舉部分)
函式 | 功能 |
---|---|
create(dir, dentry, mode, nameidata) | 建立一個inode |
lookup(dir, dentry, nameidata) | 在一個目錄檔案中查詢和dentry包含的檔名匹配的inode |
link(old_dentry, dir, new_dentry) | 建立一個指向new_dentry的硬連結,儲存在old_dentry中,該old_dentry和new_dentry指向同一個inode,即同一個檔案。 |
symlink(dir, dentry, symname) | 建立一個新的inode,該inode是一個軟連線檔案,指向引數dentry |
mkdir(dir, dentry, mode) | 為dentry建立一個目錄檔案的inode |
dentry資料結構
dentry資料結構用於將inode(表示檔案)和目錄項(表示檔案路徑)關聯起來。inode中是沒有檔案路徑的,所以需要dentry來講檔案路徑和檔案關聯起來。dentry在磁碟中沒有對應的映象,所以不需要考慮該dentry是否需要更新。
核心在進行路徑查詢的時候會為每一級都建立一個dentry,比如/home/damon/cppfile/test.cpp。就需要建立5個dentry(第一個是根目錄/,第二個是/home,第5個是/home/damon/cppfile/test.cpp)。
dentry cache將已經查詢過的路徑快取在記憶體中,這樣下次查詢的時候就不需要重新讀取每一級的目錄檔案進行查詢,可以直接通過dentry cache獲得對應的inode
包含的域(僅列舉部分)
型別 | 域名 | 描述 |
---|---|---|
atomic_t | d_count | dentry物件的使用計數器 |
struct inode * | d_inode | dentry指向的inode |
struct dentry * | d_parent | 指向上級目錄的dentry |
struct qstr | d_name | 檔名 |
struct list_head | d_subdirs | 如果當前dentry是目錄的dentry,那麼雙向連結串列儲存的是所有子目錄的dentry |
struct super_block * | d_sb | dentry對應的super block |
struct dentry_operations* | d_op | Dentry methods,dentry的操作函式 |
dentry operations(儲存在d_op中,僅列舉部分)
函式 | 功能 |
---|---|
d_revalidate(dentry, nameidata) | 判斷當前dentry物件是仍然有效,應該是dentry cache中使用的 |
d_hash(dentry, name) | 計算雜湊值,應該也是dentry cache中使用的 |
d_delete(dentry) | 在d_count為0時,刪除dentry,預設的VFS函式什麼都不做 |
file資料結構
file資料結構用於儲存程序和開啟的檔案之間互動的資訊,這個資訊只會存在於核心空間中,在磁碟上沒有對應的資訊。
包含的域(僅列舉部分)
型別 | 域名 | 描述 |
---|---|---|
struct dentry * | f_dentry | 和該file對應的dentry |
struct vfsmount * | f_vfsmnt | file所在的檔案系統(檔案系統掛載的資料結構) |
unsigned int | f_flags | 開啟檔案時使用的flag |
mode_t | f_mode | 程序access mode |
struct file_operations * | f_op | file operations,檔案操作函式 |
dentry operations(儲存在d_op中,僅列舉部分)
函式 | 功能 |
---|---|
open(inode, file) | 開啟檔案 |
llseek(file, offset, origin) | 移動檔案指標 |
read(file, buf, count, offset) | 讀檔案 |
write(file, buf, count, offset) | 寫檔案 |