分类: LINUX
2008-06-02 11:31:19
.引言
本文着重讲述如何制作基于linux的usb启动盘,此usb启动盘能够实现以下功能。
a. usb启动盘的Linux内核usblinux.kernel应支持尽可能多的硬件(包括硬盘驱动,网卡驱动,usb驱动)
b. u盘根文件系统放在ramdisk中,启动之后解压到ram中。
c. usb启动盘启动之后能够在目标机上执行分区(fdisk),格式化(mkfs.*)以及在各种块设备上安装grub
d. usb启动盘能够临时充当局域网内的网关。
e. usb启动盘能够在一台机器上快速的搭建网关服务器。
.定制操作系统所要考虑的事情
不管是在u盘上面还是在其他存储设备上(硬盘,软盘)安装Linux,我们所需要考虑的事情基本一样。
1)定制自己的内核(包括选定确当的内核版本,内核是否支持模块等等)
2) 定制自己的根文件系统(包括根文件系统的架构,根文件系统的内容,根文件系统所采用的文件系统类型等等)
3) 配备引导加载程序(bootloader)
的系统架构
结合1和2我们现在来具体定制我们的UsbLinux。
1) 内核选用
2) 内核支持模块加载功能,上面说了我们的内核应该尽可能支持多的硬件,如果我们把这些硬件的驱动全部静态编译进内核的话,内核将会变的非常庞大,这是我们不希望看到的,这不仅仅会减慢Usblinux启动的速度,而且我们不能动态的去除不必要的内核模块。
3) 根文件系统选择ext2文件系统,前面说了我们的根文件系统是放在ramdisk中的,ramdisk正如其名,存在于ram中并且功能犹如块设备。因此也就决定了根文件系统里面的东西修改之后断电不能保存,ext2文件系统的特性主要包括可读写,可压缩,不具备断电可靠性,ext3,reiserfs等日志文件系统是具备断电可靠性的,它们用在硬磁盘上比较合适。我们这里的根文件系统存放在ram中,因此选用ext2文件系统比较合适。
4) bootloader 选择grub,我们不是在搞嵌入式所以grub是一个非常好的选择。
说明:下面的所有操作都是在red hat linux 9里面完成的。
编译安装内核
选择配置内核方式
配置内核有多种方法,切换到源码目录下,
a. make oldconfig,make config(注,除非你很喜欢这种配置方式,否则建议你还是不要使用它们,它们不但不直观,而且配置容易出错)
b.make menuconfig,这是最常用的一种配置方法,方便直观。如果没有其它特殊原因,建议你使用这种配置方法。
c.make xconfig, 这个需要X窗口的支持,如果你喜欢这个配置方法也很方便
内核配置
这当然是很重要的一个步骤了,关于配置内核的具体细节网上有很多这样的文章,这里只重点说以下几个重要的地方。
Loadable module support: 这个我们支持,因为我们的驱动大多是编译成模块方式的。
Process type: 由于我们的UsbLinux是做成通用的而不是针对某个目标机的,所以处理器的选择至关重要,为什么这样说呢? 如果你的内核选择的处理器类型是的Athlon/Duron ,那么在一台386或者586的机器上到解压内核的时候可能会出问题,我试了在奔腾或者赛杨的处理器上也是到解压内核的时候就停在那边了。我们现在的处理器大多是X86系列的,因此处理器选择386,可以避免上述的麻烦,当然针对具体的机器这个必然会对机器的性能产生影响。
Block devices: 一定要选择ram disk support 和initrd support
NetWorking options: 这里面的是关于内核支持的网络功能,我们的UsbLinux需要临时充当网关,而且能够利用它搭建网关服务器,因此这里面的大多数选项都要选,这里面的选项比较多,最好对网络方面的知识有个大概的了解。
ATA/IDE support: 硬盘的类型,这个里面也有比较多的硬盘型号,我们现在大多是IDE硬盘,但是如果你想让你的UsbLinux的通用性好一些的话,最好上网查查硬盘厂商的相关资料。
SCSI support:
尽管现在很少见到SCSI的硬盘,不过我们还不得不让内核支持SCSI,为什么呢? linux把usb设备是当成SCSI看待的,因要支持USB就要支持SCSI才行。具体的把下面这两个选项编译进内核就可以了,其它的基本上用不着。
<*>SCSI Support
<*>SCSI Disk Support
另外为了在vmware上面测试,我把BusLogic这个驱动编译进了内核。
NetWork Devices Support:
网络设备支持,我们基本上只需要选择Ethernet(10 or 100Mbit)这里面的选项就可以了,其它的千M以太网卡,FDDI以及PPP之类的一般用的不多,当然还是要视具体情况而定。在
Charater Devices: 字符设备支持,把Virtual terminal,support console on virtual terminal, Standard/generitic serial support和Support for console on serial port这两项编译进内核就差不多了。
File Systems: 文件系统支持,由于我们使用的ext2文件系统,所以把Second extended fs support 这一项编译进内核。另外/proc文件系统也编译进内核,因为proc文件系统很有用。其余的ext3,fat,Vfat,ntfs 给M上吧,另外吧网络文件系统nfs也M上吧,Partion types这里面的选择PC BIOS就可以了,Native Language Support把codepage 437,936以及nls iso8859-1编译进内核,其它的就不用选了。
USB Support: Usb设备支持,把UHCI和OHCI support给M上,另外USB Mass Storage Suppor 一定要选上,把它编译进内核或者M上都可以。最后吧USB Device filesystems 给编译进内核,这会在/proc文件系统里面生成相应的选项,便于调试用。其它的声卡,红外线之类的设备都可以不选。
这里没有涉及的选项都是不常用的。
内核配置完毕,保存退出。
建立依赖关系
这一步一个命令make dep就可以搞定。内核源码树中大多数文件都会与一些头文件有依赖关系,make dep期间会在内核源码树中每个子目录下面产生一个隐藏的.depend文件。此文件内包含子目录里面各文件所依赖的头文件清单。
建立内核
make bzImage or make zImage
注意bzImage和在zImage都是经gzip算法压缩过的内核映像,所不同的是在zImage的大小无法超过500KB,而bzImage则没有这个限制,如果映象建立过程中有错误,一个就是你的内核配置不正确,还有一个可能就是需要make mrproper来消除上一次的编译记录,make mrproper之后内核源码相当于刚安装时候的状态。
建立和安装模块
make modules & make modules_install
注意模块的默认安装位置是/lib/modules目录下,目录名即是你的内核版号,如果/lib/modules目录下有一个相同的内核版本号目录,如果很重要的话你最好把它备份,因为会被新的覆盖。至此内核以及相应的模块已经建立好了。
.建立根文件系统
.根文件系统的基本结构
首先建立一个roofs文件夹用来存放根文件系统的内容。
#mkdir /mnt/rootfs
根文件系统的顶层目录: (摘自<<构建嵌入式LINUX系统>> page 179)
目录 |
内容 |
bin |
必要的用户命令(二进制文件) |
boot |
引导加载程序使用的静态文件 |
dev |
设备文件和其它特殊文件 |
etc |
系统配置文件,包括启动文件 |
home |
用户主目录 |
lib |
必要的连接库,例如C连接库,内核模块 |
mnt |
安装点,用于暂时安装文件系统 |
opt |
附加的软件套件 |
proc |
用来提供内核与进程信息的虚拟文件系统 |
root |
root用户的主目录 |
sbin |
必要的系统管理员命令(二进制文件 ) |
tmp |
暂时性的文件 |
usr |
在第二层包括对大多数用户都有用的大量应用程序和文件(包括库文件) |
var |
监控程序和工具程序所存放的可变数据 |
建立UsbLinux根文件系统的基本结构
对于u盘上的linux或者做网关的linux来说,用户的权限问题并不复杂,我们只需要设置一个超级用户以及其它系统服务必要的帐户即可(比如ftp,sshd等等),由于相当于是单用户,因此home目录也可以不要,其它的目录在我看来都是必要的,下面我们开始建立根文件系统的基本结构。
#cd /mnt/rootfs
#mkdir bin sbin dev etc lib root tmp usr var proc opt mnt
#mkdir usr/{bin,sbin ,lib,share}
#mkdir var/{lock,log,run,empty}
一般二进制命令可以存放的目录包括bin,sbin,usr/bin,usr/sbin,它们有什么区别呢?
二进制文件放在哪个目录,这与它在系统中所扮演的角色密切相关,如果这是普通用户和系统管理员必备的二进制文件(比如ls和mkdir等),就会放在bin目录下,如果只是系统管理员所具有的一些特权指令(普通用户使用这些指令有限制,如ifconfig),那么它应该放在sbin目录下,usr/bin目录下存放的是普通用户和管理员"不常用"的命令,而usr/sbin目录下是管理员"不常用"的命令。
上面已经说过我们不需要区分普通用户和系统管理员(root),因此我们只需创建一个系统管理员帐号就可以了,但是这里为什么我们还要创建这四个目录呢? 是因为我们用到busybox套件,下面说明安装定制应用程序的时候会讲到。
下面我们把刚才建立的内核模块拷贝到lib目录下(注:以后我们所有的操作都是以/mnt/rootfs为根操作的)
#mkdir lib/modules
#cp –dpR /lib/modules/
选择链接库
链接库是文件系统中一个非常重要的部分, 它也是整个根文件系统中最耗空间的一个部分。链接库是应用程序执行期间必不可少的一部分,当然如果你编译应用程序的时候库都是静态编译进去的,那么就不需要额外麻烦的自己建立链接库了,比如嵌入式系统中的uClinux,不过这个做法的缺点就是会额外的消耗存储空间。我们这里采用动态链接的方法。目前比较流行的链接库有glibc和uClibc,glibc是gnu的C链接库,一般的linux发型版都是使用的glibc库,而uClibc主要是针对嵌入式系统而定制的库,它的特点就是小。尽管uClibc也可以用在X86机器上,但它并不具有glibc的稳定性,一般在pc机上都是使用的glibc库。我们的目标机是pc机,所以我们选择glibc库,我们可以直接把宿主机上面的库文件拿来用。
建立设备文件
依照unix的传统,在Linux系统中任何对象(包括设备 )都可以视为文件,在Linux系统中所有的设备文件都放在dev目录下面,建立设备文件通常有两种方法,一个是直接把宿主机上面的拷贝过来(拷贝的时候需要加上dpR参数),另外一种方法就是自己手工用mknod创建(注意这两种方法都需要root权限)。
一般情况下我们都是使用第一种方法,但是这里面为了便于大家更深入的理解设备文件,我使用第二种方法来创建。首先来看如何使用mknod命令来创建特定的设备文件。
#cd dev
#mknod -m 666 null c 1 3
上面这条命令创建了null设备,-m 参数指定所创建设备的基本权限,null是设备名称,c代表是字符设备,相应的块设备用b表示,1是主编号,3是次编号。
关于设备的主次编号的权威信息的来源可以查看内核源码树中的
Documentation/devices.txt
我们这里需要建立的dev条目如表格所示。
文件名 |
说明 |
类型 |
主编号 |
次编号 |
权限位 |
mem |
物理内存存取 |
字符设备 |
1 |
1 |
600 |
console |
系统控制台 |
字符设备 |
5 |
1 |
600 |
urandom |
真随机数产生器 |
字符设备 |
1 |
8 |
644 |
null |
null(黑洞)设备 |
字符设备 |
1 |
3 |
666 |
zero |
以null byte(零值字节)为数据来源 |
字符设备 |
1 |
5 |
666 |
tty |
现行的tty设备 |
字符设备 |
5 |
0 |
666 |
tty0 |
现行的虚拟控制台 |
字符设备 |
4 |
0 |
600 |
tty1 |
第一个虚拟控制台 |
字符设备 |
4 |
1 |
600 |
ptyp0 |
first pseudo-tty master |
字符设备 |
2 |
0 |
666 |
ttyp0 |
first BSD pseudo-tty slave |
字符设备 |
3 |
0 |
666 |
ram0 |
第一块 ram盘 |
块设备 |
1 |
0 |
640 |
hda |
主IDE硬盘(或者光盘) |
块设备 |
3 |
0 |
660 |
hda1 |
上述主盘的第一个分区 |
块设备 |
3 |
1 |
660 |
hdb |
从IDE硬盘(或者光盘) |
块设备 |
3 |
64 |
660 |
hdb1 |
上述从盘的第一个分区 |
块设备 |
3 |
65 |
660 |
sda |
第一块scsi盘的整个盘 |
块设备 |
8 |
0 |
660 |
sda1 |
第一块scsi盘的第一个分区 |
块设备 |
8 |
1 |
660 |
sdb |
第二块scsi盘的整个盘 |
块设备 |
8 |
16 |
660 |
sdb1 |
第二块scsi盘的第一个分区 |
块设备 |
8 |
16 |
660 |
fd0 |
第一个软盘设备 |
块设备 |
2 |
0 |
660 |
下面我们写一个sh脚本mkdev.sh来创建这些设备,脚本内容如下:
#!/bin/sh
rm -rf mem console urandom null zero tty* ptyp* ram* hda* hdb* \
sda* sdb* fd* hdc cdrom
mknod -m 600 mem c 1 1
mknod -m 600 console c 5 1
mknod -m 644 urandom c 1 8
mknod -m 666 null c 1 3
mknod -m 666 zero c 1 5
mknod -m 666 tty c 5 0
for i in 0 1 2 3 4
do
mknod -m 600 tty$i c 4 $i
mknod -m 660 ttyp$i c 3 $i
done
for i in 0 1 2 3 4
do
mknod -m 660 ptyp$i c 2 $i
done
mknod -m 640 ram0 b 1 0
mknod -m 640 ram1 b 1 1
mknod -m 660 hda b 3 0
mknod -m 660 hdb b 3 64
mknod -m 660 hdc b 22 0
ln -s hdc cdrom
for i in 1 2 3 4 5 6 7 8 9 10
do
mknod -m 660 hda$i b 3 $i
mknod -m 660 hdbd$i b 3 `expr 64 + $i`
done
mknod -m 660 sda b 8 0
mknod -m 660 sdb b 8 16
for i in 1 2 3 4 5
do
mknod -m 660 sda$i b 8 $i
mknod -m 660 sdb$i b 8 `expr 16 + $i`
done
mknod -m 640 fd0 b 2 0
#end
#./mkdev.sh