linux文件系统的启动过程分析

linux文件系统的启动过程分析


启动参数:

chosen {
    bootargs = "root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rw console=ttymxc3,115200n8 lpj=7905280 initroot=10:none,initramfs,/startup/run quiet";
};

prepare_namespace

函数功能:
从设备文件路径名到处设备标识符,调用mount_root安装根文件系统

函数源码:

/*
 * Prepare the namespace - decide what/where to mount, load ramdisks, etc.
 */
void __init prepare_namespace(void)
{
    int is_floppy;

    if (root_delay) { // 如果启动参数中设置了安装根文件系统的延迟参数rootdelay,
                      // 则调用ssleep延迟安装root_delay秒
        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();

    md_run_setup();   // 初始化MD设备,MD设备主要包含了LINUX内核的软RAID实现

#ifdef CONFIG_SUPPORT_INITROOT
    try_initroot();
#endif

    if (saved_root_name[0]) {  // 由启动参数的解析得到,saved_root_name = /dev/mmcblk0p2
        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);
            goto out;
        }
        ROOT_DEV = name_to_dev_t(root_device_name);
        if (strncmp(root_device_name, "/dev/", 5) == 0)
            root_device_name += 5;
    }

    if (initrd_load())   // 具体分析参见本文
        goto out;

    /* wait for any asynchronous scanning to complete */
    if ((ROOT_DEV == 0) && root_wait) {
        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;  // 通过设备文件标识符ROOT_DEV判断启动设备文件是否是软盘,
                                                  // 并把判断结果存入局部变量is_floppy中
                                                  // 在我们的代码中,该代码不成立

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

    mount_root();   // 调用函数mount_root安装实际根文件系统,参见本文分析
out:
    devtmpfs_mount("dev");
    sys_mount(".", "/", NULL, MS_MOVE, NULL);
    sys_chroot(".");
}

mount_root

函数源码:

void __init mount_root(void)
{
#ifdef CONFIG_ROOT_NFS          // 网络文件系统,我们不用
    if (ROOT_DEV == Root_NFS) {
        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
#ifdef CONFIG_BLOCK
    create_dev("/dev/root", ROOT_DEV);  // 调用create_dev函数在/dev目录中创建root设备文件,设备标识符为ROOT_DEV
    mount_block_root("/dev/root", root_mountflags);     // 调用函数mount_block_root安装根文件系统,具体分析见下文
#endif
}

name_to_dev_t

函数功能:
把设备路径名转换成设备标识符

函数参数:
name:设备路径名

函数源码:

/*
 *  Convert a name into device number.  We accept the following variants:
 *
 *  1) device number in hexadecimal represents itself
 *  2) /dev/nfs represents Root_NFS (0xff)
 *  3) /dev/<disk_name> represents the device number of disk
 *  4) /dev/<disk_name><decimal> represents the device number
 *         of partition - device number of disk plus the partition number
 *  5) /dev/<disk_name>p<decimal> - same as the above, that form is
 *     used when disk name of partitioned disk ends on a digit.
 *  6) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
 *     unique id of a partition if the partition table provides it.
 *     The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
 *     partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
 *     filled hex representation of the 32-bit "NT disk signature", and PP
 *     is a zero-filled hex representation of the 1-based partition number.
 *  7) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to
 *     a partition with a known unique id.
 *
 *  If name doesn't have fall into the categories above, we return (0,0).
 *  block_class is used to check if something is a disk name. If the disk
 *  name contains slashes, the device name has them replaced with
 *  bangs.
 */

dev_t name_to_dev_t(char *name)
{
    char s[32];
    char *p;
    dev_t res = 0;
    int part;

#ifdef CONFIG_BLOCK
    if (strncmp(name, "PARTUUID=", 9) == 0) { 
        name += 9;
        res = devt_from_partuuid(name);
        if (!res)
            goto fail;
        goto done;
    }
#endif

    /* 如果设备文件路径名不以“/dev/”为前缀,
     * 则从设备文件路径名中直接取主设备号和次设备号,
     * 组成设备文件标识符并返回 */
    if (strncmp(name, "/dev/", 5) != 0) {
        unsigned maj, min;
        if (sscanf(name, "%u:%u", &maj, &min) == 2) {   
            res = MKDEV(maj, min);
            if (maj != MAJOR(res) || min != MINOR(res))
                goto fail;
        } else {
            res = new_decode_dev(simple_strtoul(name, &p, 16));
            if (*p)
                goto fail;
        }
        goto done;
    }

    /* 如果设备路径名中的设备文件名等于”nfs”,说明根文件系统是网络文件系统,
     * 返回网络文件系统的设备文件标识符 */
    name += 5;
    res = Root_NFS;
    if (strcmp(name, "nfs") == 0)
        goto done;

    /* 如果设备路径名中的设备文件名等于“ram”,说明根文件系统是ram文件系统,
     * 返回内存文件系统的设备文件标识符 */
    res = Root_RAM0;
    if (strcmp(name, "ram") == 0)
        goto done;

    if (strlen(name) > 31)
        goto fail;
    strcpy(s, name);

    /* 把设备文件名中的” /”替换为”!” */
    for (p = s; *p; p++)
        if (*p == '/')
            *p = '!';
    res = blk_lookup_devt(s, 0);  // 以设备文件名为参数调用blk_lookup_devt获取设备的标识符,
                                  // 如果成功则跳到最后一步
    if (res)
        goto done;

    /*
     * try non-existent, but valid partition, which may only exist
     * after revalidating the disk, like partitioned md devices
     */
    while (p > s && isdigit(p[-1]))
        p--;
    if (p == s || !*p || *p == '0')
        goto fail;

    /* try disk name without <part number> */
    part = simple_strtoul(p, NULL, 10);
    *p = '\0';
    res = blk_lookup_devt(s, part);
    if (res)
        goto done;

    /* try disk name without p<part number> */
    if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p')
        goto fail;
    p[-1] = '\0';
    res = blk_lookup_devt(s, part);
    if (res)
        goto done;

fail:
    return 0;
done:
        printk("---------------[%s][%d]   : %d\n", __FILE__, __LINE__, res);
    return res;
}

initrd_load

函数源码:

int __init initrd_load(void)
{
    if (mount_initrd) {  // 如果mount_initrd=0,则跳过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.
         */
        // 用rd_load_image函数把/initrd.image(也就是initrd的实际数据)挂载到/dev/ram0,
        // 接着判断根设备是否就是/dev/ram0,如果是则跳过initrd处理,
        // 按正常流程挂载根文件系统;否则用initrd_handle函数进行initrd处理
        // 我们的系统在 rd_load_image("/initrd.image") 不符合,所以最终还是按照正常流程来挂载根文件系统
        if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
            sys_unlink("/initrd.image");
            handle_initrd();
            return 1;
        }
    }
    sys_unlink("/initrd.image");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/deggfg/article/details/81509760