天天看點

Linux核心檔案系統學習:虛拟檔案系統(多圖)

這是我的一篇舊文,發表在 CSDN,現重新進行了整理發表到 JAVAEYE。

分析是基于 Linux核心 2.4.30。

[size=xx-large][b]一、概述[/b][/size]

Linux 檔案系統是相當複雜的,本文隻分析虛拟檔案系統的實作,對具體的檔案系統不涉及。

即使是虛拟檔案系統,要在一篇文章中講清楚也是不可能的,況且我自己的了解也不夠透徹。

為什麼選擇 Linux 2.4.30?因為可以參考《Linux 源碼情景分析》一書,減少學習難度。

[size=xx-large][b]二、基本概念[/b][/size]

先介紹一些檔案系統的基本概念:

1、一塊磁盤(塊裝置),首先要按照某種檔案系統格式(如 NTFS、EXT2)進行格式化,然後才能在其上進行建立目錄、儲存檔案等操作。

2、 在 Linux 中,有“安裝”檔案系統和“解除安裝”檔案系統的概念。一塊經過格式化的“塊裝置”(不管是剛剛格式化完的,沒有建立任何名錄和檔案;還是已經建立了目錄和檔案),隻有先被“安裝”,才能融入 Linux 的檔案系統中,使用者才可以在它上面進行正常的檔案操作。

3、 Linux 把目錄或普通檔案,統一看成“目錄節點”。通常一個“目錄節點”具有兩個重要屬性:名稱以及磁盤上實際對應的資料。本文中,“目錄節點”有時簡稱為“節點”

“符号連結”是一種特殊的目錄節點,它隻有一個名稱,沒有實際資料。這個名稱指向一個實際的目錄節點。

4、 “接口結構”:在 核心代碼中,經常可以看到一種結構,其成員全部是函數指針,例如:

struct file_operations {
  struct module *owner;
  loff_t (*llseek) (struct file *, loff_t, int);
  ssize_t (*read) (struct file *, char *, size_t, loff_t *);
  ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
  int (*readdir) (struct file *, void *, filldir_t);
  unsigned int (*poll) (struct file *, struct poll_table_struct *);
  int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
  int (*mmap) (struct file *, struct vm_area_struct *);
  int (*open) (struct inode *, struct file *);
  int (*flush) (struct file *);
  int (*release) (struct inode *, struct file *);
  int (*fsync) (struct file *, struct dentry *, int datasync);
  int (*fasync) (int, struct file *, int);
  int (*lock) (struct file *, int, struct file_lock *);
  ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
  ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
  ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
  unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};
           

這種結構的作用類似與 C++ 中的“接口類”,它是用 C 語言進行軟體抽象設計時最重要的工具。通過它,将一組通用的操作抽象出來,核心的代碼隻針對這種“接口結構”進行操作,而這些函數的具體實作由不同的“子類”去完成。

以這個 file_operations“接口”為例,它是“目錄節點”提供的操作接口。不同的檔案系統需要提供這些函數的具體實作。

[size=xx-large][b]三、虛拟檔案系統[/b][/size]

什麼是虛拟檔案系統(後文簡稱VFS)?

Linux 支援很多種檔案系統,如 NTFS、EXT2、EXT3 等等,這些都是某種具體的檔案系統的實作。

VFS 是一套代碼架構(framework),它處于檔案系統的使用者與具體的檔案系統之間,将兩者隔離開來。這種引入一個抽象層次的設計思想,即“上層不依賴于具體實作,而依賴于接口;下層不依賴于具體實作,而依賴于接口”,就是著名的“依賴反轉”,它在 Linux核心中随處可見。

[img]http://dl.iteye.com/upload/attachment/373529/b9959f95-a1f9-39fa-9499-e9c3afb438d4.jpg[/img]

VFS架構的設計,需要滿足如下需求:

1、 為上層的使用者提供統一的檔案和目錄的操作接口,如 open, read, write

2、 為下層的具體的檔案系統,定義一系列統一的操作“接口”, 如 file_operations, inode_operations, dentry_operation,而具體的檔案系統必須實作這些接口,才能融入VFS架構中。

為此,VFS 需要:

1、 定義一套檔案系統的統一概念

2、 在這套概念基礎上,實作提供給上層使用者的操作接口,如 open, read, write 等

