设为首页 收藏本站
查看: 1391|回复: 0

[经验分享] linux的initrd机制和initramfs机制之根文件挂载流程:代码分析

[复制链接]

尚未签到

发表于 2015-12-10 13:08:38 | 显示全部楼层 |阅读模式
这一篇在前两篇讲解的基础上来跟踪一下代码

linux-2.6.30

kernel_init
    do_basic_setup();  // 批注1
   
    if (!ramdisk_execute_command) // 批注2
        ramdisk_execute_command = "/init";
   
    if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
        ramdisk_execute_command = NULL;
        prepare_namespace();  // 批注3
    }
   
    init_post();

批注1:所有直接编译在kernel中的模块都是由它启动的。这里有一个地方涉及到根文件系统的挂载
当配置了CONFIG_BLK_DEV_INITRD,在这里会调用函数populate_rootfs;
如果没有配置populate_rootfs,则会调用函数default_rootfs

批注2:ramdisk_execute_command值通过“rdinit=”指定,如果未指定,则采用默认的值/init。

批注3:检查根文件系统中是否存在文件ramdisk_execute_command,如果存在的话则执行init_post(),
否则执行prepare_namespace()挂载根文件系统。

先来说说上面批注1可能的两个函数 populate_rootfs和default_rootfs





  • static int __init populate_rootfs(void)

  • {
  •     char *err = unpack_to_rootfs(__initramfs_start, // 批注1
  •              __initramfs_end - __initramfs_start);
  •     if (err)
  •         panic(err);    /* Failed to decompress INTERNAL initramfs */
  •     if (initrd_start) { // 批注2
  • #ifdef CONFIG_BLK_DEV_RAM
  •         int fd;
  •         printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
  •         err = unpack_to_rootfs((char *)initrd_start, // 批注3
  •             initrd_end - initrd_start);
  •         if (!err) {
  •             free_initrd();
  •             return 0;
  •         } else {
  •             clean_rootfs();
  •             unpack_to_rootfs(__initramfs_start,
  •                  __initramfs_end - __initramfs_start);
  •         }
  •         printk(KERN_INFO "rootfs image is not initramfs (%s)"
  •                 "; looks like an initrd\n", err);
  •         fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700); // 批注4
  •         if (fd >= 0) {
  •             sys_write(fd, (char *)initrd_start, // 批注5
  •                     initrd_end - initrd_start);
  •             sys_close(fd);
  •             free_initrd(); // 批注6
  •         }
  • #else
  •         printk(KERN_INFO "Unpacking initramfs...\n");
  •         err = unpack_to_rootfs((char *)initrd_start,
  •             initrd_end - initrd_start);
  •         if (err)
  •             printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
  •         free_initrd();
  • #endif
  •     }
  •     return 0;
  • }
批注1:unpack_to_rootfs顾名思义,就是解压包到rootfs,其具有两个功能,一个是检测
是否是属于cpio包,另外一个就是解压cpio包,通过最后一个参数进行控制。1:检测,0:解压。
其实,Initramfs也是压缩过后的CPIO文件。initramfs位于地址__initramfs_start处,
是内核在编译过程中生成的,initramfs的是作为内核的一部分而存在的,不是 boot loader加载的。
(编译的时候通过连接脚本arch/arm/kernel/vmlinux.lds将其编译到__initramfs_start~__initramfs_end,
执行完unpack_to_rootfs后将被拷贝到根目录)。

批注2:判断是否加载了initrd,无论哪种格式的initrd,都会被bootloader加载到地址initrd_start处。

批注3:判断加载的是不是CPIO-Initrd。

批注4:如果不是CPIO-Initrd,则就是Image-Initrd,将其内容保存到文件/initrd.image中。在根文件系统中创建文件/initrd.image。

批注5:将内容保存到文件/initrd.image中

批注6:释放Initrd所占用的内存空间。

default_rootfs()主要往rootfs中生成两个目录/dev和/root以及一个设备文件/dev/console。


