zoukankan      html  css  js  c++  java
  • Linux2.6中启动ramdisk分析

    一、起因

    使用busybox制作了一个cpio.gz的文件系统,然后使用这个文件系统作为qemu的启动盘进行启动,最后发现可以识别出是一个cpio文件系统,但是到最后还是出现了panic,说是找不到文件系统。大致的错误类型为"VFS: Cannot open root device "

    ……

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

    也就是通过源文件的搜索可以看到是在linux-2.6.37.1initdo_mounts.c: mount_block_root函数中出现的问题。所以就需要分析一下内核为什么会走到这一步,也就是我的ramdisk哪里出了问题。

    二、内核的启动流程

    1、对于ramdisk的使用

    linux-2.6.37.1initmain.c::kernel_init()函数中实现

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

     if (!ramdisk_execute_command)
      ramdisk_execute_command = "/init";这里设置执行的默认ramdisk命令。这个值可以通过内核启动参数rdinit设置,如果没有设置,使用默认的rd文件系统中根文件夹下的init文件,这个很奇怪,不是在/sbin/init,可能是为了简介吧

    /*

    static int __init rdinit_setup(char *str)
    {
     unsigned int i;

     ramdisk_execute_command = str;
     /* See "auto" comment in init_setup */
     for (i = 1; i < MAX_INIT_ARGS; i++)
      argv_init[i] = NULL;
     return 1;
    }
    __setup("rdinit=", rdinit_setup);

    */

     if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
      ramdisk_execute_command = NULL;
      prepare_namespace();}

    这里的代码是整个ramdisk加载的核心分水岭,如果这个ramdisk_execute_command的值非零,那么就不会有很面的尝试了,就会让ramdisk_execute_command完成整个加载过程。我今天加载失败的时候,发现我的busybox里面就没有这个文件。

    2、如果ramdisk中不存在init文件

    如果不存在,明显就是需要执行prepare_namespace函数来完成了。这个函数首先判断命令行中指定的根文件系统所在设备类型,注意,这里指定的虽然是文件,但是它很可能是一个设备文件,在Linux中,设备也是文件,只是一种特殊的文件而已。所以,可以让boot指定使用的启动设备是在ramdisk中的那个文件,从而通过该文件确定为一个设备。

    static int __init root_dev_setup(char *line)
    {
     strlcpy(saved_root_name, line, sizeof(saved_root_name));
     return 1;
    }

    __setup("root=", root_dev_setup);

    不管如何,此处可能玩出很多花样,但是此时都是最终确定一个跟文件设备,也就是设置好ROOT_DEV的值,从而为最终的启动做好准备。

    3、ramdisk中image文件的加载


    int __init initrd_load(void)
    {
     if (mount_initrd) {
      create_dev("/dev/ram", Root_RAM0);
      /*
       * Load the initrd data into /dev/ram0. Execute it as initrd
       * unless /dev/ram0 is supposed to be our actual root device,
       * in that case the ram disk is just set up here, and gets
       * mounted in the normal path.
       */
      if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
       sys_unlink("/initrd.image");
       handle_initrd();
       return 1;
      }

     镜像的加载

    int __init rd_load_image(char *from)
    out_fd = sys_open((const char __user __force *) "/dev/ram", O_RDWR, 0);
     if (out_fd < 0)
      goto out;

     in_fd = sys_open(from, O_RDONLY, 0);
     if (in_fd < 0)
      goto noclose_input;

     nblocks = identify_ramdisk_image(in_fd, rd_image_start, &decompressor);首先判断是否是一个文件系统的镜像文件,如果不是那就不做特殊处理,也就是通过dd if of 创建的一个完整备份
     }
     sys_unlink("/initrd.image");
     return 0;
    }

    static void __init handle_initrd(void)
    {
     int error;
     int pid;

    …………

      /*
      * In case that a resume from disk is carried out by linuxrc or one of
      * its children, we need to tell the freezer not to wait for us.
      */
     current->flags |= PF_FREEZER_SKIP;

     pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);同样是根文件系统下的linuxrc文件,这里创建一个单独的线程来执行该文件,注意,这里的第二个参数是一个文件的绝对路径
     if (pid > 0)
      while (pid != sys_wait4(-1, NULL, 0, NULL)) 这里同步等待新创建的linuxrc的完成,所以虽然是创建了一个单独的线程,但是依然是一个同步等待的过程。因为这个linuxrc很多时候就是完成一些特殊的驱动的加载,也就是原始ramdisk中驱动模块的选择
       yield();

    4、/initrd.image文件的由来
    static int __init populate_rootfs(void)
    {
     char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
     if (err)
      panic(err); /* Failed to decompress INTERNAL initramfs */
     if (initrd_start) {
    #ifdef CONFIG_BLK_DEV_RAM
      int fd;
      printk(KERN_INFO "Trying to unpack rootfs image as initramfs... ");
      err = unpack_to_rootfs((char *)initrd_start,
       initrd_end - initrd_start);
      if (!err) {
       free_initrd();
       return 0;
      } else {
       clean_rootfs();
       unpack_to_rootfs(__initramfs_start, __initramfs_size);
      }
      printk(KERN_INFO "rootfs image is not initramfs (%s)"
        "; looks like an initrd ", err);
      fd = sys_open((const char __user __force *) "/initrd.image",
             O_WRONLY|O_CREAT, 0700);
      if (fd >= 0) {
       sys_write(fd, (char *)initrd_start,
         initrd_end - initrd_start
    );
       sys_close(fd);
       free_initrd();
      }
    #else
      printk(KERN_INFO "Unpacking initramfs... ");
      err = unpack_to_rootfs((char *)initrd_start,
       initrd_end - initrd_start);
      if (err)
       printk(KERN_EMERG "Initramfs unpacking failed: %s ", err);
      free_initrd();
    #endif
     }
     return 0;
    }
    这个函数是通过rootfs_initcall(populate_rootfs);由init_calls调用的,所以还是比较早得。总起来说,就是首先尝试是一个cpio.gz文件,如果不是,那么假设是一个image文件,在rootfs根文件系统下创建一个/initrt.image文件,并通过sys_write将initrd中的所有内容直接写入该文件

    5、initrd_start initrd_end的由来

    这个是内核和bootloader之间约定好的方式,不同的体系结构有不同的实现方式,在386是通过参数页设置,而PowerPC下则是通过特定寄存器由loader传递给内核。

    三、网络资源

    这是一篇很好的文章,可以参考一下

    http://www.ibm.com/developerworks/cn/linux/l-k26initrd/

  • 相关阅读:
    HDOJ 1207 汉诺塔II
    [转]写代码的小女孩
    POJ Subway tree systems
    HDOJ 3555 Bomb (数位DP)
    POJ 1636 Prison rearrangement (DP)
    POJ 1015 Jury Compromise (DP)
    UVA 10003
    UVA 103 Stacking Boxes
    HDOJ 3530 Subsequence
    第三百六十二、三天 how can I 坚持
  • 原文地址:https://www.cnblogs.com/tsecer/p/10485759.html
Copyright © 2011-2022 走看看