2006年10月04日
Linux内核升级手记(2.4.20-8 ~ 2.6.8
By mutecat@byhh 2006-10-4
 
题记
    最近想研究一下驱动开发,找了一本电子版的Oreilly.linux.device.drivers.3rd.edition.2005.CHM,第三版针对的内核是2.6的,可我虚拟机里面的系统内核是2.4的,以前曾经升级过内核, 但是不知道什么原因,现在进不了2.6的内核了.所以下决心重头再来过一遍^_^. 关于内核升级的好文章网上数不胜数, 我写下这篇文章的目的一是为了和大家分享经验,二是为了便于自己以后查阅.
 
一、2.6内核有哪些特点?
       首先我推荐你看一下《Linux 2.6内核的精彩世界》, 这篇文章比较详细的介绍了2.6内核新的特性, 我们关注最多的可能是以下几个方面:
1.       内核是抢占式的, 这将显著地提高交互式和多媒体应用程序的性能。
2.       添加了sysfs文件系统, 它集成了下面3种文件系统的信息:针对进程信息的proc文件系统、针对设备的devfs文件系统以及针对伪终端的devpts文件系统。该文件系统(安装在/sys目录)是核心看到的设备树的一个直观反映。
3.       为了区别以 .o 为扩展名的常规对象文件,内核模块现在使用的扩展名是 .ko。
二、安装前的准备–升级一些必要的工具.
到底哪些工具需要升级呢? 我们首先得看看Documentation/Changes这个文件,里面比较详细的介绍了安装此内核需要工具的版本号, 我是按照里面的要求一一升级的,当然对于你用不到的设备比如pcmcia就没必要升级了.
这里我说一下module-init-tools和mkinitrd的升级.
1.       的升级module-init-tools
module-init-tools提供一组工具用于加载和卸载内核模块, 包括depmod, insmod,modprobe,rmmod,lsmod ,etc. 我下载的版本是3.1, 解压之后查看里面的README文件进行安装, 一般采用如下步骤:
#./configure –prefix=/
#make moveold
#make
#make install
每一步都在README里面有说明.
新的工具采用/etc/modprobe.conf配置文件. 因此我们需要把/etc/modules.conf转换成/etc/modprobe.conf, 只需要在解压目录下输入
./generate-modprobe.conf /etc/modprobe.conf 即可.
2.      mkinitrd的升级
这个在Changes文件里面并没有提及,但是如果不升级的话安装内核(make install)的时候会提示:
No module BusLogic found for kernel 2.6.8, aborting.
    mkinitrd failed
    make[1]: *** [install] Error 1
        make: *** [install] Error 2
由于mkinitrd的依赖关系,还需要下载lvm2.device-mapper这两个包
我采用的版本号是:
mkinitrd-4.1.18-2.i386.rpm
device-mapper-1.00.19-2.i386.rpm
lvm2-2.00.25-1.01.i386.rpm
这里给大家提供一个非常棒的rpm包下载链接:
http://rpm.pbone.net/
注意,并不是版本越高越好哦,版本越高依赖关系越多,越麻烦,所以够用就行了^_^.
、定制内核
       2.6内核的最新版本是2.6.18,我到www.kernel.org下载下来之后,安装到/usr/src目录下,然后编译,令我奇怪的是从下午1点左右一直编译到4点多还没结束,最后发现尽然一直在一个死循环里面,什么事情都没完成,我ft,得到一个教训:最新的不一定最适合!所以就换了以前用过的2.6.8的内核。
        关于内核的定制我不想说太多,一来觉得罗嗦,网上资料太多,二来也是自己懒的去细说,但是我还是要说以下几点:
1.  内核配置哪些选项该选,哪些不需要选,完全取决于你的硬件配置和你使用的目的,就拿我来说,是在Vmware里面玩得,Vmware的硬盘选择的是SCSI,它的驱动是BusLogic,网卡驱动是AMD Pcnet32,所以其他的SCSI驱动和网卡驱动一律杀除,再来说USB设备,我想大多数人都没有USB鼠标和USB硬盘,所以只要选择一般的USB支持就行了,还有什么红外,蓝牙之类的,都不需要选上。我把sound支持去掉了,graphics只支持framebuffer,因为我不需要这些东西。
2.  为了体验2.6内核新的特性,关于"Processor type and features"选项, 一定要选上"Preemptible Kernel";关于文件系统, 选上常用的那几种就可以了. 如果要体验sysfs的话, 要选上对sysfs的支持.另外关于"Loadable Module support"选项, 一定要选上"Module unloading"和
"Automatic kernel module loading"这两个选项,不然的话你不能rmmod了哦。
定制好内核后
make
make modules_install
make install
 
2.6内核安装比2.4的方便 ,make这 一步完成了make bzImage和make modules两个过程.
 
如果没有错误的话, 相信你的grub配置文件已经被修改过了吧(不要告诉我你用的lilo),在/boot目录下多了vmlinuz-2.6.8和initrd-2.6.8.img,System.map-2.6.8
这几个文件, vmlinuz-2.6.8就是arch/i386/boot目录下的bzImage, initrd-2.6.8.img是由make install生成的(mkinitrd).
 
reboot看看能不能启动2.6的内核.
修改相应文件使其适应2.6内核
      我是按这篇文章里面提到的修改的
      http://blog.csdn.net/yiminglinux/archive/2006/04/09/656058.aspx
     

1. 支持Hotplug
   需要改一个最重要的文件/etc/rc.sysconfig 这是一个ln 连接。正真的文件在/etc/rc.d/里.当然, 首先要在内核配置时配置了HOTPLUG的支持. 然后,由于/proc/ksyms改名为/proc/kallsyms,所以必需修改启动脚/etc/rc.sysinit.
为了兼容原有的linux-2.4.x系统, 可以加上下面几行:
#
# —– KERNEL 2.6.x support ——
# This is for compatibility between kernel-2.4.x and
kernel-2.6.x
#
UNAME=`uname -r`
KERNELVER=${UNAME:0:3}
if [ "$KERNELVER" = "2.6" ]; then
#
# This is kernel-2.6.x
#
KSYMS=/proc/kallsyms
else
#
# This is kernel-2.4.x
#
KSYMS=/proc/ksyms
fi
然后, 把rc.sysinit文件中所有出现/proc/ksyms的地方都换成"$KSYMS"变量引用.
用Vi打开rc.sysinit 在命令模式下输入:s/\/proc\/ksyms/\$KSYMS/g 即可.
 
2.体验sysfs
   首先, 创建目录/sys:
# mkdir /sys
 然后, 按照下列步骤修改启动脚本/etc/rc.sysinit文件:
 找到行: mount -f /proc", 在下面增加:
 if [ "$KERNELVER" = "2.6" ]; then
mount -f /sys
fi

 找到行
 "Mounting local filesystems: " mount -a -t nonfs,smbfs,ncpfs -O no_netdev
在其下面增加下列几行:
 #
# Mount /sys for kernel-2.6.x
#
if [ "$KERNELVER" = "2.6" ]; then
action $"Mounting sysfs filesystem: " mount -n -t sysfs /sys /sys
fi
 
接下来, 在/etc/fstab文件中, 加入这样一行:
 none /sys sysfs defaults 0 0
启动的时候挂载sysfs文件系统.
 
最后, 修改/etc/init.d/halt脚本中的halt_get_remaining函数:
 将:
awk ’$2 ~ /^\/$|^\/proc|^\/dev/{next}
改为:
awk ’$2 ~ /^\/$|^\/proc|^\/sys|^\/dev/{next}

3. 支持USB设备
linux-2.6.x的USB驱动模块的名字已经改变了, 所以由此引起的问题多多……,
对应于USB-2.0的host控制器的内核模块名字仍然是ehci-hcd,
对应USB-1.1的host控制器的内核模块名字已经从usb-ohci改为ochi-hcd,
对应于通用USB host控制器的内核模块名字已经从usb-uhci该为uhci-hcd.
 
然而不幸的是, 在安装module-init-tools程序包时生成的/etc/modprobe.conf配置文件却仍然使用usb-ohci这个名字. 例如:
 
alias usb-controller usb-ohci
alias usb-controller1 ehci-hcd
 
因此, 要把它改为:
 
alias usb-controller ohci-hcd
alias usb-controller1 ehci-hcd
 
支持USB键盘的模块名字也从keybdev变为usbkbd, 支持USB鼠标的模块名字也从mousedev改为usbmouse.
 由于我用不到usb鼠标和usb键盘,所以干脆在/etc/rc.sysinit里面注释这两行:
action $"Initializing USB keyboard: " modprobe kenbdev 2> /dev/null
action $"Initializing USB mouse: " modprobe mousedev 2> /dev/null
当然如果你由usb鼠标和usb键盘的话,把模块名改了就可以了.
 
最后把/etc/rc.sysinit中的needusbstorage部分从:
 
needusbstorage=
if [ $usb = "1" ]; then
needusbstorage=`LC_ALL=C grep -e "^I.*Cls=08" /proc/bus/usb/devices 2>/dev/null`
LC_ALL=C grep ’hid’ /proc/bus/usb/drivers || action $"Initializing USB HID interface: " modprobe hid 2> /dev/null
fi
改为
needusbstorage=
if [ $usb = "1" ]; then
if [ "$KERNELVER" = "2.6" ]; then
needusbstorage=`LC_ALL=C grep -e "^I.*Cls=08" /sys/bus/usb/devices 2>/dev/null`
LC_ALL=C grep ’hid’ /sys/bus/usb/drivers || action $"Initializing USB HID interface: " modprobe hid 2> /dev/null
else
needusbstorage=`LC_ALL=C grep -e "^I.*Cls=08" /proc/bus/usb/devices 2>/dev/null`
LC_ALL=C grep ’hid’ /proc/bus/usb/drivers || action $"Initializing USB HID interface: " modprobe hid 2> /dev/null
fi
fi
4.Vmware tools
修改/usr/bin/vmware-config-tools.pl文件,把里面的/proc/ksyms改为/proc/kallsyms,然后重新运行vmware-config-tools.pl,按照提示往下运行即可.
注意网卡要想工作,需要vmxnet模块的支持,这个是由vmware-config-tools.pl生成的.
FAQ
       1. 进入2.6内核之后,安装rpm包提示
          rpmdb: unable to join the environment
error: db4 error(11) from dbenv->open: Resource temporarily unavailable
error: cannot open Packages index using db3 – Resource temporarily unavailabl
error: cannot open Packages database in /var/lib/rpm
no packages   
解决办法1:
可以升级RPM,比较麻烦.
解决方法2:
export  LD_ASSUME_KERNEL=2.2.5 ,网上都说这个有用,可是我这样之后提示:
error while loading shared libraries: libc.so.6:cann’t open shared object file:No such file or directory
解决方法3:
修改/usr/lib/rpm/macros
在里面寻找
代码:
%__dbi_cdb create cdb mpool mp_mmapsize=16Mb mp_size=1Mb
修改成
代码:
%__dbi_cdb create cdb mpool mp_mmapsize=16Mb mp_size=1Mb private
 

我通过方法3解决的.^_^

欢迎大家与我交流(mutecat@126.com)
 
 
 
 
      
 
 
2006年02月18日

1.引言

本文着重讲述如何制作基于linuxusb启动盘,此usb启动盘能够实现以下功能。

a.       usb启动盘的Linux内核usblinux.kernel应支持尽可能多的硬件(包括硬盘驱动,网卡驱动,usb驱动)

b.       u盘根文件系统放在ramdisk中,启动之后解压到ram中。

c.       usb启动盘启动之后能够在目标机上执行分区(fdisk),格式化(mkfs.*)以及在各种块设备上安装grub

d.       usb启动盘能够临时充当局域网内的网关。

e.       usb启动盘能够在一台机器上快速的搭建网关服务器。

2.定制操作系统所要考虑的事情

         不管是在u盘上面还是在其他存储设备上(硬盘,软盘)安装Linux,我们所需要考虑的事情基本一样。

       1)定制自己的内核(包括选定确当的内核版本,内核是否支持模块等等)

