平时我们刚安装完成linux系统,系统显得有些庞大,有些服务不是我们需要的,是可以精简掉的,这样就可以大大地节省了我们的磁盘空间,而且也会很大程度地提升系统的性能。下面我们动手去裁剪一个微型系统,当然,可以使用busybox,但为了让大家了解整个过程是怎么回事,本文暂且不使用busybox,在此之前,我们需要理解linux的开机启动流程,在此我就不详述了,开机流程也可以在我的这篇博客了解下http://hequanlin.blog.51cto.com/2953611/1219794 。根据我的理解,具体过程大概如下:


添加IDE接口磁盘-->分区并格式化-->挂载分区-->安装grub引导、创建grub的配置文件-->提供kernel(可自行编译内核)-->制作initrd-->创建根文件系统目录结构-->创建初始化脚本、配置文件 -->移植命令及所依赖的库文件-->移植驱动模块 --> 移植服务程序


下面开始我们的系统裁剪之旅吧!

首先,我们要准备的材料:

RHEL 5.8 x86_64 (这里我用的是64位系统,也可以用32的)

vmware-workstation 9

20G的IDE接口虚拟硬盘


1、添加IDE磁盘:    

VM --> settings --> Add --> Hard Disk --> Create a new virtual disk --> IDE --> Store virtual disk as a single file --> finished


2、在/mn目录创建boot和sysroot,分区并格式化,挂载分区,在此使用脚本完成

# mkdir -pv /mnt/{boot,sysroot}
执行下面脚本对IDE磁盘进行分区并挂载
#!/bin/bash
#
echo "
n
p
l
1
+32M
n
p
2
+64M
w" | fdisk /dev/hda
sleep 1
/sbin/partprobe /dev/hda
/sbin/mke2fs -j /dev/hda1
/sbin/mke2fs -j /dev/hda2
mount /dev/hda1 /mnt/boot
mount /dev/hda2 /mnt/sysroot

RHEL5.8 系统裁剪_第1张图片

3、为IDE磁盘安装引导

# grub-install --root-directory=/mnt/ /dev/hda

RHEL5.8 系统裁剪_第2张图片

   创建gurb配置文件grub.conf

# vi /mnt/boot/grub/grub.conf
default=0
timeout=3
title My mini linux
        root (hd0,0)
        kernel /vmlinuz
        initrd /initrd.gz

4、复制当前系统的内核到/mnt/boot/vmlinuz(此处也可以使用自己编译好的内核)

# cp /boot/vmlinuz-2.6.18-308.el5 /mnt/boot/vmlinuz

5、制作initrd文件,利用当前系统的initrd文件进行修改

# mkdir /tmp/initrd
# cd /tmp/initrd
# zcat /boot/initrd-2.6.18-308.el5.img |cpio -id

   修改init脚本,为了避免出错,这里只修改下面两行

   要挂载ext3格式的根文件系统,还需要在此initrd添加ext3的支持模块,由于此initrd已经包含了ext3驱动模块,所以不需要再添加了,init脚本也有了加载该模块的指令:

   重新封装initrd

# pwd
/tmp/initrd
# find . | cpio -H newc --quiet -o|gzip -9 >/mnt/boot/initrd.gz
# ls -lh /mnt/boot/initrd.gz
-rw-r--r-- 1 root root 2.7M Jun 14 11:36 /mnt/boot/initrd.gz


6、创建根文件系统的目录


# mkdir sys proc lib usr var/{log,run,lock} home media mnt tmp etc/{rc.d/rc{0..6}.d,init.d} boot dev sbin bin usr/{sbin,bin} -p


7、创建系统初始化脚本要用到的配置文件/etc/inittab及系统初始化脚本/etc/rc.d/rc.sysinit

# cat /mnt/sysroot/etc/inittab
id:3:initdefault:
si::sysinit:/etc/rc.d/rc.sysinit


# cat /mnt/sysroot/etc/rc.d/rc.sysinit
#!/bin/bash
echo "welcome to my world !"
/bin/bash
# chmod +x /mnt/sysroot/etc/rc.d/rc.sysinit


8、移植命令(根据自己需要,init必需移植),移植命令的同时,还需要移植命令所依赖的库文件,这里采取下面脚本来完成