3、 提供一套機制,讓下層的具體的檔案系統可融入 VFS 架構中,如檔案系統的“注冊”和“安裝”

本文重點就是學習VFS的重要概念以及在此基礎上的重要操作。

[size=xx-large][b]四、VFS核心概念[/b][/size]

1、 VFS 通過樹狀結構來管理檔案系統,樹狀結構的任何一個節點都是“目錄節點”

2、 樹狀結構具有一個“根節點”

3、 VFS 通過“超級塊”來了解一個具體檔案系統的所有需要的資訊。具體檔案系統必須先向VFS注冊,注冊後,VFS就可以獲得該檔案系統的“超級塊”。

4、 具體檔案系統可被安裝到某個“目錄節點”上,安裝後,具體檔案系統才可以被使用

5、 使用者對檔案的操作,就是通過VFS 的接口,找到對應檔案的“目錄節點”,然後調用該“目錄節點”對應的操作接口。

例如下圖:

1、 綠色代表“根檔案系統”

2、 黃色代表某一個檔案系統 XXFS

3、 根檔案系統安裝到“根目錄節點”上

4、 XXFS 安裝到目錄節點B上

[img]http://dl.iteye.com/upload/attachment/373254/d6c894cb-24ce-34bb-b509-7ecc83eba887.jpg[/img]

[size=xx-large][b]五、目錄節點[/b][/size]

[size=x-large][b]1、inode 和 file_operations[/b][/size]

1、 inode 用以描述“目錄節點” ,它描述了一個目錄節點實體上的屬性,例如大小,建立時間,修改時間、uid、gid 等

2、 file_operations 是“目錄節點”提供的操作接口。包括 open, read, wirte, ioctl, llseek, mmap 等操作的實作。

3、 inode 通過成員 i_fop 對應一個 file_operations

4、 打開檔案的過程就是尋找 “目錄節點”對應的 inode 的過程

5、 檔案被打開後,inode 和 file_operation 都已經在記憶體中建立,file_operations 的指針也已經指向了具體檔案系統提供的函數,此後都檔案的操作,都由這些函數來完成。

例如打開了一個普通檔案 /root/file,其所在檔案系統格式是 ext2,那麼,記憶體中結構如下:

[img]http://dl.iteye.com/upload/attachment/372343/32d3ef51-96b1-3a15-a8b2-37d6064a3fad.jpg[/img]

[size=x-large][b]2、dentry [/b][/size]

本來,inode 中應該包括“目錄節點”的名稱,但由于符号連結的存在,導緻一個實體檔案可能有多個檔案名,是以把和“目錄節點”名稱相關的部分從 inode 結構中分開,放在一個專門的 dentry 結構中。這樣:

1、 一個dentry 通過成員 d_inode 對應到一個 inode上,尋找 inode 的過程變成了尋找 dentry 的過程。是以,dentry 變得更加關鍵,inode 常常被 dentry 所遮掩。可以說, dentry 是檔案系統中最核心的資料結構,它的身影無處不在。

2、 由于符号連結的存在,導緻多個 dentry 可能對應到同一個 inode 上

例如,有一個符号連結 /tmp/abc 指向一個普通檔案 /root/file,那麼 dentry 與 inode 之間的關系大緻如下:

[img]http://dl.iteye.com/upload/attachment/372339/145aedda-9441-36f3-aec7-5b26c15dc02a.jpg[/img]

[size=xx-large][b]六、超級塊[/b][/size]

[size=x-large][b]1、super_block 和 super_operation[/b]s[/size]

super_block 儲存了檔案系統的整體資訊,如通路權限;

super_operations 則是“超級塊”提供的操作接口

struct super_operations {

            struct inode *(*alloc_inode)(struct super_block *sb);
            void (*destroy_inode)(struct inode *);
            void (*read_inode) (struct inode *);
            void (*read_inode2) (struct inode *, void *) ;
            void (*dirty_inode) (struct inode *);
            void (*write_inode) (struct inode *, int);
            void (*put_inode) (struct inode *);
            void (*delete_inode) (struct inode *);
            void (*put_super) (struct super_block *);
            void (*write_super) (struct super_block *);
            int (*sync_fs) (struct super_block *);
            void (*write_super_lockfs) (struct super_block *);
            void (*unlockfs) (struct super_block *);
            int (*statfs) (struct super_block *, struct statfs *);
            int (*remount_fs) (struct super_block *, int *, char *);
            void (*clear_inode) (struct inode *);
            void (*umount_begin) (struct super_block *);
            struct dentry * (*fh_to_dentry)(struct super_block *sb, __u32 *fh, int len, int fhtype, int parent);
            int (*dentry_to_fh)(struct dentry *, __u32 *fh, int *lenp, int need_parent);
            int (*show_options)(struct seq_file *, struct vfsmount *);
};
           

