天天看點

EXT4 之 Dentry 和 Inode對象綜述Dentry 和Inode舉例說明題外話

  • 綜述
  • 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"

,

  1. 周遊

    task->fs->rootmnt

    所指向的vfsmount以及

    task->fs->root

    所指向的dentry
  2. 開始查找一個名為foo的并且儲存有

    d_parent

    的dentry
  3. 檢查目前dentry是否有相應的mount資訊;如果在目前的dentry上存儲有mont資訊,那麼首先檢視下到底mount在什麼地方,并使用目前的vfsmount替換dentry上原有的vfsmount,替換其root指向的dentry
  4. 對于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