当前位置: 首页 > news >正文

Linux中完成根文件系统的最终准备和切换prepare_namespace函数的实现

Linux系统根文件系统准备和挂载完整流程

阶段一:设备环境准备 (prepare_namespace前半部分)

  1. 设备文件系统建立

    mount_devfs();                    // 挂载临时devfs
    md_run_setup();                   // 初始化软件RAID
    
    • 提供临时的设备节点访问能力
    • 确保RAID设备在根文件系统挂载前就绪
  2. 根设备配置解析

    if (saved_root_name[0]) {root_device_name = saved_root_name;ROOT_DEV = name_to_dev_t(root_device_name);
    }
    
    • 解析内核命令行root=参数
    • 将设备名称转换为设备号
    • 为后续挂载提供准确的设备标识
  3. 初始RAM磁盘处理

    if (initrd_load()) goto out;
    
    • 加载并处理initrd镜像
    • 如果initrd加载成功,直接使用initrd作为临时根
    • 为驱动加载和真正的根文件系统挂载提供桥梁

阶段二:根文件系统挂载 (mount_root及相关函数)

  1. 网络根文件系统支持

    #ifdef CONFIG_ROOT_NFS
    if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {if (mount_nfs_root()) return;
    }
    #endif
    
    • 尝试挂载NFS网络根文件系统
    • 支持无盘工作站的启动场景
  2. 传统设备根文件系统挂载

    create_dev("/dev/root", ROOT_DEV, root_device_name);
    mount_block_root("/dev/root", root_mountflags);
    
    • 创建统一的根设备文件/dev/root
    • 启动文件系统探测和挂载流程
  3. 多文件系统类型探测

    get_fs_names(fs_names);
    for (p = fs_names; *p; p += strlen(p)+1) {do_mount_root(name, p, flags, data);
    }
    
    • 获取内核支持的所有文件系统类型
    • 逐个尝试直到找到匹配的文件系统
    • 智能错误处理和重试机制

阶段三:命名空间最终切换 (prepare_namespace后半部分)

  1. 临时环境清理

    umount_devfs("/dev");
    
    • 卸载临时使用的设备文件系统
    • 释放初始化阶段的临时资源
  2. 根文件系统提升

    sys_mount(".", "/", NULL, MS_MOVE, NULL);
    sys_chroot(".");
    
    • 将当前挂载的文件系统移动到根位置
    • 执行chroot操作完成根目录切换
    • 这是Unix系统根目录设置的最终步骤
  3. 安全和服务初始化

    security_sb_post_mountroot();
    mount_devfs_fs();
    
    • 调用安全模块的根文件系统挂载后处理
    • 在新的根环境下重新挂载设备文件系统

实际效果总结

最终达到的状态:

  1. 根文件系统就绪

    • / 目录指向真正的根文件系统
    • 所有必要的设备文件可用
    • 文件系统权限和标志正确设置
  2. 进程环境建立

    • 当前工作目录设置为根目录
    • 进程的根目录正确限制
    • 安全策略初始化完成

完成Linux系统根文件系统的最终准备和切换prepare_namespace

void __init prepare_namespace(void)
{int is_floppy;mount_devfs();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;mount_root();
out:umount_devfs("/dev");sys_mount(".", "/", NULL, MS_MOVE, NULL);sys_chroot(".");security_sb_post_mountroot();mount_devfs_fs ();
}

函数功能概述

prepare_namespace 函数负责在内核启动过程中准备根文件系统,包括处理initrd、挂载根文件系统、设置最终的系统根目录等关键操作

代码详细解析

函数声明和变量定义

