Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1366322
  • 博文数量: 860
  • 博客积分: 425
  • 博客等级: 下士
  • 技术积分: 1464
  • 用 户 组: 普通用户
  • 注册时间: 2011-08-20 19:57
个人简介

对技术执着

文章分类

全部博文(860)

文章存档

2019年(16)

2018年(12)

2015年(732)

2013年(85)

2012年(15)

我的朋友

分类: LINUX

2015-03-14 15:39:02

原文地址:根文件系统挂载 作者:sjj0412

------------------------------------
本文系本站原创,欢迎转载!

转载请注明出处:http://sjj0412.cublog.cn/
------------------------------------------ 

由bootload进入linux后由head.s进入了start_kernel了.

asmlinkage void __init start_kernel(void)

{

       char * command_line;

       extern struct kernel_param __start___param[], __stop___param[];

       ………………..

       setup_arch(&command_line);

       ………….

       ………..

       vfs_caches_init(num_physpages); ………….     

 

…………...

       rest_init();

}

 

重要函数解释

 

1.Setup_arch是解释bootloader传过来的参数,并附相关参数。

 

void __init setup_arch(char **cmdline_p)

{

       struct tag *tags = (struct tag *)&init_tags;

       struct machine_desc *mdesc;

       char *from = default_command_line;

 

       setup_processor();

       mdesc = setup_machine(machine_arch_type);

       machine_name = mdesc->name;

 

       if (mdesc->soft_reboot)

              reboot_setup("s");

 

       if (mdesc->boot_params)

              tags = phys_to_virt(mdesc->boot_params);

 

       /*

        * If we have the old style parameters, convert them to

        * a tag list.

        */

       if (tags->hdr.tag != ATAG_CORE)

              convert_to_tag_list(tags);

       if (tags->hdr.tag != ATAG_CORE)

              tags = (struct tag *)&init_tags;

 

       if (mdesc->fixup)

              mdesc->fixup(mdesc, tags, &from, &meminfo);

 

       if (tags->hdr.tag == ATAG_CORE) {

              if (meminfo.nr_banks != 0)

                     squash_mem_tags(tags);

              parse_tags(tags);

       }

 

       init_mm.start_code = (unsigned long) &_text;

       init_mm.end_code   = (unsigned long) &_etext;

       init_mm.end_data   = (unsigned long) &_edata;

       init_mm.brk       = (unsigned long) &_end;

 

       memcpy(saved_command_line, from, COMMAND_LINE_SIZE);

       saved_command_line[COMMAND_LINE_SIZE-1] = '\0';

       parse_cmdline(cmdline_p, from);

       paging_init(&meminfo, mdesc);

       request_standard_resources(&meminfo, mdesc);

 

       cpu_init();

 

       /*

        * Set up various architecture-specific pointers

        */

       init_arch_irq = mdesc->init_irq;

       system_timer = mdesc->timer;

       init_machine = mdesc->init_machine;

 

#ifdef CONFIG_VT

#if defined(CONFIG_VGA_CONSOLE)

       conswitchp = &vga_con;

#elif defined(CONFIG_DUMMY_CONSOLE)

       conswitchp = &dummy_con;

#endif

#endif

}

到这里就不得不说__setup宏。

__setup宏来注册关键字及相关联的处理函数,__setup宏在include/linux/init.h中定义,其原型如下:
            __setup(string, function_handler)

中:string是关键字,function_handler是关联处理函数。__setup只是告诉内核在启动时输入串中含有string时,内核要去
执行function_handlerString必须以“=”符结束以使parse_args更方便解析。紧随“=”后的任何文本都会作为输入传给
function_handler

            
下面的例子来自于init/do_mounts.c,其中root_dev_setup作为处理程序被注册给“root=”关键字:
            __setup("root=", root_dev_setup);

比如我们在启动向参数终有

  noinitrd root=/dev/mtdblock2 console=/linuxrc

 setup_arch解释时会发现root=/dev/mtdblock2,然后它就会调用root_dev_setup

static int __init root_dev_setup(char *line)

{

       strlcpy(saved_root_name, line, sizeof(saved_root_name));

       return 1;

}

我们容易看出,这个函数就是给saved_root_name赋值,saved_root_name这个全局变量非常重要,在后面的根目录加载还会讲到。

同理,其他参数也会做相应处理。

2. 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, NULL);

 

       filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,

                     SLAB_HWCACHE_ALIGN|SLAB_PANIC, filp_ctor, filp_dtor);

 

       dcache_init(mempages);

       inode_init(mempages);

       files_init(mempages);

       mnt_init(mempages);

       bdev_cache_init();

       chrdev_init();

}

 

 

 

 

