前言
大家可能见到过很多在软盘上运行的Linux系统,可在软盘上运行的FreeBSD反而比较少,虽然有PICOBSD,然而很多时候PICOBSD并不能满足我们的需要,那么可不可以自己制作一个在软盘上运行的FreeBSD系统呢?答案是肯定的。我在维护着一个FloppyFirewall的Project,它是一个基于FreeBSD和IPFilter的运行在软盘上的防火墙系统,很多网友在使用了FloppyFirewall之后发邮件来询问如何使FreeBSD运行在一张小小的软盘上。但由于前段时间事情太多一直没有时间,今天终于找到时间,所以把制作在软盘上运行的FreeBSD的过程写出来与大家分享,由于时间仓促,文中难免有错误之处,还请大家指教。
1、FreeBSD的启动过程简介
当BIOS读入MBR之后,MBR中的程序读入硬盘FreeBSDSlice(FreeBSD分区)中的引导程序,引导程序默认情况下会加载/boot/loader,然后loader将加载/kernel,此时kernel开始检测一些硬件和做一些初始化。初始化完成后kernel将mountrootdevice,然后启动系统初始化进程/sbin/init,init将根据/etc/rc中的设置来进行初始化等。
可以看出我们需要解决的部分就是:引导程序->/boot/loader->/kernel->/sbin/init->/etc/rc在了解了启动过程之后和问题所在之后,我们便可以开始制作软盘上的FreeBSD了。
2、初始化软盘
首先要做的就是要将软盘初始化,包括设置disklabel和创建文件系统(格式化成ufs格式)。
bsd#disklabel-r-wfd0afd1440
接下来是安装引导程序。
bsd#disklabel-Bfd0a
现在软盘已经能够引导了,但因为我们要在它上面放置程序,所以要创建文件系统。
bsd#newfsfd0a
刚才已经做好了引导程序,因为引导程序会加载/boot/loader,所以我们还需要将系统中的/boot/loader复制到软盘中。
bsd#mkdir/fd
bsd#mount/dev/fd0a/fd
bsd#mkdir/fd/boot
bsd#cp/boot/loader/fd/boot/loader
根据FreeBSD的启动过程,现在我们已经准备好了引导程序和loader,接下来就要准备内核了。
3、定制内核
软盘的空间有限,所以我们需要定制一个小内核,而不能直接使用系统原来的内核。由于我们只使用软盘,所以内核中的关于scsi、ata、atapi和raid等这些东西都应该删除,因为我们不需要IPv6所以INET6也应该删除,具体留下些什么要看自己的用途了,这没有什么标准。不过有几样是必须的:
optionsMFS#内存文件系统支持
optionsMD_ROOT#使用MD(内存磁盘)设备做root
optionsUFS#UFS文件系统支持
optionsUFS_ROOT#UFSROOT
pseudo-devicemd#MD设备支持
下面是我使用的一个内核配制文件:
#
#GENERIC--GenerickernelconfigurationfileforFreeBSD/i386
#
#
#$FreeBSD:src/sys/i386/conf/GENERIC,v1.246.2.382002/01/2517:41:40murrayExp$
machinei386
cpuI386_CPU
cpuI486_CPU
cpuI586_CPU
cpuI686_CPU
ident"MINI-KERNEL"
maxusers0
#maxusers最好让系统自动分配,如果设得过大,会占用过多的内存。
optionsINET#InterNETworking
optionsFFS#BerkeleyFastFilesystem
optionsFFS_ROOT#FFSusableasrootdevice[keepthis!]
optionsMFS#MemoryFilesystem
optionsMD_ROOT#MDisapotentialrootdevice
optionsCOMPAT_43#CompatiblewithBSD4.3[KEEPTHIS!]
optionsNO_SWAPPING#Disableswap
deviceisa
devicepci
#Floppydrives
devicefdc0atisa?portIO_FD1irq6drq2
devicefd0atfdc0drive0
#atkbdc0controlsboththekeyboardandthePS/2mouse
deviceatkbdc0atisa?portIO_KBD
deviceatkbd0atatkbdc?irq1flags0x1
devicevga0atisa?
#sysconsisthedefaultconsoledriver,resemblinganSCOconsole
devicesc0atisa?flags0x100
#Floatingpointsupport-donotdisable.
devicenpx0atnexus?portIO_NPXirq13
#PCIEthernetNICsthatusethecommonMIIbuscontrollercode.
#NOTE:Besuretokeepthe'devicemiibus'lineinordertousetheseNICs!
devicemiibus#MIIbussupport
devicefxp#IntelEtherExpressPRO/100B(82557,82558)
devicerl#RealTek8129/8139
devicexl#3Com3c90x
devicelnc0
atisa?port0x280irq10drq0#VMwareNic
#Pseudodevices-thenumberindicateshowmanyunitstoallocate.
pseudo-deviceloop#Networkloopback
pseudo-deviceether#Ethernetsupport
pseudo-devicemd#Memory"disks"
上面的内核基本上是一个系统要运行的最小配制了,当然如果你的机器不同具体也不同,大家按自己的情况来定,我的机器配制是:
CPU:PentiumIII733Mhz
MotherBoard:Via693AChipset
NIC:Realtek8139c
当配制好之后就是编译内核了,建议大家使用config的方式来编译,注意,最后不要使用makeinstall,否则你原来的内核会被替换。
bsd#cd/sys/i386/conf
bsd#configMINI
bsd#cd../../compile/MINI
bsd#makedepend&&makekernel
编译完成后就会成生kernel这个文件,这时它的体积还是比较大,不过现在不用管它,后面将会介绍如何处理它。
4、编译系统程序
现在就要准备系统所需要的基本程序了,首先最基本的是init和sh,init是所有进程的父进程,它负责进行一些初始化工作,它将是kernel引导完成后要运行的第一个用户进程,而sh用于解释/etc/rc中的命令。在UNIX中大部程序都使用了共享库,这有利减少磁盘空间的占用,这对于使用硬盘是非常有用的,然而对于软盘就不太适用了,因为单一个大部分程序都要使用的库libc.so就有500多K,加上其它的库软盘根本就装不下。
我们可以发现,大部分时候一个程序只是用到了库中的某个函数,但同样也要加载整个库,所以我们可以使用静态编译来使程序只包含它使用的那部分函数,这样可以减少程序的大小。
不过这样问题同样存在,如果只有少数程序这到没有什么,一旦程序很多时,那么空间问题同样存在。如果会C语言的朋友都知道,其实每一个程序中有很大一部分函数是相同的,比如printf,这个函数在大部分程序中都会用到,如果每个程序都包含一段printf的代码,那么如果有100个程序的话,就会包含100个这样的代码,然而这些代码都是相同的,实际上有99个都是浪费了空间,那么可不可以让一些程序在静态编译的情况下也能够共享一些函数呢?要知道答案,往下接着看。
幸好,PICOBSD为我们提供了这样的一个机制,使得程序即不用加载标准库也可以利用其它程序中的相同函数,这就是crunch(crunch好像是世界顶级黑客高手JohnDraper的网名,不知道这与他有没有关系。crunch是将所有需要的软件编译在一个文件中即crunch,然后当中的程序通过symbollink的方式link到它上面,这样便可以使用相应的程序(类似于linux中的busybox),同时又节约了空间。PICOBSD为我们提供了一个自己定制crunch的机会,在FreeBSD4.5Release(注:4.8Release中的crunch无法定制,至少我没有找到,所以建议大家使用4.5)中,crunch的配制文件是/usr/src/release/picobsd/custom/crunch1/crunch.conf,编辑它以选择你需要哪些软件,下面以一个例子来说明它的用法。
#$FreeBSD:src/release/picobsd/router/crunch.conf,
v1.1.2.22001/02/2002:53:35luigiExp$
#
#NOTE:thestring"/usr/src"willbeautomaticallyreplacedwiththe
#correctvaluesetin'build'script-youshouldchangeitthere
#Defaultbuildoptions
buildopts-DNOPAM-DRELEASE_CRUNCH-DNOSECURE-DNOCRYPT-DNONETGRAPH-DNOIPSEC
#othersources
srcdirs/usr/src/bin
srcdirs/usr/src/sbin/i386
srcdirs/usr/src/sbin
srcdirs/usr/src/usr.bin
srcdirs/usr/src/usr.sbin
srcdirs/usr/src/gnu/usr.bin
srcdirs/usr/src/gnu/usr.sbin
srcdirs/usr/src/libexec
#sourcesforns&vm
srcdirs/usr/src/release/picobsd/tinyware
以下为你所需要在crunch包含的程序列表,以空格分隔。
progsdmesgpingifconfigroutehostname
progscprmlscattestmkdirless
progsunamesysctl
progsinitshreboot
ln是表示建立一个别名,如lnlessmore,表示当执行more的时候实际上是执行less,lnlessmore。以下是指定编译时需要的库:
libs-lncurses-lmytinfo-lipx
libs-lz-lpcap-lalias
libs-ledit-lutil-lmd-lcrypt-lmp-lgmp-lm-lkvm
libs-lgnuregex-ltelnet
当编辑好crunch.conf之后,你就可以开始编译crunch了:
bsd#make
这时会生成一个名为crunch1的程序,我们要的就是它了。
5、建立内存磁盘
大家可以看到crunch1加上我们刚才编译的内核和loader程序,已经超出了软盘的容量,同时为了加速程序的运行我们需要使用MD(内存磁盘)来解决这个问题,MD将作为系统的根文件系统和用来存放系统程序。对于内存磁盘的大小一般不易太大,因为这样会占用过多的内存,下面我们就以建立一个3M的内存磁盘为例说明如何建立内存磁盘:
bsd#cd/root
bsd#ddif=/dev/zeroof=bsdbs=1kcount=3072#生成一个3M的文件,用来做MD
bsd#vnconfig-c-slabelsvn0cbsd
#使用bsd来创建一个vn设置,以便在其中存放程序
bsd#disklabel-w-rvn0cauto#建立disklabel
bsd#disklabel-Bvn0c#安装启动代码
bsd#newfsvn0c#创建UFS文件系统
bsd#mount/dev/vn0c/mnt#将vn0c即bsdmount到/mnt
接下来要做的就是建立目录结构,具体建立哪些目录这要视需要决定,本例中需要建立如下目录:
bsd#mkdir/mnt/etc
bsd#mkdir/mnt/sbin
bsd#mkdir/mnt/bin
bsd#mkdir/mnt/dev
然后将crunch1复制到/mnt/sbin中,再将刚才编译进crunch1中的那些命令分别做上symbollink:
bsd#cp/usr/src/release/picobsd/custom/crunch1/crunch1/mnt/sbin
bsd#cd/mnt/sbin
bsd#ln-s./crunch1init#init必须在/mnt/sbin目录中
bsd#ln-s./crunch1reboot
bsd#ln-s./crunch1sysctl
bsd#ln-s./crunch1ifconfig
bsd#ln-s./crunch1route
bsd#ln-s./crunch1ping
bsd#ln-s./crunch1dmesg
bsd#cd/mnt/bin
bsd#ln-s../sbin/crunch1sh#sh必须在/mnt/bin目录中
bsd#ln-s../sbin/crunch1hostname
bsd#ln-s../sbin/crunch1cp
bsd#ln-s../sbin/crunch1rm
bsd#ln-s../sbin/crunch1ls
bsd#ln-s../sbin/crunch1cat
bsd#ln-s../sbin/crunch1test
bsd#ln-s../sbin/crunch1mkdir
bsd#ln-s../sbin/crunch1less
bsd#ln-s../sbin/crunch1uname
bsd#ln-s../sbin/crunch1more
6、编写启动脚本
因为我们的系统只是为了测试在软盘上运行FreeBSD,因而这里的启动脚本非常简单只是让系统可以工作,没有做其它的工作,其内容如下:
#!/bin/sh
#FloppyBSDinitscript
PATH=/sbin:/bin
HOME=/
exportPATHHOME
echo
echo"Hello,it'smyFloppyBSD"
echo
因为没有使用登录验证,所以这里只是简单的一直运行shell
while:;do
/bin/sh
done
7、建立设备文件
现在需要建立一些基本的设备文件,我们使用/dev/MAKEDEV来完成这些操作:
bsd#cd/mnt/dev
bsd#cp/dev/MAKEDEV.
bsd#./MAKEDEVstd#建立标准设备
bsd#rmMAKEDEV
8、最后工作
到目前为止,我们的启动部分,Kernel和内存磁盘都已经准备好了,下面就开始整合它们了。
因为init启动的时候会查找loginclass中的daemon这个类别,如果没有则会出现错误提示,为了使init不报错,我们还需要复制/etc/login.conf到/mnt/etc中:bsd#cp/etc/login.conf/mnt/etc/login.conf因为loader支持直接加载gzip压缩格式的文件,这为我们节约磁空提供了方便,同时也解决了我们空间不够的问题,现在压缩kernel和内存磁盘bsd:
bsd#cd/root
bsd#cp/sys/compile/MINI/kernel.
bsd#gzip-9kernel#使用最大压缩率,将生成文件kernel.gz
bsd#umount/mnt
bsd#gzip-9bsd#使用最大压缩率,将生成文件bsd.gz
然后编辑loader的配制文件:
bsd#cd/fd/boot
bsd#viloader.rc
输入:
loadkernel
load-tmfs_rootbsd#表示将bsd.gz以md的方式加载,并且成为rootdevice
存盘退出,将kernel.gz和bsd.gz复制到软盘中:
bsd#cd/root
bsd#cpkernel.gz/fd
bsd#cpbsd.gz/fd
bsd#umount/fd
现在用你的这张软盘就可以启动你的机器,如果一切正常的话,你将看到"Hello,it'smyFloppyBSD"的提示,并且看到可爱的shell符"#"了,是不是很有满足感呢?:)到此为止一个基本的BSD系统已经完成了,如果你想继续扩展FloppyBSD的功能,那就按照上面的方法自己做吧!