void __init prepare_namespace(void)
{int is_floppy;

函数声明:

  • void __init: 初始化函数,完成后内存可被释放
  • prepare_namespace(void): 准备命名空间的主函数
  • int is_floppy: 标志变量,表示根设备是否是软盘

挂载devfs文件系统

	mount_devfs();

挂载设备文件系统:

  • mount_devfs(): 挂载devfs(设备文件系统)到/dev目录
  • 作用: 提供设备文件管理,允许动态创建设备节点

软件RAID设置

	md_run_setup();

多设备驱动设置:

  • md_run_setup(): 初始化并运行软件RAID设置
  • md: Multiple Device,Linux的软件RAID系统
  • 作用: 处理内核命令行中的md=参数,配置RAID设备
  • 重要性: 确保RAID设备在根文件系统挂载前就绪

根设备名称处理

	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;}

根设备配置:

  • if (saved_root_name[0]): 检查是否有保存的根设备名称
  • saved_root_name: 从内核命令行root=参数保存的设备名

设备名称处理:

  1. root_device_name = saved_root_name: 设置根设备名称
  2. ROOT_DEV = name_to_dev_t(root_device_name): 将设备名转换为设备号
    • name_to_dev_t(): 解析设备名(如"/dev/sda1")为设备号
  3. if (strncmp(root_device_name, "/dev/", 5) == 0): 如果设备名以"/dev/"开头
  4. root_device_name += 5: 跳过"/dev/"前缀,只保留设备名部分

软盘设备检查

	is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;

检测根设备类型:

  • MAJOR(ROOT_DEV): 提取根设备的主设备号
  • FLOPPY_MAJOR: 软盘设备的主设备号常量
  • is_floppy = ...: 判断根设备是否是软盘驱动器
  • 作用: 软盘设备需要特殊处理,因为访问速度较慢

initrd加载

	if (initrd_load())goto out;

初始RAM磁盘加载:

  • initrd_load(): 加载并处理initrd(初始RAM磁盘)
  • 返回值: 成功加载返回1,否则返回0
  • goto out: 如果initrd加载成功,跳转到out标签
  • initrd作用: 提供临时的根文件系统,用于加载必要的驱动后再挂载真正的根文件系统

软盘根设备处理

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

软盘根设备特殊情况:

  • is_floppy: 根设备是软盘
  • rd_doload: 应该从软盘加载RAM磁盘的标志
  • rd_load_disk(0): 从软盘加载RAM磁盘到内存
  • ROOT_DEV = Root_RAM0: 如果加载成功,将根设备设置为RAM磁盘

挂载根文件系统

	mount_root();

挂载根文件系统:

  • mount_root(): 核心函数,尝试挂载真正的根文件系统
  • 功能:
    • 尝试不同的文件系统类型
    • 在指定设备上查找可挂载的文件系统
    • 执行根文件系统的实际挂载操作

out标签:命名空间最终设置

out:umount_devfs("/dev");

卸载临时devfs

  • umount_devfs("/dev"): 卸载之前挂载的临时devfs

移动挂载点

	sys_mount(".", "/", NULL, MS_MOVE, NULL);

移动当前挂载点到根:

  • sys_mount(".", "/", NULL, MS_MOVE, NULL): 将当前目录移动到根目录
  • 参数:
    • ".": 源路径(当前挂载点)
    • "/": 目标路径(新的根目录)
    • MS_MOVE: 移动挂载点标志
  • 作用: 将当前挂载的文件系统提升为系统的根文件系统

改变根目录

	sys_chroot(".");

改变根目录:

  • sys_chroot("."): 将当前目录设置为进程的根目录
  • 作用: 完成chroot操作,限制进程的文件系统视图
  • 重要性: 这是Unix系统根目录设置的最终步骤

安全子系统后处理

	security_sb_post_mountroot();

安全模块回调:

  • security_sb_post_mountroot(): 调用安全子系统挂载根后的处理函数
  • 作用: 允许SELinux等安全模块在根文件系统挂载后执行必要的初始化

重新挂载devfs

	mount_devfs_fs ();
}

重新挂载devfs

  • mount_devfs_fs(): 在最终的根文件系统上重新挂载devfs
  • 作用: 在新的根文件系统环境下提供设备文件管理
  • 函数结束

关键函数功能

initrd_load():

  • 加载initrd到内存
  • 解压并处理initrd内容
  • 返回1表示成功并使用initrd作为根

