linux文件系統 - 初始化(一)


術語表:

struct task:進程

struct mnt_namespace:命名空間

struct mount:掛載點

struct vfsmount:掛載項

struct file:文件

struct super_block:超級塊

struct dentry:目錄

struct inode:索引節點

 

一、目的

    linux文件系統主要分為三個部分:文件系統調用;虛擬文件系統(VFS);掛載到VFS的實際文件系統。

    其中,VFS是核心,linux文件系統的本質就是在內存中創建一棵VFS樹。當根目錄被創建后,用戶就可以使用系統調用在VFS上創建文件、刪除文件、掛載各種文件系統等操作。

    該系列文章主要分析linux3.10文件系統初始化過程,分為三個階段:

    1、掛載根文件系統(rootfs);

    2、加載initrd;

    3、掛載磁盤文件系統;

 

二、常用數據結構

    linux文件系統中重要的數據結構有:文件、掛載點、超級塊、目錄項、索引節點等。每個數據結構的具體實現請參見源代碼,這里不再描述。

    為了直觀的表示數據結構之間的關系,請參見圖1:圖中含有兩個文件系統(紅色和綠色表示的部分),並且綠色文件系統掛載在紅色文件系統tmp目錄下。一般來說,每個文件系統在VFS層都是由掛載點、超級塊、目錄和索引節點組成;當掛載一個文件系統時,實際也就是創建這四個數據結構的過程,因此這四個數據結構的地位很重要,關系也很緊密。由於VFS要求實際的文件系統必須提供以上數據結構,所以不同的文件系統在VFS層可以互相訪問。

    如果進程打開了某個文件,還會創建file(文件)數據結構,這樣進程就可以通過file來訪問VFS的文件系統了。

    另外,該圖只給出了主要的關系結構,忽略了部分細節。

                               圖1

 

三、函數調用關系

    圖2描述了文件系統初始化過程中主要的函數調用關系。linux文件系統初始化過程主要分為三個階段:

    1、vfs_caches_init()負責掛載rootfs文件系統,並創建了第一個掛載點目錄:'/';

    2、rest_init()負責加載initrd文件,擴展VFS樹,創建基本的文件系統目錄拓撲;

    3、init程序負責掛載磁盤文件系統,並將文件系統的根目錄從rootfs切換到磁盤文件系統;

                   圖2

 

四、總結

    linux文件系統初始化過程主要分為三個階段:掛載rootfs,提供第一個掛載點''/;加載initrd,擴展VFS樹;執行init程序,完成linux系統的初始化。下面會詳細介紹每個階段的主要內容。

 

掛載rootfs文件系統

一、目的

    本文主要講述linux3.10文件系統初始化過程的第一階段:掛載rootfs文件系統。

    rootfs是基於內存的文件系統,所有操作都在內存中完成;也沒有實際的存儲設備,所以不需要設備驅動程序的參與。基於以上原因,linux在啟動階段使用rootfs文件系統,當磁盤驅動程序和磁盤文件系統成功加載后,linux系統會將系統根目錄從rootfs切換到磁盤文件系統。

 

二、主要函數調用過程

    圖1描述了掛載rootfs的函數調用關系(圖中紅色部分),便於后面的分析。

    從圖中發現,在掛載rootfs前會先掛載sysfs,這樣做的原因是確保sysfs能夠完整的記錄下設備驅動模型。

    sysfs_init()完成注冊和掛載sysfs文件系統的功能;init_rootfs()負責注冊rootfs,init_mount_tree()負責掛載rootfs,並將init_task的命名空間與之聯系起來。

                                   圖1

 

三、linux文件系統初始化

    vfs_cache_init()首先建立並初始化目錄hash表dentry_hashtable和索引節點hash表inode_hashtable;然后設置內核可以打開的最大文件數;最后調用mnt_init()完成sysfs和rootfs文件系統的注冊和掛載。

    linux使用哈希表存儲目錄和索引節點,以提高目錄和索引節點的查找效率;dentry_hashtable是目錄哈希表,inode_hashtable是索引節點哈希表。

 

四、掛載sysfs文件系統

    sysfs用來記錄和展示linux驅動模型,sysfs先於rootfs掛載是為全面展示linux驅動模型做好准備。

    mnt_init()調用sysfs_init()注冊並掛載sysfs文件系統,然后調用kobject_create_and_add()創建"fs"目錄。

復制代碼
2735 err = sysfs_init(); 2736 if (err) 2737 printk(KERN_WARNING "%s: sysfs_init error: %d\n", 2738 __func__, err); 2739 fs_kobj = kobject_create_and_add("fs", NULL); 2740 if (!fs_kobj) 2741 printk(KERN_WARNING "%s: kobj create error\n", __func__); 
復制代碼

    下面詳細介紹sysfs文件系統的掛載過程:

    1、sysfs_init()調用register_filesystem()注冊文件系統類型sysfs_fs_type,並加入到全局單鏈表file_systems中。sysfs_fs_type定義如下,.mount成員函數負責超級塊、根目錄和索引節點的創建和初始化工作。

