Next Previous Contents

4. 建立一个root filesystem -- Building a root filesystem

造出 root filesystem 涉及选择能让系统正常运作所必备的文档。在这一节中,我们将叙述如何建造一个 压缩的 root filesystem 。在磁片上建造一个直接挂上做为根目录 (root) 之未经压缩的 filesystem 是较不普遍的观念;这个替代方案叙述在 Non-ramdisk Root Filesystem 这一节中。

4.1 概观 -- Overview

root filesystem 必须包含支持完整 Linux 系统运作所需的每一个项目。为了能够达成这个目的,这张磁片必须包括能让 Linux 系统运作最起码 (minimum) 的需求:

当然,任何系统只有在你能于其上执行某些东西时才会显得有用,而一张 root 磁片通常只有在你能做到以下事情时才会显得有用:

我们将叙述如何建造一个 压缩的 filesystem ,就是平时被压缩在磁片上,只有当开机时,才会解压缩后存入ramdisk 。 用压缩 filesystem 的方式,你可以在一张标准的 1440K 磁片上放入很多文档 (大约 6 megabytes) 。因为 filesystem 比磁片大很多,我们不能直接把它建在磁片上。我们必须在其它地方建立它,压缩它,然后再把它 copy 到磁片上。

4.2 制作 filesystem -- Creating the filesystem

为了建造如此的一个 filesystem ,你需要一个多出而够大的设备,能够让你在压缩之前存放所有的文档。你将需要一个能够存放大约 4 megabytes文档的设备。有以下几种选择:

在你选择其中一种方法后,请准备 DEVICE 以:

        dd if=/dev/zero of=DEVICE bs=1k count=4096

这行指令送出一堆 0 把DEVICE填满。用 0 填满 device 是关键的一步,因为 filesystem之后将会被压缩,所以所有未使用的部分应被用 0 填满以达到最大的压缩比。无论何时你从你的 root filesystem 删除文档,请记得这个事实。实际上 filesystem 只是释出 (de-allocate) 这些 blocks , 但是并没有再把它们填为 0 。如果你执行过很多次删除与 copy 的动作,你的压缩 filesystem 最后会比必要的大出很多。

下一步就是造出 filesystem 。 Linux kernel 承认两种能让 root disks 自动地被 copy 到 ramdisk 上的 file system 。它们是 minix 与 ext2 ,其中 ext2 是比较受欢迎的。如果使用 ext2 ,你会发现使用 -i 选项指定比预设值更多的 inodes 是有助益的;我们建议用 -i 2000 ,这样你就不会用完 inodes 。如果不用上述选项,你可以移除许多不必要的 /dev 文档以节省 inodes 。 mke2fs 预设会造出 360 个 inodes在一张 1.44Mb 的磁片上。我发现在我目前的救援 root 磁片上, 120 个 inodes 是相当足够了,但是如果你把所有的设备档都放入 /dev 目录中,那么你很容易会超过 360 个inodes 。使用压缩的 root filesystem 可让你拥有较大的 filesystem ,同时预设会有更多的 inodes ,但是你仍然必须要不就是减少文档数量,要不就是增加 inodes 数目。

因此,你所使用的指令看起来会像这样:

        mke2fs -m 0 -i 2000 DEVICE

(如果你使用的是一个 loopback device ,那么请用你目前所用的磁盘文档替换掉上面的 DEVICE 。)

mke2fs 指令会自动地侦测可获得的空间,然后依据侦测对自身进行组态设定。 ``-m 0'' 参数避免保留空间给 root ,因此可提供更多可用的磁盘空间。

下一步,挂上这个 device :

        mount -t ext2 DEVICE /mnt
(如果 mount point 并不存在,你必须自行造出一个 mount point /mnt 。) 在剩下的章节中,所有的目的 (destination) 目录都被假设是相对于 /mnt

4.3 移植文档系统 -- Populating the filesystem

以下是你的 root filesystem 最起码该有的目录

此处所呈现之目录架构仅供 root diskette 使用。真正的 Linux 系统有一套更为复杂且设计良好的架构方法,称为 Filesystem Hierarchy Standard ,它决定文档应该如何放置。 :

上述目录的其中三个在 root filesystem 上会是空的,所以它们只需要用 mkdir 造出来。 /proc 目录基本上是一个把 proc filesystem 放置于其下的 stub 。 /mnt 与 /usr 这两个目录只是在 boot/root 系统运作时所使用的 mount points 。因此再重覆一次,这些目录只需要被造出来就可以了。

剩下的四个目录描述于以下各节。

/dev