mount_root():

  • 尝试在根设备上挂载文件系统
  • 支持多种文件系统类型探测
  • 处理挂载失败的各种情况

sys_mount() with MS_MOVE:

  • 原子性地移动挂载点
  • 保持挂载选项和标志
  • 用于提升文件系统为根

函数功能总结

主要功能:完成Linux系统根文件系统的最终准备和切换

核心处理阶段

  1. 设备准备阶段

    • 提供临时的设备文件访问
    • 配置软件RAID设备
    • 解析根设备参数
  2. 根文件系统加载阶段

    • 尝试加载initrd作为临时根
    • 处理软盘等特殊设备的RAM磁盘加载
    • 挂载真正的根文件系统
  3. 命名空间切换阶段

    • 清理临时文件系统
    • 移动挂载点到根位置
    • 执行chroot完成根目录切换
    • 重新建立设备文件系统

根文件系统挂载mount_root

void __init mount_root(void)
{
#ifdef CONFIG_ROOT_NFSif (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_FDif (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;}} elsechange_floppy("root floppy");}
#endifcreate_dev("/dev/root", ROOT_DEV, root_device_name);mount_block_root("/dev/root", 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);
retry:for (p = fs_names; *p; p += strlen(p)+1) {int err = do_mount_root(name, p, flags, root_mount_data);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_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;
}

mount_root 函数

函数声明

void __init mount_root(void)
  • __init:这个函数只在系统初始化时使用,初始化完成后内存会被释放
  • void:没有返回值
  • 这是系统启动时挂载根文件系统的主函数

NFS根文件系统支持

#ifdef CONFIG_ROOT_NFSif (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
  • CONFIG_ROOT_NFS:NFS根文件系统配置选项
  • MAJOR(ROOT_DEV) == UNNAMED_MAJOR:检查根设备是否为未命名主设备号(通常用于网络文件系统)
  • mount_nfs_root():尝试挂载NFS根文件系统
  • 如果成功,直接返回
  • 如果失败,打印错误信息

软盘根设备支持

#ifdef CONFIG_BLK_DEV_FDif (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;}} elsechange_floppy("root floppy");}
#endif
  • CONFIG_BLK_DEV_FD:软盘设备驱动配置选项
  • MAJOR(ROOT_DEV) == FLOPPY_MAJOR:检查根设备是否为软盘
  • rd_doload==2:检查是否为双initrd/ramdisk设置
  • rd_load_disk(1):从软盘加载ramdisk
  • 如果加载成功,切换到RAM disk作为根设备
  • 否则,调用change_floppy提示用户插入根文件系统软盘

创建设备和挂载根文件系统

	create_dev("/dev/root", ROOT_DEV, root_device_name);mount_block_root("/dev/root", root_mountflags);
}
  • create_dev("/dev/root", ROOT_DEV, root_device_name):创建设备文件 /dev/root
  • mount_block_root("/dev/root", root_mountflags):挂载块设备根文件系统

mount_block_root 函数

函数声明和变量定义

