- 綜述
- Dentry 和Inode
- Dentry 和Inode之間的映射關系
- Inode 和superblock的關系
- 不同檔案系統之間的關系
- 舉例說明
- 題外話
- fs_struct 和struct namespace
- struct vfsmount
綜述
檔案系統在記憶體中是通過dentry和inode來表現的,換句話說:檔案系統本身是一個虛拟的概念,但是我們可以通過它在記憶體中的表現形式dentry和inode來進行研究。Inodes是底層的對象的表現形式(也是目錄的表現形式),dentry是一個由d_name、指向一個inode的d_node、一個指向父dentry的d_parent組成。
Dentry 和Inode
加入我們有如下的結構:
/
|
foo
| \
bar bar2
在上面所示的結構中,可以提取到如下資訊:
- 有4個inode,分别是foo,bar,bar2,/
- 有3個denry, 分别是bar–>foo、bar2–>foo、foo–>/
對于上述的關系,做一個簡單的說明。對于一個
d_name
為bar的dentry來說,該dentry擁有一個指向底層檔案名為bar的指針
d_inode
、一個指向父dentry的指針
d_parent
。對于root dentry來說,
d_parent
指向自己。
Dentry 和Inode之間的映射關系
這裡需要注意的是。從dentry 到 inode之間的映射關系是由
d_inode
指針來維護的,一個inode可以對應多個dentry,多個dentry可以對應一個inode。dentry到inode是多對一的關系。在同一檔案系統中,一個檔案可以被多個dentry所引用(我們通常稱之為:硬連結);這種映射關系有這樣的特性:除非當目前檔案的所有dentry引用被删除,否則目前file是不會被删除的。
檔案和目錄的操作者是程序,當然了其實質是操作相應的資料結構。在程序操作的資料結構中包含了指向dentry的指針。其所代表的檔案在程序打開期間同樣也是無法删除的,除非檔案不再被任何dentry所引用。
Inode 和superblock的關系
另外,Inode也同superblock之間儲存有密切的聯系。在inode的結構體中有一個指向superblock的指針
i_sb
。superblock是一個描述了目前檔案系統狀态的結構體,通常是存儲在實體裝置上。
不同檔案系統之間的關系
但是從程序(或則叫使用者角度)來看:該程序不可能同時隻和同一個檔案系統發生關系,相應的會同各種不同的檔案系統有關聯;不同的檔案系統之間也存在着種種關聯,這就需要一個結構來對這種關系加以規範,這種結構就是由
vfsmount struct
所代表的mountpoint結構。
除了需要同父子vfsmounts關聯之外,每一個vfsmount都包含有如下資訊:
- mnt_root:一個指向vfsmount root dentry的指針
- mnt_mountpoint: 一個指向目前vfsmount所被挂載點的dentry指針
vfsmount和底層檔案系統之間的關系同樣也是多對一的關系。可以将同一個檔案系統mount到不同的地方,這樣的一個顯著結果是:被挂在在不同地方的檔案系統擁有同樣的dentries、inodes和superblock
舉例說明
不同的程序隸屬不同的命名空間。當某一程序使用
clone
并配合
CLONE_NEWNS
辨別建立一個新的task時,檔案系統會賦予該程序一分其父dentry的vfsmount關系樹。命名空間的root是由
task->namespace->root
指針來指明的(盡管
task->fs->root task->fs->rootmnt
看起來更像實際的起始位置,但是在擁有
chroot
的情況下,他們二者還是有很大不同的)。
當我們看到了一個絕對路徑
"/foo/bar"
,
- 周遊
所指向的vfsmount以及task->fs->rootmnt
所指向的dentrytask->fs->root
- 開始查找一個名為foo的并且儲存有
的dentryd_parent
- 檢查目前dentry是否有相應的mount資訊;如果在目前的dentry上存儲有mont資訊,那麼首先檢視下到底mount在什麼地方,并使用目前的vfsmount替換dentry上原有的vfsmount,替換其root指向的dentry
- 對于bar重複step 2 的操作,并産生新的vfsmount和dentry
step 3比較晦澀,這稍作介紹。在step 2 中所找到的dentry 實際上是可能被多方引用到多個命名空間中的;對于引用該dentry的每一方來說,可能就是不同檔案系統的挂載點(或者啥都米有)。所有即便是我們知道了dentry上的所有資訊,依然無法确定該dentry是否有被mount到啥地方,是以我們推出了vfsmount。是以通常的做法是去查找目前儲存在dentry hash table中的vfsmount,所查找到的vfsmount結果向我們展示了目前dentry所mount了什麼dentry
我們也可以在已經挂在了檔案系統的dentry上重新挂載一個新的檔案系統,此時會将前一次挂載的檔案系統隐藏。所有當我們在Dentry中發現了被挂在的vfsmount時,需要重複的去搜尋新的vfsmount以及判定目前dentry的root dentry是否又被挂載的什麼位置;一直重複這個動作直到發現一個沒有任何挂載的root dentry為止。這個流程,我們可以通過研讀
kernel/fs/namei.c:follow_mount()
來明确。
在上述的每一個階段中,所周遊到的dentry并不是我們所需要的資訊,相反我們所需要的資訊是成對的dentry和vfsmount。是以在
struct nameidata
結構體中儲存了指向dentry和vfsmount的指針,可以用來進行回溯。
struct nameidata {
struct path path;
struct qstr last;
struct path root;
struct inode *inode; /* path.dentry.d_inode */
unsigned int flags;
unsigned seq, m_seq;
int last_type;
unsigned depth;
char *saved_names[MAX_NESTED_LINKS + ];
};
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
題外話
fs_struct 和struct namespace
對于定義在include/linux/sched.h中的
task_struct
,其中抱恨了
struct fs_struct
和
struct namespace
結構。
struct fs_struct {
atomic_t count;
rwlock_t lock;
int umask;
struct dentry * root, * pwd, * altroot;
struct vfsmount * rootmnt, * pwdmnt, * altrootmnt;
};
struct namespace {
atomic_t count;
struct vfsmount * root;
struct list_head list;
struct rw_semaphore sem;
};
sys_chroot()
函數調用
set_fs_root
時,僅僅支護改變
fs->root
和
fs->rootmnt
,但是并不會改變目前實際工作的目錄(使用
pwd
指令所列印出的資訊)。盡管我們執行了chroot指令,但是的狀态其實是繼承在執行該指令之前狀态(打開了那些檔案、任務啊等等)
struct vfsmount
詳細可查閱代碼:include/linux/mount.h:
struct vfsmount
{
struct list_head mnt_hash;
struct vfsmount *mnt_parent; /* fs we are mounted on */
struct dentry *mnt_mountpoint; /* dentry of mountpoint */
struct dentry *mnt_root; /* root of the mounted tree */
struct super_block *mnt_sb; /* pointer to superblock */
struct list_head mnt_mounts; /* list of children, anchored here */
struct list_head mnt_child; /* and going through their mnt_child */
atomic_t mnt_count;
int mnt_flags;
char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */
struct list_head mnt_list;
};
原文參見:
http://www.fieldses.org/~bfields/kernel/vfs.txt