使用初始RAM磁盘(initrd) 【ChatGPT】

发布时间 2023-12-08 23:31:44作者: 摩斯电码

使用初始RAM磁盘(initrd)

由Werner Almesberger werner.almesberger@epfl.ch和Hans Lermen lermen@fgan.de于1996年、2000年编写

initrd提供了通过引导加载程序加载RAM磁盘的功能。然后,可以将此RAM磁盘挂载为根文件系统,并可以从中运行程序。然后,可以从不同设备挂载新的根文件系统。之前的根文件系统(来自initrd)然后被移动到一个目录中,并可以随后卸载。

initrd主要设计用于允许系统启动分为两个阶段,其中内核使用最小的一组编译驱动程序启动,并从initrd加载其他模块。

本文简要介绍了initrd的使用。有关引导过程的更详细讨论,请参阅1。

操作

使用initrd时,系统通常按以下方式启动:

  • 引导加载程序加载内核和初始RAM磁盘

  • 内核将initrd转换为“正常”的RAM磁盘,并释放initrd使用的内存

  • 如果根设备不是/dev/ram0,则遵循旧的(已弃用的)change_root过程。请参阅下面的“过时的根更改机制”部分。

  • 挂载根设备。如果是/dev/ram0,则将initrd映像挂载为根

  • 执行/sbin/init(这可以是任何有效的可执行文件,包括shell脚本;它以uid 0运行,并且可以执行基本上init可以执行的所有操作)。

  • init挂载“真实”的根文件系统

  • init使用pivot_root系统调用将根文件系统放置在根目录下

  • init在新的根文件系统上执行/sbin/init,执行常规的引导序列

  • 删除initrd文件系统

请注意,更改根目录不涉及卸载它。因此,在该过程中可以保留在initrd上运行的进程。还要注意,在initrd下挂载的文件系统仍然可以访问。

引导命令行选项

initrd添加了以下新选项:

initrd=<路径>(例如LOADLIN)

  将指定的文件加载为初始RAM磁盘。使用LILO时,您必须在/etc/lilo.conf中使用INITRD配置变量指定RAM磁盘映像文件。

noinitrd

  保留initrd数据,但不将其转换为RAM磁盘,并挂载“正常”的根文件系统。在这种情况下,initrd数据可以从/dev/initrd读取。请注意,在此情况下,initrd中的数据可以具有任何结构,并且不一定是文件系统映像。此选项主要用于调试。

  注意:/dev/initrd是只读的,只能使用一次。一旦最后一个进程关闭它,所有数据都将被释放,无法再打开/dev/initrd。

root=/dev/ram0

  将initrd挂载为根,并按照正常的引导过程进行,将RAM磁盘挂载为根。

压缩的cpio映像

最近的内核支持从压缩的cpio存档中填充ramdisk。在这种系统上,创建ramdisk映像不需要涉及特殊的块设备或回环设备;您只需在磁盘上创建一个具有所需initrd内容的目录,cd到该目录,然后运行(例如):

find . | cpio --quiet -H newc -o | gzip -9 -n > /boot/imagefile.img

检查现有映像文件的内容同样简单:

mkdir /tmp/imagefile
cd /tmp/imagefile
gzip -cd /boot/imagefile.img | cpio -imd --quiet

安装

首先,在“正常”的根文件系统上必须创建一个用于initrd文件系统的目录,例如:

# mkdir /initrd

名称不重要。有关pivot_root(2)手册页的更多详细信息,请参阅。

如果根文件系统是在引导过程中创建的(即如果您正在构建安装软盘),则根文件系统创建过程应创建/initrd目录。

如果在某些情况下不会挂载initrd,则如果已创建以下设备,则仍然可以访问其内容:

# mknod /dev/initrd b 1 250
# chmod 400 /dev/initrd

其次,必须使用RAM磁盘支持和初始RAM磁盘启用支持编译内核。此外,至少必须将所有需要从initrd执行程序的组件(例如可执行格式和文件系统)编译到内核中。

第三,您必须创建RAM磁盘映像。这可以通过在块设备上创建文件系统,根据需要将文件复制到其中,然后将块设备的内容复制到initrd文件中来完成。使用最新的内核,至少有三种类型的设备适用于此:

  • 软盘(在任何地方都可以工作,但速度非常慢)

  • RAM磁盘(速度快,但分配物理内存)

  • 回环设备(最优雅的解决方案)