void __init mount_block_root(char *name, int flags)
{char *fs_names = __getname();char *p;char b[BDEVNAME_SIZE];
  • name:根设备名称(如 /dev/root
  • flags:挂载标志
  • fs_names = __getname():分配内核空间获取文件系统名称列表
  • p:文件系统名称遍历指针
  • b[BDEVNAME_SIZE]:设备名缓冲区

获取文件系统名称列表

	get_fs_names(fs_names);
  • get_fs_names(fs_names):获取内核支持的文件系统类型名称列表

文件系统遍历和重试循环

retry:for (p = fs_names; *p; p += strlen(p)+1) {int err = do_mount_root(name, p, flags, root_mount_data);
  • retry::重试标签,用于只读重试
  • for (p = fs_names; *p; p += strlen(p)+1):遍历所有文件系统类型
  • p += strlen(p)+1:移动到下一个文件系统名称(跳过\0)
  • do_mount_root(name, p, flags, root_mount_data):尝试用当前文件系统类型挂载

错误处理开关

		switch (err) {case 0:goto out;case -EACCES:flags |= MS_RDONLY;goto retry;case -EINVAL:continue;}
  • case 0:挂载成功,跳转到清理代码
  • case -EACCES:权限错误,添加只读标志并重试所有文件系统
  • case -EINVAL:无效参数,继续尝试下一个文件系统

挂载失败处理

	        /** 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);}
  • 允许用户区分打开设备失败和超级块错误
  • __bdevname(ROOT_DEV, b):将设备号转换为设备名称
  • 打印详细的错误信息,指导用户修正启动参数
  • 触发内核panic,系统无法继续启动

所有文件系统尝试失败

	panic("VFS: Unable to mount root fs on %s", __bdevname(ROOT_DEV, b));
out:putname(fs_names);
}
  • 如果所有文件系统类型都尝试失败,触发panic
  • out::成功挂载的退出点
  • putname(fs_names):释放文件系统名称列表内存

do_mount_root 函数

函数声明

static int __init do_mount_root(char *name, char *fs, int flags, void *data)
  • name:设备名称(如 /dev/root
  • fs:文件系统类型(如 “ext4”)
  • flags:挂载标志
  • data:文件系统特定的挂载数据

执行挂载操作

	int err = sys_mount(name, "/root", fs, flags, data);if (err)return err;
  • sys_mount(name, "/root", fs, flags, data):执行实际的挂载系统调用
  • 参数:源设备、挂载点、文件系统类型、标志、数据
  • 如果挂载失败,返回错误码

切换到根目录和设置

	sys_chdir("/root");ROOT_DEV = current->fs->pwdmnt->mnt_sb->s_dev;
  • sys_chdir("/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;
}
  • 打印成功挂载的信息,包括文件系统类型和是否只读
  • 返回0表示成功

函数功能详解

mount_root 功能

主要功能:系统启动时挂载根文件系统的总控函数

详细执行流程

  1. 网络根文件系统尝试

    • 检查是否配置了NFS根文件系统支持
    • 如果根设备是网络设备,尝试挂载NFS
    • 失败时回退到本地设备
  2. 软盘根设备处理

    • 检查软盘驱动支持
    • 支持从软盘加载ramdisk的双重启动设置
    • 提供用户交互提示
  3. 设备文件准备

    • 创建统一的根设备文件 /dev/root
    • 为后续挂载提供标准接口

mount_block_root 功能

主要功能:尝试多种文件系统类型挂载根文件系统

详细执行流程

  1. 文件系统发现

    • 获取内核编译时支持的所有文件系统类型
    • 准备遍历尝试
  2. 智能重试机制

    • 遍历所有文件系统类型
    • 针对权限错误自动切换到只读模式重试
    • 跳过不兼容的文件系统类型
  3. 详细错误报告

    • 区分设备打开失败和超级块错误
    • 提供清晰的用户指导信息
    • 确保故障原因可诊断

do_mount_root 功能

主要功能:执行实际的根文件系统挂载操作

详细执行流程

  1. 挂载执行

    • 调用系统挂载调用
    • 处理文件系统特定的挂载数据
  2. 环境切换

    • 改变当前工作目录到新根文件系统
    • 更新全局根设备信息
  3. 状态报告

    • 打印成功的挂载信息
    • 记录文件系统类型和挂载模式

控制initrd的加载和执行流程initrd_load

int __init initrd_load(void)
{if (mount_initrd) {create_dev("/dev/ram", Root_RAM0, NULL);/** 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;}}sys_unlink("/initrd.image");return 0;
}
int __init rd_load_image(char *from)
{int res = 0;int in_fd, out_fd;unsigned long rd_blocks, devblocks;int nblocks, i, disk;char *buf = NULL;unsigned short rotate = 0;
#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_PPC_ISERIES)char rotator[4] = { '|' , '/' , '-' , '\\' };
#endifout_fd = sys_open("/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);if (nblocks < 0)goto done;if (nblocks == 0) {
#ifdef BUILD_CRAMDISKif (crd_load(in_fd, out_fd) == 0)goto successful_load;
#elseprintk(KERN_NOTICE"RAMDISK: Kernel does not support compressed ""RAM disk images\n");
#endifgoto done;}/** NOTE NOTE: nblocks is not actually blocks but* the number of kibibytes of data to load into a ramdisk.* So any ramdisk block size that is a multiple of 1KiB should* work when the appropriate ramdisk_blocksize is specified* on the command line.** The default ramdisk_blocksize is 1KiB and it is generally* silly to use anything else, so make sure to use 1KiB* blocksize while generating ext2fs ramdisk-images.*/if (sys_ioctl(out_fd, BLKGETSIZE, (unsigned long)&rd_blocks) < 0)rd_blocks = 0;elserd_blocks >>= 1;if (nblocks > rd_blocks) {printk("RAMDISK: image too big! (%dKiB/%ldKiB)\n",nblocks, rd_blocks);goto done;}/** OK, time to copy in the data*/if (sys_ioctl(in_fd, BLKGETSIZE, (unsigned long)&devblocks) < 0)devblocks = 0;elsedevblocks >>= 1;if (strcmp(from, "/initrd.image") == 0)devblocks = nblocks;if (devblocks == 0) {printk(KERN_ERR "RAMDISK: could not determine device size\n");goto done;}buf = kmalloc(BLOCK_SIZE, GFP_KERNEL);if (buf == 0) {printk(KERN_ERR "RAMDISK: could not allocate buffer\n");goto done;}printk(KERN_NOTICE "RAMDISK: Loading %dKiB [%ld disk%s] into ram disk... ",nblocks, ((nblocks-1)/devblocks)+1, nblocks>devblocks ? "s" : "");for (i = 0, disk = 1; i < nblocks; i++) {if (i && (i % devblocks == 0)) {printk("done disk #%d.\n", disk++);rotate = 0;if (sys_close(in_fd)) {printk("Error closing the disk.\n");goto noclose_input;}change_floppy("disk #%d", disk);in_fd = sys_open(from, O_RDONLY, 0);if (in_fd < 0)  {printk("Error opening disk.\n");goto noclose_input;}printk("Loading disk #%d... ", disk);}sys_read(in_fd, buf, BLOCK_SIZE);sys_write(out_fd, buf, BLOCK_SIZE);
#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_PPC_ISERIES)if (!(i % 16)) {printk("%c\b", rotator[rotate & 0x3]);rotate++;}
#endif}printk("done.\n");successful_load:res = 1;
done:sys_close(in_fd);
noclose_input:sys_close(out_fd);
out:kfree(buf);sys_unlink("/dev/ram");return res;
}
static void __init handle_initrd(void)
{int error;int i, pid;real_root_dev = new_encode_dev(ROOT_DEV);create_dev("/dev/root.old", Root_RAM0, NULL);/* mount initrd on rootfs' /root */mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);sys_mkdir("/old", 0700);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");sys_mount(".", "/", NULL, MS_MOVE, NULL);sys_chroot(".");mount_devfs_fs ();pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);if (pid > 0) {while (pid != sys_wait4(-1, &i, 0, NULL))yield();}/* move initrd to rootfs' /old */sys_fchdir(old_fd);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);umount_devfs("/old/dev");if (new_decode_dev(real_root_dev) == Root_RAM0) {sys_chdir("/old");return;}ROOT_DEV = new_decode_dev(real_root_dev);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);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 < 0) {error = fd;} else {error = sys_ioctl(fd, BLKFLSBUF, 0);sys_close(fd);}printk(!error ? "okay\n" : "failed\n");}
}

initrd_load 函数

函数声明和初始检查

int __init initrd_load(void)
{if (mount_initrd) {create_dev("/dev/ram", Root_RAM0, NULL);
  • __init:初始化函数,完成后内存可释放
  • mount_initrd:内核配置选项,控制是否启用initrd
  • create_dev("/dev/ram", Root_RAM0, NULL):创建设备文件 /dev/ram

initrd 镜像加载和处理

		/** 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;}}
  • initrd数据加载到 /dev/ram0,除非 /dev/ram0 就是实际的根设备
  • rd_load_image("/initrd.image"):加载initrd镜像文件
  • ROOT_DEV != Root_RAM0:检查RAM disk不是最终的根设备
  • 如果加载成功且不是最终根设备,调用 handle_initrd() 处理initrd
  • 返回1表示使用了initrd

清理和返回

	sys_unlink("/initrd.image");return 0;
}
  • 删除initrd镜像文件
  • 返回0表示没有使用initrd

rd_load_image 函数

函数声明和变量定义

int __init rd_load_image(char *from)
{int res = 0;int in_fd, out_fd;unsigned long rd_blocks, devblocks;int nblocks, i, disk;char *buf = NULL;unsigned short rotate = 0;
#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_PPC_ISERIES)char rotator[4] = { '|' , '/' , '-' , '\\' };
#endif
  • frominitrd镜像文件路径
  • res:结果标志
  • in_fd, out_fd:输入输出文件描述符
  • rd_blocks, devblocks:块数量
  • nblocks, i, disk:循环计数器和磁盘编号
  • buf:数据缓冲区
  • rotate, rotator:进度指示器旋转动画

打开输出设备(RAM disk)

	out_fd = sys_open("/dev/ram", O_RDWR, 0);if (out_fd < 0)goto out;
  • 以读写方式打开RAM disk设备
  • 如果打开失败,跳转到清理代码

打开输入文件(initrd镜像)

	in_fd = sys_open(from, O_RDONLY, 0);if (in_fd < 0)goto noclose_input;
  • 以只读方式打开initrd镜像文件
  • 如果打开失败,跳转到不关闭输入的清理代码

识别RAM disk镜像格式

	nblocks = identify_ramdisk_image(in_fd, rd_image_start);if (nblocks < 0)goto done;
  • identify_ramdisk_image():识别镜像格式并返回块数
  • 如果识别失败,跳转到完成代码

压缩镜像处理

	if (nblocks == 0) {
#ifdef BUILD_CRAMDISKif (crd_load(in_fd, out_fd) == 0)goto successful_load;
#elseprintk(KERN_NOTICE"RAMDISK: Kernel does not support compressed ""RAM disk images\n");
#endifgoto done;}
  • nblocks == 0 表示压缩的镜像
  • 如果支持压缩RAM disk,调用 crd_load 加载
  • 否则打印不支持压缩镜像的警告

容量检查

	/** NOTE NOTE: nblocks is not actually blocks but* the number of kibibytes of data to load into a ramdisk.*/if (sys_ioctl(out_fd, BLKGETSIZE, (unsigned long)&rd_blocks) < 0)rd_blocks = 0;elserd_blocks >>= 1;if (nblocks > rd_blocks) {printk("RAMDISK: image too big! (%dKiB/%ldKiB)\n",nblocks, rd_blocks);goto done;}
  • nblocks实际上是KiB数而不是块数
  • BLKGETSIZE 获取设备大小(单位是512字节扇区)
  • >> 1 转换为KiB单位
  • 检查镜像是否超过RAM disk容量

确定设备大小

	if (sys_ioctl(in_fd, BLKGETSIZE, (unsigned long)&devblocks) < 0)devblocks = 0;elsedevblocks >>= 1;if (strcmp(from, "/initrd.image") == 0)devblocks = nblocks;if (devblocks == 0) {printk(KERN_ERR "RAMDISK: could not determine device size\n");goto done;}
  • 获取输入设备的大小
  • 如果是initrd镜像文件,直接使用nblocks作为设备大小
  • 检查设备大小是否有效

分配缓冲区

	buf = kmalloc(BLOCK_SIZE, GFP_KERNEL);if (buf == 0) {printk(KERN_ERR "RAMDISK: could not allocate buffer\n");goto done;}
  • 分配块大小的缓冲区
  • 如果分配失败,打印错误并跳转

数据拷贝循环

	printk(KERN_NOTICE "RAMDISK: Loading %dKiB [%ld disk%s] into ram disk... ",nblocks, ((nblocks-1)/devblocks)+1, nblocks>devblocks ? "s" : "");for (i = 0, disk = 1; i < nblocks; i++) {
  • 打印加载信息,包括总大小和磁盘数量
  • 开始数据拷贝循环

多磁盘处理

		if (i && (i % devblocks == 0)) {printk("done disk #%d.\n", disk++);rotate = 0;if (sys_close(in_fd)) {printk("Error closing the disk.\n");goto noclose_input;}change_floppy("disk #%d", disk);in_fd = sys_open(from, O_RDONLY, 0);if (in_fd < 0)  {printk("Error opening disk.\n");goto noclose_input;}printk("Loading disk #%d... ", disk);}
  • 处理多磁盘情况(当数据跨越多张软盘时)
  • 关闭当前磁盘,提示用户更换磁盘,打开新磁盘

数据读写和进度显示

		sys_read(in_fd, buf, BLOCK_SIZE);sys_write(out_fd, buf, BLOCK_SIZE);
#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_PPC_ISERIES)if (!(i % 16)) {printk("%c\b", rotator[rotate & 0x3]);rotate++;}
#endif}printk("done.\n");
  • 读取一个块的数据并写入RAM disk
  • 每16个块显示一次旋转进度指示器
  • 循环结束后打印完成信息

清理和返回

successful_load:res = 1;
done:sys_close(in_fd);
noclose_input:sys_close(out_fd);
out:kfree(buf);sys_unlink("/dev/ram");return res;
}
  • 设置成功标志
  • 关闭文件描述符
  • 释放缓冲区
  • 删除临时设备文件
  • 返回结果

handle_initrd 函数

函数声明和初始设置

static void __init handle_initrd(void)
{int error;int i, pid;real_root_dev = new_encode_dev(ROOT_DEV);create_dev("/dev/root.old", Root_RAM0, NULL);
  • 保存真实的根设备号
  • 创建设备文件 /dev/root.old 指向RAM disk

挂载initrd

	/* mount initrd on rootfs' /root */mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);sys_mkdir("/old", 0700);root_fd = sys_open("/", 0, 0);old_fd = sys_open("/old", 0, 0);
  • 以读写方式挂载initrd/root
  • 创建 /old 目录用于后续操作
  • 保存根目录和old目录的文件描述符

切换到initrd环境

	/* move initrd over / and chdir/chroot in initrd root */sys_chdir("/root");sys_mount(".", "/", NULL, MS_MOVE, NULL);sys_chroot(".");mount_devfs_fs ();
  • 切换到initrd的挂载点
  • initrd移动到根位置
  • 执行chroot切换到initrd环境
  • 挂载设备文件系统

执行initrd中的linuxrc

	pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);if (pid > 0) {while (pid != sys_wait4(-1, &i, 0, NULL))yield();}
  • 创建内核线程执行 /linuxrc 脚本
  • 等待linuxrc执行完成

恢复原始根环境

	/* move initrd to rootfs' /old */sys_fchdir(old_fd);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);umount_devfs("/old/dev");
  • 切换回原始根文件系统
  • initrd移动到 /old
  • 恢复原始根目录
  • 关闭文件描述符
  • 卸载设备文件系统

处理RAM disk作为根设备的情况

	if (new_decode_dev(real_root_dev) == Root_RAM0) {sys_chdir("/old");return;}
  • 如果真实的根设备就是RAM disk,直接使用initrd
  • 切换到 /old 目录并返回

挂载真正的根文件系统

	ROOT_DEV = new_decode_dev(real_root_dev);mount_root();
  • 恢复真实的根设备号
  • 挂载真正的根文件系统

清理initrd

	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);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 < 0) {error = fd;} else {error = sys_ioctl(fd, BLKFLSBUF, 0);sys_close(fd);}printk(!error ? "okay\n" : "failed\n");}
}
  • 尝试将旧的initrd移动到 /initrd
  • 如果移动失败,卸载并释放RAM disk内存

