歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

mini6410 Linux--根文件系統的掛載過程分析

前言:

本篇文章以S3C6410公版的Linux BSP和U-Boot來進行分析,文中所有提及的名詞和數據都是以該環境為例,所有的代碼流程也是以該環境為例來進行分析。哈哈。如果有不正確或者不完善的地方,歡迎前來拍磚留言或者發郵件到[email protected]進行討論,先行謝過。

簡單的來說,根文件系統包括虛擬根文件系統和真實根文件系統。在Kernel啟動的初始階段,首先去創建虛擬的根文件系統,接下來再去調用do_mount來加載真正的文件系統,並將根文件系統切換到真正的文件系統,也即真實的文件系統。

一.什麼是根文件系統

在傳統的Windows機器上目錄結構中,可能會包括C:或者D:盤,而他們一般就稱之為特定邏輯磁盤的根目錄。從文件系統的層面來說,每一個分區都包含了一個根目錄區,也即系統中存在多個根目錄。

但是,在Linux系統中,目錄結構與Windows上有較大的不同。系統中只有一個根目錄,路徑是“/”,而其它的分區只是掛載在根目錄中的一個文件夾,如“/proc”和“system”等,這裡的“/”就是Linux中的根目錄。

對應根目錄也就存在一個根目錄文件系統的概念,我們可以將某一個分區掛載為根目錄文件系統,如6410公版中就將mtdblk2掛載為根目錄文件系統。程序中可以通過U-Boot給Kernel指定參數或者編譯選項來指定,如目前的開發板中就通過如下的編譯選項來制定根目錄文件系統:

CONFIG_CMDLINE="console=ttyS0,115200 mem=108M rdinit=/linuxrc root=/dev/mtdblock2"

簡單的來說,根目錄文件系統就是一種目錄結構,包括了Linux啟動的時候所必須的一些目錄結構和重要文件。

根文件系統有兩種,一種是虛擬根文件系統,另外一種是真實的根文件系統。一般情況下,會首先在虛擬的根文件系統中做一部分工作,然後切換到真實的根文件系統下面。

籠統的來說,虛擬的根文件系統包括三種類型,即Initramfs、cpio-initrd和image-initrd。

二.相關重要概念

1. Initrd

Initrd是在Linux中普遍采用的一種技術,就是由Bootloader加載的內存盤。在系統啟動的過程中,首先會執行Initrd中的“某一個文件” 來完成驅動模塊加載的任務,第二階段才會執行真正的根文件系統中的/sbin/init。這裡提到的第一階段是為第二階段服務的,主要是用來加載根文件系統以及根文件系統存儲介質的驅動程序。

資料中提到,存在多種類型的Initrd,實際應用中包括無Initrd、Linux Kernel和Initrd打包、Linux Kernel和Initrd分離以及RAMDisk Initrd。

目前,手中項目采用的就是第四種策略。在系統啟動的時候,U-Boot會將Linux Kernel和Rootfs加載到內存,並跳轉到Linux Kernel的入口地址執行程序。這篇文章將側重對該種情況進行分析。

三.根文件系統加載代碼分析

1. VFS的注冊

首先不得不從老掉牙的Linux系統的函數start_kernel()說起。函數start_kernel()中會去調用vfs_caches_init()來初始化VFS。

下面看一下函數vfs_caches_init ()的代碼:

void __init vfs_caches_init(unsigned long mempages)

{

unsigned long reserve;

/* Base hash sizes on available memory, with a reserve equal to

150% of current kernel size */

reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);

mempages -= reserve;

names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,

SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);

dcache_init();

inode_init();

files_init(mempages);

[1] mnt_init();

bdev_cache_init();

chrdev_init();

}

代碼【1】:vfs_caches_init()中最重要的函數。函數mnt_init()會創建一個rootfs,這是個虛擬的rootfs,即內存文件系統,後面還會指向真實的文件系統。

接下來看一下函數mnt_init():

Void __init mnt_init(void)

{

unsigned u;

int err;

init_rwsem(&namespace_sem);

mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),

0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);

mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);

if (!mount_hashtable)

panic("Failed to allocate mount hash table\n");

printk("Mount-cache hash table entries: %lu\n", HASH_SIZE);

for (u = 0; u < HASH_SIZE; u++)

INIT_LIST_HEAD(&mount_hashtable[u]);

err = sysfs_init();

if (err)

printk(KERN_WARNING "%s: sysfs_init error: %d\n",

__func__, err);

fs_kobj = kobject_create_and_add("fs", NULL);

if (!fs_kobj)

printk(KERN_WARNING "%s: kobj create error\n", __func__);

[1] init_rootfs();

[2] init_mount_tree();

}

代碼[1]:創建虛擬根文件系統;

代碼[2]:注冊根文件系統。

接下來看一下函數init_mount_tree()的代碼:

static void __init init_mount_tree(void)

{

struct vfsmount *mnt;

struct mnt_namespace *ns;

struct path root;

[1] mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);

if (IS_ERR(mnt))

panic("Can't create rootfs");

ns = kmalloc(sizeof(*ns), GFP_KERNEL);

if (!ns)

panic("Can't allocate initial namespace");

atomic_set(&ns->count, 1);

INIT_LIST_HEAD(&ns->list);

init_waitqueue_head(&ns->poll);

ns->event = 0;

list_add(&mnt->mnt_list, &ns->list);

ns->root = mnt;

mnt->mnt_ns = ns;

init_task.nsproxy->mnt_ns = ns;

get_mnt_ns(ns);

root.mnt = ns->root;

root.dentry = ns->root->mnt_root;

set_fs_pwd(current->fs, &root);

[2] set_fs_root(current->fs, &root);

}

代碼[1]:創建虛擬文件系統;

代碼[2]:將當前的文件系統配置為根文件系統。

可能有人會問,為什麼不直接把真實的文件系統配置為根文件系統?

答案很簡單,內核中沒有根文件系統的設備驅動,如USB等存放根文件系統的設備驅動,而且即便你將根文件系統的設備驅動編譯到內核中,此時它們還尚未加載,其實所有的Driver是由在後面的Kernel_Init線程進行加載。所以需要CPIO Initrd、Initrd和RAMDisk Initrd。另外,我們的Root設備都是以設備文件的方式指定的,如果沒有根文件系統,設備文件怎麼可能存在呢?

Copyright © Linux教程網 All Rights Reserved