4. 构建根文件系统

创建根文件系统涉及到选择系统运行所需的文件。在本节中,我们将描述如何构建压缩根文件系统。一个不太常见的选择是在软盘上构建一个未压缩的文件系统,该文件系统直接挂载为根目录;这种替代方案在第 9.1 节中描述。

4.1. 概述

根文件系统必须包含支持完整 Linux 系统所需的一切内容。为了做到这一点,磁盘必须包含 Linux 系统的最低要求

当然,只有当您可以在其上运行某些程序时,任何系统才变得有用,而根磁盘通常只有在您可以执行以下操作时才变得有用,例如

我们描述如何构建压缩文件系统,之所以称为压缩文件系统,是因为它在磁盘上被压缩,并且在启动时被解压缩到 ramdisk 上。使用压缩文件系统,您可以将许多文件(大约六兆字节)放入标准的 1440K 软盘中。因为文件系统比软盘大得多,所以它不能在软盘上构建。我们必须在其他地方构建它,压缩它,然后将其复制到软盘。

4.2. 创建文件系统

为了构建这样的根文件系统,您需要一个备用设备,该设备足够大,可以在压缩之前容纳所有文件。您将需要一个能够容纳大约四兆字节的设备。有几种选择

在您选择了这些选项之一后,使用以下命令准备 DEVICE
        dd if=/dev/zero of=DEVICE bs=1k count=4096

此命令将设备清零。

Important

将设备清零至关重要,因为文件系统稍后将被压缩,因此所有未使用的部分都应填充零以实现最大压缩。在文件系统上移动或删除文件时,请记住这一点。文件系统将正确地取消分配块,但不会再次将其清零。如果您执行大量删除和复制操作,则压缩后的文件系统最终可能会比必要的要大得多。

接下来,创建文件系统。Linux 内核识别两种文件系统类型,用于自动复制到 ramdisk 的根磁盘。这些是 minix 和 ext2,其中 ext2 是首选。如果使用 ext2,您可能会发现使用以下选项很有用-N选项来指定比默认值更多的 inodes;-N 2000建议这样做,这样您就不会耗尽 inodes。或者,您可以通过删除大量不必要的/dev文件来节省 inodes。mke2fs 默认情况下将在 1.44Mb 软盘上创建 360 个 inodes。我发现 120 个 inodes 在我当前的救援根磁盘上足够了,但是如果您包含所有设备在/dev中,您将很容易超过 360 个。使用压缩根文件系统允许更大的文件系统,因此默认情况下有更多的 inodes,但您可能仍然需要减少文件数量或增加 inodes 数量。

因此您使用的命令将如下所示
        mke2fs -m 0 -N 2000 DEVICE

(如果您使用的是环回设备,则应使用您使用的磁盘文件代替此 DEVICE。)

mke2fs 命令将自动检测可用空间并相应地配置自身。``-m 0'' 参数阻止它为 root 保留空间,从而在磁盘上提供更多可用空间。

接下来,挂载设备
        mount -t ext2 DEVICE /mnt
(您必须创建一个挂载点/mnt如果它尚不存在。)在其余部分中,所有目标目录名称都假定相对于/mnt.

4.3. 填充文件系统

这是您的根文件系统的合理最低目录集 [1]

这些目录中的三个在根文件系统上将为空,因此它们只需要使用 mkdir 创建即可。/proc目录基本上是放置 proc 文件系统的存根。目录/mnt/usr仅是在启动/根系统运行后使用的挂载点。因此,这些目录也只需要创建。

剩余的四个目录在以下各节中描述。

4.3.1. /dev

一个/dev目录,其中包含系统中所有要使用的设备的特殊文件,对于任何 Linux 系统都是强制性的。目录本身是一个普通目录,可以使用以下命令创建mkdir以正常方式。但是,设备特殊文件必须以特殊方式创建,使用 mknod 命令。

不过,有一个快捷方式——从您现有的硬盘复制设备文件/dev目录。唯一的要求是您使用以下命令复制设备特殊文件-R选项。这将复制目录,而不会尝试复制文件的内容。务必使用*大写 R*。例如
        cp -dpR /dev/fd[01]* /mnt/dev
        cp -dpR /dev/tty[0-6] /mnt/dev
假设软盘挂载在/mntdp开关确保符号链接被复制为链接,而不是使用目标文件,并确保原始文件属性被保留,从而保留所有权信息。

如果您想以更困难的方式进行,请使用ls -l以显示您想要的设备的主设备号和次设备号,并使用以下命令在软盘上创建它们mknod.