函数功能详解

initrd_load 功能

主要功能:控制initrd的加载和执行流程

详细执行流程

  1. 配置检查

    • 检查是否启用了initrd支持
    • 创建设备文件为加载做准备
  2. 镜像加载决策

    • 加载initrd镜像到RAM disk
    • 根据根设备配置决定是否执行initrd
    • 只在RAM disk不是最终根设备时执行initrd
  3. 资源清理

    • 删除临时文件
    • 返回执行状态

rd_load_image 功能

主要功能:将initrd镜像加载到RAM disk中

详细执行流程

  1. 设备准备

    • 打开RAM disk设备作为输出
    • 打开initrd镜像文件作为输入
  2. 镜像识别

    • 识别镜像格式(压缩或未压缩)
    • 处理压缩镜像的特殊加载
  3. 容量验证

    • 检查RAM disk容量是否足够
    • 验证设备参数
  4. 数据传输

    • 分块读取和写入数据
    • 支持多磁盘切换
    • 提供进度显示
  5. 资源管理

    • 正确关闭文件和释放内存
    • 清理临时资源

handle_initrd 功能

主要功能:执行initrd中的初始化脚本并切换到真正的根文件系统

详细执行流程

  1. 环境准备

    • 保存真实根设备信息
    • 挂载initrd为临时根文件系统
  2. 脚本执行

    • 切换到initrd环境
    • 执行/linuxrc初始化脚本
    • 等待脚本完成
  3. 环境恢复

    • 切换回原始根文件系统
    • 挂载真正的根文件系统
  4. 资源清理

    • 移动或清理initrd
    • 释放RAM disk内存

