• 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/

  • 相关阅读:
    堆栈详解
    结构体内存对齐
    const限定符
    硬盘及其分区(0819整理)
    Android编译环境搭建(0818-0819)
    wordpress导入模板数据
    git新建仓库
    android 镜像源
    js 获取浏览器可视窗口大小,滚动条高度
    jquery 获取浏览器可视窗口大小,滚动条高度
  • 原文地址:https://www.cnblogs.com/tsecer/p/10485759.html
Copyright © 2020-2023  润新知