#!/bin/bash
#
read -t 30 -p "Target System Directory[/mnt/sysroot]: " DEST
DEST=${DEST:-/mnt/sysroot}
libcp() {
  LIBPATH=${1%/*}
  [ ! -d $DEST$LIBPATH ] && mkdir -p $DEST$LIBPATH
  [ ! -e $DEST${1} ] && cp $1 $DEST$LIBPATH && echo "copy lib $1 finished."
}
bincp() {
  CMDPATH=${1%/*}
  [ ! -d $DEST$CMDPATH ] && mkdir -p $DEST$CMDPATH
  [ ! -e $DEST${1} ] && cp $1 $DEST$CMDPATH
  for LIB in  `ldd $1 | grep -o "/.*lib\(64\)\{0,1\}/[^[:space:]]\{1,\}"`; do
    libcp $LIB
  done
}
read -p "Your command: " CMD
until [ $CMD == 'q' ]; do
   ! which $CMD && echo "Wrong command" && read -p "Input again:" CMD && continue
  COMMAND=` which $CMD | grep -v "^alias" | grep -o "[^[:space:]]\{1,\}"`
  bincp $COMMAND
  echo "copy $COMMAND finished."
  read -p "Continue: " CMD
done

   移植命令init,bash,ls

RHEL5.8 系统裁剪_第3张图片

执行sync命令把修改的内容同步到磁盘,挂起当前的虚拟机,然后新建一台虚拟机,使用上面的IDE磁盘,再启动新的虚拟机,启动成功如下,ls命令也可以正常使用

RHEL5.8 系统裁剪_第4张图片


扩展:

1、实现关机和重启功能,init 0进行关机, init 6进行重启

   这里解析下redhat 执行init n(运行级别)是怎样的一个过程。

   以init 0为例吧,当执行 init 0时,系统会退出当前级别,切换到0级别,读取init的配置文件/etc/inittab找到l0:0:wait:/etc/rc.d/rc 0 并运行/etc/rc.d/rc 脚本,rc这个脚本根据它后面的参数 0 去运行0级别目录下的所有脚本(每个级别都有其对应的目录),各级别的目录下的脚本都是以K和S开头的脚本,而这些脚本都链接文件,一般都是/etc/rc.d/init.d目录下的服务脚本的链接文件,以K开头的脚本,表示运行此级别时,停止此服务,而以S开头的脚本,则表示启动此服务。

   现在来实现关机和重启功能吧

1)创建脚本/etc/rc.d.d/rc


# vi /mnt/sysroot/etc/rc.d/rc
#!/bin/bash
#
runlevel=$1
for i in /etc/rc.d/rc$runlevel.d/K*
do
    [ -f $i ] && $i stop
done
for i in /etc/rc.d/rc$runlevel.d/S*
do
    [ -f $i ] && $i start
done

2)创建目录/etc/rc.d/rc0.d和/etc/rc.d/rc6.d

# pwd
/mnt/sysroot
# mkdir -p etc/rc.d/rc{0,6}.d/
# ls -d etc/rc.d/rc{0,6}.d/
etc/rc.d/rc0.d/  etc/rc.d/rc6.d/


3)创建脚本/etc/rc.d/init.d/halt



# vi etc/rc.d/init.d/halt
#!/bin/bash
#
case $0 in
    *halt)
    COMMAND="/sbin/halt -p"
    ;;
    *reboot)
    COMMAND="/sbin/reboot"
    ;;
    *)
    echo "This script must only called by alias"
    ;;
esac
exec $COMMAND
# chmod +x etc/rc.d/init.d/halt


4)创建halt脚本的链接文件到相应级别下,注意链接文件的命名


# cd /mnt/sysroot/etc/rc.d/rc0.d/
# ln -s ../init.d/halt S01halt
# cd /mnt/sysroot/etc/rc.d/rc6.d/
# ln -s ../init.d/halt S01reboot


5)移植halt脚本用到的命令halt和reboot,使用之前创建的命令移植脚本来移植

RHEL5.8 系统裁剪_第5张图片

至此执行sync命令把修改过的数据同步到磁盘,然后进行测试
重启:

RHEL5.8 系统裁剪_第6张图片

关机:

RHEL5.8 系统裁剪_第7张图片


2、设定主机名

1) 我们都知道,红帽系统设定主机名的配置文件是/etc/sysconfig/network,因此我们也来根据这个配置文件来设定主机名

创建目录/etc/sysconfig并创建network文件


# cd /mnt/sysroot/etc/
# mkdir sysconfig
# vi sysconfig/network
HOSTNAME=www.magedu.com.


2)系统主机名的设定是在系统的初始化时设定的,于是在初始化脚本rc.sysinit追加设定主机名的语句,注意下面代码要添加在/bin/bash前面


# vi /mnt/sysroot/etc/rc.d/rc.sysinit
if [ -f /etc/sysconfig/network ];then
. /etc/sysconfig/network
fi
if [ -z "$HOSTNAME" -o "$HOSTNAME" == "(zone)" ];then
        HOSTNAME=localhost
fi
/bin/hostname $HOSTNAME
/bin/bash

3) 不要忘记移植hostname命令

RHEL5.8 系统裁剪_第8张图片

 4)执行sync命令把数据同步到磁盘,进行测试

RHEL5.8 系统裁剪_第9张图片

3、实现在初始化时配置好IP地址

   我们知道在内核探测硬件加载驱动时,驱动模块主要是从initrd文件中加载,但initrd的主要功能是提供内核访问真正根文系统所需要的驱动,一般不initrd文件中装载驱动,所以我们要手动复制当前系统的网卡驱动模块到根文件系统,这里要注意的是网卡驱动模块的依赖模块也要复制,查看驱动模块的依赖关系可以使用命令modinfo  [模块名称]

   1)首先创建根文件系统中存放驱动模块的目录/lib/modules


# mkdir /mnt/sysroot/lib/modules


   2)获取网卡驱动的依赖关系,由下图可以看出模块pcnet32依赖依赖mii,因此pcnet32和mii都需要移植到根文件系统

RHEL5.8 系统裁剪_第10张图片


3)复制相关模块到根文件系统

# cp  /lib/modules/2.6.18-308.el5/kernel/drivers/net/pcnet32.ko /mnt/sysroot/lib/modules/
# cp /lib/modules/2.6.18-308.el5/kernel/drivers/net/mii.ko /mnt/sysroot/lib/modules/
# ls -l /mnt/sysroot/lib/modules/
total 130
-rwxr--r-- 1 root root 41496 Jun 14 15:20 mii.ko
-rwxr--r-- 1 root root 86032 Jun 14 15:19 pcnet32.ko


4)在系统初始化脚本添加加载网卡驱动模块的指令,注意要首先装载pcnet32所依赖的模块mii,再装载pcnet32

# cd /mnt/sysroot/
# vi etc/rc.d/rc.sysinit
#!/bin/bash
echo "welcome to my world !"
if [ -f /etc/sysconfig/network ];then
. /etc/sysconfig/network
fi
if [ -z "$HOSTNAME" -o "$HOSTNAME" == "(zone)" ];then
        HOSTNAME=localhost
fi
/bin/hostname $HOSTNAME
#
echo -e "\033[31mLoading the network card module ...\033[0m"
insmod /lib/modules/mii.ko
insmod /lib/modules/pcnet32.ko
#
echo -e "\033[31mConfiguring ip address for eth0 ... \033[0m"
ifconfig eth0 192.168.1.254 netmask 255.255.255.0 up
echo -e  "\033[31mConfiguring ip address for local loopback interface ... \033[0m"
ifconfig lo 127.0.0.1 netmask 255.0.0.0 up
#
/bin/bash


5)移植指令insmod、ifconfig、ping


# ./bincp.sh
Target System Directory[/mnt/sysroot]:
Your command: ifconfig
/sbin/ifconfig
copy /sbin/ifconfig finished.
Continue: insmod
/sbin/insmod
copy /sbin/insmod finished.
Continue: ping
/bin/ping
copy /bin/ping finished.
Continue: q

6)执行sync命令把数据同步到磁盘,进行测试

RHEL5.8 系统裁剪_第11张图片


4、实现启动多个终端

   由于这里启动终端的程序会调用/bin/login程序,而系统的/bin/login程序要涉及到pam的验证,pam比较复杂,所以在此就不实现用户登录的认证了,只是实现系统启动后打开3个终端

   1)修改inittab文件如下:


# cd /mnt/sysroot/
# vi etc/inittab
id:3:initdefault:
si::sysinit:/etc/rc.d/rc.sysinit
l0:0:wait:/etc/rc.d/rc 0
l6:6:wait:/etc/rc.d/rc 6
1:2345:respawn:/sbin/agetty 38400 -n -l /bin/bash tty1
2:2345:respawn:/sbin/agetty 38400 -n -l /bin/bash tty2
3:2345:respawn:/sbin/agetty 38400 -n -l /bin/bash tty3


   2)修改/etc/rc.d/rc.sysinit,把最后一行的/bin/bash去掉,不然此脚本启动了bash,就不会启动其他的tty

   3)移植命令agetty、ps、grep、wc


# ./bincp.sh
Target System Directory[/mnt/sysroot]:
Your command: agetty
/sbin/agetty
copy /sbin/agetty finished.
Continue: ps
/bin/ps
copy /bin/ps finished.
Continue: grep
/bin/grep
copy /bin/grep finished.
Continue: wc
/usr/bin/wc
copy /usr/bin/wc finished.
Continue: q


   4)执行sync命令把数据同步到磁盘,进行测试

RHEL5.8 系统裁剪_第12张图片


好了,本文实现的功能就到此,我将会在下一篇博文使用busybox来制作linux,实现用户认证功能,提供ssh服务,移植web服务