1. 程式人生 > >linux文件系統 - 初始化(一)

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

成員函數 cat 文章 記錄 inf htm 源代碼 設備驅動模型 proxy

術語表:

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上進行了。

技術分享圖片 圖3

六、總結

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

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