2)  定制自己的根文件系统(包括根文件系统的架构,根文件系统的内容,根文件系统所采用的文件系统类型等等)

3)  配备引导加载程序(bootloader

3.  UsbLinux 的系统架构

结合12我们现在来具体定制我们的UsbLinux

1 内核选用2.4.20-8custom(Redhat9自带的一个源码包),你当然可以选用其他的内核,2.2或者2.6的都可以,但并不是说版本越高就越稳定,这些关于不同内核版本之间的差异需要你平时多留心。内核的源码包可以从www.kernel.org上面取得,你也许应该尝试不同的版本并分别进行测试,因为这个过程有益无害。

2 内核支持模块加载功能,上面说了我们的内核应该尽可能支持多的硬件,如果我们把这些硬件的驱动全部静态编译进内核的话,内核将会变的非常庞大,这是我们不希望看到的,这不仅仅会减慢Usblinux启动的速度,而且我们不能动态的去除不必要的内核模块。

3 根文件系统选择ext2文件系统,前面说了我们的根文件系统是放在ramdisk中的,ramdisk正如其名,存在于ram中并且功能犹如块设备。因此也就决定了根文件系统里面的东西修改之后断电不能保存,ext2文件系统的特性主要包括可读写,可压缩,不具备断电可靠性,ext3reiserfs等日志文件系统是具备断电可靠性的,它们用在硬磁盘上比较合适。我们这里的根文件系统存放在ram中,因此选用ext2文件系统比较合适。

4 bootloader 选择grub,我们不是在搞嵌入式所以grub是一个非常好的选择。

 

说明:下面的所有操作都是在red hat linux  9里面完成的。

4.  编译安装内核

4.1. 选择配置内核方式

                      

 配置内核有多种方法,切换到源码目录下,

a. make oldconfigmake config(注,除非你很喜欢这种配置方式,否则建议你还是不要使用它们,它们不但不直观,而且配置容易出错)

b.make  menuconfig,这是最常用的一种配置方法,方便直观。如果没有其它特殊原因,建议你使用这种配置方法。

c.make  xconfig, 这个需要X窗口的支持,如果你喜欢这个配置方法也很方便

4.2. 内核配置

这当然是很重要的一个步骤了,关于配置内核的具体细节网上有很多这样的文章,这里只重点说以下几个重要的地方。

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,为什么呢? linuxusb设备是当成SCSI看待的,因要支持USB就要支持SCSI才行。具体的把下面这两个选项编译进内核就可以了,其它的基本上用不着。

        <*>SCSI Support

              <*>SCSI Disk Support

             另外为了在vmware上面测试,我把BusLogic这个驱动编译进了内核。

NetWork Devices Support

网络设备支持,我们基本上只需要选择Ethernet10 or 100Mbit)这里面的选项就可以了,其它的千M以太网卡,FDDI以及PPP之类的一般用的不多,当然还是要视具体情况而定。在10M或者100M以太网卡这里面,全部M吧,虽然有些现在基本上很少见了,但还是先M吧,防止有古董出现!

Charater Devices        字符设备支持,把Virtual  terminalsupport  console  on  virtual terminal Standard/generitic serial supportSupport for console on serial port这两项编译进内核就差不多了。

File Systems                   文件系统支持,由于我们使用的ext2文件系统,所以把Second  extended  fs support 这一项编译进内核。另外/proc文件系统也编译进内核,因为proc文件系统很有用。其余的ext3,fat,Vfatntfs M上吧,另外吧网络文件系统nfsM上吧,Partion types这里面的选择PC BIOS就可以了,Native Language Supportcodepage 437,936以及nls iso8859-1编译进内核,其它的就不用选了。

USB  Support               Usb设备支持,把UHCIOHCI  supportM上,另外USB Mass Storage Suppor   一定要选上,把它编译进内核或者M上都可以。最后吧USB Device  filesystems 给编译进内核,这会在/proc文件系统里面生成相应的选项,便于调试用。其它的声卡,红外线之类的设备都可以不选。

这里没有涉及的选项都是不常用的。

内核配置完毕,保存退出。

4.3. 建立依赖关系

这一步一个命令make dep就可以搞定。内核源码树中大多数文件都会与一些头文件有依赖关系,make dep期间会在内核源码树中每个子目录下面产生一个隐藏的.depend文件。此文件内包含子目录里面各文件所依赖的头文件清单。

4.4. 建立内核

make  bzImage   or   make zImage

注意bzImage和在zImage都是经gzip算法压缩过的内核映像,所不同的是在zImage的大小无法超过500KB,而bzImage则没有这个限制,如果映象建立过程中有错误,一个就是你的内核配置不正确,还有一个可能就是需要make mrproper来消除上一次的编译记录,make mrproper之后内核源码相当于刚安装时候的状态。

4.5. 建立和安装模块

make modules  & make modules_install

注意模块的默认安装位置是/lib/modules目录下,目录名即是你的内核版号,如果/lib/modules目录下有一个相同的内核版本号目录,如果很重要的话你最好把它备份,因为会被新的覆盖。至此内核以及相应的模块已经建立好了。

5.建立根文件系统 

5.1.根文件系统的基本结构

              首先建立一个roofs文件夹用来存放根文件系统的内容。

        #mkdir   /mnt/rootfs

              根文件系统的顶层目录: (摘自<<构建嵌入式LINUX系统>>  page 179)

           

目录

内容

bin

必要的用户命令(二进制文件)

boot

引导加载程序使用的静态文件

dev

设备文件和其它特殊文件

etc

系统配置文件,包括启动文件

home

用户主目录

lib

必要的连接库,例如C连接库,内核模块

mnt

安装点,用于暂时安装文件系统

opt

附加的软件套件

proc

用来提供内核与进程信息的虚拟文件系统

root

root用户的主目录

sbin

必要的系统管理员命令(二进制文件      

tmp

暂时性的文件

usr

在第二层包括对大多数用户都有用的大量应用程序和文件(包括库文件)

var

监控程序和工具程序所存放的可变数据

 

5.2. 建立UsbLinux根文件系统的基本结构

对于u盘上的linux或者做网关的linux来说,用户的权限问题并不复杂,我们只需要设置一个超级用户以及其它系统服务必要的帐户即可(比如ftpsshd等等),由于相当于是单用户,因此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}

一般二进制命令可以存放的目录包括binsbinusr/bin,usr/sbin,它们有什么区别呢?

二进制文件放在哪个目录,这与它在系统中所扮演的角色密切相关,如果这是普通用户和系统管理员必备的二进制文件(比如lsmkdir等),就会放在bin目录下,如果只是系统管理员所具有的一些特权指令(普通用户使用这些指令有限制,如ifconfig),那么它应该放在sbin目录下,usr/bin目录下存放的是普通用户和管理员"不常用"的命令,而usr/sbin目录下是管理员"不常用"的命令。

上面已经说过我们不需要区分普通用户和系统管理员(root),因此我们只需创建一个系统管理员帐号就可以了,但是这里为什么我们还要创建这四个目录呢? 是因为我们用到busybox套件,下面说明安装定制应用程序的时候会讲到。

下面我们把刚才建立的内核模块拷贝到lib目录下(注:以后我们所有的操作都是以/mnt/rootfs为根操作的)

#mkdir  lib/modules

#cp –dpR  /lib/modules/2.4.20-8custom    lib/modules

5.3. 选择链接库

     链接库是文件系统中一个非常重要的部分, 它也是整个根文件系统中最耗空间的一个部分。链接库是应用程序执行期间必不可少的一部分,当然如果你编译应用程序的时候库都是静态编译进去的,那么就不需要额外麻烦的自己建立链接库了,比如嵌入式系统中的uClinux,不过这个做法的缺点就是会额外的消耗存储空间。我们这里采用动态链接的方法。目前比较流行的链接库有glibcuClibcglibcgnuC链接库,一般的linux发型版都是使用的glibc库,而uClibc主要是针对嵌入式系统而定制的库,它的特点就是小。尽管uClibc也可以用在X86机器上,但它并不具有glibc的稳定性,一般在pc机上都是使用的glibc库。我们的目标机是pc机,所以我们选择glibc库,我们可以直接把宿主机上面的库文件拿来用。

5.4 建立设备文件

依照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 pseudotty 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

5.5. 定制应用程序。

        这个是个大头,但是由于一般的发行版自带的应用程序都很庞大,而且应用程序依赖的动态链接库也大的出奇,所以就有人想出很多办法,其中busybox在这方面做的非常的好,目前busybox在嵌入式方面应用非常广泛,BusyBox真如其名,它把所有的应用程序集成在一个命令里面,目前BusyBox支持unix/linux命令集里面的大部分命令,包括基本的命令cpcdchmodchowndatecatar等,编辑工具viawk等,网络工具ifconfignetstatroutetelnetftpgetftpputtelnetd等,模块工具lsmodinsmodrmmodmodprobe,压缩解压缩工具gzipgunziptarbzipbunzipzipunzip等,查找工具findgrepxargsinit工具initpoweroffhaltreboot,帐号密码管理工具addgroupdelgroupadduserdelusergettyloginshadow,进程相关工具freekillkillalltopps等,其它的lastfdiskdmesgswaponswapoff等等都有,另外,BusyBox还提供几个shell,我们一般用ash,它和bash非常的相似。

       下面说说如何使用busybox定制自己的应用程序。

