歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

解析linux根文件系統的掛載過程

一:前言
前段時間在編譯kernel的時候發現rootfs掛載不上。相同的root選項設置舊版的image卻可以。為了徹底解決這個問題。研究了一下rootfs的掛載過程。特總結如下,希望能給這部份知識點比較迷茫的朋友一點幫助。
二:rootfs的種類
總的來說,rootfs分為兩種:虛擬rootfs和真實rootfs.現在kernel的發展趨勢是將更多的功能放到用戶空間完成。以保持內核的精簡。虛擬rootfs也是各linux發行廠商普遍采用的一種方式。可以將一部份的初始化工作放在虛擬的rootfs裡完成。然後切換到真實的文件系統.
在虛擬rootfs的發展過程中。又有以下幾個版本:
initramfs:
Initramfs是在 kernel 2.5中引入的技術,實際上它的含義就是:在內核鏡像中附加一個cpio包,這個cpio包中包含了一個小型的文件系統,當內核啟動時,內核將這個 cpio包解開,並且將其中包含的文件系統釋放到rootfs中,內核中的一部分初始化代碼會放到這個文件系統中,作為用戶層進程來執行。這樣帶來的明顯的好處是精簡了內核的初始化代碼,而且使得內核的初始化過程更容易定制。這種這種方式的rootfs是包含在kernel image之中的.

cpio-initrd: cpio格式的rootfs

image-initrd:傳統格式的rootfs
關於這兩種虛擬文件系統的制作請自行參閱其它資料

三:rootfs文件系統的掛載過程
這裡說的rootfs不同於上面分析的rootfs。這裡指的是系統初始化時的根結點。即/結點。它是其於內存的rootfs文件系統。這部份之前在<< linux啟動過程分析>>和文件系統中已經分析過。為了知識的連貫性這裡再重復一次。
Start_kernel()àmnt_init():
void __init mnt_init(void)

{
……
……
init_rootfs();
init_mount_tree();
}

Init_rootfs的代碼如下:
int __init init_rootfs(void)

{
int err;

err = bdi_init(&ramfs_backing_dev_info);

if (err)
return err;

err = register_filesystem(&rootfs_fs_type);
if (err)
bdi_destroy(&ramfs_backing_dev_info);

return err;
}
這個函數很簡單。就是注冊了rootfs的文件系統.
init_mount_tree()代碼如下:
static void __init init_mount_tree(void)

{
struct vfsmount *mnt;
struct mnt_namespace *ns;
struct path root;

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);
set_fs_root(current->fs, &root);
}
在這裡,將rootfs文件系統掛載。它的掛載點默認為”/”.最後切換進程的根目錄和當前目錄為”/”.這也就是根目錄的由來。不過這裡只是初始化。等掛載完具體的文件系統之後,一般都會將根目錄切換到具體的文件系統。所以在系統啟動之後,用mount命令是看不到rootfs的掛載信息的.

四:虛擬文件系統的掛載
根目錄已經掛上去了,可以掛載具體的文件系統了.
在start_kernel()àrest_init()àkernel_init():
static int __init kernel_init(void * unused)

{
……
……
do_basic_setup();
if (!ramdisk_execute_command)

ramdisk_execute_command = "/init";


if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {

ramdisk_execute_command = NULL;

prepare_namespace();
}

/*
* Ok, we have completed the initial bootup, and

* we're essentially up and running. Get rid of the

* initmem segments and start the user-mode stuff..

*/
init_post();
return 0;
}
do_basic_setup()是一個很關鍵的函數,所有直接編譯在kernel中的模塊都是由它啟動的。代碼片段如下:
static void __init do_basic_setup(void)

{
/* drivers will send hotplug events */
init_workqueues();
usermodehelper_init();
driver_init();
init_irq_proc();
do_initcalls();
}
Do_initcalls()用來啟動所有在__initcall_start和__initcall_end段的函數,而靜態編譯進內核的modules也會將其入口放置在這段區間裡。
跟根文件系統相關的初始化函數都會由rootfs_initcall()所引用。注意到有以下初始化函數:
rootfs_initcall(populate_rootfs);
也就是說會在系統初始化的時候會調用populate_rootfs進行初始化。代碼如下:
static int __init populate_rootfs(void)

{
char *err = unpack_to_rootfs(__initramfs_start,
__initramfs_end - __initramfs_start, 0);
if (err)
panic(err);
if (initrd_start) {
#ifdef CONFIG_BLK_DEV_RAM

int fd;
printk(KERN_INFO "checking if image is initramfs...");

err = unpack_to_rootfs((char *)initrd_start,

Copyright © Linux教程網 All Rights Reserved