无论设备文件是如何创建的,请检查您需要的任何特殊设备是否已放置在救援软盘上。例如,ftape使用磁带设备,因此如果您打算从启动盘访问软盘磁带驱动器,则需要复制所有这些设备。

请注意,每个设备特殊文件都需要一个 inode,并且 inodes 有时可能是一种稀缺资源,尤其是在软盘文件系统上。您需要有选择地选择您包含的设备文件。例如,如果您没有 SCSI 磁盘,您可以安全地忽略/dev/sd*;如果您不打算使用串口,您可以忽略/dev/ttyS*.

如果在构建根文件系统时,您收到错误设备上没有剩余空间但是一个df命令显示仍有可用空间,您可能已经耗尽了 inodes。一个df -i将显示 inode 使用情况。

Important

务必包含此目录中的以下文件console, kmem, mem, null, ram0tty1.

4.3.2. /etc

/etc 目录包含配置文件。它应该包含什么取决于您打算运行的程序。在大多数系统中,这些可以分为三组

  1. 始终需要,例如rc, fstab, passwd.

  2. 可能需要,但没有人能确定。

  3. 偷偷溜进来的垃圾。

通常可以使用以下命令识别非必要文件
        ls -ltru
这会按上次访问日期的倒序排列文件,因此如果任何文件未被访问,则可以从根软盘中省略它们。

