天天看點

linux系統裁剪

這是自己在2003年初發表與華中白雲黃鶴BBS Linux讨論區的一篇文章了,摘錄于此,權做一個紀念,順便更正當中的一些錯誤。不過文中的一些内容現在看來是有備援的了,配置檔案的了解也不如現在透徹,以後在撰文詳述吧,搞一個patch?呵呵

發信站: 武漢白雲黃鶴站 (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 (0x42000000)

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

把/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/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,内容為:

# 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: &n

bsp; 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 0x0100 > /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的新手,萬望海涵。 -

繼續閱讀