5.5.1  busybox的配置

首先到网上下载一个稳定的BusyBox版本,我用的版本是busybox-1.0.1,然后解压到工作目录下。busybox的配置方法和标准的linux内核的配置方法非常相似,我们只要切换到busybox的根目录下,使用make menuconfig就会轻松定制出适合特定用途的一组应用程序。

#make menuconfig  (不知道的选项看看help就可以了)

#make   

#make install

注意busybox默认是安装到根目录下的_install文件文件夹。

#cd _install

#ls

你会发现bin目录下只有一个busybox二进制文件,其它的命令,binsbin以及

usr/binusr/sbin目录下的都是指向busybox的链接文件。牛逼吧,这么多命令一个应用程序就搞定了,我们看看busybox有多大

#du -k bin/busybox

420

这是我定制出来的busybox的大小,够小的吧。

5.5.2  安装busybox到文件系统

busybox的根目录下编译好二进制程序之后。

#make install  PREFIX=/mnt/roofs

这样,应用程序就会安装到指定位置,切换到/mnt/rootfs下看看,bin目录,sbin目录,usr/bin目录,usr/sbin目录下是不是出现了很多的应用程序。

busybox安装好之后,看看它依赖的动态链接库

#ldd  busybox

libm.so.6 => /lib/tls/libm.so.6 (0×40026000)

        libcrypt.so.1 => /lib/libcrypt.so.1 (0×40048000)

        libc.so.6 => /lib/tls/libc.so.6 (0×42000000)

        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0×40000000)

#cd  /mnt/rootfs

#cd  lib

#cp  -dpR  /lib/libc-2.3.2.so  ./

#cp  -dpR  /lib/libc.so.6     ./

#cp  -dpR  /lib/libm*       ./

#cp  -dpR  /lib/libcrypt-2.3.2.so  ./

#cp  -dpR  /lib/libcrypt.so.1   ./

#mkdir   tls

#ln  -s  libc-2.3.2.so  tls/libc.so.6

#ln  -s  libm-2.3.2.so  tls/libm.so.6   

5.5.3  busyboxinit

   除了基本的命令之外,BusyBox还支持init功能,如同其它的init一样,busyboxinit也是完成系统的初始化工作,关机前的工作等等,我们知道在Linux的内核被载入之后,机器就把控制权转交给内核,linux的内核启动之后,做了一些工作,然后找到根文件系统里面的init程序,并执行它,BusyBoxinit进程会依次进行以下工作:(参考<<构建嵌入式LINUX系统>> p201)

1.       init设置信号处理过程

2.       初始化控制台

3.       剖析/etc/inittab文件

4.       执行系统初始化命令行,缺省情况下会使用/etc/init.d/rcS

5.       执行所有导致init暂停的inittab命令(动作类型:wait

6.       执行所有仅执行一次的inittab(动作类型:once

一旦完成以上工作,init进程便会循环执行以下进程:

       1.  执行所有终止时必须重新启动的inittab命令(动作类型:once

       2.  执行所有终止时必须重新启动但启动前必须询问用户的inittab命令(动作类型:askfirst)

       初始化控制台之后,BusyBox会检查/etc/inittab文件是否存在,如果此文件不存在,BusyBox会使用缺省的inittab配置,它主要为系统重引导,系统挂起以及init重启动设置缺省的动作,此外它还会为四个虚拟控制台(tty1tty4)设置启动shell的动作。如果未建立这些设备文件,BusyBox会报错。

       inittab文件中每一行的格式如下所示:(busybox的根目录下的example文件夹下有详尽的inittab文件范例)     

       id:runlevel:action:process

尽管此格式与传统的Sytem V init类似,但是,idBusyBoxinit中具有不同的意义。对BusyBox而言,id用来指定启动进程的控制tty。如果所启动的进程并不是可以交互的shell,例如BusyBoxshash),应该会有个控制tty,如果控制tty不存在,Busyboxsh会报错。BusyBox将会完全忽略runlevel字段,所以空着它就行了,你也许会问既然没用保留着它干吗,我想大概是为了和传统的Sytem V init保持一致的格式吧。process字段用来指定所执行程式的路径,包括命令行选项。action字段用来指定下面表中8个可应用到process的动作之一。

动作

结果

sysinit

init提供初始化命令行的路径

respawn

每当相应的进程终止执行便会重新启动

askfirst

类似respawn,不过它的主要用途是减少系统上执行的终端应用程序的数量。它将会促使init在控制台上显示“Please press Enter to active this console”的信息,并在重新启动之前等待用户按下enter

wait

告诉init必须等到相应的进程完成之后才能继续执行

once

仅执行相应的进程一次,而且不会等待它完成

ctratldel

当按下Ctrl+Alt+Delete组合键时,执行相应的进程

shutdown

当系统关机时,执行相应的进程

restart

init重新启动时,执行相应的进程,通常此处所执行的进程就是init本身

 

以下是我的usblinuxinittab文件

::sysinit:/etc/init.d/rcS

::respawn:/sbin/getty  38400  tty1

tty2::askfirst:-/bin/sh

tty3::askfirst:-/bin/sh

::restart:/sbin/init

::ctrlaltdel:/bin/umount -a -r

这个inittab执行下列动作

1.       /etc/init.d/rcS设置成系统的初始化文件

2.       38400 bps的虚拟终端tty1上启动一个登陆会话 (注意getty的用法)

3.       在虚拟终端tty2tty3上启动askfirst动作的shell

4.       如果init重新启动,将/sbin/init设置成它会执行的程序

5.       告诉init,在系统关机的时候执行umount命令卸载所有文件系统,并且在卸载失败时用只读模式冲新安装以保护文件系统。

5.6  系统的配置文件

5.6.1 /etc/init.d/rcS文件

配置文件一般放在/etc/目录下,5.5.3节说了init初始化命令行的文件是/etc/init.d/rcS,下面是我的rcS文件

       #!/bin/sh

    /bin/mount -n -t proc  /proc  /proc

    /bin/mount -n -o  remount,rw  /

    /bin/mount -av

    /bin/hostname usblinux

    /etc/init.d/rc.nic

    /etc/init.d/rc.network

    /etc/init.d/rc.usb

    /etc/init.d/rc.local

1.       安装proc文件系统,但是不写入/etc/mtab文件(mtab文件是df命令读取的文件)

2.       以读写模式重新挂载根文件系统(需要/etc/fstab文件)

3.       mount -av 挂载所有在/etc/fstab里面列出的文件系统

4.       主机名命名为usblinux

5.       依次执行rc.nic(自动检测网卡),rc.network(配置网络),rc.usb(加载usb模块),rc.local(执行本地的一些服务)

5.6.2 /etc/fstab

上面使用mount命令的时候已经涉及到了fstab文件,下面是我的fstab文件

/dev/ram0       /       ext2    defaults    0   0

proc            /proc   proc    defaults    0   0

关于fstab配置文件的语法可以man fstab

其实rcS里面的mount -av没有必要加上去。

5.6.3 /etc/mtab

这个文件里面的内容是动态变化的,当mount一个文件系统的时候,如果mount 没有加上-n参数,那么安装信息就会写入mtab文件,df命令读取的就是这个文件。

5.6.4 /etc/profile

这个文件是sh用的,当用户获得一个shell后,sh就会根据这个文件配置用户的登陆环境,下面是我的profile文件。

PATH=/bin:/sbin:/usr/bin:/usr/sbin

PS1=’[\u@\h \W]\$ ‘

HOSTNAME=’/bin/hostname’

export PATH HOSTNAME PS1

alias l.=’ls -d .[a-zA-Z]* –color=tty’

alias rm=’rm -i’

alias cp=’cp -i’

alias mv=’mv -i’

 export PATH=/usr/local/bin:$PATH

export PATH=/usr/local/sbin:$PATH

     

其中PATH环境变量指定当用户键入一个命令时,sh寻找这个命令的路径。

PS1指定sh提示符的格式

其它的export命令,alias命令不用说了吧,busybox里面的ashbash非常相似,因此只要熟悉bash就会不成问题。

5.6.5 /etc/ld.so.conf /etc/ld.so.cache

这两个文件是与lib库有关的,它们用来指定应用程序寻找lib库的路径。

我的ld.so.conf文件内容如下。

/lib

/usr/lib

/usr/local/lib

ld.so.cache里面的内容是由是ldconfig命令生成的,ldconfig根据ld.so.conf配置文件生成ld.so.cache里面的内容,应用程序读取的是ld.so.cache文件,因此如果变动了ld.so.conf文件,需要运行一次ldconfig才能是新的配置生效,另外如果新加了lib库文件,也需要ldconfig

5.6.6   /etc/passwd,/etc/shadow,/etc/group

group里面存放的是用户组的信息。

我的group文件内容如下:

root:x:0:root

ftp:x:50:

passwd里面存放的是用户的信息

我的passwd文件内容如下:

root:x:0:0:root:/root:/bin/sh

ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

sshd:x:74:74:piviledge-separated SSH:/var/empty/sshd:/sbin/nologin

注意,grouppasswd里面的每一行都不是多余的。

shadow文件是pam认证用到的密码存放文件

5.6.7.   /etc/services

网络应用程序用到的标准服务端口映射表

我的services文件如下:

tcpmux    1/tcp

tcpmux    1/udp

ftp-data  20/tcp

ftp       21/tcp

ssh      22/tcp

ssh      22/udp

telnet    23/tcp

nameserver  42/tcp   name

 syslog    514/udp

auth      113/tcp  authentication  tap ident

因为我需要开启sshdsyslogd服务,同时会使用ftpsshtelnet应用程序。所以就会加上上面的东西,但是注意并不是所有的网络应用程序都会依赖与services文件,比如telnet,但是也许telnetd需要。

5.6.8.   /etc/nsswitch.conf

这个文件的作用很大,没有它很多程序都不能正常运行,关于它的具体作用还是用man  nsswitch.conf看看吧。

我的nsswitch.conf文件内容如下:

passwd:     files

shadow:     files

group:      files

hosts:      files dns

ethers:     files

netmasks:   files

networks:   files

protocols:  files

services:   files

netgroup:   files

publickey:  files

automount:  files

aliases:    files

注意hosts这一行一定要加上dns,不然即使有resolv.conf文件,域名也不能解析,注意nsswitch.conf是对应一组lib库的,这里我们用了filesdns,因此需要拷贝相应的lib库到/lib目录下.

#cd  /mnt/rootfs

#cp -dpR  /lib/libnss_files*   ./lib

#cp -dpR  /lib/libnss_dns*   ./lib

5.6.9 .  /etc/hosts,/etc/ host.conf, /etc/resolv.conf

       我的hosts文件内容如下,不用说也应该清楚什么意思了,不清楚的man hosts

       127.0.0.1   localhost:localdomain   localhost

       host.confresolv.conf是域名解析的时候用到的配置文件,其中resolv.conf里面是域名服务器的ip地址,host.conf里面放的是主机的查找规则。不了解的还是man host.confman  resolv.conf

我的host.confresolv.conf文件内容分别如下:

#cat  /mnt/rootfs/etc/host.conf

order hosts,bind

#cat  /mnt/rootfs/etc/resolv.conf

nameserver 202.112.20.131

5.6.10.  /etc/syslog.conf

syslogd服务程序读取的配置文件。

我的文件内容如下:

# Log all kernel messages to the console.

# Logging much else clutters up the screen.

#kern.*                                                 /dev/console

 

# Log anything (except mail) of level info or higher.

# Don’t log private authentication messages!

*.info;mail.none;authpriv.none;cron.none                /var/log/messages

 

# The authpriv file has restricted access.

authpriv.*                                              /var/log/secure

 

 

# Save boot messages also to boot.log

local7.*                                                /var/log/boot.log

 

5.7 系统初始化的脚本

       上面说到rcS文件时,涉及到了/etc/init.d/rc.nic,/etc/init.d/rc.network,/etc/init.d/rc.usb,/etc/init.d/rc.local四个初始化脚本文件。下面来说说它们的作用。

5.7.1   /etc/init.d/rc.nic

此文件用来自动加载网卡对应的模块(驱动),并且删除没有用到的网卡驱动模块。

#!/bin/sh

#

# detect network cards shell 

#

dmesg  -n 1  #prevent  the messages from appearing on the console

ERR_DEV=/dev/null                # redirect verbose messages

NetcardPath=/lib/modules/2.4.20-8custom/kernel/drivers/net/

loaded=4  #the kernel already detected cardyou may want to change it

           #according to file `/proc/net/dev`

# invoked by the init script or by the user?

[ "`basename $0`" = "rc.nic" ] && MODE=init || MODE=user

 

# number of NICs already detected

NICs=$((`cat /proc/net/dev | wc -l` -$loaded))

 

# when init, skip auto probing if we already found some NIC(s)

if [ $NICs -ge 1 -a "$MODE" = "init" ]; then

  echo "Found $NICs network card(s). Skip auto probing …"

  exit 0

fi

 

# official NIC modules

NET_MODs=`find /lib/modules/\`uname -r\`/kernel/drivers/net/    \

                -type f -printf "%f\n" 2>$ERR_DEV               \

        | cut -d "." -f1`

# save the original dmesg messages first

[ -e /var/log/dmesg ] || dmesg > /var/log/dmesg

 

echo "Probing network cards … "

CUR_NICs=$NICs

for i in $NET_MODs ; do

  ORG_MODs=`cat /proc/modules | wc -l`

  ORG_NICs=$CUR_NICs

 

  echo -en "\r\t$i        \t"

  #if  modprobe fail we think that this module is not needed

  #so we delete it to save the space

  if ! modprobe $i 1>$ERR_DEV 2>$ERR_DEV

  then

        temp=`find  $NetcardPath  -name $i.o`

        if [ -f $temp ]

        then

                rm  -rf $temp

        fi

  fi

 

  CUR_MODs=`cat /proc/modules | wc -l`

  CUR_NICs=$((`cat /proc/net/dev | wc -l` -$loaded))

 

  if [ $CUR_NICs -eq $ORG_NICs ]; then  

if [ $CUR_MODs -gt $ORG_MODs ]; then  #it  is not a netcard module

                                                                             #so we delete it     

      RMMODs=`head -$(($CUR_MODs-$ORG_MODs)) /proc/modules | cut -d" " -f1`

      for m in $RMMODs ; do

        rmmod $m 1>$ERR_DEV 2>$ERR_DEV

      done

    fi

  else

    echo " ($(($CUR_NICs-$ORG_NICs)))"

  fi

done

echo -e "\r                       \rFound $(($CUR_NICs-$NICs)) card(s), done."

rmmod -as

exit $(($CUR_NICs-$NICs))

看懂这个脚本就需要你熟悉shell编程了。由于比较大,我这里不作解释。

这个脚本的内容大部分是从cdlinux上面copy过来的,但是对其进行了修改,其中最大的修改就是添加了删除没有用到的模块的功能,这主要是为了节省空间所用。

      

5.7.2   /etc/init.d/rc.network

       这个脚本主要是初始化网络配置,

#!/bin/sh

#/sbin/ifconfig eth1    218.199.20.98  up

/sbin/ifconfig eth0    192.168.0.3   up

/sbin/ifconfig lo      127.0.0.1     up

route add default gw  192.168.0.1  dev eth0

这个脚本需要根据不同的环境进行修改。接触过linux的人相信这个不难看懂。

5.7.3   /etc/init.d/rc.usb

加载usb驱动模块,当然如果你的内核是静态编译进usb模块的,那就没有必要在这里多此一举了,不过如果我们要做个网关服务器,还是把它做成动态的模块比较好,因为网关服务器基本上不需要用到u盘,我们大可删除掉usb驱动模块,以节省空间。我们之所以需要usb的驱动模块,不要忘了我们的usbinux是放在u盘上面运行的。当然并不是说要在u盘上面运行linux,一定需要内核支持usb才行,不管是硬盘,u盘,还是软盘都只是个载体,第一章已经说了,我们的文件系统是在ramdisk中的,因此只要内核和文件系统被载入内存,我们就不再需要载体(存储设备)。但是我们很多东西可以放在u盘上面,以节省ramdisk的存储空间,所以还是需要内核识别u盘,等系统启动之后再把需要的东西从u盘拷贝到ram里面。

   rc.usb内容如下: 

#!/bin/sh

##            This script is to initilize usb controller and

##                  The driver module usb-storage

##            To use usb under linux the module usbcore,scsi_mod

##                  and sd_mod are needed,in this

##                  system they are complied in kernel

 

#/sbin/usb is the small script to start or stop  usb support

/sbin/usb start

#script  rc.usb  end

这个脚本里面用到了/sbin/usb这个程序,其实并不是一个真正的程序,它是我写的一个加载和卸载usb模块的shell脚本。/sbin/usb脚本内容如下:

#!/bin/sh

#

#A simple startup script to start usb for linux

#

case "$1" in

start)

         /sbin/modprobe usbcore

              /sbin/modprobe usb-uhci

              /sbin/modprobe usb-storage

              ;;

stop)

      /sbin/rmmod    usbcore

              /sbin/rmmod    usb-uhci

              /sbin/rmmod    usb-storage

              ;;

        *)

               echo "Usage: $0 {start|stop}"

               exit 1

        esac

