理解 initramfs
这里是 linux kernel Documentation 阅读系列第一篇,在这个系列中,我会记录每一次阅读到的 linux 内核文档。这一篇阅读的是 ramfs, rootfs and initramfs,主要是介绍 initramfs。
What is ramfs?
ramfs 是 linux 中利用 linux 的 disk caching 机制(page cache 和 dentry cache)实现的可动态伸缩的基于 RAM 做存储的文件系统。
文件一般放在磁盘上,当需要对其读写的时候会将其加载到内存中。为了提高文件的读写效率,根据局部性原理,linux 基本上都会文件缓存到内存中。
- 当文件的数据被 OS 从后端存储读取到内存的时候,因为这段数据可能之后会被用到,并不会被马上释放,这块数据将会被标记为
clean,就是说可以被释放掉。只有当 VM 系统需要用到这块内存做其他事情的时候,这些数据才会被释放掉。 - 当有数据写到文件后,这段数据也会被标记为
clean,但是仍然保存在内存中而不释放,以用作缓存的目的。只有当 VM 系统重新申请这块内存的时候,这段数据才会被释放掉。
对于 dentry cache,linux 也有类似的机制,从而大大加快了对于目录的访问。
上述说的是我们常见文件系统的机制,对于 ramfs而言,这里根本没有所谓的后端存储。当你要向 ramfs写文件时,我们像原来一样,在内存上分配 page cache和dentry cache,但是这些 cache 不会被写到磁盘这些后端存储中 。所以,这些 cache 根本不会被标记为 clean,而会一直存在于内存中,VM 系统也无法回收他们的内存,重新分配做它用。
实现 ramfs需要的代码量非常的少,因为他们基本上就是依靠 Linux 现有的caching infastructure。对于用户而言,我们只是把一个 disk cache加载成文件系统。因此,ramfs就不是通过menuconfig可以被移去的可选模块,它底层的机制是 linux 所必须的。
下面做了一个简单的实验,这是当前系统能够看到的文件系统。
|
|
我们可以通过下列命令自己创建 ramfs
|
|
ramfs and ramdisk
ramdisk是比ramfs出现更早的机制,是利用 RAM 模拟生成一个块设备,以此作为文件系统的后端存储。这个块设备是固定大小的,所以它上面 mount 的文件系统也是固定大小的。和实际的块设备一样,我们需要把page cache从这块假的块设备复制到内存,然后把改变复制回去,对于 dentry cache也是一样。除此之外,它还需要文件系统驱动(比如 ext2)去格式化和解释这段数据。
相较于ramfs,ramdisk会浪费更多的内存,占用更多的内存总线带宽,给 CPU 带来更多的工作,并且污染 CPU 的 cache。相较而言,ramfs的实现机制更加简单和高效。
loopback devices是导致 ramdisk 淘汰的另一个原因,它相对于ramdisk而言提供了一种更加灵活和方便的方式来创建块设备,现在是通过文件而不是通过内存。
ramfs and tmpfs
ramfs的一个问题是,你可以一直往 ramfs里面写数据,直到你用完了所有的内存。而且 VM 系统也不能释放这段内存,因为 VM 认为这些数据应该被写到后端存储,而对于 ramfs而言他没有后端存储。因此,只有 root 用户能够往 ramfs写数据。
为了解决上述问题,linux 内核开发者又发明了 tmpfs,给添加了大小的限制和普通用户写数据的权限。
| 特性 | tmpfs | ramfs |
|---|---|---|
| 达到空间上限时继续写入 | 提示错误信息并终止 | 可以继续写尚未分配的空间 |
| 是否固定大小 | 是 | 否 |
| 是否使用 swap | 是 | 否 |
| 具有易失性 | 是 | 是 |
What is rootfs?
rootfs是ramfs或者tmpfs的一种特殊实例,根文件系统包含系统启动时所必须的目录和关键性的文件,以及使其他文件系统得以挂载(mount)所必要的文件。例如:
- init 进程的应用程序必须运行在根文件系统上
- 根文件系统提供了根目录“/”
- linux 挂载分区时所依赖的信息存放于根文件系统/etc/fstab 这个文件中
- shell 命令程序必须运行在根文件系统上,譬如 ls、cd 等命令
一套 linux 体系,只有内核本身是不能工作的,必须要 rootfs(上的 etc 目录下的配置文件、/bin /sbin 等目录下的 shell 命令,还有/lib 目录下的库文件等···)相配合才能工作。
Linux 启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。成功之后可以自动或手动挂载其他的文件系统。
下面是 linux 的内核代码。rootfs 是基于内存的文件系统,所有操作都在内存中完成;也没有实际的存储设备,所以不需要设备驱动程序的参与。基于以上原因,linux 在启动阶段使用 rootfs 文件系统,当磁盘驱动程序和磁盘文件系统成功加载后,linux 系统会将系统根目录从 rootfs 切换到磁盘文件系统。
|
|
下面是 init_rootfs的代码,可以看到,如果 CONFIG_TMPFS开启,rootfs 将会默认使用 tmpfs,否则使用 ramfs。
|
|
What is initramfs?
initramfs 是一种 ramfs 文件系统,在内核启动完成后把它复制到 rootfs 中,作为内核初始的根文件系统,它的任务是挂载系统真正的根文件系统。所有的 2.6 版本的 linux 内核都包含一个 gzip 压缩过的 cpio 存档,在 kernel 启动的时候,将会将其解压成 rootfs。解压之后,内核将会检查 rootfs 是否有一个 init文件,如果有的话,将会执行 init程序作为 PID 为 1 的进程。在这之后,init进程将会负责其启动整个系统,包括找到并加载真正的根设备。
相对于老的 initrd机制,initramfs有以下几点区别:
- 老的
initrd总是一个独立的文件,而initramfs存档是被链接到内核镜像中去的 - 老的
initrd文件是一个 gzip 压缩过的文件系统镜像,新的initramfs是 gzip 压缩过的 cpio 存档,相对而言更简单 - 以往的基于 ramdisk 的 initrd 使用 pivot_root 命令切换到新的根文件系统,然后卸载 ramdisk。但是 initramfs 是 rootfs,而 rootfs 既不能 pivot_root,也不能 umount。为了从 initramfs 中切换到新根文件系统,需要作如下处理:
- 删除 rootfs 的全部内容,释放空间
find -xdev / -exec rm '{}' ';' - 安装新的根文件系统,并切换
cd /newmount; mount --move . /; chroot . - 把 stdin/stdout/stderr 附加到新的/dev/console,然后执行新文件系统的 init 程序
- 删除 rootfs 的全部内容,释放空间
上述步骤比较麻烦,而且要解决一个重要的问题:第一步删除 rootfs 的所有内容也删除了所有的命令,那么后续如何再使用这些命令完成其他步骤?busybox 的解决方案是,提供了 switch_root 命令,完成全部的处理过程,使用起来非常方便。
switch_root命令的格式是:
|
|
newrootdir是实际的根文件系统的挂载目录,执行 switch_root 命令前需要挂载到系统中- init
是实际根文件系统的init程序的路径,一般是/sbin/init; - args to init`则是传递给实际的根文件系统的 init 程序的参数,也是可选的。
需要特别注意的是:switch_root 命令必须由 PID=1 的进程调用,也就是必须由 initramfs 的 init 程序直接调用,不能由 init 派生的其他进程调用,否则会出错,提示:switch_root: not rootfs。也是同样的原因,init 脚本调用 switch_root 命令必须用 exec 命令调用,否则也会出错,提示:switch_root: not rootfs
Contents of initramfs
An initramfs archive is a complete self-contained root filesystem for Linux.
|
|
参考资料
-
No backlinks found.