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系統的初始化。下面會詳細介紹每個階段的主要內容。
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上進行了。
六、總結
linux文件系統在初始化時,同時掛載了sysfs和rootfs文件系統,但是只有rootfs處於進程的命名空間中,且進程的root目錄和pwd目錄都指向rootfs的根目錄。至此,linux的VFS已經準備好了根目錄(rootfs的根目錄‘/‘),此時用戶可以使用系統調用對VFS樹進行擴展。
linux文件系統 - 初始化(一)