復制代碼
173     err = register_filesystem(&sysfs_fs_type); 174 if (!err) { 175 sysfs_mnt = kern_mount(&sysfs_fs_type); 176 if (IS_ERR(sysfs_mnt)) { 177 printk(KERN_ERR "sysfs: could not mount!\n"); 178 err = PTR_ERR(sysfs_mnt); 179 sysfs_mnt = NULL; 180 unregister_filesystem(&sysfs_fs_type); 181 goto out_err; 182 } 
復制代碼
復制代碼
 
152 static struct file_system_type sysfs_fs_type = { 153 .name = "sysfs", 154 .mount = sysfs_mount, 155 .kill_sb = sysfs_kill_sb, 156 .fs_flags = FS_USERNS_MOUNT, 157 }; 
復制代碼

 

    2、sysfs_init()->kern_mount()->vfs_kern_mount()創建並初始化struct mount掛載點,並使用全局變量sysfs_mnt保存該掛載點的掛載項(mnt成員)。

 
783     mnt = alloc_vfsmnt(name); 784 if (!mnt) 785 return ERR_PTR(-ENOMEM); 

 

    3、kern_mount()調用sysfs_fs_type的.mount成員sysfs_mount()創建並初始化超級塊、根目錄'/'、根目錄的索引節點等數據結構;並且把超級塊添加到全局單鏈表super_blocks中,把索引節點添加到hash表inode_hashtable和超級塊的inode鏈表中。

    目前,我們可以得出一個重要結論:kern_mount()主要完成掛載點、超級塊、根目錄和索引節點的創建和初始化操作,可以看成是一個原子操作,這個函數以后會頻繁使用。

復制代碼
790     root = mount_fs(type, flags, name, data); 1091 struct dentry * 1092 mount_fs(struct file_system_type *type, int flags, const char *name, void*data) 1093 { 1094 struct dentry *root; ... 1108 1109 root = type->mount(type, flags, name, data); 
復制代碼
復制代碼
 
107 static struct dentry *sysfs_mount(struct file_system_type *fs_type, 108 int flags, const char *dev_name, void *data) 109 { ... 112 struct super_block *sb; ... 125 sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info); ... 130 if (!sb->s_root) { 131 error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); 4、vfs_kern_mount()初始化掛載點的根目錄和超級塊。
復制代碼

 

 

復制代碼
 

796 mnt->mnt.mnt_root = root; 797 mnt->mnt.mnt_sb = root->d_sb; 798 mnt->mnt_mountpoint = mnt->mnt.mnt_root; 799 mnt->mnt_parent = mnt; 5、mnt_init()調用kobject_create_and_add()創建"fs"目錄。
復制代碼

 

 

    通過以上步驟,sysfs文件系統在VFS中的視圖如圖2所示:掛載點指向超級塊和根目錄;超級塊處在super_blocks單鏈表中,並且鏈接起所有屬於該文件系統的索引節點;根目錄'/'和目錄"fs"指向各自的索引節點;為了提高查找效率,索引節點保存在hash表中。

                    圖2

 

五、掛載rootfs文件系統

    mnt_init()調用init_rootfs()注冊rootfs,然后調用init_mount_tree()掛載rootfs。

    下面詳細介紹rootfs文件系統的掛載過程:
    1、mnt_init()調用init_rootfs()注冊文件系統類型rootfs_fs_type,並加入到全局單鏈表file_systems中。

rootfs_fs_type定義如下,mount成員函數負責超級塊、根目錄和索引節點的建立和初始化工作。


265 static struct file_system_type rootfs_fs_type = { 266 .name = "rootfs", 267 .mount = rootfs_mount, 268 .kill_sb = kill_litter_super, 269 }; 

 

    2、init_mount_tree()調用vfs_kern_mount()掛載rootfs文件系統,詳細的掛載過程與sysfs文件系統類似,不再贅述。

    3、init_mount_tree()調用create_mnt_ns()創建命名空間,並設置該命名空間的掛載點為rootfs的掛載點,同時將rootfs的掛載點鏈接到該命名空間的雙向鏈表中。

 
復制代碼
2459 static struct mnt_namespace *create_mnt_ns(struct vfsmount *m) 2460 { 2461 struct mnt_namespace *new_ns = alloc_mnt_ns(&init_user_ns); 2462 if (!IS_ERR(new_ns)) { 2463 struct mount *mnt = real_mount(m); 2464 mnt->mnt_ns = new_ns; 2465 new_ns->root = mnt; 2466 list_add(&mnt->mnt_list, &new_ns->list); 2467 } 
復制代碼

 

    4、init_mount_tree()設置init_task的命名空間,同時調用set_fs_pwd()和set_fs_root()設置init_task任務的當前目錄和根目錄為rootfs的根目錄'/'。

 
復制代碼
2696     ns = create_mnt_ns(mnt); 2697 if (IS_ERR(ns)) 2698 panic("Can't allocate initial namespace"); 2699 2700 init_task.nsproxy->mnt_ns = ns; 2701 get_mnt_ns(ns); 2702 2703 root.mnt = mnt; 2704 root.dentry = mnt->mnt_root; 2705 2706 set_fs_pwd(current->fs, &root); 2707 set_fs_root(current->fs, &root); 
復制代碼

 

    通過以上分析,我們發現sysfs和rootfs的區別在於:雖然系統同時掛載了sysfs和rootfs文件系統,但是只有rootfs處於init_task進程的命名空間內,也就是說系統當前實際使用的是rootfs文件系統。

    此時,sysfs和rootfs在VFS中的視圖如圖3所示:為了突出主要關系,省略了掛載點指向超級塊和根目錄。
從圖中看出,rootfs處於進程的命名空間中,並且進程的fs_struct數據結構的root和pwd都指向了rootfs的根目錄'/',所以用戶實際使用的是rootfs文件系統。另外,rootfs為VFS提供了'/'根目錄,所以文件操作和文件系統的掛載操作都可以在VFS上進行了。

                   圖3
 

六、總結

    linux文件系統在初始化時,同時掛載了sysfs和rootfs文件系統,但是只有rootfs處於進程的命名空間中,且進程的root目錄和pwd目錄都指向rootfs的根目錄。至此,linux的VFS已經准備好了根目錄(rootfs的根目錄'/'),此時用戶可以使用系統調用對VFS樹進行擴展。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM