自己定制软盘上的Linux系统
2004-04-23 15:18 pm
作者:作者
来自:Linux知识宝库
http://www.douzhe.com/docs/linux/
Contact: 无名
一.前言
嵌入式Linux是由一个几百KB的Linux内核和一个根据需要制定的文件系统所构成了, 由于Linux是开放源代码的操作系统,所以在嵌入式领域有着非常广阔的前景,并已经广泛应用在许多手机、PDA、MP3播放器等许多电子产品中。本文将介绍一种两张软盘上的Linux系统,它可以当作系统应急修复盘、路由器或防火墙等许多地方,通过对它的研究,也可以加深对嵌入式系统的理解。
二.Linux启动过程
所有的PC机在加电之后,BIOS会寻找到启动盘第一个扇区,并将其复制到RAM中来执行它,对于两种不同的启动方式,这个扇区通常含有两种不同的代码:引导程序(比如Lilo或Grub等)的代码,引导程序会帮助定位内核的位置。内核的代码,这通常是从软盘启动时使用的引导的方式。对于前者,通常需要内核支持initrd。如果是后者,使用的Boot Loader就是arch/i386/boot/bootsect.S。当内核被编译的时候,这段执行代码就被链接到内核image的最开始的地方。这样很容易就能只要把内核复制到起始位置为第一个扇区的软盘上就能得到可自启动的软盘。内核会初始化设备驱动和内部的数据结构,之后它会到一个特定的位置 ――Ramdisk Word来获得根文件系统的位置。内核必须知道去那里寻找这个根文件系统,否则它将停机。
在使用软盘启动的方式时,内核可以把一个压缩的文件系统释放到RAM中,称之为Ramdisk,这是一个内存区域,但内核会把它当作磁盘一样使用。
本文中介绍的例子使用Grub做为引导程序,并使用initrd来辅助Linux的启动。两张软盘分别命名为bootldr盘和rootfs盘,在 bootldr盘中内容为grub、内核、initrd,rootfs盘中是压缩过的根文件系统。系统启动时bootldr盘的Grub定位并执行内核,然后内核解开initrd,并执行linuxrc文件,这个文件负责提示用户更换rootfs盘并将其中内容解压至内存中,然后执行刚刚解压的init继续启动过程。
为了方便理解这个例子,先介绍目录结构如下:
/home/papaya
├─bootldr/
│ ├─grub/
│ ├─kernel/
│ │ ├─images/
│ │ └─linux-2.4.21/
│ └─initrd/
│ ├─mkinitrd.sh
│ ├─local/
│ └─ramdisk/
├─rootfs/
│ ├─mkrootfs.sh
│ ├─ramdisk/
│ └─local/
└─lib/
三.定制Grub引导程序
插入一张软盘,然后将其格式化,然后加载到/mnt/floppy
#mke2fs /dev/fd0
#mount -t ext2 /dev/fd0 /mnt/floppy -o loop
在其中创建/boot/grub目录
#mkdir -p /mnt/floppy/boot/grub
将系统中/boot/grub下的device.map, stage1, stage2 复制到/mnt/floppy/boot/grub中,然后在/mnt/floppy/boot/grub目录下创建grub.conf文件:
default=0
timeout=10
title Floppy Linux
kernel (fd0)/bzImage root=/dev/ram0
initrd (fd0)/initrd.gz
然后创建一个链接
#ln -s grub.conf menu.lst
执行
/sbin/grub --batch --device-map=/dev/null <device (fd0) /dev/fd0
root (fd0)
setup (fd0)
quit
EOF
这样grub就被安装到bootldr盘上了。
四.定制Linux内核
由于软盘大小的限制,内核应尽可能只包含必要的一些支持,对于本文中的例子一定要选上initrd支持。比如如果做为系统修复盘的话,必要的支持包括: IDE,PCI,和需要的文件系统类型等等就可以了,而没有必要网络支持,当然,如果做为路由器或者防火墙的话,网络支持是必要的,而其他的这可相应的删除掉。
#make [xconfig | menuconfig | config]
#make bzImage
如果添加了模块的支持,还需要
#make modules
之后就得到了内核镜像bzImage。如果bzImage的大小超出了软盘的限制,就需要重新再来配置一下。将编译好的bzImage放到bootldr盘的根目录下,如果把bzImage改了名字,要注意与grub.conf中的名字一致。
五.制定initrd
在initrd/local目录下建立bin, dev, etc, lib, proc, sysroot, usr目录。其中dev目录下包括必要的设备文件,比如tty, ram, console等等, bin中必要的可执行文件有bzip2, chroot, cp, cpio, dd, echo, mount, pivot_root, readkey, sh, test等。Busybox提供了其中大部分。 bzip2, dd, cpio用来解压缩第二张软盘上的内容,chroot, pivot_root用来转换根目录。
编辑initrd/local/linuxrc文件:
#!/bin/sh
把sysroot目录mount到一块内存上,并建立tmpfs文件系统。
echo "Mounting new root filsystem ..."
mount tmpfs /sysroot -t tmpfs
cd /sysroot
下面的readkey是一个很简单的程序,当启动过程执行到这里的时候暂停,等待换入第二章软盘,然后接受任意键输入继续执行启动过程。这个小程序读者可以自己实现,要注意的是最好使用静态链接。
echo " "
echo -en "Insert the second disk and press ANY key..."
readkey > /dev/null
echo " "
将第二章软盘上的内容解压到sysroot目录(内存)中。
echo "Loading root-archive from floppy ..."
dd if=/dev/fd0 bs=1k | bzip2 -d | cpio -idv
下面将initrd中的文件copy到sysroot/bin目录下,这样可以把根文件系统中一部分内容放到initrd(第一张软盘)中,因为软盘容量有限,当第一张软盘空间有剩余,而第二章软盘空间紧张的时候这会非常有用。
echo "Copying:"
for file in bzip2 chroot cp cpio echo readkey; do
echo -en " "; echo -n $file
cp /bin/$file ./bin/$file
done
下面将/目录设定为当前目录,即sysroot,并执行刚刚从rootfs盘中解压出来的init。
echo " "
echo "Pivoting / ..."
pivot_root . mnt/initrd
echo "Starting init process..."
exec chroot . /sbin/init /dev/console 2>&1
echo -en"Something went wrong ..."
/bin/sh || /mnt/initrd/bin/sh
当initrd所有必须的文件都放到bootldr/initrd/local目录下之后,就可以执行bootldr/initrd/mkinitrd.sh来创建initrd镜像文件。mkinitrd.sh的内容为:
#!/bin/sh
mount -t ext2 /dev/fd0 /mnt/floppy
rm -f /mnt/floppy/initrd.gz
rm -f initrd.gz
取4M大小的内存块格式化为ext2格式,并将其mount到bootldr/initrd/ramdisk上。
dd if=/dev/zero of=/dev/ram9 bs=1k count=4096
mke2fs /dev/ram9
mount -t ext2 /dev/ram9 ramdisk/
把local中的文件复制到ramdisk目录中,也就是那块内存中。
cp -R local/* ramdisk/
umount ramdisk
将内存中的内容压缩为initrd.gz,并复制到bootldr盘中
dd if=/dev/ram9 bs=1k | gzip -v9 > initrd.gz
cp initrd.gz /mnt/floppy/
umount /mnt/floppy
这样,bootldr盘就完成了。
六.定制根文件系统
一个根文件系统需要包含支持Linux系统运行的所有文件。通常包括:
基本的文件系统结构
基本的目录: /dev, /proc, /bin, /sbin, /etc, /usr, /tmp等。
基本的工具: sh, ls, cp, cd, mv等。
基本的配置文件: rc, inittab, fstab等。
设备: /dev/hd*, /dev/tty*, /dev/fd0, /dev/ram*, /dev/console等.
基本的运行库。
Busybox和Tinylogin是在嵌入式系统上常用的工具包,它们包含了上面提到的常用的工具和目录结构等,而且经过重新改写后所生成的代码比普通的Linux系统上的工具要小的多。
编辑Busybox的Config.h文件,选择自己需要的工具。修改Busybox和Tinylogin的Makefile文件,制定它们使用静态链接方式(DOSTATIC=true),这样就不需要在生成的系统中添加运行库了。将编译好的Busybox和Tinylogin文件放到 rootfs/local中。
在rootfs/local中在自己创建下面几个目录:dev/, tmp/, etc/, proc/
可以将系统中/dev下的设备复制到这个目录下,只需要复制必要的就可以了,例如:
#cp -dpR /dev/tty[0-9] /mnt/rootfs/dev
#cp -dpR /dev/ram* /mnt/rootfs/dev
但是要注意一定要包含必要的接各设备/dev/console, /dev/kmem, /dev/mem, /dev/tty, /dev/ram0, /dev/null等。
etc/ 目录下包含了目标系统运行所必须的配置文件,它包括的内容依赖与目标系统所要运行的程序。最低限度,它包括下面几个文件:inittab、rc、 fstab、passwd、group、shadow、termcap等。做为init进程的参数,inittab可以非常简单,仅需要包括下面几行即可:
::sysinit:/etc/rc
::askfirst:/bin/login
tty2::askfirst:/bin/login
tty3::askfirst:/bin/login
tty4::askfirst:/bin/login
::ctrlaltdel:/sbin/reboot
::restart:/sbin/init
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
其中sysinit指明系统初始化脚本rc。rc所包含内容也可以非常少:
#!/bin/sh
/bin/mount -av
/bin/umount /mnt/initrd
/bin/hostname papaya
fstab的内容为:
none /proc proc defaults 0 0
none /tmp tmpfs defaults 0 0
其他的配置文件可以从原来的系统中获得,然后修剪掉不必要的内容即可。
现在在/mnt/rootfs中已经包含了运行一个最低限度Linux系统所必须的所有文件和工具,下面需要将它们压缩成一个文件系统了。插入rootfs软盘并执行bootldr/rootfs/mkrootfs.sh
#!/bin/sh
rm -f rootfs.cpio.bz2
dd if=/dev/zero of=/dev/ram0 bs=1k count=4096
mke2fs /dev/ram0
mount -t ext2 /dev/ram0 ramdisk/
cp -R local/* ramdisk/
cd ramdisk/
find . -depth -print | cpio -o > ../rootfs.cpio
cd ..
bzip2 rootfs.cpio
umount ramdisk
dd if=rootfs.cpio.bz2 of=/dev/fd0 bs=1k
OK,rootfs盘也完成了,可以重启机器验证了。
七.其他方法
将内核与文件系统进行整合,如果不用Grub引导还有两种选择,不过根文件系统就不能象上面那样打包再压缩, 也不再使用initrd。把所有根文件系统文件放到一个目录中(比如上面的rootfs/local),然后执行
dd if=/dev/zero of=/dev/ram0 bs=1k count=4096
mke2fs /dev/ram0
mount -t ext2 /dev/ram0 ramdisk/
cp -R local/* ramdisk/
umount ramdisk
dd if=/dev/ram0 bs=1k | gzip -v9 > rootfs.gz
1.将内核与文件系统放置在一张软盘上
确定内核的大小和的大小之合没有超出软盘的限制。记住内核的大小,然后将内核写到软盘上:
#dd if=bzImage of=/dev/fd0 bs=1k
353+1 records in
353+1 records out
之后,设置根设备为软盘本身,并且设置根以读写方式装载
#rdev /dev/fd0 /dev/fd0
#rdev -R /dev/fd0 0
上面这个例子表示dd写了353个完整记录和一个部分记录到软盘上,因此内核占用了软盘的前354个记录块。记住这个数字,然后设置内核的Ramdisk Word。Ramdisk Word可以通过rdev命令设置,它的内容为:
如果15位设置的话,内核在加载文件系统之前会进行提示,这在下面将内核与文件系统盘分开的情况时是必要的。
对于上面的情况,需要在0-10位指出ramdisk的偏移,并将14位置1,所以得出的ramdisk word十进制表示为:355 + 2^14 = 355 + 16384 = 16739
#rdev -r /dev/fd0 16739
之后
#dd if=rootfs.gz of=/dev/fd0 bs=1k seek=354
这样一张同时包含内核和文件系统的软盘就成功了。
2.内核与文件系统分别占用一张软盘
与上面一样
#dd if=bzImage of=/dev/fd0 bs=1k
#rdev /dev/fd0 /dev/fd0
#rdev -R /dev/fd0 0
不同的是ramdisk word为 0 + 2^14 + 2^15 = 49152
#rdev -r /dev/fd0 49152
然后换零一张软盘
#dd if=rootfs.gz of=/dev/fd0 bs=1k
内核、根文件系统、引导程序之间的整合方法有很多,他们各有各自的特点,有待读者自己思考。
八.前景
按照本文方法所构造的Linux系统虽然还不完善,但通过逐步的改进,将能做出一个有价值的系统来。真正的Linux嵌入式系统根据不同的应用方向通常还包括图形用户界面或者是网络浏览器等应用程序。值得庆幸的是很多OpenSource的项目提供了所需要的组件,结合这些优秀的开源项目,就可以形成一套真正的嵌入式Linux操作系统。
九.参考文献:
The Linux Bootdisk HOWTO,
http://www.tldp.org
http://www.busybox.net
http://www.gnu.org/software/grub/