我們通過分析“擷取一個 inode ”的過程來隻了解這個“接口”中兩個成員 alloc_inode 和 read_inode 的作用。

在檔案系統的操作中,經常需要獲得一個“目錄節點”對應的 inode,這個 inode 有可能已經存在于記憶體中了,也可能還沒有,需要建立一個新的 inode,并從磁盤上讀取相應的資訊來填充。

對應的代碼是 iget() (inlcude/linux/fs.h),過程如下:

1、 通過 iget4_locked() 擷取 inode。如果 inode 在記憶體中已經存在,則直接傳回;否則建立一個新的 inode

2、 如果是新建立的 inode,通過 super_block->s_op->read_inode() 來填充它。也就是說,如何填充一個新建立的 inode, 是由具體檔案系統提供的函數實作的。

static inline struct inode *iget(struct super_block *sb, unsigned long ino)
{
            struct inode *inode = iget4_locked(sb, ino, NULL, NULL);


            if (inode && (inode->i_state & I_NEW)) {
                        sb->s_op->read_inode(inode);
                        unlock_new_inode(inode);
            }
            return inode;
}
           

iget4_locked() 首先在全局的 inode hash table 中尋找,如果找不到,則調用 get_new_inode() ,進而調用 alloc_inode() 來建立一個新的 inode

在 alloc_inode() 中可以看到,如果具體檔案系統提供了建立 inode 的方法,則由具體檔案系統來負責建立,否則采用系統預設的的建立方法。

static struct inode *alloc_inode(struct super_block *sb)
{

            static struct address_space_operations empty_aops;
            static struct inode_operations empty_iops;
            static struct file_operations empty_fops;
            struct inode *inode;

             if (sb->s_op->alloc_inode)
                        inode = sb->s_op->alloc_inode(sb);
            else {
                        inode = (struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL);
                         if (inode)
                                    memset(&inode->u, 0, sizeof(inode->u));
            }

             if (inode) {
                        struct address_space * const mapping = &inode->i_data;
                        inode->i_sb = sb;
                        inode->i_dev = sb->s_dev;
                        inode->i_blkbits = sb->s_blocksize_bits;
                        inode->i_flags = 0;
                        atomic_set(&inode->i_count, 1);
                        inode->i_sock = 0;
                        inode->i_op = &empty_iops;
                        inode->i_fop = &empty_fops;
                        inode->i_nlink = 1;
                        atomic_set(&inode->i_writecount, 0);
                        inode->i_size = 0;
                        inode->i_blocks = 0;
                        inode->i_bytes = 0;
                        inode->i_generation = 0;
                        memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
                        inode->i_pipe = NULL;
                        inode->i_bdev = NULL;
                        inode->i_cdev = NULL;

                        mapping->a_ops = &empty_aops;
                        mapping->host = inode;
                        mapping->gfp_mask = GFP_HIGHUSER;
                        inode->i_mapping = mapping;
            }
            return inode;
}
           

super_block 是在安裝檔案系統的時候建立的,後面會看到它和其它結構之間的關系。

[size=xx-large][b]七、 注冊檔案系統[/b][/size]

一個具體的檔案系統,必須首先向VFS注冊,才能被使用。

通過register_filesystem() ,可以将一個“檔案系統類型”結構 file_system_type注冊到核心中一個全局的連結清單file_systems 上。

檔案系統注冊的主要目的,就是讓 VFS 建立該檔案系統的“超級塊”結構。

struct file_system_type {

            const char *name;
            int fs_flags;
            struct super_block *(*read_super) (struct super_block *, void *, int);
            struct module *owner;
            struct file_system_type * next;
            struct list_head fs_supers;
};

