5 根文件系统
创建根文件系统比编译内核要复杂的多,也更难理解。这里的关键是掌握init
rd(初始化RAM盘)的使用方法。
5.1 根文件系统
这里我们将要创建的根文件系统与通常Linux主机的根文件系统类似,只是它应
该仅仅包括系统运行所必须的应用程序、库和相关文件的最小集合。根文件系统的
尺寸大小是一个重要的指标。
5.2 文件系统的内容
5.2.1 应用程序(applications)
/bin,/sbin,/usr/bin,/usr/sbin
应用程序大致可以分为3部分,第一是操作系统正常运行所需的基本工具软件,
比如bash,cp,rm等;第二是提供某项服务的服务器软件,比如httpd,telnetd,
proftpd等;第三是我们所开发的应用程序。其实后面讲的配置文件等也可以粗略按
照这个原则来分类。
到底需要复制哪些软件,弹性是比较大的,很多软件都是可要可不要,可以实
际情况灵活选择。这个列表可能很长,并且变化也比较大,因此不在这里列出。
为了进一步减小所创建的根文件系统的尺寸,可以考虑使用下列工具包软件来
替代某些标准的工具:
O BusyBox(
O TinyLogin(
O Embutils(
其详细使用方法请参考相关资料,此处不再赘述。LIPS的实现目前没有使用这
些软件包。
5.2.2 设备文件(device files)
/dev
设备文件也可以称作设备节点(device node)。设备文件非常重要,缺少某些
有些设备文件可能导致系统不能正常运行甚至不能引导。有些设备文件是必须的,
而更多的是根据具体目标系统的硬件配置来进行取舍。
比如硬盘的设备文件,在完整的系统中一般有hda,hdb,……,hdt,即最多支
持20个IDE硬盘,每个硬盘有hdX1,hdX2,……,hdX32,(其中X表示a-t),即支
持32个分区,另外还有表示SCSI硬盘的节点。根据实际情况,如果只需要支持少量
的硬盘、少量的分区,这些节点可以被大大简化。如果目标系统中没有的设备,其
对应的设备文件也可以省掉。
设备文件
描述
/dev/console
系统控制台设备,非常重要。
/dev/fd0
第一个软驱
/dev/hda
/dev/hda[1-8]
IDE硬盘及分区
/dev/initctl
实际上是一个FIFO设备,跟init有关(切换运行级别时用于新init与原init通信)
/dev/initrd
Initial RAM disk
/dev/input
(目录)Input core(包括游戏杆、鼠标等)
/dev/kmem
内核虚拟内存
/dev/loop[0-7]
Loopback设备
/dev/mem
访问物理内存
/dev/null
NULL设备
/dev/psaux
PS/2鼠标
/dev/ptmx
UNIX98 PTY master
/dev/pts
(目录)UNIX98 PTY slaves
/dev/ptyp[0-7]
伪终端主设备(远程登录使用)
/dev/ram[0-7]
/dev/ramdisk
/dev/ram
RAM Disk设备。至少/dev/ram0是应用initrd机制所必须的。
/dev/ramdisk链接到/dev/ram0,是为了兼容老版本内核而保留的。
/dev/ram链接到/dev/ram1。
/dev/random
随机数发生器
/dev/sda
/dev/sda[1-8]
SCSI磁盘及分区设备
/dev/shm
共享内存设备
/dev/systty
指向系统tty设备的符号链接,一般是tty0。
/dev/tty
当前TTY设备
/dev/tty[0-7]
虚控制台(Virtual console)
/dev/ttyp[0-7]
伪终端从设备
/dev/ttyS0
/dev/ttyS1
串口(COM1和COM2)
/dev/urandom
速度更快、安全性较差的随机数发生器
/dev/zero
零设备,只能读0出来
设备节点的主设备号(Major)、次设备号(Minor)的文档是内核源代码中的
/Documentation/device.txt,如果有疑问可以查看这个文件[8]。
5.2.3 脚本和配置文件(scripts and configuration files)
/etc
/etc/rc.d目录下的启动脚本是系统的重要部分。必须对启动脚本做相应的修改
以简化系统的启动过程。
系统和各种应用程序用到的几乎所有的配置文件都位于/etc目录,是裁减Linu
x最麻烦的部分,最容易出问题。配置文件的选择需要综合很多方面的信息,需要对
系统有比较全面、深入的了解,并结合经验才能做出正确的判断。
配置文件
描述
/etc/default
(目录)某个命令(比如useradd)的缺省设置(man useradd(8))
/etc/ld.so.cache
由ldconfig命令根据/etc/ld.so.conf文件产生
/etc/ld.so.conf
库文件路径配置文件,ldconfig命令根据该配置文件生成/etc/ld.so.cache
/etc/localtime
本地时间、时区设置
/etc/login.defs
全局缺省设置
/etc/fstab
文件系统列表(man fstab(5))
/etc/group
组文件(man group(5))
/etc/hosts
列出主机名和IP地址(man hosts(5))
/etc/init.d
符号链接到/etc/rc.d/init.d
/etc/initlog.conf
Initlog日志配置文件(man initlog(8))
/etc/inittab
Init配置文件(man inittab(5))
/etc/ioctl.save
该文件包含了用于单用户模式的串口和终端参数,因为这些参数是由getty设置的,
而在单用户模式时没有运行getty,所以用该文件保存参数。单用户模式对系统安全
是个威胁,我们应该禁止使用单用户模式,因此这个文件实际上并没有必要复制过
来。
/etc/issue
登录信息和标识文件(man issue(5))
/etc/modules.conf
模块的配置文件(man modules.conf(5))
/etc/mtab
已经挂载的文件系统列表(man mount(8))
/etc/nsswitch.conf
Name Service Switch的配置文件(配置名称服务数据源和查询的顺序)(man nss
witch.conf(5))
/etc/pam.d
放置PAM配置文件的目录(有关PAM请参考5.5节)
/etc/passwd
用户口令文件(man passwd(5))
/etc/profile
系统环境变量和登录配置文件
/etc/rc.d
放置启动脚本的目录
/etc/services
列出可用的网络服务及其端口(man services(5))
/etc/termcap
终端(terminal)功能数据库(man termcap(5))
还有那些跟特定应用程序相关的配置文件,比如apache服务器需要的/etc/htt
pd/conf/httpd.conf 等,此处不再一一列出。
5.2.4 库文件(libraries)
/lib,/usr/lib,/usr/share
库文件也是系统运行所必需的。到底需要哪些库文件,是根据所复制的可执行
程序用 ldd 工具来确定的。比如,要知道/bin/bash需要哪些库文件,使用如下命
令:
[root@lips xmdong]# ldd /bin/bash
libtermcap.so.2 => /lib/libtermcap.so.2 (0x40020000)
libdl.so.2 => /lib/libdl.so.2 (0x40024000)
libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
对复制到新的根文件系统的每个可执行程序,都要使用这种方法来确定其所需
要的库,然后把这些库文件也复制过来。
需要注意的是,有些库文件使用上述方法是找不出来的,但是却是系统必须的
。比如:
库文件
描述
/lib/libnss_files*
这个库是跟/etc/nsswitch.conf有关的,必不可少,否则系统不能正常使用。(ma
n nsswitch.conf(5))
/lib/security/pam_unix.so
这个库跟PAM有关,是安全、认证方面的,必不可少,否则系统无法登录。
5.2.5 必要的目录
/home,/mnt,/proc,/root,
/var,/var/log,/var/run,/var/lock/subsys
5.3 初始化RAM盘(initrd)
(参考资料[5])
初始化RAM盘(initrd)提供了引导器加载RAM盘的能力。这个RAM盘可以被挂载
(mount)成根文件系统,并执行其上的程序。然后,可以从另一个设备挂载一个新
的根文件系统,而原来的根文件系统(也就是initrd)会被移到一个目录里并卸载
。
initrd技术主要设计用来让系统启动过程可以分两个阶段进行,首先让内核以
一组最小的、被编译进内核里的驱动程序来启动,然后从initrd中加载其他的模块
。
5.3.1 操作步骤
使用initrd时,系统典型的引导步骤如下:
1) 引导器加载内核和初始化RAM盘;
2) 内核把initrd转到一个一般的RAM盘中,并且释放原来被initrd占用的RAM;
3) initrd以可读写模式被挂载到根目录;
4) 执行/linuxrc(linuxrc可以是任何可执行文件,包括shell脚本在内;它具有u
id 0即超级用户的权限,基本上可以做init程序中能够做的任何事情);
5) 在linuxrc中挂载真正的根文件系统;
6) linuxrc使用pivot_root系统调用,把真正使用的根文件系统挂载到根目录;
7) 在根文件系统上执行通常的引导过程(比如执行/sbin/init);
8) initrd文件系统被删除。
注意,改变根目录并不包括卸载旧的根文件系统,因此有可能在转变过程中仍
有进程在initrd上运行。另外,挂载在initrd目录下的根文件系统仍然是可用的。
5.3.2 引导选项
initrd技术增加了下列引导选项:
initrd=
装入指定的文件作为初始化RAM盘。当使用 LILO 当引导器时,你可以用/etc/
lilo.conf 文件中 INITRD 这个配置参数,来指定初始化RAM盘文件。
noinitrd
initrd的数据仍会保留,但不会装入到一个RAM盘里,真正使用的根文件系统将
会被挂载。initrd的数据能夠从 /dev/initrd 这个设备中被读出来。注意,initr
d的数据可以是任何结构,并不一定必须是一个文件系统的映像,这个选项主要用来
进行debug。
注意:/dev/initrd 是一个只读并且只能使用一次的设备,最后一个程序一旦
关闭它, 所有数据将被释放,而且设备也不能再被打开。
root=/dev/ram0
initrd 先被挂载成根目录,接着进行正常的启动过程(这时RAM盘仍被挂载成
根)。
5.3.3 安装
首先:我们要在正常的根文件系统中创建一个容纳initrd文件系统的目录,例
如:
# mkdir /initrd
对目录名称并没有特别的限制,在pivot_root(2)的man手册页中有更详细的说
明。
如果根文件系统是在引导程序时被建立的(例如,你在制作安装软盘),在建
立根文件系统的同时应该创建 /initrd 目录。
在某些情况下initrd虽然未被挂载,只要有下列设备存在,它的內容仍是可被
访问的。(注意:这个设备无法在 devfs 下使用)
# mknod /dev/initrd b 1 250
# chmod 400 /dev/initrd
第二:支持初始化RAM盘的能力,及所有执行格式和文件系统模块,都必须直接
编译进内核,不能采用可加载模块的方式。
第三:必须制作一个RAM盘映像文件。大概的步骤是,在一个块设备上创建一个
文件系统,把需要的文件复制进去,然后把这个块设备的内容输出成一个initrd文
件。目前至少有3种设备适合作为这种块设备:
O 软盘(能拿到任何地方试验,但速度太慢);
O RAM盘(最快,但浪费内存);
O Loopback设备(比较合适的解决方案)。
下面,我们将讨论使用loopback设备创建initrd文件的方法。
1)
确认lookback设备已经配置到内核里。
2)
创建一个适当大小的空白文件系统,例如:
# dd if=/dev/zero of=initrd bs=300k count=1
# mke2fs -F -m0 initrd
3)
把这个文件系统挂载进来,例如:
# mount -t ext2 -o loop initrd /mnt
4)
创建控制台设备。
# mkdir /mnt/dev
# mknod /mnt/dev/console c 5 1
5)
复制所有可能在initrd环境中用到的文件到/mnt目录中。(别忘了/linuxrc文
件)
6)
initrd执行环境的测试需要反复进行修正,为了避免不断的重新启动,可以使
用下列命令:
# chroot /mnt /linuxrc
当然这样启用initrd还是有限制的,就是不能干扰正常系统的执行状态(比如
重新配置网络接口等等。如果在一个用pivot_root指令转换根目录的执行环境中,
就可以做这些事情了)。
7)
卸载这个文件系统:
# umount /mnt
8)
这时,初始化RAM盘系统就在“initrd”这个文件中了。可以把它压缩小一点:
# gzip -9 initrd
最后,必须启动内核并载入initrd系统。需要设置下列引导参数:
root=/dev/ram0 init=/linuxrc rw
(只有需要写入initrd文件系统时才需要附加rw参数)。
使用 LOADLIN 当引导器时,可以执行:
LOADLIN C:\LINUX\BZIMAGE initrd=C:\LINUX\INITRD.GZ root=/dev/ram0 i
nit=/linuxrc rw
使用 LILO当引导器时,可以在 /etc/lilo.conf 中如下设置:
image = /boot/bzImage
initrd = /boot/initrd.gz
append = "root=/dev/ram0 init=/linuxrc rw"
如果使用其他引导器,请参考相关文档。
5.3.4 改变根目录设备
在执行完重要任务的尾声,linuxrc一般会执行改变根目录设备的动作,并让所
有程序在真实的根文件系统中展开。这个过程包括下列步骤:
O 挂载新的根文件系统
O 把initrd自己转到这个根文件系统中
O 结束所有旧的对initrd根文件系统的存取动作
O 卸载initrd文件系统并释放RAM盘内存
挂载新的根文件系统很简单:只需要把它挂载到当前根文件系统中的一个目录
下即可。例如:
# mkdir /new-root
# mount -o ro /dev/hda1 /new-root
最终改变根文件系统由pivot_root()系统调用或者pivot_root工具来完成(参
考手册页pivot_root(8))。pivot_root可以把当前根文件系统转移到新根的一个目
录中,然后把指定的目录当作新的根。在调用pivot_root之前,必须先为旧根准备
目录,例如:
# cd /new-root
# mkdir initrd
# pivot_root . initrd
现在,linuxrc仍然可以访问旧的根文件系统。执行下列指令将完全结束与旧根
的联系:
# exec chroot . what-follows dev/console 2>&1
其中what-follows是新根下面的一个程序,比如/sbin/init。(注意,这时如
果没有/dev/console,系统不能启动)。同时,为了保持不同版本之间的兼容性,
需要特别注意下列事项:
O 在调用pivot_root之前,当前目录应该是新的根目录;
O 使用“.”(当前目录)作为pivot_root的第一个参数(新根),为旧根指定目录
的第二个参数也要用相对路径;
O chroot这个命令应该在新、旧根系统当中都能使用;
O 然后调用chroot转移根文件系统;
O 在exec命令中使用象 dev/console 这样的相对路径。
特别提示:让initrd文件系统的目录结构与新根文件系统的结构一致,有利于
转换过程的顺利。
这时,initrd可以卸载,RAM盘也可以释放:
# umount /initrd
# blockdev --flushbufs /dev/ram0
注意:如果linuxrc或者它调用的其他程序的执行因为某种原因中断,则会启用
旧的change_root机制。
5.3.5 使用场合
实现initrd的主要动机是允许系统安装时模块化的内核配置。这个过程大致如
下:
1) 系统可以用一个最小配置的内核从软盘或者其他存储媒体启动,然后载入initr
d系统;
2) /linuxrc来决定需要什么来进一步挂载真实的根文件系统(如设备类型、驱动程
序等)或者支持发行版媒体(如CD-ROM,网络,磁带等);
3) /linuxrc载入必须的内核模块;
4) /linuxrc建立并安装根文件系统;
5) /linuxrc调用pivot_root改变根文件系统,并通过chroot一个程序(继续安装过
程);
6) 安装引导器;
7) 引导器被配置成装入initrd以及相关的模块,建立起系统环境;
8) 现在,系统可以引导,并执行其他的安装任务。
Initrd的关键作用是能够多重配置一个正常操作的系统,而不需要用一个庞大
的内核,或者重新编译、连接内核。在制作Linux发行版(光盘等)、系统恢复盘等
方面有广泛的应用。
5.3.6 淘汰的根转换机制(change_root)
可以通过改写/proc/sys/kernel/real-root-dev这个文件的数字值来改变真实
的根设备。例如:
# echo 0x301 >/proc/sys/kernel/real-root-dev
但是这个机制已经被淘汰,虽然目前的内核仍然支持,不能保证以后的内核会
支持。所以不要使用。