Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / Linux / mini6410 Linux--根文件系统的挂载过程分析

前言:本篇文章以S3C6410公版的Linux BSP和U-Boot来进行分析,文中所有提及的名词和数据都是以该环境为例,所有的代码流程也是以该环境为例来进行分析。哈哈。如果有不正确或者不完善的地方,欢迎前来拍砖留言或者发邮件到guopeixin@126.com进行讨论,先行谢过。简单的来说,根文件系统包括虚拟根文件系统和真实根文件系统。在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 to150% 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 ");printk("Mount-cache hash table entries: %lu ", 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 ",__func__, err);fs_kobj = kobject_create_and_add("fs", NULL);if (!fs_kobj)printk(KERN_WARNING "%s: kobj create error ", __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设备都是以设备文件的方式指定的,如果没有根文件系统,设备文件怎么可能存在呢?