int register_filesystem(struct file_system_type * fs)
{
            int res = 0;
            struct file_system_type ** p;

            if (!fs)
                        return -EINVAL;

            if (fs->next)
                        return -EBUSY;

            INIT_LIST_HEAD(&fs->fs_supers);
            write_lock(&file_systems_lock);
            p = find_filesystem(fs->name);

            if (*p)
                        res = -EBUSY;
            else
                        *p = fs;

            write_unlock(&file_systems_lock);
            return res;
}
           

這個結構中最關鍵的就是 read_super() 這個函數指針,它就是用于建立并設定 super_block 的目的的。

因為安裝一個檔案系統的關鍵一步就是要為“被安裝裝置”建立和設定一個 super_block,而不同的具體的檔案系統的 super_block 有自己特定的資訊,是以要求具體的檔案系統首先向核心注冊,并提供 read_super() 的實作。

[size=xx-large][b]八、 安裝檔案系統[/b][/size]

1、 一個經過格式化的塊裝置,隻有安裝後,才能融入 Linux 的 VFS 之中。

2、 安裝一個檔案系統,必須指定一個目錄作為安裝點。

3、 一個裝置可以同時被安裝到多個目錄上。

4、 如果某個目錄下原來有一些檔案和子目錄,一旦将一個裝置安裝到目錄下後,則原有的檔案和子目錄消失。因為這個目錄已經變成了一個安裝點。

5、 一個目錄節點下可以同時安裝多個裝置。

[size=x-large][b]1、“根安裝點”、“根裝置”和“根檔案系統”[/b][/size]

安裝一個檔案系統,除了需要“被安裝裝置”外,還要指定一個“安裝點”。“安裝點”是已經存在的一個目錄節點。例如把 /dev/sda1 安裝到 /mnt/win 下,那麼 /mnt/win 就是“安裝點”。

可是檔案系統要先安裝後使用。是以,要使用 /mnt/win 這個“安裝點”,必然要求它所在檔案系統已也經被安裝。

也就是說,安裝一個檔案系統,需要另外一個檔案系統已經被安裝。

這是一個雞生蛋,蛋生雞的問題:最頂層的檔案系統是如何被安裝的?

答案是,最頂層檔案系統在核心初始化的時候被安裝在“根安裝點”上的,而根安裝點不屬于任何檔案系統,它對應的 dentry 、inode 等結構是由核心在初始化階段憑空構造出來的。

最頂層的檔案系統叫做“根檔案系統”。Linux 在啟動的時候,要求使用者必須指定一個“根裝置”,核心在初始化階段,将“根裝置”安裝到“根安裝點”上,進而有了根檔案系統。這樣,檔案系統才算準備就緒。此後,使用者就可以通過 mount 指令來安裝新的裝置。

[size=x-large][b]2、安裝連接配接件 vfsmount[/b][/size]

“安裝”一個檔案系統涉及“被安裝裝置”和“安裝點”兩個部分,安裝的過程就是把“安裝點”和“被安裝裝置”關聯起來,這是通過一個“安裝連接配接件”結構 vfsmount 來完成的。

vfsmount 将“安裝點”dentry 和“被安裝裝置”的根目錄節點 dentry 關聯起來。

每安裝一次檔案系統,會導緻:

1、 建立一個 vfsmount

2、 為“被安裝裝置”建立一個 super_block,并由具體的檔案系統來設定這個 super_block。(我們在“注冊檔案系統”一節将再來分析這一步)

3、 為被安裝裝置的根目錄節點建立 dentry

4、 為被安裝裝置的根目錄節點建立 inode, 并由 super_operations->read_inode() 來設定此 inode

5、 将 super_block 與“被安裝裝置“根目錄節點 dentry 關聯起來

6、 将 vfsmount 與“被安裝裝置”的根目錄節點 dentry 關聯起來

在核心将根裝置安裝到“根安裝點”上後,記憶體中有如下結構關系:

[img]http://dl.iteye.com/upload/attachment/372349/15ae40c0-f681-34ef-9b32-a3565fadaf14.jpg[/img]

現在假設我們在 /mnt/win 下安裝了 /dev/sda1, /dev/sda1 下有 dir1,然後又在 dir1 下安裝了 /dev/sda2,那麼記憶體中就有了如下的結構關系

[img]http://dl.iteye.com/upload/attachment/372351/4990b4ef-e920-3434-a1c6-5fd0694304b8.jpg[/img]