/dev 目录包含一群特别的文档,这些文档是给系统上所有设备使用的,这样的 /dev 目录每个 Linux 系统都一定会有。这个目录本身是一个普通目录,可以以一般的方法用 mkdir 造出来。然而,这些特别的文档必须以特别的方法用 mknod 指令造出来。

但还是有一条捷径 -- 直接 copy 你现有 /dev 目录的内容,然后再清除你不想要的设备档。唯一的要求是 copy 这些特别的设备档时,要用 -R 选项。这个选项会 copy 整个目录中的文档,但是不会 copy 这些文档的内容。请确定使用 大写字母 R 。这个指令是:

        cp -dpR /dev /mnt

在此我们假设磁片是被挂在 /mnt 底下。 dp 选项 (switches) 确保 symbolic links 是以 links 的方式来 copy ,而不是 copy 链结档所指向的 target file ,同时原本的文档属性也被保留,因此保留了文档的所有权信息。

如果你想要用高难度技巧完成这个任务,就利用 ls -l 列出你想要的设备档之 major 与 minor device numbers ,然后再用 mknod 在磁片上造出它们。

无论如何 copy 这些设备档,还是要检查任何你所需之设备档 (device special file) 是否已放入这张救援磁片中。举例来说, ftape 使用磁带设备,如果你想要从 bootdisk 存取软式磁带机,你就需要 copy 所有有关的设备档。

请注意,每一个设备档需要一个 inode ,但 inodes 一直都是稀少的资源,特别是在磁片 filesystems 上。因此,从磁片上的 /dev 目录移除任何你所不需要的设备档是有意义的。举例来说,如果你没有 SCSI 磁盘,你可以放心地移除所有以 sd 开头的设备档。同样地,如果你并不想使用你的序列埠 (serial port) ,那么你也可以移除所有以 cua 开头的设备档。

请确定从这个目录放入了以下文档的: console, kmem, mem, null, ram0 and tty1.

/etc

这个目录包含了重要的组态设定档。在大部分的系统上,这些文档被分为三个群组:

  1. 一直都是必备的, e.g. rc, fstab, passwd
  2. 可能是必备的,但是没有人能十分确定。
  3. 偷跑进来的垃报。

通常可以用以下指令识出哪些是非基本的文档:

        ls -ltru

这个指令将文档依据上次被存取的日期,以先早后晚 (reverse) 的顺序列出,所以如果有任何文档不会被存取,那么它们就可以从 root 磁片中删去。