#script /sbin/usb  end

我之所以写了这个usb脚本,是因为我发现,启动usblinux后,当机子用过一个u盘时,即使这个uumount掉了,再使用另外一个u盘的时候就会出现问题。也许是我的哪边配置有误,不过不管怎么说,这个脚本还是有用的。

注意:虽然上面只列出了,usbcoreusb-uhci,usb-storage三个模块,要能够成功挂载u盘,还需要scsi的支持,也就是需要scsi_mod.o  sd_mode.o两个模块,我的内核是把这两个模块静态编译进内核的,因此也就不需要再手动加载了。我之所以把对scsi支持的功能静态编译进内核,是为了测试方便的原故,我测试工具使用的是vmware虚拟机,因为vmware基本上使用scsi硬盘,为了避免添加linuxrc文件,我把vmware的硬盘驱动BusLogic.o静态编译进了内核,所以上面的scsi的两个模块也要编译进内核。

5.7.4   /etc/init.d/rc.local

这个shell脚本没有什么好说的,主要是初始化本地的一些服务,比如sshdsyslogd等等

我的rc.local脚本内容如下:

    #/bin/sh

       #/sbin/sshd

/sbin/syslogd

在本地启动syslogd服务。

5.8  添加额外的应用程序

5.8.1  前言

       虽然busybox支持大多数应用程序,但是往往我们需要的程序它并不支持,这时候我们就只有copy我们的宿主机上的了,第一章说了,我们的usblinux需要能够fdisk存储设备,能够mkfs文件系统,能够使用grub安装grub到指定存储设备。busyboxfdisk,但是mkfs.*在我使用的busybox版本里面还不支持,另外ldconfig这个必须的应用程序busybox里面好像也没有。另外我们也许想在usblinux里面临时使用一下ftp服务器,用来传输文件用,调试的时候strace是必不可少的工具,加进硬盘检测工具smartmontools也是不错的主意,充当网关服务器sshd必不可少吧,虽然可以用其它的比如telnetd等代替,但是它们并没有sshd来的安全,因此我还是选择了sshd

       上面说了这么多的应用程序,如果不采取任何措施直接copyrootfs里面的话,可想而知,根文件系统的大小会相当大,sshd一个就会净增文件系统3M多的空间。因此我们必须想出一个好办法来处理这个问题。

5.8.2  如何节省ramdisk空间?

         有些应用程序譬如sshdmkfs*,grub,strace等等,我们平时很少用到,因此如果我们能想出一个办法需要这个应用程序的时候调度出来,不需要的时候把它删掉,那就解决了问题。方法也许有很多,我使用的方法是把这些应用程序及其依赖的库文件压缩,然后把它们的压缩文件安装到opt目录下,需要的时候解压就行了,不需要的时候把解压出来的文件删除掉。我opt目录文件内容如下:

   #ls  opt

   ldconfig.tar.gz  rz.tar.gz             strace.tar.gz

   grub.tar.gz      mkfs.tar.gz      smartmontools.tar.gz

   iptables.tar.gz  pureftpd.tar.gz  sshd.tar.gz

   我之所以把ldconfig压缩,是因为ldconfig536k之大,rz是学校校园上网认证的一个程序,strace是个调试程序,grubmkfs不用说了吧,smartmontools是硬盘坏道检测工具,iptables做网关服务器所必须的,pureftpd一个很方便的ftp服务器程序,sshd远程登陆工具,包括服务端和客户端的。

