平时我们刚安装完成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
3、为IDE磁盘安装引导
# grub-install --root-directory=/mnt/ /dev/hda
创建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
执行sync命令把修改的内容同步到磁盘,挂起当前的虚拟机,然后新建一台虚拟机,使用上面的IDE磁盘,再启动新的虚拟机,启动成功如下,ls命令也可以正常使用
扩展:
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,使用之前创建的命令移植脚本来移植
至此执行sync命令把修改过的数据同步到磁盘,然后进行测试
重启:
关机:
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命令
4)执行sync命令把数据同步到磁盘,进行测试
3、实现在初始化时配置好IP地址
我们知道在内核探测硬件加载驱动时,驱动模块主要是从initrd文件中加载,但initrd的主要功能是提供内核访问真正根文系统所需要的驱动,一般不initrd文件中装载驱动,所以我们要手动复制当前系统的网卡驱动模块到根文件系统,这里要注意的是网卡驱动模块的依赖模块也要复制,查看驱动模块的依赖关系可以使用命令modinfo [模块名称]
1)首先创建根文件系统中存放驱动模块的目录/lib/modules
# mkdir /mnt/sysroot/lib/modules
2)获取网卡驱动的依赖关系,由下图可以看出模块pcnet32依赖依赖mii,因此pcnet32和mii都需要移植到根文件系统
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命令把数据同步到磁盘,进行测试
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命令把数据同步到磁盘,进行测试
好了,本文实现的功能就到此,我将会在下一篇博文使用busybox来制作linux,实现用户认证功能,提供ssh服务,移植web服务