在我的 root 磁片上,我的组态档数目已减至 15 个。这可减少我处理以下三种文档的工作:

  1. 我必须为 boot/root 系统进行组态设定的文档:
    1. rc.d/* -- 系统启动与改变 run level 的 scripts
    2. fstab -- 要被挂上的 file systems 清单
    3. inittab -- 给 init process 的参数,于开机时启动的第一个 process 。
  2. 我们应该为 boot/root 系统整理的文档:
    1. passwd -- 重要的使用者、 home 目录等其它项目的清单。
    2. group -- 使用者群组。
    3. shadow -- 使用者的密码。你可能没有这个档。
    4. termcap -- the terminal capability database.
    如果系统安全 (security) 对你很重要,那么 passwd 与 shadow 应该被删减,以避免将使用者密码 copy 出系统,这样当你从磁片开机时,不想要的 logins 会被拒绝。 请确定 passwd 至少包含了 root 。如果你要让其他的使用者 login ,请确定他们的 home 目录与 shells 都存在。 termcap ,终端机资料库,一般而言有几百个 kilobytes 。你 boot/root 磁片的版本应该被删减到只包含你所使用的终端机,这通常就是 linuxlinux-console 项目 (entry) 。
  3. The rest. They work at the moment, so I leave them alone.

Out of this, 我实际上只必须设定两个档,而它们所应包含的项目惊人地少。

你的 inittab 应该被改变,以使其中 sysinit 这行能执行 rc 或无论什么将被执行的基本开机 script 。同时,如果你想要确保不可从序列埠 login ,请在所有行尾包括 ttys 或 ttyS 的 getty 项目前加上“#”符号 (comment out) 。请保留 tty 埠以让你可以在 console 前 login 。

一个最起码的 inittab 档看起来样这样:

        id:2:initdefault:
        si::sysinit:/etc/rc
        1:2345:respawn:/sbin/getty 9600 tty1
        2:23:respawn:/sbin/getty 9600 tty2

inittab 档定义了系统在各种不同的情况中将执行什么项目,包括 startup 、切换至多使用者模式等等。请仔细地检查在 inittab 中被提及的文档名称 (filenames) ;如果 init 不能找到所提及的程序,那么 bootdisk 将会停止运作,而你甚至不会得到错误信息。

请注意,某些程序不能被移到其它地方,因为其它程序已在撰写时,就把它们的文档位置写死了 (hardcode) 。举例来说在我的系统上, /etc/shutdown 已把 /etc/reboot 的位置写死在其中。如果我移动了 reboot 到 /bin/reboot,然后下达一个 shutdown 指令,将会因为找不到 reboot 档而发生错误。

剩下来的,就是 copy 在你 /etc 目录中的所有文字档 (text files) ,再加上在你 /etc 目录中,你无法确定你需不需要的所有可执行档。需要指示 (guide) 者,请参考在 Sample roodisk directory listings 的样本清单。也许只要 copy 这些文档就足够了,但是系统差异会有很大的影响,所以你无法确定你系统上相同的文档组合,就一定等于清单中的文档。唯一确定的方法就是从 inittab 著手,并找出需要什么。

现在大部分的系统使用 /etc/rc.d/ 目录,其中包含给不同 run levels 的shell scripts 。最起码会有一个单一的 rc script,但是仅从你现存的系统 copy inittab 与 /etc/rc.d 这两个目录,然后删减 rc.d 目录中的 shell scripts 以移除和磁片系统环境无关的 processing ,会是较为简单的做法。

/bin 与 /sbin

/bin 目录是一个放置为了执行基本作业 (operations) 而所需之额外工具程序的方便好地方,这些工具程序诸如 ls, mv, catddbin/ 与 /sbin 这两个目录的文档清单范例请见 Sample rootdisk directory listings 。但范例中并没有包括任何从备份复原时所需之工具程序,诸如 cpio, targzip 。这是因为我把这些东西放在另一张 (separate) 工具程序磁片上,以节省 boot/root 磁片的空间。一旦 boot/root 磁片被开机启动,就会被 copy 到 ramdisk 并释放软盘机,让软盘机能挂上另一张磁片,就是工具程序片。我通常把它挂上当做 /usr 。

工具程序磁片 (utility diskette) 的制作被描述在下面 Building a utility disk 这节。保留一份相同版本之备份用工具程序的copy是比较好的,这个备份用工具程序被用来制作备份,如此你就不用浪费时间在尝试安装不能读取你备份磁带的版本。

请确定你包括了以下程序: init, getty 或相等的程序, login, mount, 某种可以执行你rc scripts 的 shell, 以及一个从 sh 指向这个 shell 的 link 。

/lib

在 /lib 中,你要放入必要的共享函式库 (libraries) 与载入程序 (loaders) 。如果无法在你的 /lib 目录中找到必要的函式库,那么系统将不能够开机。如果你很幸运,你可能会看到告诉你为什么会发生如此情况的错误信息。

近来每一个程序至少都要求 libc 函式库, libc.so.N ,其中 N 是目前版本的编号。请检查你的 /lib 目录。 Libc.so.N 通常是一个 symlink ,它指向一个具有完整版本编号的档名:

% 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)
在右手边的每一个文档是一定要有的。有些文档可能是一个 symbolic link 。

请注意某些函式库 相当地大 ,而且并不能轻易地配合你的 root filesystem 。举例来说,上述的 libc.so 大约有 4 megabytes 。因此,在你 copy 它们到你 root filesystem 的同时,你可能需要除去一些函式库。请看 Reducing root filesystem size 这一节以了解 instructions 。

在 /lib 内,你也必须包括一个 loader 供这些函式库使用。这个 loader 不是 ld.so (给 A.OUT 函式库使用),就是 ld-linux.so (给 ELF 函式库使用)。较新版的 ldd 会如同上述的例子,正确地告诉你需要哪一种 loader ,然而旧版的就不会。如果你并不确定你需要哪一种 loader ,就对函式库执行 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.33 以及 2.1.1 版是给 ELF 使用。

请 copy 你所需要的指定 loader(s) 到你所建立的 root filesystem 。针对所包括的二进位档,函式库与 loaders 应该被 仔细地检查 。如果 kernel 不能载入所需的函式库,那么 kernel 就会在没有错误信息的情况下停止运作。

4.4 对 PAM 与 NSS 的提供 -- Providing for PAM and NSS

你的系统可能会需要动态地载入未被 ldd 所见的函式库。如果你没有提供函式库给这些项目,那么你会在登录 (log in) 或使用你的 bootdisk 时遭遇到困难。

PAM (Pluggable Authentication Modules)

如果你的系统使用 PAM (Pluggable Authentication Modules) ,那么你必须在你的 bootdisk上为 PAM 做一些预备。简而言之, PAM 是一种复杂精密的模块化方法,针对使用者认证以及控制使用者对服务的存取。一个决定你的系统是否使用 PAM 的简单方法,是对你的 login 可执行档执行 ldd ;如果输出包括 libpam.so ,你就需要 PAM 。

幸运地,安全性通常并非 bootdisk 所关心的议题,因为任何对机器有实际存取权的人,通常能做任何他们无论如何想做的事。因此,你可以有效地关闭 PAM ,只要在你的 root filesystem 造出一个简单的 /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

请记得也 copy /lib/security/pam_permit.so 这个档到你的 root filesystem 。这个函式库只有大约 8K ,因此它只占用极小量的 overhead 。

请记得这个组态设定允许任何人对这台机器上的文档以及服务有完整的存取权。如果你因某种理由而在乎你 bootdisk 的安全性,那么你就必须 copy 一些或是全部你硬盘的 PAM setup 到你的 root filesystem 。请确定曾仔细地读过 PAM 文件,并且 copy 任何在 /lib/security 中所需要的函式库到你的 root filesystem 上。

你同时必须包含 /lib/libpam.so 到你的 bootdisk 上。但是你已知这件事,因为你已对 /bin/login 执行过 ldd ,这动作显示了其相依性。

NSS (Name Service Switch)

如果你正使用 glibc (aka libc6) ,你就必须为名称服务 (name switch) 进行预备,否则你会无法 log in 。 /etc/nsswitch.conf 这个文档控制资料库对各式服务之搜寻 (lookups) 。如果你并不打算从网路上存取服务 (比如说: 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

这样就指定每一项服务只被 local files 提供。你同时需要包括 /lib/libnss_files.so.X ,在此 X 是 1 的话是指 glibc 2.0 ,而 2 的话是指 glibc 2.1 。这个函式库将被以动态方式载入以处理文档搜寻。

如果你打算从你的 bootdisk 存取网路,那么你会想要制作一个更精巧复杂的 nsswitch.conf 档。细节请参考 nsswitch man page。最后,请记得你必须为你所指定的每一项 服务 (service) ,把 /lib/libnss_service.so.1 档放入 bootdisk 中。

4.5 模块 -- Modules

如果你有一个模块化的 kernel ,你必须考量在开机后,你想要从你的 bootdisk 上载入哪一种模块。你可能会想要包括 ftapezftape 模块 (如果你的备份磁带是floppy tape),以及给 SCSI 设备用的模块 (如果你有 SCSI 设备),也可能是 PPP 或 SLIP 支持的模块 (如果你在紧急情况下想要存取网路)。

这些模块可能会被放在 /lib/modules 。你同时也应该包括 insmod, rmmodlsmod 。根据你是否想要自动地载入模块,你可能也要包括 modprobe, depmodswapout 。如果你使用 kerneld,请与 /etc/conf.modules 一起包括进来。

然而,使用模块的主要优点在于你可以把非关键 (non-critical)模块移到 utility disk 上,在需要用到时才载入,这样在你的 root disk 上会使用比较少的空间。如果你要处理许多不同的设备,这个方法比建立一个内建支持许多设备的巨大 kernel 来得好。

请注意,为了启动 (boot) 一个压缩的 ext2 filesystem ,你必须有 ramdisk 与内建 ext2 支持。 它们不能够以模块的方式被提供。

4.6 一些最后的细节 -- Some final details

某些系统程序,诸如 login ,当 /var/run/utmp 档与 /var/log 目录不存在时,会发出警告。所以:

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

最后,在你设定 (set up) 完所有你所需的函式库后,执行 ldconfig 以在 root filesystem上重新制作 (remake) /etc/ld.so.cache 。这个 cache 会告诉 loader 到哪里找到函式库。要重新制作 ld.so.cache,请下达以下指令:

        chdir /mnt; chroot /mnt /sbin/ldconfig

chroot 是必要的,因为 ldconfig 总是会为 root filesystem 重新制作 cache 。

4.7 Wrapping it up

一旦你完成 root filesystem 的建构工作,就 unmount 它,将之 copy 成一个文档并压缩它:

        umount /mnt
        dd if=DEVICE bs=1k | gzip -v9 > rootfs.gz

结束后,你会有一个名为 rootfs.gz 的文档,这就是你被压缩过的 root filesystem。你应该检查它的 size 以确保它能放在一张软盘片上;如果不行,你就必须回溯并移除一些文档。 Reducing root filesystem size 这节有一些提示,告诉你有关减少 root filesystem 的 size 。


Next Previous Contents