smart-os/初识 initramfs .md
2023-06-08 22:02:39 +08:00

55 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 简述
内核启动时,并没加载根文件系统,有些是内核启动阶段不易完成的工作(驱动现在和内核很多都是分开的,位于外存储系统上,所以内核启动后很多事情都做不了),还有些根文件系统是加密的,反正有多种限制和需求。
所以内核启动后会先启动一个内存模式的根文件系统,通常就是 initramfs ,早期一般都是采用 initrd initrd 是一个位于内存中的完整模拟的块设备,需要整个文件系统的开销,有固定的大小,不灵活,它是一个真
实的,静态的设备,消耗 Linux 内核中的缓存和文件分页,带来更多的内存开销. 鉴于 initrd 存在很多弊端,后续逐步被 initramfs 所代替. initramfs 不是设备,而是能直接运行在内存中的根文件系统,规避了 initrd
的弊端,好处大大的。
### 为什么驱动不集成到内核?
1. Linux 是一个通用的系统,目标是针对各种不同的硬件平台,况且还有很多新的设备源源不断的出来并集成到内核,将所有的驱动全部集成到内核非常不合理,因此需要把驱动和内核解耦分开,通常这些驱动模块编译成模块,存在外部的根文件系统中
2. 根文件系统不可能在一个简单的硬盘上,当使用磁盘阵列 RAID 时根文件系统可能跨几个存储设备根文件系统也可能在某个网络设备上比如NFS ,还需要网络配置,网络认证,还有加解密的需要,等各种需求场景
为了解决这些问题initramfs 横空出世initramfs 是一个临时的文件系统,其中包含了必要的设备如硬盘、网卡、文件系统等的驱动以及加载驱动的工具及其运行环境,比如基本的 C 库,动态库的链接加载器等等
早期的处理时基于 initrd initrd 是基于 ramdisk 技术的,而 ramdisk 就是一个基于内存的块设备,因此 initrd 也具有块设备的一切属性。比如 initrd 容量是固定的,一旦创建 initrd 时设定了一个大小,就不能再进行动态调整,所以后续被 initramfs 逐步代替
# initramfs 的好处
我们知道 Linux 存在着 live 版本live 启动后会卸载光盘,把系统加载到内存中,这样尝试一个新系统或对另一个系统进行修复时不会伤害到已安装的系统,非常方便。
此外还可以
1. 加载模块,如第三方驱动
2. 定制化启动系统
3. 制作一个很小的 rescue shell
4. 内核不能,但是用户态可以完成的命令
5. 逻辑卷管理器
6. 软件 RAID
7. 蓝牙驱动
# initramfs 的组成
内核挂载 initramfs 时,文件系统的根分区并没挂载,所以无法访问文件系统中的文件。所以 initramfs 至少包含一个文件 /init ,内核的第一个进程,就是咱们常常听说的大名鼎鼎的 init 进程。
别的根据需要定制,当然 /usr, /bin 等目录也是必要的。initramfs 是通过 cpio 进行归档的,能够兼容大部分设备。
# initramfs 的运行
initramfs 通常和内核一起存储bootloader 引导内核时,通常会把 initramfs 以参数的形式传递给内核,内核检测到 initramfs ,会创建一个 tmpfs 文件系统,并把 initramfs 解压到 tmpfs 中,内核启动 tmpfs 文件系统中的 init 脚本,该程序将探测硬件设备、加载驱动,挂载真正的文件系统,执行文件系统上的 /sbin/init进而切换到真正的用户空间。真正的文件系统挂载后initramfs 即完成了使命,其占用的内存也会被释放,继而进行后续的启动过程
当2.6版本的内核引导时,在挂载真正的根文件系统之前,首先将挂载一个名为 rootfs 的文件系统,并将 rootfs 的根作为虚拟文件系统目录树的总根。那么为什么要使用 rootfs 这么一个中间过程呢?原因之一还是为了解决鸡和蛋的问题。内核需要根文件系统上的驱动以及程序来驱动和挂载根文件系统,但是这些驱动和程序有可能没有编译进内核,而在根文件系统上。如果不借助第三方,内核是没有办法挂载真正的根文件系统的。而 rootfs 虽然名称为 rootfs但是并不是什么新的文件系统事实上rootfs 就是一个 ramfs只不过换了一个名称。换句话说rootfs 是在内存中的,内核不需要特殊的驱动就可以挂载 rootfs所以内核使用 rootfs 作为一个过渡的桥梁。        
在挂载了 rootfs 后,内核将 Bootloader 加载到内存中的 initramfs 中打包的文件解压到 rootfs 中而这些文件中包含了驱动以及挂载真正的根文件系统的工具内核通过加载这些驱动、使用这些工具实现了挂载真正的根文件系统。此后rootfs 也完成了历史使命被真正的根文件系统覆盖overmount。但是 rootfs 作为虚拟文件系统目录树的总根并不能被卸载。但是这没有关系前面我们已经谈到了rootfs 基于 ramfs删除其中的文件即可释放其占用的空间
1、挂载 rootfs
用于不同操作系统的文件系统其物理存储结构是不同的,但是 Linux 的虚拟文件系统通过为这些文件系统建立中间适配层,实现了 Linux 对各个文件系统的支持Linux 的虚拟文件系统将文件系统组织为树形结构。在初始化阶段,内核挂载 rootfs 文件系统虚拟文件系统从无到有rootfs 的根作为虚拟文件系统这棵大树中的第一个节点,自然成为所有后来创建的节点的祖先。也就是说,虚拟文件系统目录树的根就是 rootfs 的根本质上rootfs 就是一个 ramfs 文件系统。        
通过挂载 rootfs虚拟文件系统的根目录已经建立起来根目录已经可以容纳文件了。所以接下来内核解压 initramfs 的内容到虚拟文件系统的根中,利用 initramfs 中的内容挂载并切换到真正的根文件系统。
2、解压 initramfs 到 rootfs
在挂载了 rootfs 后,内核将 Bootloader 加载到内存中的 initramfs 中的文件解压到 rootfs 中。而这些文件中包含了驱动以及挂载真正的根文件系统的工具,内核通过加载这些驱动、使用这些工具实现挂载真正的根文件系统。
3、挂载并切换到真正的根目录
将 initramfs 成功解压后,挂载真正的根文件系统所需的驱动、程序等已经全部俱备,可以挂载真正的根文件系统了,在 grub.cfg 文件中对应的就是 root=XXX。内核将真正的根文件系统挂载到 initramfs 文件系统中的 /root 目录下。        
挂载真正的根文件系统后rootfs 中的内容已经没有保留的意义,但是并不能将 rootfs 卸载,因为 rootfs 是整个虚拟文件系统的根。因此,为了不占用内存空间,将 rootfs 中的内容(文件)释放掉即可,然后将真正的根文件系统移动到虚拟文件系统的根(即 rootfs 的根)下,最后再将进程的文件系统的 namespace 切换到真正的根文件系统。
# initramfs文件基本操作
1. 查看 initramfs
lsinitrd
2. 解压 initramfs
/usr/lib/dracut/skipcpio initramfs-3.10.0-229.el7.x86_64.img | zcat | cpio -ivd
3. 创建 initramfs
find . | cpio -o -H newc | gzip -9 > /tmp/test.img