prepare_namespace()函数 挂载rootfs

JFFS2与SQASHFS挂载区别
本文探讨了JFFS2与SQASHFS两种文件系统在嵌入式系统中的应用特点,特别是针对内核挂载过程中的不同之处。文章指出,JFFS2需要设置FS_REQUIRES_DEV标志来确保它被识别为非虚拟文件系统,并且需要通过内核命令行参数rootfstype=jffs2来指定。相比之下,SQASHFS则不需要这些额外的配置。

今天发现sqashfs制作的根文件系统不需要像jffs2那样需要增加内核命令行:rootfstype=jffs2。看了一下内核挂载根文件系统个过程,发现是JFFS2注册文件系统时少设置了一个标志:FS_REQUIRES_DEV。
修改点: fs/jffs2/super.c


static struct file_system_type jffs2_fs_type = {
    .owner =    THIS_MODULE,
    .name =        "jffs2",
    .get_sb =    jffs2_get_sb,
    .kill_sb =    jffs2_kill_sb,
    .fs_flags = FS_REQUIRES_DEV,
};
有了这个标志表示该文件系统不是虚文件系统,必须要有实际的块设备。(参考: http://www.win.tue.nl/~aeb/linux/lk/lk-8.html)


通过cat /proc/filesystems, 如果没有'nodev'这个字符串,就说明带了这个标志。


$ cat /proc/filesystems 
nodev    sysfs
nodev    rootfs
nodev    bdev
nodev    proc
nodev    sockfs
nodev    pipefs
nodev    futexfs
nodev    tmpfs
nodev    eventpollfs
nodev    devpts
               squashfs
nodev    ramfs
               jffs2
nodev    autofs
nodev    mqueue


内核在挂载根文件系统时,如果没有命令行参数指定根文件系统类型,会从注册的所有文件系统中挑选有这个标志的文件系统一一尝试。


prepare_namespace()
if (saved_root_name[0]) {
root_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 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;
}
mount_root()
#ifdef CONFIG_BLOCK //block device
create_dev("/dev/root", ROOT_DEV);
mount_block_root("/dev/root", root_mountflags);
#endif


void __init mount_block_root(char *name, int flags)
get_fs_names(fs_names)
retry:
for (p = fs_names; *p; p += strlen(p)+1) { //in this loop,
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;
}
#ifdef CONFIG_BLOCK
__bdevname(ROOT_DEV, b); //b is a char array.
scnprintf(b, BDEVNAME_SIZE, "unknown-block(%u,%u)", MAJOR(ROOT_DEV), MINOR(ROOT_DEV))
#endif
printk("VFS: Cannot open root device \"%s\" or %s\n",
root_device_name, b); //root_device_name = "mtdblock3", b = "unknown-block(0,0)(commonly)"
printk("Please append a correct \"root=\" boot option; here are the available partitions:\n");


printk_all_partitions();
panic("VFS: Unable to mount root fs on %s", b);
}
out:
putname(fs_names);
### unpack_to_rootfs 函数功能分析 `unpack_to_rootfs` 函数的核心功能是将 CPIO 格式的压缩包解压到根文件系统(rootfs)中,并具备检测输入数据是否为合法的 CPIO 包的功能。该函数的两个主要用途是: - **检测模式(参数为 1)**:检查给定的数据是否为有效的 CPIO 归档文件。 - **解压模式(参数为 0)**:将有效的 CPIO 文件解压到 rootfs 中,通常用于内核初始化阶段加载 initramfs 或 initrd。 该函数的设计使得在系统启动早期阶段可以处理嵌入在内核中的初始 RAM 文件系统(initramfs)或由引导加载程序(如 U-Boot)提供的 RAM 磁盘镜像(initrd)[^3]。 ### 实现机制分析 `unpack_to_rootfs` 函数的实现涉及多个关键步骤,包括 CPIO 文件头的解析、数据校验、文件解压和写入根文件系统等。 #### 1. CPIO 文件格式识别 CPIO 是一种归档文件格式,通常用于 Linux 内核的 initramfs。其文件结构由多个连续的文件项组成,每个文件项包括一个文件头和可选的数据块。文件头的结构定义如下(简化版): ```c struct cpio_newc_header { char c_magic[6]; // 固定值 "070701" 或 "070702" char c_ino[8]; // inode 号 char c_mode[8]; // 文件权限和类型 ... }; ``` 函数首先读取输入数据的起始部分,检查 `c_magic` 字段是否为合法的 CPIO 标识符(如 `"070701"` 或 `"070702"`),以判断是否为 CPIO 文件。此过程即为检测模式的主要逻辑[^1]。 #### 2. 解压与写入 rootfs 在确认输入数据为合法 CPIO 文件后,函数进入解压模式。其主要流程如下: - **逐个解析 CPIO 条目**:从输入数据中逐个读取 CPIO 条目(包括文件头和数据块)。 - **创建文件节点**:根据文件头中的 `c_mode` 创建相应的文件、目录或设备节点。 - **写入文件内容**:若条目包含文件数据,则将其写入对应的文件节点中。 - **处理特殊文件类型**:如符号链接、硬链接、设备文件等,需根据文件头信息进行特殊处理。 例如,以下伪代码展示了如何解析并处理一个 CPIO 条目: ```c while (current < end_of_data) { struct cpio_newc_header *hdr = (struct cpio_newc_header *)current; if (!is_valid_cpio_header(hdr)) break; char *filename = current + sizeof(struct cpio_newc_header); uint32_t namesize = get_name_size(hdr); uint32_t filesize = get_file_size(hdr); current += sizeof(struct cpio_newc_header) + namesize; if (strncmp(filename, "TRAILER!!!", 10) == 0) break; // 创建文件节点 create_file_in_rootfs(filename, hdr); // 写入文件内容 if (filesize > 0) { write_file_data(filename, current, filesize); current += ALIGN(filesize, 4); // CPIO 数据块通常按 4 字节对齐 } } ``` #### 3. 与内核初始化流程的集成 在 Linux 内核启动过程中,`unpack_to_rootfs` 通常由 `populate_rootfs` 调用,用于解压内核内置的 initramfs 或由 U-Boot 提供的 initrd: ```c static int __init populate_rootfs(void) { char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size); // 解压 initramfs if (initrd_start) { err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start); // 解压 initrd ... } return 0; } ``` 此过程发生在 `rest_init` -> `kernel_init` -> `populate_rootfs` 的调用链中,确保在挂载真正的根文件系统之前完成初始文件系统的解压与准备[^4]。 ### 特性与优化 - **内存效率**:由于在内核启动早期阶段可用内存有限,`unpack_to_rootfs` 在实现上尽量避免使用动态内存分配,而是直接操作静态缓冲区。 - **错误处理**:函数在检测到非法 CPIO 文件或解压失败时会返回错误信息,但通常在内核中此类错误会导致 panic。 - **对齐处理**:CPIO 文件中的每个条目数据块通常按 4 字节对齐,因此在读取时需进行相应的对齐跳过。 ### 小结 `unpack_to_rootfs` 是 Linux 内核中用于处理 CPIO 格式初始文件系统的核心函数,其设计兼顾了检测与解压功能,确保了系统在启动早期阶段能够正确加载 initramfs 或 initrd,为后续挂载真正的根文件系统做好准备。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值