5.8.3  调入调出的shell脚本

下面使用两个脚本来调入和调出/opt下的指定应用程序。

1.       调入脚本expand

 #!/bin/sh

TOOL=$1

if [ ! -f /opt/$TOOL.tar.gz ]

then

        echo "No such tool!"

        exit

fi

mkdir /tmp/tool

cp  /opt/$TOOL.tar.gz    /tmp/tool

cd  /tmp/tool

tar  zxf  $TOOL.tar.gz

rm -rf $TOOL.tar.gz

cp  -dpR  /tmp/tool/$TOOL/etc/*    /usr/local/etc   2>/dev/null

cp  -dpR  /tmp/tool/$TOOL/sbin/*   /usr/local/sbin  2>/dev/null

cp  -dpR  /tmp/tool/$TOOL/bin/*   /usr/local/bin    2>/dev/null

cp  -dpR  /tmp/tool/$TOOL/lib/*   /usr/local/lib    2>/dev/null

rm -rf /tmp/tool

echo  "done"

2.       调出脚本/sbin/depand

#!/bin/sh

path=/usr/local

case  $1  in

"ldconfig")

        rm -rf $path/sbin/ldconfig

        echo  "done";;

"grub")

        rm -rf  $path/sbin/grub

        echo  "done";;

"smartmontools")

        rm -rf  $path/sbin/smartctl

        echo  "done";;

"strace")

        rm  -rf $path/bin/strace

        echo  "done";;

"rz")

        rm -rf  $path/bin/rz

        rm -rf  $path/etc/rz.conf

        rm -rf  $path/lib/libpcap*

        echo  "done";;

"mkfs")

        rm -rf  $path/sbin/mkfs*

        rm -rf  $path/lib/libext2*

        rm -rf  $path/lib/libe2p*

        rm -rf  $path/lib/libcom_err*

        rm -rf  $path/lib/libuuid*

        echo  "done";;

"pureftpd")

        rm -rf  $path/sbin/*pure*

        echo "done";;

*)

        echo  "this tool is not installed!"

esac

 

譬如要调入grub这个工具,只需要在命令行敲入expand grub即可,使用完grub之后,只要depand grub就可释放grub占有的ram空间。

(注意:expanddepand并不适用与iptablessshd这两个应用程序,对它们我们需要额外编写脚本,因此expand应该加上对这两个程序的拒绝执行动作,但是我这里没有加,为了安全起见,建议还是应该加上)

5.8.4  opt目录下的应用程序压缩文件制作原则

       opt目录下的压缩文件并不是随便做的,我们必须遵循一定的规范,下面我们以制作mkfs工具来说明制作过程。

#cd  /mnt/rootfs/opt

#mkdir mkfs

#cd  mkfs

#mkdir bin sbin etc lib

#cp  -dpR  /lib/libext2fs.so.2*   ./lib

#cp  -dpR  /lib/libe2p.so.2*     ./lib

#cp  -dpR  /lib/libuuid.so.1*    ./lib

#cp  -dpR  /lib/libcom_err.so.2* ./lib

#cp  -dpR  /sbin/mkfs*        ./sbin

#cd  ..

#tar  zcf  mkfs.tar.gz  mkfs

#rm -rf mkfs

至此一个mkfs的压缩文件mkfs.tar.gz就做好了。

通过这个过程再配备上面的expand脚本你应该知道制作的规则了吧。  

下面用目录树了来表示这个架构:

 

                        $TOOL

                  

bin        sbin       lib       etc

5.8.5  sshd.tar.gz的制作

sshd以及ssh这两个程序依赖的库文件比较多,而且涉及的其它文件也比较多。然而它却是网关服务器所必不可少的。

要正确运行sshd要做以下几件事情

1.       安装sshd所依赖的库文件

2.       安装/lib/security目录

3.       安装/etc/pam.d文件夹

4.       安装/etc/ssh文件夹

5.       创建sshd帐号

6.       创建/var/empty/sshd目录

  下面是整个流程

  #cd   opt

  #mkdir sshd

  #cd  sshd

  #mkdir bin etc lib

  #cp  /usr/bin/ssh    bin

  #cp  /usr/sbin/sshd  bin

  #ldd  /usr/sbin/sshd

        libwrap.so.0 => /usr/lib/libwrap.so.0 (0×40026000)

        libpam.so.0 => /lib/libpam.so.0 (0x4002f000)

        libdl.so.2 => /lib/libdl.so.2 (0×40037000)

        libresolv.so.2 => /lib/libresolv.so.2 (0x4003a000)

        libutil.so.1 => /lib/libutil.so.1 (0x4004c000)

        libz.so.1 => /usr/lib/libz.so.1 (0x4004f000)

        libnsl.so.1 => /lib/libnsl.so.1 (0×4005e000)

        libcrypto.so.4 => /lib/libcrypto.so.4 (0×40073000)

        libkrb5.so.3 => /usr/kerberos/lib/libkrb5.so.3 (0×40164000)

        libk5crypto.so.3 => /usr/kerberos/lib/libk5crypto.so.3 (0x401c2000)

        libcom_err.so.3 => /usr/kerberos/lib/libcom_err.so.3 (0×401d2000)

        libc.so.6 => /lib/tls/libc.so.6 (0×42000000)

        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0×40000000)

        libgssapi_krb5.so.2 => /usr/kerberos/lib/libgssapi_krb5.so.2 (0×401d5000)

  (把这里面列出的库文件拷贝到lib目录下,注意拷贝的时候加上dpR参数,把原文件和链接文件全部拷贝过来)

  #cp  -dpR   /lib/security  ./ (拷贝pam认证时需要的库文件)

  #cp -R  /etc/ssh   etc/  (拷贝ssh以及sshd的配置文件)

  #mkdir   etc/pam.d

  #cp  /etc/pam.d/system-auth   etc/pam.d

  #cp  /etc/pam.d/sshd           etc/pam.d

  (把宿主机上的这两个文件拷贝过来,然后加以适当的修改,修改之后两个文件内容如下)

  #cat  etc/pam.d/system-auth

#%PAM-1.0

# This file is auto-generated.

# User changes will be destroyed the next time authconfig is run.

auth        required      /lib/security/pam_env.so

auth        sufficient    /lib/security/pam_unix.so likeauth nullok

auth        required      /lib/security/pam_deny.so

 

account     required      /lib/security/pam_unix.so

 

password    required      /lib/security/pam_cracklib.so retry=3 type=

password    sufficient    /lib/security/pam_unix.so nullok use_authtok md5 shadow

password    required      /lib/security/pam_deny.so

 

session     required      /lib/security/pam_limits.so

session     required      /lib/security/pam_unix.so

#cat  etc/pam.d/sshd

#%PAM-1.0

#auth       required    /lib/security/pam_securetty.so

auth       required     /lib/security/pam_stack.so service=system-auth

auth       required     /lib/security/pam_nologin.so

account    required     /lib/security/pam_stack.so service=system-auth

password   required     /lib/security/pam_stack.so service=system-auth

session    required     /lib/security/pam_stack.so service=system-auth

session    optional     /lib/security/pam_console.so

(根据这两个文件所用到的/lib/security目录下的库文件,对上面拷贝过来的库文件做适当的裁减,下面是我裁减了的security下的库文件列表)

#ls -l  security

-rwxr-xr-x  1 root root  9684 12 19 15:21 pam_access.so

-rwxr-xr-x  1 root root 47584 12 19 15:21 pam_console.so

-rwxr-xr-x  1 root root 12936 12 19 15:21 pam_cracklib.so

-rwxr-xr-x  1 root root  3404 12 19 15:21 pam_deny.so

-rwxr-xr-x  1 root root 11592 12 19 15:21 pam_env.so

-rwxr-xr-x  1 root root  8468 12 19 15:21 pam_lastlog.so

-rwxr-xr-x  1 root root 12324 12 19 15:21 pam_limits.so

-rwxr-xr-x  1 root root  4856 12 19 15:21 pam_nologin.so

-rwxr-xr-x  1 root root  3708 12 19 15:21 pam_permit.so

-rwxr-xr-x  1 root root  6544 12 19 15:21 pam_securetty.so

-rwxr-xr-x  1 root root 11132 12 19 15:21 pam_stack.so

lrwxrwxrwx  1 root root    11 12 25 20:10 pam_unix_acct.so -> pam_unix.so

lrwxrwxrwx  1 root root    11 12 25 20:10 pam_unix_auth.so -> pam_unix.so

lrwxrwxrwx  1 root root    11 12 25 20:10 pam_unix_passwd.so -> pam_unix.so

lrwxrwxrwx  1 root root    11 12 25 20:10 pam_unix_session.so -> pam_unix.so

-rwxr-xr-x  1 root root 48520 12 19 15:21 pam_unix.so

(注意就是动态链接库,它也有一定的依赖关系,因此最好对lib/security目录下的库文件依次检查其依赖关系,然后检查/mnt/rootfs/opt/sshd/lib目录下是否有其缺少的库文件,如果缺少的需要拷贝相应的动态链接库到/mnt/rootfs/opt/sshd/lib目录下.)

 

到这里,前4步已经做完了,第5步已经在前面的配置文件一节做完了。最后一步

#mkdir -p  /mnt/rootfs/var/empty/sshd

下面我们把opt目录下的sshd文件夹打包压缩。

#cd  /mnt/rootfs/opt/

#tar   zcf  sshd.tar.gz   sshd

#rm  -rf  sshd

 

到这里sshd工具就做完了,下面我们写个shell脚本,来控制sshd的调入与调出

脚本放在/sbin目录下,脚本文件名为ssh_sv,内容如下:

 

#!/bin/sh

case "$1" in

        start)

        ssh_sv  stop

        cd  /tmp

        tar zxf /opt/sshd.tar.gz

        cd  sshd

        mv  etc/*  /etc

        mv  bin/ssh  /usr/bin

        mv  bin/sshd /sbin/sshd

        mv  lib/*    /usr/local/lib

        mv  security  /lib

        rm -rf  /tmp/*

        expand  ldconfig

        ldconfig

        depand  ldconfig

        echo done;;

        stop)

 

        rm -rf /lib/security

        rm -rf /etc/ssh

        rm -rf /etc/pam.d

        rm -rf /usr/bin/ssh

        rm -rf /sbin/sshd

 

        for  i  in  libcom_err.so  libcom_err.so.3 libcom_err.so.3.0  \

                libcrack.so libcrack.so.2  libcrack.so.2.7 \

                libcrypto.so.0.9.7a libcrypto.so.4 libglib-1.2.so.0 \

libglib-1.2.so.0.0.10 libglib-2.0.so.0 libglib-2.0.so.0.200.1  \                  

libgssapi_krb5.so libgssapi_krb5.so.2 libgssapi_krb5.so.2.2   \

                libk5crypto.so libk5crypto.so.3 libk5crypto.so.3.0  \

                libkrb5.so libkrb5.so.3  libkrb5.so.3.1 libnsl-2.3.2.so \

                libnsl.so.1 libpam.so.0 libpam.so.0.75 libutil-2.3.2.so \

                libutil.so libutil.so.1 libwrap.so libwrap.so.0 \

                libwrap.so.0.7.6  libz.so libz.so.1  libz.so.1.1.4

        do

                rm -rf  /usr/local/lib/$i

        done

        ;;

        *)

                echo "Usage: $0 {start|stop}"

                exit 1

esac

 

我们只要使用ssh_sv  start 便可调入sshdssh,使用ssh_sv  stop便可以调出sshdssh

5.8.6  iptables.tar.gz的制作

iptables.tar.gz 是我们网关服务器最重要的一个压缩包,我们的网关服务器的防火墙的配置,以及端口的映射等等都是通过iptables配置的。

#cd opt

#mkdir   iptables

#cd   iptables

#mkdir  lib   sbin   etc

#cp  -dpR   /lib/iptables   ./lib

#cp  /sbin/iptables  ./sbin

#touch  etc/init.d/rc.firewall

rc.firewall为网关服务器的防火墙以及端口映射配置脚本。

我贴一个例子到这里,关于iptables的使用方法可参考相关文档。

echo 1 > /proc/sys/net/ipv4/ip_forward

/sbin/modprobe ip_conntrack

/sbin/modprobe ip_conntrack_ftp

/sbin/modprobe ip_nat_ftp

/sbin/iptables -F INPUT

/sbin/iptables -F OUTPUT

/sbin/iptables -F FORWARD

/sbin/iptables -t nat -F POSTROUTING

/sbin/iptables -t nat -F PREROUTING

/sbin/iptables -t nat -A POSTROUTING -o eth1 -s 192.168.0.0/24 -j MASQUERADE

/sbin/iptables -t nat -A PREROUTING -i eth1 -p tcp –dport 80 -j DNAT –to-destination 192.168.0.1:80

我只解释前四句,其它的可以man  iptables

第一句打开内核的ip转发功能

后三句加载必要的模块,当然如果这些模块是静态编译进内核的就不需要手动加载了。

#cd  ..

#tar  zcf  iptables.tar.gz  iptables

#rm  -rf  iptables

调入和调出iptables的脚本我就不说了,由前面的说明你应该能够轻松的写一个脚本出来。

5.9 根文件系统里面的最后一些东东

#cd  var 

#ls

empty  lock  log  run

#touch  run/utmp   (who命令读取的文件)

#touch  log/wtmp   last命令用到的配置文件)

#touch  log/lastlog   (sshd开启登陆会话时读取的文件)

#cd  .. 

#mkdir  -p   usr/share/terminfo/l

#cp  /usr/share/terminfo/l/linux  usr/share/terminfo/l 

(linuxgrub成功启动所需要的终端)

6.创建ramdisk

5章已经把根文件系统里面的内容做完了,现在我们建立ramdisk来存储根文件系统。

#du  -ks  rootfs

8356

我们的rootfs8M多的大小,但是注意我们这里还有很多的压缩文件,特别是sshd解压会有3M多的大小,但是同时/lib/modules下的网卡驱动在系统启动后没有用到的会被删除。大概会减小1M多的空间,因此我们创建一个12Mramdisk足以对付一切。

 #cd  /mnt

 #dd  if=/dev/zero  of=usblinux  bs=1M  count=12

 (前面提到让ramdisk是虚拟的ram盘,它是在内存中虚拟出一块硬盘,因此我们需要对其进行格式化)

 #mkfs.ext2  -m  0  usblinux 

 #mkdir  usb

 #mount  -o loop  usblinux usb

 #cp -dpR  /mnt/rootfs/*  ./usb

 #umount  usb

 #gzip  -v9  usblinux

7 . 安装kernel ramdiskgrubu

要想从U盘启动linux,可能需要把U盘格式化成启动盘,这里我们不关心这个,我们已经假定你的U盘可以以USB-HDD或者USB-ZIP方式启动。

插上准备好的U

#mount /dev/sda1    /mnt/usb  (我这里的u盘设备是/dev/sda1,这个可以用fdisk l命令查看)

#cd  /mnt/usb

#mkdir boot/grub

#cp /usr/src/linux-2.4/arch/i386/boot/bzImage   boot/usblinux.kernel

(把做好的内核拷贝到U盘上)

#cp  /mnt/usblinux.gz   boot/usblinux.img

(把做好的文件系统安装到U盘上)

#cp  /boot/grub/{stage1,stage2}    boot/grub/

(u盘上安装grub需要用到的文件)

#vi  boot/grub/grub.conf

default=0

timeout=10

title  UsbLinux

   root(hd0,0)

   kernel /boot/usblinux.kernel  ramdisk_size=13000 ro root=/dev/ram0

   initrd  /boot/usblinux.img

#cd ; umount  /mnt/usb

最后一步就是安装grubu盘上了。

#grub

 GNU GRUB  version 0.95  (640K lower / 3072K upper memory)

      [ Minimal BASH-like line editing is supported.  For the first word, TAB

 lists possible command completions.  Anywhere else TAB lists the   

 Possible completions of a device/filename.]

grub>root hd1,0)    (注,u盘所在的设备号,不同的系统可能不一样,用fdisk -l先看看,不要搞错了,另外,注意设备的命名规则)

grub>setup  (hd1,0)

grub>quit

#

(注:关于grub的安装及其使用可以参考grub手册

http://www.gnu.org/software/grub/manual/grub.html#initrd

 

到此为止,应该说我们的目的基本完成了,不过启动的时候可能会出问题,请查看faq

8Faq

   1. 为什么我的usblinux启动后,到uncompressing usblinux.kernel…………booting the kernel 之后就没有反应了呢?

   答:这种情况我碰到的有两个可能的原因

1)  内核的cpu型号选择错误,譬如如果选择athoncpu,那么在奔4的或者赛杨的机器上跑就会出现这种情况。我们的内核cpu型号选择i386就可以避免这种情况

2)  内核的Charater devices这一项里面没有把Virtual terminal 以及support console on virtual terminal两个选项编译进内核。

   2. 内核为什么么启动到Free unused kernel memory *k,然后就没有反应了?

   答:如果cpu选择386的话,而lib库还是使用的宿主机上的686lib库,那么就会出现这种情况,在red hat 9glibc库有386的版本和686的版本,我们需要从光盘上取出386的版本,然后用windows 下面的7zip工具把里面的相关库文件去出来,把我们的usblinux文件系统里面对应的库文件换掉即可。

   3. 为什么busyboxinit启动之后,在启动getty的终端输入root密码总是提示出错?

   答:busyboxlogin/passwd认证有两种方法,一个是使用它内部的认证方法,这是无需nsswitch.conf的支持,另外一种就是使用linux系统的认证,这时需要nsswitch.conf文件。我们的密码一般放在/etc/shadow文件里面,但是我两种方法试了都不行,也让busybox支持了shadow,后来用askfirst动作得到一个shell进去之后,用passwd修改密码才发现它的密码是存在/etc/passwd里面的。

     具体的修改密码后passwd变成下面这样的格式

     root::$1$ccZny60u$o4MBUmzFjENUGWlVPKbTp.:0:0:root:/root:/bin/sh

        /etc/shadow文件里面的内容根本没有任何变化。

下面你知道怎么解决这个问题了吧。

   4. 为什么我开启sshd服务后,远程登陆输入密码后没有反映了呢?

ssh远程登陆到主机,需要主机的/dev/目录下有ptyp*设备以及ttyp*设备,当然如果内核支持pts文件系统,那么也可以使用pts/* 设备。

如果内核没有支持pts,或者pts文件系统没有正确安装,那么登陆会话就会使用ttyp*终端。但是如果没有ptyp*设备,就会出现上面的情况。

发信站: 武汉白云黄鹤站 (2003年01月14日15:36:07 星期二), 站内信件 

小弟近日对Linux进行了小小的裁减,偶有心得,愿大家共享

说到裁减Linux,无非是为了减小磁盘占用或者是为了某些特定场合的应用(如嵌入式系统)。以RedHat 7.3为例,其最小安装仍然达到了300M,这不得不让人对一直号称小而全的Linux系统感到疑惑。

作为自己手中课题的一个铺垫,不久前我尝试了对Linux进行裁减,虽然没有达到预期的一张软盘大小,但结果也相当有吸引力。下面我对此一一做说明。

参考文档:
Linux bootdisk-HOWTO: 
    http://www.linux.org.tw/CLDP/gb/Bootdisk-HOWTO.html

initrd introduce on Linux system: 
   /usr/src/linux-2.4/Documentation/initrd.txt

裁减Linux一般有两种办法,其一是重新生成kernel和文件系统,其二是在原有的系统上删除不必要的文件缩小“体积”

对从一个完整的RedHat 7.3版本而言,其最小安装也有300M,因此,第二个方法是不太现实的。于是重构文件系统和kernel成为了必然。

裁减目标:构成一最小Linux系统担负实验室网关工作,系统载体为硬盘,运行使用RAMDISK,从而减小意外断电造成的文件系统修整消耗提高系统可靠性。

目标平台:P2-400,8G/64M,8139LAN adapt x 2

首先裁减kernel,既然是最小系统,则kernel里所有必须的部件都将直接编译进入内核。但是内核对module的支持需要保留。 

如何编译内核,不再累述,具体说说哪些选项先:
Code maturity level options —> 不选
Loadable module support —> 当中的Set version….的可以不要,其他两个留着
Processor type and features —> 按照目标系统选择对应的Process Family ,其他的嘛,留下Machine Check Exception、Low Latency….、HIGHMEM Support,其余都可以不要 
General setup —>当中,PCI的选上,其他的不要(注意对照你的系统),SYSTEM V IPC、BSD Process accounting、sysctl support留下,Kernel support ELF binary留着,其他的可以不要
Binary emulation of other systems —>
Memory Technology Devices (MTD) —>
Parallel port support —>
以上三项都是可以不要的
Plug and Play configuration —> 选上,不过如果没有ISA设备,可以不选对ISA P&P的支持(比如我的目标系统)
Block devices —>各取所需了,一般来说,如果你要用软盘,就选上Normal floppy disk support,大多数嵌入式系统是不要的。中间几个也是没有的;Loopback device是一定要的,Network block device我也没把握,可能可以不要不过我选了,呵呵,RAM Disk一定要,Initrd RAM Disk support当然要选。至于Default Ramdisk size就无所谓了,反正可以在启动的时候修改,呵呵。 

Multi-device support (RAID and LVM) —> 这个一般也用不上,不选了。
Networking options —>既然是打算做网关,呵呵,里面大部分东西都要选上而且是[*],编译入内核(前面已经说过了,没有编译为模块的)。从上到下一直选到IP: Virtual Server Configuration —>(从这个开始(含),可以不要了)。

需要说明的是,其中的IP: Netfilter Configuration —>子项即便选择全部编译到内核,似乎并没有什么变化,用iptables的时候一样要iptables的.so支持 :( 不过对irc和ftp的跟踪倒是不需要insmod了 

Telephony Support —>
SCSI support —>
Fusion MPT device support —>
I2O device support —>
Amateur Radio support —>
IrDA (infrared) support —>
ISDN subsystem —>
Old CD-ROM drivers (not SCSI, not IDE) —>
上面几个都不用,为什么RedHat那么大,他们有不小的功劳哟~~~

Network device support —>里面找出你目标系统的网卡(我这里是8139)选上,其他的统统去掉吧。 
Input core support —>如果你不是用的USB接口鼠标键盘,可以不用选他们。 
Character devices —>这里面我只选了Virtual Terminal以及Support for console on virtual terminal,其他好多东西都没有选。 
Multimedia devices —>
Crypto Hardware support —>

这两个对一般的最小系统来说都是不用的 

File systems —>这个是内核大小的大头,ext2(Second extended…)是必要的,ext3也用上吧,/proc有必要,DOS FAT/VFAT(win-95)估计你可能也需要,分区表只要支持PC BIOS就可以了,Native Language我把iso8859-1给内置了
console drivers->我只选了VGA text console
Sound —>
USB support —>
Additional device driver support —>
Kernel hacking —>
这几个都没有选,make dep;make clean;make bzImage
看看吧,内核大概是700~800k左右


以前我总以为裁减kernel就是裁减Linux了,后来才发现是大错特错。
以前总以为最难的是裁减kernel,后来才发现自己多么无知。学习裁减内核,大概只用了一两天,编译一次内核也就20分钟不到,可是后来居然重建文件系统花了一两个星期,呜呜~~~

关于文件系统和kernel的关系,从参考文档里面可以知道,大家自己去看。如果连这个都不懂,建议暂时不要做裁减的事情,以为照着我的文章依葫芦画瓢多半是不能成功的。 

首先按照ramdisk的生成方法或者loopback device的生成方法生成一个8M的磁盘挂接到/mnt(或者其他目录)上,就可以以/mnt为根目录构造文件系统。注意将其按照ext2方式格式化
lrwxrwxrwx    1 root     root            4 Dec 28 09:31 bin -> sbin
drwxr-xr-x    5 root     root         1024 Dec 27 13:42 dev
drwxr-xr-x    7 root     root         1024 Jan  6 15:14 etc
drwxr-xr-x    2 root     root         1024 Dec 12 08:33 initrd
drwxr-xr-x    4 root     root         1024 Dec 30 06:52 lib
drwxr-xr-x    2 root     root         1024 Dec 11 07:52 mnt

dr-xr-xr-x   24 root     root            0 Jan  6 15:14 proc
drwxr-xr-x    2 root     root         1024 Dec 26 03:03 root
drwxr-xr-x    2 root     root         1024 Dec 30 07:28 sbin
drwxr-xr-x    2 root     root         1024 Dec 26 03:04 sysroot
drwxr-xr-x    2 root     root         1024 Apr 19  2002 tmp
drwxr-xr-x    3 root     root         1024 Dec 12 07:45 usr
drwxr-xr-x    5 root     root         1024 Dec 12 02:43 var

这几个目录是必须的

先看看bin下面有什么
lrwxrwxrwx    1 root     root            6 Dec 30 07:28 ash -> ./bash
-rwxr-xr-x    1 root     root       541096 Dec 30 07:27 bash
-rwxr-xr-x    1 root     root        16020 Dec 13 08:56 cat
-rwxr-xr-x    1 root     root        16680 Dec 27 15:40 chmod
-rwxr-xr-x    1 root     root        36360 Dec 28 09:10 cp
-rwxr-xr-x    1 root     root        62756 Dec 28 09:25 ftp
-rwxr-xr-x    1 root     root       100624 Dec 28 09:14 grep
-rwxr-xr-x    1 root     root         8672 Dec 26 03:27 halt
-rwxr-xr-x    1 root     root         9624 Dec 28 09:14 hostname
-rwxr-xr-x    1 root     root        54316 Dec 28 09:14 ifconfig
-rwxr-xr-x    1 root     root        26920 Dec 12 02:42 init
-rwxr-xr-x    1 root     root       105768 Dec 27 13:44 ip
-rwxr-xr-x    1 root     root        60764 Dec 28 09:15 iptables
-rwxr-xr-x    1 root     root         7764 Dec 26 17:26 kill
-rwxr-xr-x    1 root     root        19080 Dec 12 02:25 login
-rwxr-xr-x    1 root     root         9172 Dec 11 07:54 losetup
-rwxr-xr-x    1 root     root        46888 Dec 13 08:55 ls
-rwxr-xr-x    1 root     root        10316 Dec 13 08:37 mingetty
-rwxr-xr-x    1 root     root        17992 Dec 27 14:15 mkdir
-rwsr-xr-x    1 root     root        60104 Dec 11 07:54 mount
-rwxr-xr-x    1 root     root        43496 Dec 28 10:02 mv
-rwxr-xr-x    1 root     root        22196 Dec 26 02:09 nash
-rwxr-xr-x    1 root     root        29464 Dec 28 09:49 ping
-r-xr-xr-x    1 root     root        63304 Dec 26 16:57 ps
lrwxrwxrwx    1 root     root            4 Dec 26 03:33 reboot -> halt
-rwxr-xr-x    1 root     root        26216 Dec 26 17:35 rm
lrwxrwxrwx    1 root     root            6 Dec 30 07:28 sh -> ./bash
-rwxr-xr-x    1 root     root        14952 Dec 11 09:44 shutdown
-rwxr-xr-x    1 root     root       219932 Dec 28 10:06 ssh
-rwxr-xr-x    1 root     root       260616 Dec 27 14:04 sshd
lrwxrwxrwx    1 root     root            6 Dec 26 16:48 swapoff -> swapon
-rwxr-xr-x    1 root     root         7108 Apr  1  2002 swapon
-rwxr-xr-x    1 root     root        27208 Dec 27 14:13 syslogd
-rwxr-xr-x    1 root     root        78808 Dec 28 09:30 telnet
-rwsr-xr-x    1 root     root        30664 Dec 27 14:23 umount
-rwxr-xr-x    1 root     root         7832 Dec 12 01:54 update
-rwxr-xr-x    1 root     root       386120 Dec 28 09:13 vi
-rwxr-xr-x    1 root     root        13896 Dec 30 06:53 who

这里面包含了ftp、telnet、ssh客户端以及sshd服务器常用的命令和网络设置命令,iptables防火墙,vi编辑器,shell用的是bash,虽然ash很小但是总是不习惯没有auto complete功能,tcsh不大不小功能又全,可是对一些shell脚本的支持不太好。nash用来解析linuxrc,后面会讲到(如果你看了最前面提到的initrd.txt)也会明白。

接着,用ldd命令看bin目录下面的各个可执行文件分别都和哪些动态库连接把他们cp到/mnt/lib目录下,如用ldd看mv命令,结果如下

   libc.so.6 => /lib/i686/libc.so.6 (0×42000000) 
    /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0×40000000)

把/lib/i686/libc.so.6和/lib/ld-linux.so.2复制到/mnt/lib下面即可。
libc.so.6是基本的libc库,好像不同的处理器还不一样,我就在一台P-MMX下面用P2的libc,结果死掉了。查了半天 :(
iptables命令除了其显示的以外,还要把/lib/iptables目录复制到/mnt/lib下面
/lib/security下面是PAM需要的库,最小系统只需要复制/lib/security下面的pam_unix.so、pam_stack.so到/mnt/lib/security下面即可

/etc下面的东西最是麻烦,要改的不是一点点
/etc/inittab init的配置文件,我改的是这样的: 

id:3:initdefault:
si::sysinit:/etc/rc
1:2345:respawn:/sbin/mingetty tty1
2:23:respawn:/sbin/mingetty tty2
l0:0:wait:/etc/rc0
l6:6:wait:/etc/rc6 

呵呵,简单吧 

/etc/fstab记录系统启动应该mount的文件系统,因为系统在ramdisk上面跑,所以是这样的: 

/dev/ram0               /                       ext2    defaults        1 0
none                    /proc                   proc    defaults        0 0

从inittab里面知道,启动后执行脚本/etc/rc我是这样写的: 

#!/bin/sh
/bin/mount -n -t proc /proc /proc
/bin/mount -n -o remount,rw /
/bin/mount -av
/bin/hostname MiniLinux
/etc/rc.network
/etc/rc.firewall
/bin/sshd 

第一行mount /proc,第二行把根remount为rw模式(漏了这个害得我查了两三天),第三行检查fstab里面是否还有其他的需要mount的分区,第四行设置主机名,后面分别根据脚本设置网络和防火墙,最后开启sshd服务。

设置网络在最小系统里面再也不是像/etc/init.d/network start那么简单,
呵呵,其实也不麻烦。通过命令ip、ifconfig可以很方便的设置。例如rc.network为: 

#!/bin/sh
/bin/ifconfig eth0 192.168.0.254
/bin/ifconfig eth1 211.69.200.1
/bin/ip route add default via 211.69.200.2 dev eth1
/bin/ip route replace 192.168.0.0/24 dev eth0 scope link
/bin/ip route replace 211.69.200.0/24 dev eth1 scope link 

前两行设置ip地址,第三行设置缺省网关,后面两行更改本网路由。 

rc.firewall的写法大家可以自己参照iptables的HOWTO去完成 

/etc/passwd,/etc/shadow,/etc/group记录有系统帐号信息,在最小系统上,我只留了root组(用户)的信息。

裁减Linux过程中,PAM是一个很关键的部分,由于资料不多,很多人束手无策。 

如果仅仅要使用最小系统,从console登陆需要修改/etc/pam.d/login,从ssh 上来修改/etc/pam.d/sshd,不妨复制系统原来的配置文件略作修改/etc/pam.d/login为 

#%PAM-1.0
auth       required     /lib/security/pam_stack.so service=system-auth
account    required     /lib/security/pam_stack.so service=system-auth
password   required     /lib/security/pam_stack.so service=system-auth
session    required     /lib/security/pam_stack.so service=system-auth
 
/etc/pam.d/sshd和login的内容一样。从其可知它们调用了system-auth这个服务,则还需要/etc/pam.d/system-auth,内容为: 

#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        sufficient    /lib/security/pam_unix.so likeauth nullok
account     required      /lib/security/pam_unix.so
password    sufficient    /lib/security/pam_unix.so nullok  md5 shadow
session     required      /lib/security/pam_unix.so


关机和重启

关机和重启在完整的Linux下面是有很长的脚本支持的,就像启动脚本/etc/rc.sysinit 等等。但是在最小系统上面,这些都需要自己来写,复制原有系统的肯定不行。 

不过从前面/etc/inittab里面可以知道,最小系统上面reboot执行的是/etc/rc6,关机是/etc/rc0,如果不需要“善后”,则很简单,rc6如下: 

[root@MiniLinux etc]# cat rc6
/sbin/reboot -i -d 

rc0则为:
[root@MiniLinux etc]# cat rc0
/sbin/halt -i -d -p 

整个etc目录下的东西列表大致为:
[root@MiniLinux etc]# ll
total 891
-rw-r–r–    1 root     root         6639 Apr 19  2002 fonts.cgz
-rw-r–r–    1 root     root          109 Dec 30 06:19 fstab
-rw-r–r–    1 root     root           14 Dec 14 00:10 group
-rw-r–r–    1 root     root          146 Dec 27 15:41 inittab
-rw——-    1 root     root           60 Jan  6 15:14 ioctl.save
-rw-r–r–    1 root     root           57 Dec 12 01:26 issue
-rw-r–r–    1 root     root        28436 Apr 19  2002 keymaps.gz
-rw-r–r–    1 root     root         3758 Apr 19  2002 kon.cfg
-rw-r–r–    1 root     root         1281 Apr 19  2002 lang-table
-rw-r–r–    1 root     root         1320 Dec 30 06:55 ld.so.cache
-rw-r–r–    1 root     root           18 Dec 12 07:53 ld.so.conf
-rw-r–r–    1 root     root        54692 Apr 19  2002 loader.tr
-rw-r–r–    1 root     root         1180 Dec 23 09:07 login.defs
-rw-r–r–    1 root     root        30303 Apr 19  2002 minikon.fnt
-rw-r–r–    1 root     root            0 Dec 13 23:39 mtab
-rw-r–r–    1 root     root          270 Dec 23 04:03 nsswitch.conf
drwxr-xr-x    2 root     root         1024 Dec 30 04:48 pam.d
-rw-r–r–    1 root     root           28 Dec 30 06:29 passwd
-rwxr-xr-x    1 root     root          401 Dec 30 07:43 profile
-rw-r–r–    1 root     root        12359 Apr 19  2002 ramfs.img
lrwxrwxrwx    1 root     root            7 Dec 26 03:03 rc -> rc.d/rc
drwxr-xr-x    2 root     root         1024 Dec 27 15:33 rc.d
-rwxr-xr-x    1 root     root         2631 Jan  6 02:18 rc.firewall
-rwxr-xr-x    1 root     root          246 Jan  6 02:17 rc.network
-rwxr-xr-x    1 root     root           20 Dec 27 15:38 rc0
-rwxr-xr-x    1 root     root           19 Dec 27 15:39 rc6
-r——–    1 root     root           59 Dec 30 06:20 shadow
drwxr-xr-x    2 root     root         1024 Dec 26 06:37 ssh
-rw-r–r–    1 root     root       737535 Dec 23 10:18 termcap 

其中目录ssh为sshd的配置文件,复制原来机器上的即可。其他的大部分文件都是按照HOWTO上面提到的一些必备文件复制的。 

nsswitch.conf是系统寻找一些配置文件的配置文件,呵呵,很拗口,man nsswitch.conf看看吧,稍作修改为:
[root@MiniLinux /]# cat etc/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 
 

profile文件是bash shell的登陆脚本,主要为了限制历史命令记录大小 

[root@MiniLinux etc]# cat profile
# /etc/profile
# System wide environment and startup programs, for login setup
# Functions and aliases go in /etc/bashrc
HISTSIZE=1000
HISTFILESIZE=20
PATH=/bin
PS1=’[\u@\h \W]\$ ‘
HOSTNAME=’/bin/hostname’
export PATH HISTSIZE HISTFILESIZE HOSTNAME PS1
alias l.=’ls -d .[a-zA-Z]* –color=tty’

alias ll=’ls -l –color=tty’
alias ls=’ls –color=tty’
alias rm=’rm -i’
alias cp=’cp -i’
alias mv=’mv -i’

关于/dev目录 

完整linux的/dev目录下有很多设备文件,不过仔细辨别一下就会发现很多其实
用不上。我列出在我的最小Linux下面用到的设备文件:
[root@MiniLinux dev]# ls
agpgart  hda   hda6     input  loop3     psaux  ptyp3  ram3  tty3   ttyp4
console  hda1  hda7     kbd    loop4     ptmx   ptyp4  ram4  tty4   urandom
fb       hda2  hda8     kmem   loop5     pts    ram    shm   ttyp0  zero
fb0      hda3  hda9     loop0  mem       ptyp0  ram0   tty0  ttyp1
fd0      hda4  initctl  loop1  null      ptyp1  ram1   tty1  ttyp2
fd1      hda5  initrd   loop2  openprom  ptyp2  ram2   tty2  ttyp3

其中input、shm、pts是目录,似乎是系统自己生成的,fb连接到fb0,ram连接到ram0 ,关于硬盘的保留了hda?,loop[0-5]用于支持回环设备(loopback devices),tty[0-4]用于支持主机直接操作,ttyp[0-4] & ptyp[0-4]用于共同支持ssh登陆,ram?用于支持虚拟盘,urandom是sshd服务必须的设备。

所有的设备文件均可以用cp -dpR从原系统的/dev目录下复制过来。

关于linuxrc
linuxrc是一个在initrd.img里面展开后直接自动执行的一个脚本。关于这个脚本的用途,建议大家读一下/usr/src/linux-2.4/Documentation/initrd.txt,我也是直接把系统提供的initrd-2.4.18-3.img里面带的linuxrc拿来用而已:
[root@MiniLinux /]# cat linuxrc
#!/bin/nash
echo Mounting /proc filesystem
mount -t proc /proc /proc
echo Creating root device
mkrootdev /dev/root
echo 0×0100 > /proc/sys/kernel/real-root-dev
echo Mounting root filesystem
mount –ro -t ext2 /dev/root /sysroot
umount /proc
pivot_root /sysroot /sysroot/initrd 

注意,它用的shell是/bin/nash,而不是通常用的/bin/sh,大家man nash可以看到很多有意思的东西。

最后的一些工作 

当你把一个文件虚拟为一个磁盘并挂接在系统上,复制了需要的可执行文件、库文件、配置文件并做了必要的修改之后,一个文件系统基本上已经成形了。
前面列出的最小系统的目录,如果没有特别提到都是留空的。这里要说的,最后的工作就是如何把kernel和文件系统结合起来。 

我看到过很多讲一张或者两张软盘启动的linux,里面都提到用rdev定位文件系统,还要如何如何算。我是看明白了,不过觉得特别繁琐,就投机取巧了一番。 

首先假定刚才我们挂载的根文件系统是挂载到现在的/mnt目录下,文件名是/root/newfs则首先umount 

#umount /root/newfs 

接着将newfs压缩 

#gzip -v9 /root/newfs 

此时会生成newfs.gz,接着rename: 

#mv newfs.gz newfs.img 

把它和前面编译的内核bzImage放到/boot目录下去。 

我用的Linux引导器是grub。为什么不用LILO?我基本上没有用过LILO,是出道很晚很晚的Linux使用者,从grub的介绍上我发现它比LILO功能强很多,使用新内核不需要像grub那样重新安装,而且内置支持一些常见的文件系统。 

看看/boot/grub/grub.conf吧,在其中增加一段:
title Test Combine
        root (hd0,4)
        kernel /bzImage ro ramdisk_size=8192 root=/dev/ram0
        initrd /newfs.img
 
当然,你不可照抄我的配置,需要按照你的系统更改root (hd?,?),如果你建立的文件系统(未压缩前)容量是其他数值的,请用合适的值替代8192,单位是KB(还记得我前面说过的编译内核的时候不需要刻意更改缺省ramdisk容量吗?就在这里指定即可) 

好了,可以试试裁减以后的系统了。大概有多大呢?我裁减出来的系统内核大约是<800k,文件系统8M,压缩成.img的是约3M,很小吧。

感想 及 说明 

先说说这个最小Linux的启动过程。grub将内核载入以后,让内核将newfs.img载入内存并展开(自动展开到/dev/ram0)为临时根文件系统,此时执行/linuxrc,在linuxrc中又指定了新的文件系统。接着内核按照grub带入的参数root=/dev/ram0作为根文件系统正式init,此时/dev/ram0中的内容正是newfs.img的内容(如果你仔细观察的话,会发现完整linux启动过程中有Unmounting initrd….的字样,说明initrd.img是在进入init脚本的过程中才被卸载的),按照/etc/inittab脚本执行,缺省为runlevel 3,执行/etc/rc,最后由mingetty启动login完成引导。 

我个人感觉,/linuxrc里面的脚本似乎对内核正式init时的根分区指定没有多少影响,并不像/usr/src/linux-2.4/Documentation/initrd.txt里面linuxrc例子那样要求严格。好像决定正式init根分区的是由grub带入内核的参数root=… 来决定的更多。有经验的大侠请多指点。 

经过这次实验,感受颇多。首先对linux引导过程以及/etc下面的很多配置文件有了深入了解。Linux可改变的弹性很大,不过也需要大家沉得住气,慢慢去研究,man、HOWTO等等是少不了看的,第一手的资料还是man和英文HOWTO最权威,其次,缩减以后的大小让人非常振奋,裁减以后,系统加载重启登陆的速度都变得非常快,很多东西简直就是一眨眼就过去了。 

文末,感谢各位耐心看完,不对之处请斧正。本人也是linux的新手,万望海涵。