Mnt_init会创建一个rootfs,这个是虚拟的rootfs,是内存文件系统(和ramfs),后面还会指向具体的根文件系统。

void __init mnt_init(unsigned long mempages)

{

       struct list_head *d;

       unsigned int nr_hash;

       int i;

 

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

                     0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);

       sysfs_init();

       init_rootfs();//注册rootfs文件系统。

       init_mount_tree();//创建rootfs文件系统

}

static void __init init_mount_tree(void)

{

       struct vfsmount *mnt;

       struct namespace *namespace;

       struct task_struct *g, *p;

 

       mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);//创建了文件系统

       if (IS_ERR(mnt))

              panic("Can't create rootfs");

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

       if (!namespace)

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

       atomic_set(&namespace->count, 1);

       INIT_LIST_HEAD(&namespace->list);

       init_rwsem(&namespace->sem);

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

       namespace->root = mnt;

       mnt->mnt_namespace = namespace;

 

       init_task.namespace = namespace;

       read_lock(&tasklist_lock);

       do_each_thread(g, p) {

              get_namespace(namespace);

              p->namespace = namespace;

       } while_each_thread(g, p);

       read_unlock(&tasklist_lock);

 

       set_fs_pwd(current->fs, namespace->root, namespace->root->mnt_root);

       set_fs_root(current->fs, namespace->root, namespace->root->mnt_root);//rootfs设为根文件系统,这里只是暂时的。

}

大家可能会会说为什么要这样一个过程了,为什么不直接将我们设置的root=/dev/mtdblock2设备做为根文件系统挂载啊。

首先有两方面的原因

 

1.可能内核中没有根文件系统设备的驱动(usb,sata硬盘的驱动),需要像initrdinitramdiskcpio-initrd安装驱动,然后再去根文件系统设备取数据然后挂载,而这些往往需要以文件系统格式访问,故首先需要建立文件系统。

2.因为我们的root设备往往以设备文件的形式给出,如果没有文件系统,怎么会有设备文件之说呢,内核怎么知道如何访问根文件系统设备,这就是鸡蛋和鸡的问题,也许有人又说哪这个虚拟的根文件系统的设备文件在哪。其实由于其是虚拟的,叫内存文件系统,也就是人为的给它一个设备号(0255),人为的创建内存根目录。

 

3.rest_init

static void noinline rest_init(void)

       __releases(kernel_lock)

{

       kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);//大名鼎鼎的init进程

       numa_default_policy();

       unlock_kernel();

       preempt_enable_no_resched();

 

       /*

        * The boot idle thread must execute schedule()

        * at least one to get things moving:

        */

       schedule();

 

       cpu_idle();

}

下面我们就来Init函数

static int init(void * unused)

{

       ……………

………….

 

       populate_rootfs();//检测initrdinitramdiskcpio-initrd

 

       do_basic_setup();

 

       /*

        * check if there is an early userspace init.  If yes, let it do all

        * the work

        */

       if (sys_access((const char __user *) "/init", 0) == 0)//

              execute_command = "/init";

       else

              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..

        */

       free_initmem();

       unlock_kernel();

       system_state = SYSTEM_RUNNING;

       numa_default_policy();

 

       if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)

              printk(KERN_WARNING "Warning: unable to open an initial console.\n");

 

       (void) sys_dup(0);

       (void) sys_dup(0);

      

       /*

        * We try each of these until one succeeds.

        *

        * The Bourne shell can be used instead of init if we are

        * trying to recover a really broken machine.

        */

 

       if (execute_command)

              run_init_process(execute_command);

 

       run_init_process("/sbin/init");

       run_init_process("/etc/init");

       run_init_process("/bin/init");

       run_init_process("/bin/sh");

 

       panic("No init found.  Try passing init= option to kernel.");

}

 

下面分析上面重要的函数

1. populate_rootfs负责检测initrd

void __init populate_rootfs(void)

{

       char *err = unpack_to_rootfs(__initramfs_start,

                      __initramfs_end - __initramfs_start, 0);

       if (err)

              panic(err);

#ifdef CONFIG_BLK_DEV_INITRD

       if (initrd_start) {//initrd检测

              int fd;

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

              err = unpack_to_rootfs((char *)initrd_start,

                     initrd_end - initrd_start, 1);

              if (!err) {

                     printk(" it is\n");

                     unpack_to_rootfs((char *)initrd_start,

                            initrd_end - initrd_start, 0);

                     free_initrd_mem(initrd_start, initrd_end);

                     return;

              }

              printk("it isn't (%s); looks like an initrd\n", err);

              fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 700);

              if (fd >= 0) {

                     sys_write(fd, (char *)initrd_start,

                                   initrd_end - initrd_start);

//将内存中的initrd(通常由bootload加载到内存中)赋值到initrd.image中,以释放其占用的内存资源。

 

                     sys_close(fd);

                     free_initrd_mem(initrd_start, initrd_end);//释放内存资源

 

              }

       }