我们将描述回环设备方法:

  • 确保回环块设备已配置到内核中

  • 创建适当大小的空文件系统,例如:

    # dd if=/dev/zero of=initrd bs=300k count=1
    # mke2fs -F -m0 initrd
(如果空间紧张,您可能希望使用Minix FS而不是Ext2)
  • 挂载文件系统,例如:
    # mount -t ext2 -o loop initrd /mnt
  • 创建控制台设备:
    # mkdir /mnt/dev
    # mknod /mnt/dev/console c 5 1
  • 复制所有需要正确使用initrd环境的文件。不要忘记最重要的文件/sbin/init

    注意
    /sbin/init权限必须包括“x”(可执行)。

  • 可以经常测试initrd环境的正确操作,即使不重新启动,也可以使用以下命令:

    # chroot /mnt /sbin/init
    

    当然,这仅限于不干扰一般系统状态的initrd(例如通过重新配置网络接口、覆盖已挂载设备、尝试启动已运行的守护程序等)。但请注意,通常可以在这样的chroot'ed initrd环境中使用pivot_root。

  • 卸载文件系统:

    # umount /mnt
    
  • 现在initrd位于文件“initrd”中。可选地,现在可以对其进行压缩:

    # gzip -9 initrd
    

对于尝试initrd的实验,您可能希望使用救援软盘,并仅将符号链接从/sbin/init添加到/bin/sh。或者,您可以尝试使用实验性的newlib环境2来创建一个小的initrd。

最后,您必须引导内核并加载initrd。几乎所有Linux引导加载程序都支持initrd。由于引导过程仍与旧的机制兼容,因此必须提供以下引导命令行参数:

root=/dev/ram0 rw

(仅在写入initrd文件系统时需要rw。)

使用LOADLIN,您只需执行:

LOADLIN <kernel> initrd=<disk_image>

例如:

LOADLIN C:\LINUX\BZIMAGE initrd=C:\LINUX\INITRD.GZ root=/dev/ram0 rw

使用LILO,您将INITRD=<path>选项添加到/etc/lilo.conf中的全局部分或相应内核的部分,并使用APPEND传递选项,例如:

image = /bzImage
  initrd = /boot/initrd.gz
  append = "root=/dev/ram0 rw"

然后运行/sbin/lilo

对于其他引导加载程序,请参阅相应的文档。

现在,您可以引导并享受使用initrd。

更改根设备

当完成其职责时,init通常会更改根设备,并继续在“真实”根设备上启动Linux系统。

该过程涉及以下步骤:

  • 挂载新的根文件系统
  • 将其转换为根文件系统
  • 移除对旧(initrd)根文件系统的所有访问
  • 卸载initrd文件系统并释放RAM磁盘

挂载新的根文件系统很容易:它只需要挂载到当前根目录下的一个目录。例如:

# mkdir /new-root
# mount -o ro /dev/hda1 /new-root

根的更改是通过pivot_root系统调用完成的,这也可以通过pivot_root实用程序实现(参见pivot_root(8)手册页;pivot_root随util-linux版本2.10h或更高版本一起分发)。pivot_root将当前根移动到新根下的一个目录,并将新根放在原来的位置上。在调用pivot_root之前,旧根的目录必须存在。例如:

# cd /new-root
# mkdir initrd
# pivot_root . initrd

现在,init进程仍然可以通过其可执行文件、共享库、标准输入/输出/错误和其当前根目录访问旧根。以下命令将删除所有这些引用:

# exec chroot . what-follows <dev/console >dev/console 2>&1

其中what-follows是新根下的一个程序,例如/sbin/init。如果新的根文件系统将与udev一起使用,并且没有有效的/dev目录,必须在调用chroot之前初始化udev以提供/dev/console。

注意:pivot_root的实现细节可能会随时间而变化。为了确保兼容性,应遵守以下几点:

  • 在调用pivot_root之前,调用进程的当前目录应指向新根目录
  • 使用.作为第一个参数,使用旧根目录的相对路径作为第二个参数
  • chroot程序必须在旧根和新根下都可用
  • 在chroot之后切换到新根
  • 在exec命令中使用相对路径的dev/console

