早期用户空间支持

上次更新:2004-12-20 tlh

“早期用户空间”是一组库和程序,它们提供了在 Linux 内核启动时必须可用的各种重要功能,但这些功能不需要在内核本身内部运行。

它由几个主要的基础设施组件组成

  • gen_init_cpio,一个构建包含根文件系统映像的 cpio 格式存档的程序。此存档被压缩,压缩后的映像被链接到内核映像中。

  • initramfs,一段代码,在内核启动过程的中途解压缩压缩的 cpio 映像。

  • klibc,一个用户空间 C 库,目前单独打包,针对正确性和小尺寸进行了优化。

initramfs 使用的 cpio 文件格式是 “newc”(又名 “cpio -H newc”)格式,并记录在文件 “initramfs 缓冲区格式” 中。 有两种方法可以添加早期用户空间映像:指定要用作映像的现有 cpio 存档,或让内核构建过程从规范构建映像。

CPIO 存档方法

您可以创建一个包含早期用户空间映像的 cpio 存档。您的 cpio 存档应在 CONFIG_INITRAMFS_SOURCE 中指定,它将被直接使用。CONFIG_INITRAMFS_SOURCE 中只能指定单个 cpio 文件,并且不允许将目录和文件名与 cpio 存档组合使用。

映像构建方法

内核构建过程还可以从源部分构建早期用户空间映像,而不是提供 cpio 存档。此方法提供了一种创建具有 root 拥有的文件的映像的方法,即使该映像是由非特权用户构建的。

映像在 CONFIG_INITRAMFS_SOURCE 中指定为一个或多个源。源可以是目录或文件 - 从源构建时,不允许使用 cpio 存档。

源目录及其所有内容都将被打包。指定的目录名称将映射到 ‘/’。打包目录时,可以执行有限的用户和组 ID 转换。INITRAMFS_ROOT_UID 可以设置为需要映射到用户 root (0) 的用户 ID。INITRAMFS_ROOT_GID 可以设置为需要映射到组 root (0) 的组 ID。

源文件必须是 usr/gen_init_cpio 实用程序所需的格式的指令(运行 ‘usr/gen_init_cpio -h’ 以获取文件格式)。文件中的指令将直接传递给 usr/gen_init_cpio。

当指定目录和文件的组合时,initramfs 映像将是它们所有内容的聚合。通过这种方式,用户可以创建一个 “root-image” 目录并将所有文件安装到其中。由于设备特殊文件不能由非特权用户创建,因此特殊文件可以在 “root-files” 文件中列出。“root-image” 和 “root-files” 都可以列在 CONFIG_INITRAMFS_SOURCE 中,并且非特权用户可以构建完整的早期用户空间映像。

作为技术说明,当指定目录和文件时,整个 CONFIG_INITRAMFS_SOURCE 将传递给 usr/gen_initramfs.sh。这意味着 CONFIG_INITRAMFS_SOURCE 实际上可以解释为 gen_initramfs.sh 的任何合法参数。如果将目录指定为参数,则会扫描内容,执行 uid/gid 转换,并输出 usr/gen_init_cpio 文件指令。如果将目录指定为 usr/gen_initramfs.sh 的参数,则文件的内容将简单地复制到输出。来自目录扫描和文件内容复制的所有输出指令都由 usr/gen_init_cpio 处理。

另请参阅 ‘usr/gen_initramfs.sh -h’。

这一切都指向哪里?

klibc 发行版包含一些使早期用户空间有用的必要软件。 klibc 发行版目前与内核分开维护。

您可以从 https://linuxkernel.org.cn/pub/linux/libs/klibc/ 获取 klibc 的一些不频繁的快照

对于活跃用户,您最好使用 klibc git 存储库,位于 https://git.kernel.org/?p=libs/klibc/klibc.git

除了 klibc 库之外,独立的 klibc 发行版目前还提供三个组件

  • ipconfig,一个配置网络接口的程序。它可以静态配置它们,或者使用 DHCP 动态获取信息(又名“IP 自动配置”)。

  • nfsmount,一个可以挂载 NFS 文件系统的程序。

  • kinit,“粘合剂”,它使用 ipconfig 和 nfsmount 来替换对 IP 自动配置的旧支持,通过 NFS 挂载文件系统,并继续使用该文件系统作为 root 来启动系统。

kinit 被构建为单个静态链接的二进制文件以节省空间。

最终,希望有更多的内核功能将移动到早期用户空间

  • 几乎所有 init/do_mounts*(这部分已经开始实现)

  • ACPI 表解析

  • 在此处插入不需要在内核空间中的笨拙子系统

如果 kinit 不能满足您当前的需求并且您有足够的字节可以使用,klibc 发行版包含一个小的 Bourne 兼容 shell (ash) 和许多其他实用程序,因此您可以替换 kinit 并构建完全满足您需求的自定义 initramfs 映像。

如有问题和帮助,您可以在 https://www.zytor.com/mailman/listinfo/klibc 注册早期用户空间邮件列表

它是如何工作的?

内核目前有 3 种方式来挂载根文件系统

  1. 所有必需的设备和文件系统驱动程序都编译到内核中,没有 initrd。 init/main.c:init() 将调用 prepare_namespace() 来挂载最终的根文件系统,基于 root= 选项和可选的 init= 来运行 init/main.c:init() 末尾列出的其他 init 二进制文件。

  2. 一些设备和文件系统驱动程序被构建为模块并存储在 initrd 中。 initrd 必须包含一个二进制文件 ‘/linuxrc’,该二进制文件应该加载这些驱动程序模块。也可以通过 linuxrc 挂载最终的根文件系统并使用 pivot_root 系统调用。 initrd 通过 prepare_namespace() 挂载和执行。

  3. 使用 initramfs。必须跳过对 prepare_namespace() 的调用。这意味着二进制文件必须完成所有工作。上述二进制文件可以通过修改 usr/gen_init_cpio.c 或通过新的 initrd 格式(一个 cpio 存档)存储到 initramfs 中。它必须被称为 “/init”。此二进制文件负责执行 prepare_namespace() 将要完成的所有事情。

    为了保持向后兼容性,/init 二进制文件仅在它通过 initramfs cpio 存档传递时才会运行。如果不是这种情况,init/main.c:init() 将运行 prepare_namespace() 来挂载最终的 root 并执行其中一个预定义的 init 二进制文件。

Bryan O’Sullivan <bos@serpentine.com>