Next Previous Contents

4. 建立一個root filesystem -- Building a root filesystem

造出 root filesystem 涉及選擇能讓系統正常運作所必備的檔案。在這一節中,我們將敘述如何建造一個 壓縮的 root filesystem 。在磁片上建造一個直接掛上做為根目錄 (root) 之未經壓縮的 filesystem 是較不普遍的觀念;這個替代方案敘述在 Non-ramdisk Root Filesystem 這一節中。

4.1 概觀 -- Overview

root filesystem 必須包含支援完整 Linux 系統運作所需的每一個項目。為了能夠達成這個目的,這張磁片必須包括能讓 Linux 系統運作最起碼 (minimum) 的需求:

當然,任何系統只有在你能於其上執行某些東西時才會顯得有用,而一張 root 磁片通常只有在你能做到以下事情時才會顯得有用:

我們將敘述如何建造一個 壓縮的 filesystem ,就是平時被壓縮在磁片上,只有當開機時,才會解壓縮後存入ramdisk 。 用壓縮 filesystem 的方式,你可以在一張標準的 1440K 磁片上放入很多檔案 (大約 6 megabytes) 。因為 filesystem 比磁片大很多,我們不能直接把它建在磁片上。我們必須在其它地方建立它,壓縮它,然後再把它 copy 到磁片上。

4.2 製作 filesystem -- Creating the filesystem

為了建造如此的一個 filesystem ,你需要一個多出而夠大的設備,能夠讓你在壓縮之前存放所有的檔案。你將需要一個能夠存放大約 4 megabytes檔案的設備。有以下幾種選擇:

在你選擇其中一種方法後,請準備 DEVICE 以:

        dd if=/dev/zero of=DEVICE bs=1k count=4096

這行指令送出一堆 0 把DEVICE填滿。用 0 填滿 device 是關鍵的一步,因為 filesystem之後將會被壓縮,所以所有未使用的部分應被用 0 填滿以達到最大的壓縮比。無論何時你從你的 root filesystem 刪除檔案,請記得這個事實。實際上 filesystem 只是釋出 (de-allocate) 這些 blocks , 但是並沒有再把它們填為 0 。如果你執行過很多次刪除與 copy 的動作,你的壓縮 filesystem 最後會比必要的大出很多。

下一步就是造出 filesystem 。 Linux kernel 承認兩種能讓 root disks 自動地被 copy 到 ramdisk 上的 file system 。它們是 minix 與 ext2 ,其中 ext2 是比較受歡迎的。如果使用 ext2 ,你會發現使用 -i 選項指定比預設值更多的 inodes 是有助益的;我們建議用 -i 2000 ,這樣你就不會用完 inodes 。如果不用上述選項,你可以移除許多不必要的 /dev 檔案以節省 inodes 。 mke2fs 預設會造出 360 個 inodes在一張 1.44Mb 的磁片上。我發現在我目前的救援 root 磁片上, 120 個 inodes 是相當足夠了,但是如果你把所有的設備檔都放入 /dev 目錄中,那麼你很容易會超過 360 個inodes 。使用壓縮的 root filesystem 可讓你擁有較大的 filesystem ,同時預設會有更多的 inodes ,但是你仍然必須要不就是減少檔案數量,要不就是增加 inodes 數目。

因此,你所使用的指令看起來會像這樣:

        mke2fs -m 0 -i 2000 DEVICE

(如果你使用的是一個 loopback device ,那麼請用你目前所用的磁碟檔案替換掉上面的 DEVICE 。)

mke2fs 指令會自動地偵測可獲得的空間,然後依據偵測對自身進行組態設定。 ``-m 0'' 參數避免保留空間給 root ,因此可提供更多可用的磁碟空間。

下一步,掛上這個 device :

        mount -t ext2 DEVICE /mnt
(如果 mount point 並不存在,你必須自行造出一個 mount point /mnt 。) 在剩下的章節中,所有的目的 (destination) 目錄都被假設是相對於 /mnt

4.3 移植檔案系統 -- Populating the filesystem

以下是你的 root filesystem 最起碼該有的目錄