系统启动中的关键作用

initrd机制的核心价值

  1. 模块化启动:在挂载真正根文件系统前加载必要驱动
  2. 硬件检测:执行硬件特定的初始化脚本
http://www.dtcms.com/a/545588.html

相关文章:

  • A800 部署 Qwen2-VL-8B-Instruct 完整指南
  • rust:第一个程序HelloWorld
  • 给新公司建网站用代理访问永久域名
  • 启动Hana失败 FAIL: process hdbdaemon HDB Daemon not running
  • iOS 26 内存占用监控 多工具协同下的性能稳定性分析实战
  • Kubernetes service管理
  • 布吉企业网站建设百度网站两两学一做心得体会
  • 深入仓颉(Cangjie)编程语言:循环的革命——从“命令式”操控到“声明式”安全迭代
  • 画出网站和目录结构图wordpress 自定义表
  • linux gpio errno EBUSY问题举例分析
  • 如何在 macOS 中清理 Homebrew 软件包 ?
  • 手机网站设计立找亿企邦湖南长沙房价2023年最新房价
  • 如何选择徐州网站开发wordpress新建页面位置
  • nestjs引篇
  • apmserv 设置网站目录yy头像在线制作网站
  • 基于YOLO+多模态大模型+人脸识别+视频检索的智慧公安综合研判平台(vue+flask+AI算法)
  • 二手车网站程序德阳网站建设 选哪家好
  • 极智算服务器用的还是自己的网络吗安全吗
  • Jenkins vs GitLab CI/CD vs Arbess,CI/CD工具一文纵评
  • 机器学习—— 回归分析之如何建立回归模型
  • MySQL中SUBSTRING_INDEX函数作用
  • 网站设计评级杭州网站搜索排名
  • 2.1 AI与大模型介绍
  • 厦门网站建设阿里流量型网站 cms
  • 【笔试真题】- 科大讯飞研发岗-2025.09.27
  • 20.15 多模态系统测试实战:跨模态对齐实现98%准确率的关键方案
  • GXDE 2025 Edition RC 开始测试
  • 网站设计标注图怎么做广东阳江发布最新消息
  • Java中使用Collator实现对象List按照中文姓名属性进行A-Z的排序实现
  • Oracle AWR 报告中的SQL来自哪儿?