[size=xx-large][b]九、尋找目标節點[/b][/size]

VFS 中一個最關鍵以及最頻繁的操作,就是根據路徑名尋找目标節點的 dentry 以及 inode 。

例如要打開 /mnt/win/dir1/abc 這個檔案,就是根據這個路徑,找到‘abc’ 對應的 dentry ,進而得到 inode 的過程。

[size=x-large][b]1、 尋找過程[/b][/size]

尋找過程大緻如下:

1、 首先找到根檔案系統的根目錄節點 dentry 和 inode

2、 由這個 inode 提供的操作接口 i_op->lookup(),找到下一層節點 ‘mnt’ 的 dentry 和 inode

3、 由 ‘mnt’ 的 inode 找到 ‘win’ 的 dentry 和 inode

4、 由于 ‘win’ 是個“安裝點”,是以需要找到“被安裝裝置”/dev/sda1 根目錄節點的 dentry 和 inode,隻要找到 vfsmount B,就可以完成這個任務。

5、 然後由 /dev/sda1 根目錄節點的 inode 負責找到下一層節點 ‘dir1’ 的 dentry 和 inode

6、 由于 dir1 是個“安裝點”,是以需要借助 vfsmount C 找到 /dev/sda2 的根目錄節點 dentry 和 inode

7、 最後由這個 inode 負責找到 ‘abc’ 的 dentry 和 inode

可以看到,整個尋找過程是一個遞歸的過程。

完成尋找後,記憶體中結構如下,其中紅色線條是尋找目标節點的路徑

[img]http://dl.iteye.com/upload/attachment/372347/e65cd37c-e81c-334b-a5dd-3ad1ea2c7600.jpg[/img]

現在有兩個問題:

1、在尋找過程的第一步,如何得到“根檔案系統”的根目錄節點的 dentry?

答案是這個 dentry 是被儲存在程序的 task_struct 中的。後面分析程序與檔案系統關系的時候再說這個。

2、如何尋找 vfsmount B 和 C?

這是接下來要分析的。

[size=x-large][b]2、vfsmount 之間的關系[/b][/size]

我們知道, vfsmount A、B、C 之間形成了一種父子關系,為什麼不根據 A 來找到 B ,根據 B 找到 C 了?

這是因為一個檔案系統可能同時被安裝到不同的“安裝點”上。

假設把 /dev/sda1 同時安裝到 /mnt/win 和 /mnt/linux 下

現在 /mnt/win/dir1 和 /mnt/linux/dir1 對應的是同一個 dentry!!!

然後,又把 /dev/sda2 分别安裝到 /mnt/win/dir1 和 /mnt/linux/dir1 下

現在, vfsmount 與 dentry 之間的關系大緻如下。可以看到:

1、 現在有四個 vfsmount A, B, C, D

2、 A 和B對應着不同的安裝點 ‘win’ 和 ‘linux’,但是都指向 /dev/sda1 根目錄的 dentry

3、 C 和D 對應着這相同的安裝點 ‘dir1’,也都指向 /dev/sda2 根目錄的 dentry

4、 C 是 A 的 child, A是 C 的 parent

5、 D 是 B 的 child, B 是 D 的 parent

[img]http://dl.iteye.com/upload/attachment/372353/7af1ed28-c580-3380-82b1-dffeb5e13d20.jpg[/img]

[size=x-large][b]3、 搜尋輔助結構 nameidata[/b][/size]

在遞歸尋找目标節點的過程中,需要借助一個搜尋輔助結構 nameidata,這是一個臨時結構,僅僅用在尋找目标節點的過程中。

struct nameidata {

       struct dentry *dentry;
       struct vfsmount *mnt;
       struct qstr last;
       unsigned int flags;
       int last_type;
};
           

在搜尋初始化時,建立 nameidata,其中 mnt 指向 current->fs->rootmnt,dentry 指向 current->fs->root

dentry 随着目錄節點的深入而不斷變化;

而 mnt 則在每進入一個新的檔案系統後發生變化

以尋找 /mnt/win/dir1/abc 為例

開始的時候, mnt 指向 vfsmount A,dentry 指向根裝置的根目錄

随後,dentry 先後指向 ‘mnt’ 和 ‘win’ 對應的 dentry