#endif

}

 

2.然后就是

prepare_namespace(),它负责具体根文件系统挂载。

void __init prepare_namespace(void)

{

       int is_floppy;

 

       mount_devfs();//挂载devfs文件系统到/dev目录。这个是必须的,因为initrd要放到/dev/ram0

 

       if (root_delay) {

              printk(KERN_INFO "Waiting %dsec before mounting root device...\n",

                     root_delay);

              ssleep(root_delay);

       }

 

       md_run_setup();

 

       if (saved_root_name[0]) {

              root_device_name = saved_root_name;

              ROOT_DEV = name_to_dev_t(root_device_name);

              if (strncmp(root_device_name, "/dev/", 5) == 0)

                     root_device_name += 5;

       }

 

       is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;

 

       if (initrd_load())

              goto out;

 

       if (is_floppy && rd_doload && rd_load_disk(0))

              ROOT_DEV = Root_RAM0;//

//如果我们在bootoption哟参数root=/dev/mtdblock2ROOT_DEV就为/dev/mtdblock设备号。

       mount_root();

 

out:

       umount_devfs("/dev");  //devfs从虚拟的根文件系统的/dev umount

       sys_mount(".", "/", NULL, MS_MOVE, NULL);//将挂载点从当前目录 【/root】(在mount_root函数中设置的)移到根目录

 

       sys_chroot("."); //将当前目录即【/root】(真正文件系统挂载的目录)做为系统根目录, security_sb_post_mountroot();

       mount_devfs_fs ();//devfs挂到真正根文件系统的/dev

}

 

 

void __init mount_root(void)

{

#ifdef CONFIG_ROOT_NFS

       if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {

              if (mount_nfs_root())

                     return;

 

              printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");

              ROOT_DEV = Root_FD0;

       }

#endif

#ifdef CONFIG_BLK_DEV_FD

       if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {

              /* rd_doload is 2 for a dual initrd/ramload setup */

              if (rd_doload==2) {

                     if (rd_load_disk(1)) {

                            ROOT_DEV = Root_RAM1;

                            root_device_name = NULL;

                     }

              } else

                     change_floppy("root floppy");

       }

#endif

       create_dev("/dev/root", ROOT_DEV, root_device_name);//创建ROOT_DEV设备文件即为根文件系统设备文件。

       mount_block_root("/dev/root", root_mountflags);//挂载根文件系统,root_mountflags为文件系统类型。

}

 

void __init mount_block_root(char *name, int flags)

{

       char *fs_names = __getname();

       char *p;

       char b[BDEVNAME_SIZE];

get_fs_names(fs_names);//获得文件系统类型,如果在bootoption里有,则就为这个文件系统类型,如果没有指定,则返回filesytem链上所有类型,下面再对每个进行尝试.

retry:

       for (p = fs_names; *p; p += strlen(p)+1) {

              int err = do_mount_root(name, p, flags, root_mount_data);//将文件系统挂到/root目录,p为文件系统类型,由get_fs_names得到

              switch (err) {

                     case 0:

                            goto out;

                     case -EACCES:

                            flags |= MS_RDONLY;

                            goto retry;

                     case -EINVAL:

                            continue;

              }

               /*

               * Allow the user to distinguish between failed sys_open

               * and bad superblock on root device.

               */

              __bdevname(ROOT_DEV, b);

              printk("VFS: Cannot open root device \"%s\" or %s\n",

                            root_device_name, b);

              printk("Please append a correct \"root=\" boot option\n");

 

              panic("VFS: Unable to mount root fs on %s", b);

       }

       panic("VFS: Unable to mount root fs on %s", __bdevname(ROOT_DEV, b));

out:

       putname(fs_names);

}

 

static int __init do_mount_root(char *name, char *fs, int flags, void *data)

{

       int err = sys_mount(name, "/root", fs, flags, data);

       if (err)

              return err;

 

       sys_chdir("/root");/将当前目录设为/root目录

       ROOT_DEV = current->fs->pwdmnt->mnt_sb->s_dev;

       printk("VFS: Mounted root (%s filesystem)%s.\n",

              current->fs->pwdmnt->mnt_sb->s_type->name,

              current->fs->pwdmnt->mnt_sb->s_flags & MS_RDONLY ?

              " readonly" : "");

       return 0;

}

 

到此根文件系统挂载完成。

整个函数调用路径如下

Start_kernel->rest_init->init-> prepare_namespace-> mount_root-> mount_block_root do_mount_root-> sys_mount(name, "/root", fs, flags, data)-> sys_chroot(".");

阅读(279) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~