此處所呈現之目錄架構僅供 root diskette 使用。真正的 Linux 系統有一套更為複雜且設計良好的架構方法,稱為 Filesystem Hierarchy Standard ,它決定檔案應該如何放置。 :

上述目錄的其中三個在 root filesystem 上會是空的,所以它們只需要用 mkdir 造出來。 /proc 目錄基本上是一個把 proc filesystem 放置於其下的 stub 。 /mnt 與 /usr 這兩個目錄只是在 boot/root 系統運作時所使用的 mount points 。因此再重覆一次,這些目錄只需要被造出來就可以了。

剩下的四個目錄描述於以下各節。

/dev

/dev 目錄包含一群特別的檔案,這些檔案是給系統上所有設備使用的,這樣的 /dev 目錄每個 Linux 系統都一定會有。這個目錄本身是一個普通目錄,可以以一般的方法用 mkdir 造出來。然而,這些特別的檔案必須以特別的方法用 mknod 指令造出來。

但還是有一條捷徑 -- 直接 copy 你現有 /dev 目錄的內容,然後再清除你不想要的設備檔。唯一的要求是 copy 這些特別的設備檔時,要用 -R 選項。這個選項會 copy 整個目錄中的檔案,但是不會 copy 這些檔案的內容。請確定使用 大寫字母 R 。這個指令是:

        cp -dpR /dev /mnt

在此我們假設磁片是被掛在 /mnt 底下。 dp 選項 (switches) 確保 symbolic links 是以 links 的方式來 copy ,而不是 copy 鏈結檔所指向的 target file ,同時原本的檔案屬性也被保留,因此保留了檔案的所有權資訊。

如果你想要用高難度技巧完成這個任務,就利用 ls -l 列出你想要的設備檔之 major 與 minor device numbers ,然後再用 mknod 在磁片上造出它們。

無論如何 copy 這些設備檔,還是要檢查任何你所需之設備檔 (device special file) 是否已放入這張救援磁片中。舉例來說, ftape 使用磁帶設備,如果你想要從 bootdisk 存取軟式磁帶機,你就需要 copy 所有有關的設備檔。

請注意,每一個設備檔需要一個 inode ,但 inodes 一直都是稀少的資源,特別是在磁片 filesystems 上。因此,從磁片上的 /dev 目錄移除任何你所不需要的設備檔是有意義的。舉例來說,如果你沒有 SCSI 磁碟,你可以放心地移除所有以 sd 開頭的設備檔。同樣地,如果你並不想使用你的序列埠 (serial port) ,那麼你也可以移除所有以 cua 開頭的設備檔。

請確定從這個目錄放入了以下檔案的: console, kmem, mem, null, ram0 and tty1.

/etc

這個目錄包含了重要的組態設定檔。在大部分的系統上,這些檔案被分為三個群組:

  1. 一直都是必備的, e.g. rc, fstab, passwd
  2. 可能是必備的,但是沒有人能十分確定。
  3. 偷跑進來的垃報。

通常可以用以下指令識出哪些是非基本的檔案:

        ls -ltru

這個指令將檔案依據上次被存取的日期,以先早後晚 (reverse) 的順序列出,所以如果有任何檔案不會被存取,那麼它們就可以從 root 磁片中刪去。