然後當尋找到 vfsmount B 後,mnt 指向了它,而 dentry 則指向了 /dev/sda1 根目錄的 dentry

有了這個結構,上一節的問題就可以得到解決了:

在尋找 /mnt/win/dir1/abc 的過程中,首先找到 A,接下來在要決定選 C 還是 D,因為是從 A 搜尋下來的, C 是 A 的 child,是以選擇 C 而不是 D;同樣,如果是尋找 /mnt/linux/dir1/abc,則會依次選擇 B 和D。這就是為什麼 nameidata 中要帶着一個 vfsmount 的原因。

[size=xx-large][b]十、檔案的打開與讀寫[/b][/size]

[size=x-large][b]1、 “打開檔案”結構 file[/b][/size]

一個檔案每被打開一次,就對應着一個 file 結構。

我們知道,每個檔案對應着一個 dentry 和 inode,每打開一個檔案,隻要找到對應的 dentry 和 inode 不就可以了麼?為什麼還要引入這個 file 結構?

這是因為一個檔案可以被同時打開多次,每次打開的方式也可以不一樣。

而dentry 和 inode 隻能描述一個實體的檔案,無法描述“打開”這個概念。

是以有必要引入 file 結構,來描述一個“被打開的檔案”。每打開一個檔案,就建立一個 file 結構。

file 結構中包含以下資訊:

打開這個檔案的程序的 uid,pid

打開的方式

讀寫的方式

目前在檔案中的位置

實際上,打開檔案的過程正是建立file, dentry, inode 之間的關聯的過程。

[img]http://dl.iteye.com/upload/attachment/372341/21c984ad-8b73-3413-98db-2bf63d4a1f91.jpg[/img]

[size=x-large][b]2、檔案的讀寫[/b][/size]

檔案一旦被打開,資料結構之間的關系已經建立,後面對檔案的讀寫以及其它操作都變得很簡單。就是根據 fd 找到 file 結構,然後找到 dentry 和 inode,最後通過 inode->i_fop 中對應的函數進行具體的讀寫等操作即可。

[size=xx-large][b]十一、程序與檔案系統的關聯[/b][/size]

最後,來了解一下一個程序,與檔案系統有哪些關聯。

[size=x-large][b]1、 “打開檔案”表和 files_struct結構[/b][/size]

一個程序可以打開多個檔案,每打開一個檔案,建立一個 file 結構。所有的 file 結構的指針儲存在一個數組中。而檔案描述符正是這個數組的下标。

我記得以前剛開始學習程式設計的時候,怎麼都無法了解這個“檔案描述符”的概念。現在從核心的角度去看,就很容易明白“檔案描述符”是怎麼回事了。使用者僅僅看到一個“整數”,實際底層對應着的是 file, dentry, inode 等複雜的資料結構。

files_struct 用于管理這個“打開檔案”表。

其中的 fd_arrar[] 就是“打開檔案”表。

task_struct 中通過成員 files 與 files_struct 關聯起來。

[size=x-large][b]2、 struct fs_struct[/b][/size]

task_struct 中與檔案系統相關的還有另外一個成員 fs,它指向一個 fs_struct 。

struct fs_struct {

       atomic_t count;
       rwlock_t lock;
       int umask;
       struct dentry * root, * pwd, * altroot;
       struct vfsmount * rootmnt, * pwdmnt, * altrootmnt;
};
           

其中:

root 指向此程序的“根目錄”,通常就是“根檔案系統”的根目錄 dentry

pwd 指向此程序目前所在目錄的 dentry

是以,通過 task_struct->fs->root,就可以找到“根檔案系統”的根目錄 dentry,這就回答了 5.1 小節的第一個問題。

rootmnt :指向“安裝”根檔案系統時建立的那個 vfsmount

pwdmnt:指向“安裝”目前工作目錄所在檔案系統時建立的那個 vfsmount

這兩個域用于初始化 nameidata 結構。

[size=x-large][b]3、 程序與檔案系統的結構關系圖[/b][/size]

下圖描述了程序與檔案系統之間的結構關系圖:

[img]http://dl.iteye.com/upload/attachment/372345/5aa31237-9605-3660-8048-3c406c1617bb.jpg[/img]

[size=xx-large][b]十二、參考資料[/b][/size]

1、《Linux 源碼情景分析》上冊

2、Linux 2.4.30 源碼