在我的根软盘上,我将配置文件数量减少到 15 个。这减少了我的工作量,只需处理三组文件

  1. 我必须为启动/根系统配置的文件

    1. rc.d/*-- 系统启动和运行级别更改脚本

    2. fstab-- 要挂载的文件系统列表

    3. inittab-- init 进程的参数,这是启动时启动的第一个进程。

    4. gettydefs-- init 进程的参数,这是启动时启动的第一个进程。

  2. 我应该为启动/根系统整理的文件

    1. passwd-- 用户、主目录等的关键列表。

    2. group-- 用户组。

    3. shadow-- 用户密码。您可能没有这个。

    4. termcap-- 终端能力数据库。

    如果安全很重要,passwdshadow应该进行精简,以避免将用户密码复制出系统,并使在您从软盘启动时拒绝不必要的登录。

    确保passwd至少包含root。如果您打算让其他用户登录,请确保其主目录和 shell 存在。

    termcap,终端数据库,通常有几百千字节。您的启动/根磁盘上的版本应精简为仅包含您使用的终端,通常只是linuxlinux-console条目。

  3. 其余的。它们目前可以工作,所以我让它们保持原样。

在这些文件中,我实际上只需要配置两个文件,而且它们应该包含的内容非常少。

  • rc应该包含
            #!/bin/sh       
            /bin/mount -av
            /bin/hostname Kangaroo
    确保它是可执行的,确保它顶部有 "#!" 行,并确保任何绝对文件名都是正确的。您实际上不需要运行 hostname —— 这样做只是看起来更好。

  • fstab应该至少包含
            /dev/ram0       /               ext2    defaults
            /dev/fd0        /               ext2    defaults
            /proc           /proc           proc    defaults
    您可以从现有的fstab中复制条目,但是您不应自动挂载任何硬盘分区;使用noauto关键字。当使用启动盘时,您的硬盘可能已损坏或损坏。

您的inittab应该更改,以便其sysinit行运行rc或将使用的任何基本启动脚本。此外,如果您想确保串口上的用户无法登录,请注释掉所有getty其中包括ttysttyS行末尾的设备。保留tty端口,以便您可以在控制台登录。

一个最小的inittab文件看起来像这样
        id:2:initdefault:
        si::sysinit:/etc/rc
        1:2345:respawn:/sbin/getty 9600 tty1
        2:23:respawn:/sbin/getty 9600 tty2
inittab文件定义了系统在各种状态下将运行什么,包括启动、移动到多用户模式等。仔细检查

中提到的文件名;如果inittab;如果init无法找到提到的程序,启动盘将挂起,您甚至可能不会收到错误消息。

请注意,某些程序无法移动到其他位置,因为其他程序已对其位置进行了硬编码。例如,在我的系统上,/etc/shutdown已在其中硬编码/etc/reboot。如果我移动reboot/bin/reboot,然后发出一个shutdown命令,它将失败,因为它找不到reboot文件。

对于其余的,只需复制您/etc目录中的所有文本文件,以及您/etc目录中您不确定不需要的所有可执行文件。作为指导,请参阅 附录 C 中的示例列表。可能只需复制那些文件就足够了,但是系统差异很大,因此您不能确定您系统上的同一组文件与列表中的文件等效。唯一可靠的方法是从inittab开始,并弄清楚需要什么。

大多数系统现在使用一个/etc/rc.d/目录,其中包含用于不同运行级别的 shell 脚本。最小值是一个单一的rc脚本,但可能只需复制inittab/etc/rc.d目录从您现有的系统,并修剪rc.d目录中的 shell 脚本,以删除与软盘系统环境无关的处理。

4.3.3. /bin 和 /sbin

/bin目录是存放您需要执行基本操作的额外实用程序的便捷位置,例如 lsmvcatdd 等实用程序。请参阅 附录 C 以获取 /bin/sbin目录中的文件示例列表。它不包括从备份还原所需的任何实用程序,例如 cpiotargzip。那是因为我将这些程序放在单独的实用程序软盘上,以节省启动/根磁盘上的空间。一旦启动了启动/根磁盘,它就会被复制到 ramdisk,从而使软盘驱动器可以自由挂载另一个软盘,即实用程序软盘。我通常将其挂载为/usr.

实用程序软盘的创建在下面的 第 9.2 节中描述。最好保留用于编写备份的相同版本的备份实用程序的副本,这样您就不会浪费时间尝试安装无法读取备份磁带的版本。

Important

务必包含以下程序init, getty或等效程序,login, mount,一些能够运行您的 rc 脚本的 shell,从sh到 shell 的链接。

4.3.4. /lib

/lib中,您放置必要的共享库和加载程序。如果在您的/lib目录中找不到必要的库,则系统将无法启动。如果您幸运的话,您可能会看到一条错误消息告诉您原因。

几乎每个程序都至少需要libc库,libc.so.N,其中 N 是当前版本号。检查您的/lib目录。文件libc.so.N通常是指向具有完整版本号的文件名的符号链接

% ls -l /lib/libc*
-rwxr-xr-x   1 root     root      4016683 Apr 16 18:48 libc-2.1.1.so*
lrwxrwxrwx   1 root     root           13 Apr 10 12:25 libc.so.6 -> libc-2.1.1.so*

在这种情况下,您想要libc-2.1.1.so。要查找其他库,您应该遍历您计划包含的所有二进制文件,并使用 ldd 检查它们的依赖项。例如
        % ldd /sbin/mke2fs
        libext2fs.so.2 => /lib/libext2fs.so.2 (0x40014000)
        libcom_err.so.2 => /lib/libcom_err.so.2 (0x40026000)
        libuuid.so.1 => /lib/libuuid.so.1 (0x40028000)
        libc.so.6 => /lib/libc.so.6 (0x4002c000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
右侧的每个文件都是必需的。该文件可能是符号链接。

请注意,某些库非常大,并且不容易放入您的根文件系统中。例如,libc.so上面列出的库大约有 4 兆字节。在将库复制到根文件系统时,您可能需要剥离库。有关说明,请参见 第 8 节

/lib您还必须包含库的加载程序。加载程序可以是ld.so(对于 A.OUT 库,现在不再常见)或ld-linux.so(对于 ELF 库)。较新版本的 ldd 会告诉您确切需要哪个加载程序,如上面的示例所示,但较旧的版本可能不会。如果您不确定您需要哪个,请在库上运行 file 命令。例如
% file /lib/libc.so.4.7.2 /lib/libc.so.5.4.33 /lib/libc-2.1.1.so
/lib/libc.so.4.7.2: Linux/i386 demand-paged executable (QMAGIC), stripped
/lib/libc.so.5.4.33: ELF 32-bit LSB shared object, Intel 80386, version 1, stripped
/lib/libc-2.1.1.so: ELF 32-bit LSB shared object, Intel 80386, version 1, not stripped
QMAGIC表示4.7.2用于 A.OUT 库,而ELF表示5.4.332.1.1用于 ELF。

将您需要的特定加载程序复制到您正在构建的根文件系统。库和加载程序应仔细对照包含的二进制文件进行检查。如果内核无法加载必要的库,则内核可能会挂起,而不会显示错误消息。

4.4. 为 PAM 和 NSS 提供支持

您的系统可能需要动态加载的库,这些库对于ldd不可见。如果您不提供这些库,您可能在登录或使用启动盘时遇到问题。

4.4.1. PAM(可插拔身份验证模块)

如果您的系统使用 PAM(可插拔身份验证模块),您必须在启动盘上为其提供一些支持。简而言之,PAM 是一种用于验证用户身份并控制其对服务访问权限的复杂模块化方法。确定您的系统是否使用 PAM 的一种简单方法是运行ldd在您的login可执行文件上;如果输出包含libpam.so,您需要 PAM。

幸运的是,对于启动盘来说,安全性通常不是问题,因为任何可以物理访问计算机的人通常都可以为所欲为。因此,您可以通过创建一个简单的/etc/pam.conf文件在您的根文件系统中,如下所示
OTHER   auth       optional     /lib/security/pam_permit.so
OTHER   account    optional     /lib/security/pam_permit.so
OTHER   password   optional     /lib/security/pam_permit.so
OTHER   session    optional     /lib/security/pam_permit.so
还要复制文件/lib/security/pam_permit.so到您的根文件系统。该库只有大约 8K,因此开销最小。

此配置允许任何人完全访问您计算机上的文件和服务。如果您出于某种原因关心启动盘上的安全性,则必须将硬盘 PAM 设置的部分或全部复制到根文件系统。请务必仔细阅读 PAM 文档,并将

中所需的任何库复制到您的根文件系统。/lib/security到您的根文件系统上。

您还必须包含/lib/libpam.so在您的启动盘上。但是您已经知道这一点,因为您在/bin/login上运行了 ldd,它显示了此依赖项。

4.4.2. NSS(名称服务切换)

如果您使用的是 glibc(又名 libc6),您将不得不为名称服务做准备,否则您将无法登录。文件/etc/nsswitch.conf控制各种服务的数据库查找。如果您不打算从网络访问服务(例如,DNS 或 NIS 查找),您只需要准备一个简单的nsswitch.conf文件,如下所示
     passwd:     files 
     shadow:     files 
     group:      files 
     hosts:      files
     services:   files
     networks:   files
     protocols:  files
     rpc:        files
     ethers:     files
     netmasks:   files     
     bootparams: files
     automount:  files 
     aliases:    files
     netgroup:   files
     publickey:  files
这指定每个服务仅由本地文件提供。您还需要包含/lib/libnss_files.so.X,其中 X 对于 glibc 2.0 为 1,对于 glibc 2.1 为 2。此库将被动态加载以处理文件查找。

如果您计划从启动盘访问网络,您可能需要创建一个更详细的nsswitch.conf文件。请参阅nsswitch手册页以获取详细信息。您必须包含一个文件/lib/libnss_service.so.1对于您指定的每个服务

4.5. 模块

如果您有一个模块化内核,您必须考虑在启动后可能要从启动盘加载哪些模块。如果您的备份磁带在软盘磁带上,您可能需要包含 ftapezftape 模块;如果您有 SCSI 设备,则需要 SCSI 设备模块;如果您想在紧急情况下访问网络,则可能需要 PPP 或 SLIP 支持模块。

这些模块可以放置在/lib/modules。您还应该包含 insmodrmmodlsmod。根据您是否要自动加载模块,您可能还需要包含 modprobedepmodswapout。如果您使用 kerneld,请将其与/etc/conf.modules.

然而,使用模块的主要优点是您可以将非关键模块移动到实用程序磁盘并在需要时加载它们,从而减少根磁盘上的空间使用。如果您可能必须处理许多不同的设备,那么这种方法比构建一个内置许多驱动程序的大型内核更好。

Important

为了启动压缩的 ext2 文件系统,您必须内置 ramdisk 和 ext2 支持。它们不能作为模块提供。

4.6. 一些最后的细节

一些系统程序,例如 login,如果文件/var/run/utmp和目录/var/log不存在,则会报错。所以

        mkdir -p /mnt/var/{log,run}
        touch /mnt/var/run/utmp

最后,在您设置完所有需要的库之后,运行 ldconfig 以重新创建/etc/ld.so.cache在根文件系统上。缓存告诉加载程序在哪里找到库。您可以使用以下命令执行此操作
        ldconfig -r /mnt

4.7. 总结

当您完成构建根文件系统后,卸载它,将其复制到文件并压缩它
        umount /mnt
        dd if=DEVICE bs=1k | gzip -v9 > rootfs.gz
完成后,您将得到一个文件rootfs.gz。这是您的压缩根文件系统。您应该检查其大小以确保它适合软盘;如果它不适合,您将不得不返回并删除一些文件。有关减小根文件系统大小的一些建议,请参见 第 8 节

注释

[1]

此处介绍的目录结构仅用于根软盘。真正的 Linux 系统有一套更复杂和规范的策略,称为 文件系统层次结构标准,用于确定文件应放置在何处。)