在我的 root 磁片上,我的組態檔數目已減至 15 個。這可減少我處理以下三種檔案的工作:

  1. 我必須為 boot/root 系統進行組態設定的檔案:
    1. rc.d/* -- 系統啟動與改變 run level 的 scripts
    2. fstab -- 要被掛上的 file systems 清單
    3. inittab -- 給 init process 的參數,於開機時啟動的第一個 process 。
  2. 我們應該為 boot/root 系統整理的檔案:
    1. passwd -- 重要的使用者、 home 目錄等其它項目的清單。
    2. group -- 使用者群組。
    3. shadow -- 使用者的密碼。你可能沒有這個檔。
    4. termcap -- the terminal capability database.
    如果系統安全 (security) 對你很重要,那麼 passwd 與 shadow 應該被刪減,以避免將使用者密碼 copy 出系統,這樣當你從磁片開機時,不想要的 logins 會被拒絕。 請確定 passwd 至少包含了 root 。如果你要讓其他的使用者 login ,請確定他們的 home 目錄與 shells 都存在。 termcap ,終端機資料庫,一般而言有幾百個 kilobytes 。你 boot/root 磁片的版本應該被刪減到只包含你所使用的終端機,這通常就是 linuxlinux-console 項目 (entry) 。
  3. The rest. They work at the moment, so I leave them alone.

Out of this, 我實際上只必須設定兩個檔,而它們所應包含的項目驚人地少。

你的 inittab 應該被改變,以使其中 sysinit 這行能執行 rc 或無論什麼將被執行的基本開機 script 。同時,如果你想要確保不可從序列埠 login ,請在所有行尾包括 ttys 或 ttyS 的 getty 項目前加上「#」符號 (comment out) 。請保留 tty 埠以讓你可以在 console 前 login 。

一個最起碼的 inittab 檔看起來樣這樣:

        id:2:initdefault:
        si::sysinit:/etc/rc
        1:2345:respawn:/sbin/getty 9600 tty1
        2:23:respawn:/sbin/getty 9600 tty2

inittab 檔定義了系統在各種不同的情況中將執行什麼項目,包括 startup 、切換至多使用者模式等等。請仔細地檢查在 inittab 中被提及的檔案名稱 (filenames) ;如果 init 不能找到所提及的程式,那麼 bootdisk 將會停止運作,而你甚至不會得到錯誤訊息。

請注意,某些程式不能被移到其它地方,因為其它程式已在撰寫時,就把它們的檔案位置寫死了 (hardcode) 。舉例來說在我的系統上, /etc/shutdown 已把 /etc/reboot 的位置寫死在其中。如果我移動了 reboot 到 /bin/reboot,然後下達一個 shutdown 指令,將會因為找不到 reboot 檔而發生錯誤。

剩下來的,就是 copy 在你 /etc 目錄中的所有文字檔 (text files) ,再加上在你 /etc 目錄中,你無法確定你需不需要的所有可執行檔。需要指示 (guide) 者,請參考在 Sample roodisk directory listings 的樣本清單。也許只要 copy 這些檔案就足夠了,但是系統差異會有很大的影響,所以你無法確定你系統上相同的檔案組合,就一定等於清單中的檔案。唯一確定的方法就是從 inittab 著手,並找出需要什麼。

現在大部分的系統使用 /etc/rc.d/ 目錄,其中包含給不同 run levels 的shell scripts 。最起碼會有一個單一的 rc script,但是僅從你現存的系統 copy inittab 與 /etc/rc.d 這兩個目錄,然後刪減 rc.d 目錄中的 shell scripts 以移除和磁片系統環境無關的 processing ,會是較為簡單的做法。

/bin 與 /sbin

/bin 目錄是一個放置為了執行基本作業 (operations) 而所需之額外工具程式的方便好地方,這些工具程式諸如 ls, mv, catddbin/ 與 /sbin 這兩個目錄的檔案清單範例請見 Sample rootdisk directory listings 。但範例中並沒有包括任何從備份復原時所需之工具程式,諸如 cpio, targzip 。這是因為我把這些東西放在另一張 (separate) 工具程式磁片上,以節省 boot/root 磁片的空間。一旦 boot/root 磁片被開機啟動,就會被 copy 到 ramdisk 並釋放軟碟機,讓軟碟機能掛上另一張磁片,就是工具程式片。我通常把它掛上當做 /usr 。

工具程式磁片 (utility diskette) 的製作被描述在下面 Building a utility disk 這節。保留一份相同版本之備份用工具程式的copy是比較好的,這個備份用工具程式被用來製作備份,如此你就不用浪費時間在嘗試安裝不能讀取你備份磁帶的版本。

請確定你包括了以下程式: init, getty 或相等的程式, login, mount, 某種可以執行你rc scripts 的 shell, 以及一個從 sh 指向這個 shell 的 link 。

/lib

在 /lib 中,你要放入必要的共享函式庫 (libraries) 與載入程式 (loaders) 。如果無法在你的 /lib 目錄中找到必要的函式庫,那麼系統將不能夠開機。如果你很幸運,你可能會看到告訴你為什麼會發生如此情況的錯誤訊息。

近來每一個程式至少都要求 libc 函式庫, libc.so.N ,其中 N 是目前版本的編號。請檢查你的 /lib 目錄。 Libc.so.N 通常是一個 symlink ,它指向一個具有完整版本編號的檔名:

% ls -l /lib/libc*
-rwxr-xr-x   1 root     root      4016683 Apr 16 18:48 libc-2.1.1.so*
lrwxrwxrwx   1 root     root           13 Apr 10 12:25 libc.so.6 -> libc-2.1.1.so*

在這個情況下,你會想要 libc-2.1.1.so 。為了找到其它函式庫,你應該要看過所有你打算包括的二進位檔,並且用 ldd 指令檢查它們的相依性。舉例來說:

        % ldd /sbin/mke2fs
        libext2fs.so.2 => /lib/libext2fs.so.2 (0x40014000)
        libcom_err.so.2 => /lib/libcom_err.so.2 (0x40026000)
        libuuid.so.1 => /lib/libuuid.so.1 (0x40028000)
        libc.so.6 => /lib/libc.so.6 (0x4002c000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
在右手邊的每一個檔案是一定要有的。有些檔案可能是一個 symbolic link 。

請注意某些函式庫 相當地大 ,而且並不能輕易地配合你的 root filesystem 。舉例來說,上述的 libc.so 大約有 4 megabytes 。因此,在你 copy 它們到你 root filesystem 的同時,你可能需要除去一些函式庫。請看 Reducing root filesystem size 這一節以了解 instructions 。

在 /lib 內,你也必須包括一個 loader 供這些函式庫使用。這個 loader 不是 ld.so (給 A.OUT 函式庫使用),就是 ld-linux.so (給 ELF 函式庫使用)。較新版的 ldd 會如同上述的例子,正確地告訴你需要哪一種 loader ,然而舊版的就不會。如果你並不確定你需要哪一種 loader ,就對函式庫執行 file 指令。舉例來說:

% file /lib/libc.so.4.7.2 /lib/libc.so.5.4.33 /lib/libc-2.1.1.so
/lib/libc.so.4.7.2: Linux/i386 demand-paged executable (QMAGIC), stripped
/lib/libc.so.5.4.33: ELF 32-bit LSB shared object, Intel 80386, version 1, stripped
/lib/libc-2.1.1.so: ELF 32-bit LSB shared object, Intel 80386, version 1, not stripped
QMAGIC 指出 4.7.2 版是給 A.OUT 函式庫使用,而 ELF 指出 5.4.33 以及 2.1.1 版是給 ELF 使用。

請 copy 你所需要的指定 loader(s) 到你所建立的 root filesystem 。針對所包括的二進位檔,函式庫與 loaders 應該被 仔細地檢查 。如果 kernel 不能載入所需的函式庫,那麼 kernel 就會在沒有錯誤訊息的情況下停止運作。

4.4 對 PAM 與 NSS 的提供 -- Providing for PAM and NSS

你的系統可能會需要動態地載入未被 ldd 所見的函式庫。如果你沒有提供函式庫給這些項目,那麼你會在登錄 (log in) 或使用你的 bootdisk 時遭遇到困難。

PAM (Pluggable Authentication Modules)

如果你的系統使用 PAM (Pluggable Authentication Modules) ,那麼你必須在你的 bootdisk上為 PAM 做一些預備。簡而言之, PAM 是一種複雜精密的模組化方法,針對使用者認證以及控制使用者對服務的存取。一個決定你的系統是否使用 PAM 的簡單方法,是對你的 login 可執行檔執行 ldd ;如果輸出包括 libpam.so ,你就需要 PAM 。

幸運地,安全性通常並非 bootdisk 所關心的議題,因為任何對機器有實際存取權的人,通常能做任何他們無論如何想做的事。因此,你可以有效地關閉 PAM ,只要在你的 root filesystem 造出一個簡單的 /etc/pam.conf 檔,這個檔看起來像這樣:


OTHER   auth       optional     /lib/security/pam_permit.so
OTHER   account    optional     /lib/security/pam_permit.so
OTHER   password   optional     /lib/security/pam_permit.so
OTHER   session    optional     /lib/security/pam_permit.so

請記得也 copy /lib/security/pam_permit.so 這個檔到你的 root filesystem 。這個函式庫只有大約 8K ,因此它只佔用極小量的 overhead 。

請記得這個組態設定允許任何人對這台機器上的檔案以及服務有完整的存取權。如果你因某種理由而在乎你 bootdisk 的安全性,那麼你就必須 copy 一些或是全部你硬碟的 PAM setup 到你的 root filesystem 。請確定曾仔細地讀過 PAM 文件,並且 copy 任何在 /lib/security 中所需要的函式庫到你的 root filesystem 上。

你同時必須包含 /lib/libpam.so 到你的 bootdisk 上。但是你已知這件事,因為你已對 /bin/login 執行過 ldd ,這動作顯示了其相依性。

NSS (Name Service Switch)

如果你正使用 glibc (aka libc6) ,你就必須為名稱服務 (name switch) 進行預備,否則你會無法 log in 。 /etc/nsswitch.conf 這個檔案控制資料庫對各式服務之搜尋 (lookups) 。如果你並不打算從網路上存取服務 (比如說: DNS或 NIS 搜尋),那麼你只需要準備一個簡單的 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

這樣就指定每一項服務只被 local files 提供。你同時需要包括 /lib/libnss_files.so.X ,在此 X 是 1 的話是指 glibc 2.0 ,而 2 的話是指 glibc 2.1 。這個函式庫將被以動態方式載入以處理檔案搜尋。

如果你打算從你的 bootdisk 存取網路,那麼你會想要製作一個更精巧複雜的 nsswitch.conf 檔。細節請參考 nsswitch man page。最後,請記得你必須為你所指定的每一項 服務 (service) ,把 /lib/libnss_service.so.1 檔放入 bootdisk 中。

4.5 模組 -- Modules

如果你有一個模組化的 kernel ,你必須考量在開機後,你想要從你的 bootdisk 上載入哪一種模組。你可能會想要包括 ftapezftape 模組 (如果你的備份磁帶是floppy tape),以及給 SCSI 設備用的模組 (如果你有 SCSI 設備),也可能是 PPP 或 SLIP 支援的模組 (如果你在緊急情況下想要存取網路)。

這些模組可能會被放在 /lib/modules 。你同時也應該包括 insmod, rmmodlsmod 。根據你是否想要自動地載入模組,你可能也要包括 modprobe, depmodswapout 。如果你使用 kerneld,請與 /etc/conf.modules 一起包括進來。

然而,使用模組的主要優點在於你可以把非關鍵 (non-critical)模組移到 utility disk 上,在需要用到時才載入,這樣在你的 root disk 上會使用比較少的空間。如果你要處理許多不同的設備,這個方法比建立一個內建支援許多設備的巨大 kernel 來得好。

請注意,為了啟動 (boot) 一個壓縮的 ext2 filesystem ,你必須有 ramdisk 與內建 ext2 支援。 它們不能夠以模組的方式被提供。

4.6 一些最後的細節 -- Some final details

某些系統程式,諸如 login ,當 /var/run/utmp 檔與 /var/log 目錄不存在時,會發出警告。所以:

        mkdir -p /mnt/var/{log,run{
        touch /mnt/var/run/utmp

最後,在你設定 (set up) 完所有你所需的函式庫後,執行 ldconfig 以在 root filesystem上重新製作 (remake) /etc/ld.so.cache 。這個 cache 會告訴 loader 到哪裡找到函式庫。要重新製作 ld.so.cache,請下達以下指令:

        chdir /mnt; chroot /mnt /sbin/ldconfig

chroot 是必要的,因為 ldconfig 總是會為 root filesystem 重新製作 cache 。

4.7 Wrapping it up

一旦你完成 root filesystem 的建構工作,就 unmount 它,將之 copy 成一個檔案並壓縮它:

        umount /mnt
        dd if=DEVICE bs=1k | gzip -v9 > rootfs.gz

結束後,你會有一個名為 rootfs.gz 的檔案,這就是你被壓縮過的 root filesystem。你應該檢查它的 size 以確保它能放在一張軟碟片上;如果不行,你就必須回溯並移除一些檔案。 Reducing root filesystem size 這節有一些提示,告訴你有關減少 root filesystem 的 size 。


Next Previous Contents