现在,initrd可以卸载,并且RAM磁盘分配的内存可以被释放:

# umount /initrd
# blockdev --flushbufs /dev/ram0

还可以使用initrd与NFS挂载的根,有关详细信息,请参阅pivot_root(8)手册页。

使用场景

实现initrd的主要动机是允许在系统安装时进行模块化内核配置。该过程如下:

  • 系统从软盘或其他介质引导,使用最小内核(例如支持RAM磁盘、initrd、a.out和Ext2 FS),并加载initrd
  • /sbin/init确定所需的内容,以便(1)挂载“真实”根文件系统(即设备类型、设备驱动程序、文件系统)和(2)分发介质(例如CD-ROM、网络、磁带等)。这可以通过询问用户、自动探测或使用混合方法来完成。
  • /sbin/init加载必要的内核模块
  • /sbin/init创建并填充根文件系统(这不一定是一个非常可用的系统)
  • /sbin/init调用pivot_root来更改根文件系统,并通过chroot执行一个程序来继续安装
  • 安装引导加载程序
  • 配置引导加载程序以加载使用的模块集的initrd(例如/initrd可以被修改,然后卸载,并最终,将镜像从/dev/ram0或/dev/rd/0写入文件)
  • 现在系统可以启动,并且可以执行其他安装任务

initrd在这里的关键作用是在正常系统操作期间重用配置数据,而无需使用臃肿的“通用”内核或重新编译或重新链接内核。

第二种情况是在Linux在单个管理域中运行具有不同硬件配置的系统的安装。在这种情况下,理想情况下只需生成一小组内核(最好只有一个),并尽可能保持系统特定部分的配置信息较小。在这种情况下,可以生成一个通用的initrd,其中包含所有必要的模块。然后,只需/sbin/init或由其读取的文件需要有所不同。

第三种情况是更方便的恢复磁盘,因为无需在启动时提供根FS分区的位置,但从initrd加载的系统可以调用用户友好的对话框,并且还可以执行一些健全性检查(甚至某种形式的自动检测)。

最后,CD-ROM发行商可以使用它来更好地从CD进行安装,例如通过使用引导软盘并通过initrd从CD引导一个更大的RAM磁盘;或者通过使用LOADLIN等加载程序直接从CD-ROM引导,并从CD加载RAM磁盘,而无需使用软盘。

过时的根更改机制

在引入pivot_root之前使用了以下机制。当前内核仍然支持它,但您不应该依赖其持续可用性。

它的工作原理是在linuxrc退出时将“真实”根设备(即在内核映像中使用rdev或在引导命令行中使用root=...设置的设备)挂载为根文件系统。然后卸载initrd文件系统,或者如果它仍在使用中,则将其移动到新根文件系统上的一个目录/initrd,如果新根文件系统上存在这样的目录。

要使用此机制,您不必指定引导命令选项root、init或rw。(如果指定了,它们将影响真实根文件系统,而不是initrd环境。)

如果已挂载/proc,则可以通过在linuxrc中向特殊文件/proc/sys/kernel/real-root-dev写入新根FS设备的编号来从linuxrc内部更改“真实”根设备,例如:

# echo 0x301 >/proc/sys/kernel/real-root-dev

请注意,该机制与NFS和类似的文件系统不兼容。

这种旧的、不推荐使用的机制通常称为change_root,而新的、受支持的机制称为pivot_root。

混合change_root和pivot_root机制

如果您不想使用root=/dev/ram0来触发pivot_root机制,可以在initrd映像中创建/linuxrc和/sbin/init两者。

/linuxrc只包含以下内容:

#! /bin/sh
mount -n -t proc proc /proc
echo 0x0100 >/proc/sys/kernel/real-root-dev
umount -n /proc

一旦linuxrc退出,内核将再次将您的initrd挂载为根,这次执行/sbin/init。同样,这将是init的职责,在最终执行真正的/sbin/init之前构建正确的环境(可能使用在命令行上传递的root=设备)。

资源

  1. Almesberger, Werner; "Booting Linux: The History and the Future" https://www.almesberger.net/cv/papers/ols2k-9.ps.gz
  2. newlib package (experimental), with initrd example https://www.sourceware.org/newlib/
  3. util-linux: Miscellaneous utilities for Linux https://www.kernel.org/pub/linux/utils/util-linux/