下面来继续看下面的函数:prepare_namespace




  • void __init prepare_namespace(void)

  • {
  •     int is_floppy;

  •     if (root_delay) { // 批注1
  •         printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
  •                root_delay);
  •         ssleep(root_delay);
  •     }

  •     /*
  •      * wait for the known devices to complete their probing
  •      *
  •      * Note: this is a potential source of long boot delays.
  •      * For example, it is not atypical to wait 5 seconds here
  •      * for the touchpad of a laptop to initialize.
  •      */
  •     wait_for_device_probe(); // 批注2

  •     md_run_setup();

  •     if (saved_root_name[0]) { // 批注3
  •         root_device_name = saved_root_name;
  •         if (!strncmp(root_device_name, "mtd", 3) ||
  •          !strncmp(root_device_name, "ubi", 3)) {
  •             mount_block_root(root_device_name, root_mountflags); // 批注4
  •             goto out;
  •         }
  •         ROOT_DEV = name_to_dev_t(root_device_name); // 批注5
  •         if (strncmp(root_device_name, "/dev/", 5) == 0)
  •             root_device_name += 5;
  •     }

  •     if (initrd_load()) // 批注6
  •         goto out;

  •     /* wait for any asynchronous scanning to complete */
  •     if ((ROOT_DEV == 0) && root_wait) { // 批注7
  •         printk(KERN_INFO "Waiting for root device %s...\n",
  •             saved_root_name);
  •         while (driver_probe_done() != 0 ||
  •             (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
  •             msleep(100);
  •         async_synchronize_full();
  •     }

  •     is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;

  •     if (is_floppy && rd_doload && rd_load_disk(0))
  •         ROOT_DEV = Root_RAM0;

  •     mount_root();
  • out:
  •     sys_mount(".", "/", NULL, MS_MOVE, NULL); // 批注8
  •     sys_chroot("."); // 批注9
  • }
批注1:对于将根文件系统存放到USB或者SCSI设备上的情况,Kernel需要等待这些耗费时间比较久的设备
驱动加载完毕,所以这里存在一个Delay。

批注2:等待根文件系统所在的设备探测函数的完成。

批注3:参数saved_root_name存放的是uboot参数root=所指定的设备文件。

批注4:将saved_root_nam指定的设备加载。

批注5:参数ROOT_DEV存放设备节点号。

批注6:挂载initrd,见下面的详解。

批注7:如果指定mount_initrd为true,即没有指定在函数initrd_load中mount的话,则在这里重新realfs的mount操作。

批注8:将挂载点从当前目录(实际当前的目录在mount_root中或者在mount_block_root中指定)移到根目录。
即是如/dev/mtdblock2。

批注9:将当前目录当作系统的根目录,至此虚拟系统根目录文件系统切换到了实际的根目录文件系统。






  • int __init initrd_load(void)

  • {
  •     if (mount_initrd) { // 批注1
  •         create_dev("/dev/ram", Root_RAM0); // 批注2
  •         /*
  •          * 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) { // 批注3
  •             sys_unlink("/initrd.image");
  •             handle_initrd(); // 批注4
  •             return 1;
  •         }
  •     }
  •     sys_unlink("/initrd.image");
  •     return 0;
  • }
批注1:可以通过Kernel的参数“noinitrd“来配置mount_initrd的值,默认为1,很少看到有项目区配置该值,
所以一般情况下,mount_initrd的值应该为1。

批注2:创建一个Root_RAM0的设备节点/dev/ram。

批注3:如果根文件设备号不是Root_RAM0则程序就会执行代码就进入执行,如指定的/dev/mtdblock4设备节点
肯定就不是Root_RAM0。另外还将文件initrd.image释放到节点/dev/ram0,也就是对应image-initrd的操作。

批注4:函数handle_initrd主要功能是执行Initrd中的linuxrc文件,并且将realfs的根目录设置为当前
目录。其实前面也已经提到了,这些操作只对image-cpio的情况下才会去执行。。





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

    real_root_dev = new_encode_dev(ROOT_DEV); // 批注1
    create_dev("/dev/root.old", Root_RAM0); // 批注2
    /* mount initrd on rootfs' /root */
    mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
    sys_mkdir("/old", 0700); // 批注3
    root_fd = sys_open("/", 0, 0);
    old_fd = sys_open("/old", 0, 0);
    /* move initrd over / and chdir/chroot in initrd root */
    sys_chdir("/root"); // 批注4
    sys_mount(".", "/", NULL, MS_MOVE, NULL);
    sys_chroot(".");

    /*
     * 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); // 批注5
    if (pid > 0)
        while (pid != sys_wait4(-1, NULL, 0, NULL))
            yield();

    current->flags &= ~PF_FREEZER_SKIP;

    /* move initrd to rootfs' /old */
    sys_fchdir(old_fd); // 批注6
    sys_mount("/", ".", NULL, MS_MOVE, NULL);
    /* switch root and cwd back to / of rootfs */
    sys_fchdir(root_fd);
    sys_chroot(".");
    sys_close(old_fd);
    sys_close(root_fd);

    if (new_decode_dev(real_root_dev) == Root_RAM0) { // 批注7
        sys_chdir("/old");
        return;
    }

    ROOT_DEV = new_decode_dev(real_root_dev); // 批注8
    mount_root();

    printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
    error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
    if (!error)
        printk("okay\n");
    else {
        int fd = sys_open("/dev/root.old", O_RDWR, 0);
        if (error == -ENOENT)
            printk("/initrd does not exist. Ignored.\n");
        else
            printk("failed\n");
        printk(KERN_NOTICE "Unmounting old root\n");
        sys_umount("/old", MNT_DETACH);
        printk(KERN_NOTICE "Trying to free ramdisk memory ... ");
        if (fd

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-149241-1-1.html 上篇帖子: linux的initrd机制和initramfs机制之initrd 下篇帖子: linux的initrd机制和initramfs机制之initramfs
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表