Acknowledgement: 這篇文章是坊主我為一本Linux書(未出版)所寫的一個章節,這個章節是這本書從初階到進階的一個轉捩點,個人覺得是非常精彩的一章,因為這裡面有參考很多HowTo寫滴哦。這本書的目錄(Table of Content)是我設計的,所以特別要求老師,本章節讓我來撰寫。雖然我不知道這本書什麼時候會出版,不過應該還很久,呵呵,因為這本書裡頭有很多重要的技術,是目前工業界最需要的,例如:Linux downsizing、Linux Device Driver、Kernel Modules、以及Gtk+Glade等等……可以說是相當具規模的一本書,當然我快當兵了,這本書其他章節會讓學弟接手,希望學弟能好好寫這本書丫……呵呵。本來這個檔案是一個Word檔,如果各位想下載Word檔,請按這裡。
在前面幾個章節中,目的在於使讀者能適應這個極powerful的系統,而且能成為Linux的「使用者」,可以說是讀者對Linux的「初體驗」。但在現實社會中,只會「使用」往往不能滿足那些想「駕馭」的人。沒錯,從本章開始,讀者會與Linux做「第二類接觸」,讓你更進一步的認識這個作業系統以及前幾個章節無法交代清楚的地方,都會在這裡做一個更深入的探討。
前面許多章節,讀者學會了利用Red Hat所提供的高階管理程式或其他工具來設定一個屬於您個人Style的Linux。然而Linux的發行版本(distribution)本當多,而每一款發行版本所提供的高階設定程式都不盡相同。所以在面對非Red Hat的發行版本時,可能會因為找不到這些高階的設定程式,而手足無措。如果有這種情況,表示您對Linux並未完全熟悉,只能說是對Red Hat發行版本的Linux熟稔而已。因此,這一章所要探討的內容,無關發行版本,純粹以Linux最根本的工具來做最細部的調整。而且,本章不但要您能做細部調整,而且更要讓讀者能全盤了解和掌握Linux的整個運作,包括開機程序、Linux Loader、Runlevel、以及其他重要的基本設定等……。
其實在介紹本章時,有時會用到shell script,但是要完全了解shell script要等到下一章Bash Programming時才會介紹。這也是筆者頭痛的地方,按理說,應該先介紹系統的基本開機程序,接下來再深入探討各種Programming的技巧,但是,要介紹的開機程序中,又有幾個部分會牽涉到shell script。所以,在本章的某些小節的shell script,筆者會儘量以最清楚易懂的方式來說明,當然,如果讀者迫不及待想知道更多shell script的寫法,也可以先到下一章Bash Programming去嘗試一下shell script的寫法。
讀完第七章以及之後的章節,不但能使讀者的Linux功力大增,而且經過自行製作root filesystem,更能讓您創造一個屬於自己的Linux發行版本。尤其是想幫Linux做「廋身」的讀者,本章更是重要的一環。
本章的開始,要先介紹Linux的開機程序。因為Linux的開機程序看似簡單,好像只要LILO載入Kernel後就沒事了。事實上,這裡頭隱藏著許多學問,也有很多地方是可以調整的,所以為了能順利的了解以後所要介紹的各種設定,先了解開機程序,是有絕對的必要的。
首先是圖7.1中的第一個格子,Linux Loader部分。當您打開電腦電源後,是先執行BIOS偵測及檢查硬體,BIOS做完基本的工作後,接著會從第一個開機碟的第0磁柱的第0軌第一個磁區開始執行一連串的指令。這個地方就是鼎鼎大名的MBR(Master Boot Record),因為這個地方可以直接放各種作業系統的Kernel(如Linux Lernel),所以這個地方也就成為各作業系統的必爭之地了。但是如果您想要安裝多種作業系統在同一個硬碟中,那就必需有一位協調者出面,而LILO就是其中一種協調者。我們將在下一小節介紹LILO。
再下來是圖7.1中的第二個格子,Linux Kernel部分。不管您的Kernel是被如何被載入的,只要Kernel一動作之後,便開始對裝置驅動程式(device driver)和內部的資料結構(internal data structure)做初始化的動作。
再下來輪到圖7.1中的第三個格子,掛載root filesystem。當Kernel完成初始化程序之後,則會去查尋它自己的Image裡的一個稱之為ramdisk word的地方。這個地方記錄著該去哪裡找到root filesystem。前幾章提過,root filesystem是一個硬碟的partition,而這個partition將會被掛載到「根」的目錄,也就是「/」。當然,並不是隨便一個partition都可以拿來當root filesystem的,本章最後一節將會介紹如何建立一個最陽春的root filesystem。而當root filesystem順利的掛載完成之後,則會在開機過程中顯示以下的字樣。VFS: Mounted root (ext2 filesystem) readonly.接下來輪到圖7.1中最後一格,執行init的部分。有了root filesystem後,接下來的工作就交給了/sbin/init(or /bin/init)。而init會去找其設定檔/etc/inittab內的sysinit所定義的script(如/etc/rc)來執行。這個由一堆shell script所寫成的檔案是用來做啟動各種系統基本設定和服務的script,例如:開機後的fsck磁碟掃瞄、載入kernel module、設定網路連結、以及掛載/etc/fstab所指示的file system等工作。完成這個script的執行後,控制權又回到init。接下來init會進入/etc/inittab中所定義的initdefault的runlevel。有關init和runlevel,我們也將在本章的第5小節做完整的介紹。
最後,init若進入標準runlevel之後,則引發/sbin/getty程式來做console和tty之間的溝通,而/sbin/getty再引發/bin/login而出現Login:字樣。如此,就是整個開機進入login畫面的所有過程。
在Linux的世界中,有兩個相當知名的Loader,一個是LILO(LInux LOader)另一個Loadlin。而這兩個所用的策略也不一樣。LILO是以調停者的身份,以大軍進駐MBR(Master Boot Record)的方式,強制干涉各作業系統開機;而Loadlin則是寄人籬下(Real Mode MSDOS),而當Loadlin被執行時,才上演「篡位」的劇碼。LILO雖然霸道了點,但是有的時候真的非它不可呢;而雖然Loadlin是寄人籬下,但是該有的功能,也是一樣不缺的。
7.2.1. 開機參數
首先,LILO大概是Linux的各種Distribution中,最受觀迎的。它是由Werner Almesberger所設計出來的程式。它的特點是可以開啟多種Kernel (DOS,OS/2,Linux,FreeBSD等)、它的設定檔是一個純文字的檔案 、而且它能很有彈性的以參數方式,在LILO:指令列直接加開機參數(boot argument)加以調整。而一般來說,有LILO的電腦開機後,會停在顯示「LILO:」的地方。這個時候,按tab鍵,會顯示有幾個開機選項,如:
LILO: linux win95當然如果一般都會有預設值,可能是設在linux,或其他。但是LILO還允許使用者在決定哪個系統開機時,順便設定開機參數。例如:LILO: linux root=/dev/hda4上例中,我們可以加上root=/dev/hda4的參數來告訴LILO:「不管預設root-filesystem在什麼地方,請以/dev/hda4來當root-filesystem開機」。LILO: linux single如上例,或是加上single來告訴LILO:「不管/etc/inittab裡預設的runlevel,請以single user mode的runlevel開機」。
說到Loadlin,也要來好好的介紹一下了,作者Hans Lermen,Loadlin是MSDOS環境中的一個執式,它不會像LILO那樣霸道地占著MBR,但是只要它一被執行,毫無「防禦」能力的Real Mode MSDOS也只能乖乖的交出系統控制權的。而很多有CD開機安裝功能的Linux CD Distribution也都是靠Loadlin來掌權的,可見Loadlin也是相當受到重視的一種Linux loader。而且它和LILO的參數相仿,又是在同一個時間,納入Linux Kernel原始檔中的一部分(從1.3.37之後)。
一般來說,loadlin的使用方法如下,在DOS的提示字元下輸入:loadlin kernel_imag root=/dev/hda1 ro如此,Loadlin在載入kernel_image後,就會以/dev/hda1來當root-filesystem。
以下是RedHat版Linux安裝光碟裡,CD開機後Loadlin所執行的批次檔內容(\dosutils\autoboot.bat):loadlin autoboot\vmlinuz initrd=..\misc\src\trees\initrd-local.img經過以上的介紹,讀者應該對這兩個loader有一點概觀性的了解。接下來,就可以介紹boot parameter了。那麼以下筆者就列舉常用的boot parameter,如果讀者想對這方面有更深的認識,可以查閱BootPrompt HowTo或查閱LILO和Loadlin的manual檔。
Root Filesystem參數
root=/dev/hdaN ~ /dev/hddN 實體的IDE裝置。 root=/dev/sdaN ~ /dev/sheN 實體的SCSI裝置。 root=/dev/xdaN ~ /dev/xdbN 相容XT的裝置。 root=/dev/fdN 軟碟機裝置。 root=/dev/nfs 非實體硬碟機,透過網路取得root filesystem。 ro readonly,在開機過程中,會用到root-filesystem裡的幾個檔案 (/etc/inittab和/etc/rc等),而在/etc/rc裡會做許多設定或是檢查 root-filesystem等工作。而在root-filesystem被掛載為readonly模式時, 才能安全的檢查root-filesystem。如fsck這樣的程式, 可以做root-filesystem的檢查,當然,它必須確定這個裝置在檢查過程中, 不會有寫入的動作發生,否則結果會很難預期。 而如果root-filesystem是以readonly的模式掛載,則fsck可以很安全的做 root-filesystem的全身檢查。當然,一般來說,當/etc/rc這個script完成 磁碟檢查之後,就會把它remount成readwrite(ex. mount -n -o remount,rw /)。 rw 用這個參數,表示無論如何,都會將root-filesystem掛載成readwrite模式。Ramdisk 參數
ramdisk_start=<offset> 這個參數用於製作Bootdisk時,用來定義ramdisk image在磁片中的起始位址, 好讓kernel載入這個image到ramdisk。如果是用兩張磁片,一片放kernel, 另一片放ramdisk image。這樣的話,ramdisk image一定是從第0個sector, 那就可以不用設定此參數了,因為這個參數的default值就是0。 load_ramdisk=1 or 0 這個參數將告訴Kernel是否要從磁片載入ramdisk image,如果是1表示要載入,0則否。 prompt_ramdisk=1 or 0 如果是用同一片磁片(Kernel和ramdisk在同一磁片)則這個參數不必設定 。而如果是兩張磁片開機,則勢必有另一張磁片是存放ramdisk, 所以必須將這個參數設為1,這樣的話,Kernel要載入ramdisk image時, 會先提示使用者:「請插入ramdisk image的磁片」。 ramdisk_size=<k-byte> 這個參數的default值是4096,也就是4MB。但是有的情況, 我們的ramdisk image會很大,如筆者曾經嘗試將X-window系統 做在一個loopback device裡,而變成ramdisk image,容量高達28MB。 而對目前PC而言,記憶體動不動就是256MB,所以硬要讓這麼大的ramdisk image當成root-filesystem,就必須動用這個參數了。Memory Handling參數
mem=<memory_size> 這個參數用來告訴或限制Kernel所能使用的記憶體。 而且在以前設定BIOS的時候,除了之前的Y2K問題之外, 也有另一個問題存在。那就是當Kernel呼叫BIOS call來 取得系統所安裝的記憶體時,BIOS最大能回應的記憶體容 量只有64MB。真是要命,現在動不動就256MB,怎麼辦呢? 只好用這個參數來告訴Kernel真正的記憶體容量是多少。 然而,以多報少比較不會出問題,但是以少報多的話就得 小心了。BootPrompt HowTo上特別引用了Linux Kernel原 創祖師爺Linus的話:「The kernel will accept any `mem =xx' parameter you give it, and if it turns out that you lied to it, it will crash horribly sooner or later. The parameter indicates the highest addressable RAM address, so `mem=0x1000000' means you have 16MB of memory, for example. For a 96MB machine this would be `mem=0x6000000'. If you tell Linux that it has more memory than it actually does have, bad things will happen: maybe not at once, but surely eventually.」。祖師爺的這番話就是告誡使用者們, 切勿以少報多,以免當機。mem=0x????的用法,其實還可以用 可讀性較高的寫法,也就是使用k(kilo-byte)和M(Mega-byte) ,大小寫無關。如256MB的記憶體,可以設定mem=256m。其他重要參數
single 這個參數會傳送到init這個process產生時,才由init來 接收這個參數。一般來說,如果沒有使用這個參數,則在 init執行時,會去找/etc/inittab所定義的default runlevel ,而開機後就會進入這個runlevel。而這個參數的用意, 是強制init執行後進入single user的runlevel。通常會用 這個參數的時機,都是因為忘了root的密碼,所以才進入不用 輸入密碼的單使用者模式,才能重新設定root密碼。以上所列舉的是一些較為常用的Boot parameter,適用於LILO和Loadlin。因為LILO和Loadlin所使用的策略不同,所以有些參數不能在上面介紹(如initrd)。所以LILO和Loadlin的細節就留到下兩節來討論。
7.2.2. LILO (LInux LOader)
設定LILO,不外乎就是設定/etc/lilo.conf這個檔案。這個檔案的內容,大致上分為Global Options和Per-Image Options。
Global Options:
backup=backup-file 拷貝原來的開機磁區(boot sector)裡的內容到backup-file。 boot=boot-device 指定要安裝到哪一個裝置上,如果是某一個某一個磁碟, 則此碟之MBR會被寫入。如果是某一個partition,則寫 入partition前頭。預設值是lilo所在的磁碟。 compact 當有這個參數時,LILO會試著在一次讀取的請求中,讀 取目標磁區以及其鄰近的磁區的內容,以增加開機的速度。 default=label_name 當delay的時間到,或使用者直接按下Enter時,會以 default所定義的image來開機。(預設在第一個Image) delay=tsecs 等待使用者做溝通的時間,十倍秒表示,預設為0。 disk=device-name 用來變更BIOS的開機碟找尋順序,如 disk=/dev/sda bios=0x80 disk=/dev/had bios=0x81 disktab=disktab-file /etc/disktab存放各式disk參數的檔案,若沒有則省略。 force-backup=backup-file 和backup一樣,但是會overwrite舊檔。 install=boot-sector 安裝所指定的檔案到boot sector。預設是/boot/boot.b。 linear 使用這個參數時,則產生線性的sector位址來取代 sector/head/cylinder位址。但是,如果是製作Bootdisk 的話,可能會減少這個Bootdisk的可攜性。但對於容量很 大的硬碟來說,產生的線性位址足以達到sector/head/cylinder 位址所無法表示的磁區。 map=map-file 指定map-file的位置。預設在/boot/map)。 message=message-file 指定message-file位置。這個message-file的內容會在LILO: 之前顯示。但限制這個檔案的最大容量65535bytes。 prompt 使用這個參數,可以直接顯示開機選項。 timeout=tsecs 在tsecs裡,沒有按鍵輸入,則開啟default Image。預設是無限。Per-Image Options:
image=pathname 由image帶頭的,可以有以下的參數設定: read-only 掛載root-filesystem的模式為read-only。 read-write 掛載root-filesystem的模式為read-write。 ramdisk=<size> 指示Kernel開機時,初始化ramdisk的大小。 root=<root-device> 開機後要被掛載到「/」的partition。 vga=<mode> 設定VGA模式,0代表80x25,1代表80x50 label=<name> 選項名稱。 other=pathname 由other帶頭的,可以有以下的參數設定: table=device boot sector不會傳遞partition information到這個OS。 label=<name> 選項名稱。因為case by case,每個人所使用的環境都不相同,不過在一般的情況,/etc/lilo.conf的內容是這樣的。boot = /dev/hda # 吃掉/dev/hda的MBR install=/boot.b # 安裝的boot sector map=/boot/map delay = 10 # 延遲時間,這段時間內可以和LILO互動 image = /boot/vmlinux # Linux Kernel Image root = /dev/hda1 # 要掛載的root-filesystem label = Linux # 顯示出的選項名稱 read-only # 以read-only模式掛載root other = /dev/hda4 # 這是「其他」系統的開機碟 table = /dev/hda # the current partition table label = dos # 顯示出的選項名稱第一個boot=/dev/hda指示LILO要吃掉哪一塊硬碟的MBR,當然這裡也可以是某一個分割區,如果是一個分割區的話,那MBR還是需要一個可以選擇開機的系統。所以一般都會讓LILO直接吃掉MBR。delay=10這個設定的用意,是為了要讓使用者有時間和LILO做溝通,選擇要開機的系統。而底下兩個Block則是開機時的兩個選項。
注意這兩個Block一個是由image=/boot/vmlinux帶頭,這意謂著這個開機選項是Linux相仿的系統,而跟隨後面的設定,就是開機過程中的所有設定。另一個是以other=/dev/hda4帶頭的,則是MS-DOS或其相仿的系統。
上面的範例,算是一個很General的case。但是很顯然的,在很多實際的應用上,會有很多Special case。當然這些Special case很難去一一討論的,就連LILO-mini HowTo也是case by case的介紹。話說回來,像Linux這樣的一個開放的系統,使用者要學習它,更要以「舉一反三」的學習態度來面對。
7.2.3. Loadlin
如前所說,Loadlin是一個寄居於DOS下的程式,因為Real mode DOS的防衛系統非常差(可以說跟本沒有)。不過這也是DOS可愛的地方,因為你想在這個系統上做什麼事都可以,當然也包括毀滅DOS。如Loadlin就是一個毀滅性的程式,想想DOS身為一個「作業系統」,卻允許別的程式毀滅DOS,想想真是覺得可愛。
而Loadlin和一般DOS程式一樣,有很多參數可以用,而關於它的參數部分,7.2.1節的所有介紹的參數都可以使用上去。所以這裡我們僅舉幾個例子來說明Loadlin。在這之前,先看看Loadlin.exe的用法。Loadlin.exe就如上列用法,Loadlin就是這麼簡單,而讓我們看看一般的Linux載入:[options] Loadlin.exe vmlinuz root=/dev/hda1 ro上列就是以vmlinuz為linux kernel。開啟後以/dev/hda1當root,掛載模式為read-only。
再來看看另一個採用ramdisk方式的作法:Loadlin.exe vmlinuz root=/dev/ram ro initrd=fs_image ramdisk_size=8000k7.2.4. rdev工具
如前所說過的,linux kernel也是可以放到MBR來開機的,而不需要用到任何loader,但是如果硬要直接這樣開機,則勢必要有一個可以調整kernel image中的ramdisk word的工具,才能改變kernel image,而在開機過程中,找到該找的root-filesystem、swap-device、或其他的設定。而rdev這個工具就是用來設定可開機之linux kernel內部ramdisk word的內容的執式。
為了讓讀者能很快的了解這個工具,筆者決定以範例方式說明。(kernel_image可以視為一個檔案,或用dd轉移過的磁碟,如/dev/fd0)rdev kernel_image 顯示kernel_image裡的設定。rdev kernel_image /dev/hda1 改變kernel_image裡的Root-filesystem,設定為/dev/hda1。rdev -R kernel_image 1 改變kernel_image裡的Root-flag,設定1就表示read-only;0為read-write。rdev -s kernel_image /dev/hda3 改變kernel_image裡的Swap device,設定為/dev/hda3。rdev -r kernel_image 8000 改變kernel_image裡的RAM Disk Size,設定為8000(KB)。rdev -v kernel_image 1 改變kernel_image裡的Video Mode,設定值如下: -3 = ask, -2=Extended, -1=NormalVGA, 0=key0, 1=key1, ... 正的數值會先到目前系統尋找設定。
init是Linux Kernel啟動後,第一個產生的process。大部分的系統上,這個執行檔位於/sbin/init。它的設定檔為/etc/inittab。它的功用,是依所給定的runlevel來執行system initial的工作。而system initial的工作,則是靠下一節所介紹的System initial rc這個script來做事。
那runlevel又是什麼呢?runlevel筆者真不知道該怎麼翻譯,所以乾脆就不翻了。雖然不知道該怎麼翻譯runlevel,不過runlevel的涵意,是指開機後,所要進入的模式。如果用M$ windows來做的比喻,就像開機後選擇要進入命令提示(command prompt)模式、安全(safety)模式、或是正常(normal)模式等…。這樣說,讀者應該就明白了吧。而linux的開機模式有7種,而且,除了3種是保留值以外,其他四種還可以自行定義哦。現在就讓我們看看有哪些是保留值開機模式。
Runlevel保留值:
0, halt the system
1, get system down into single user mode
6, reboot the system
以上這些就是init的保留值,而且除了runlevel 1(single user mode)之外,是不可以用來當/etc/inittab裡的default runlevel,畢竟您不會想開機後就進入關機或重新開機的程序吧。
而其他的2, 3, 4, 5,則可以用來自行定義,而RedHat是這樣定義的:
2, Multi-user mode, without NFS.
3, Full Multi-user mode.
4, 未用
5, X11
另外,之前也說過init在切換runlevel時,是引發System init rc的script來做事,所以System init rc的撰寫也是很重要的。
接下來我們可以看看一個最陽春的/etc/inittab的寫法,而我們用它來介紹一下/etc/inittab的格式,如果讀者想知道更多關於/etc/inittab,可以查閱inittab的man page(man inittab):id:2:initdefault: si::sysinit:/etc/rc 1:2345:respawn:/sbin/getty tty1 2:2345:respawn:/sbin/getty tty2 3:2345:respawn:/sbin/getty tty3 4:2345:respawn:/sbin/getty tty4 5:2345:respawn:/sbin/getty tty5 6:2345:respawn:/sbin/getty tty6在這個檔案中,以「#」開頭者,視為註解。而這一句「id:2:initdefault:」是定義預設要進入的runlevel是2。而「si::sysinit:/etc/rc」這句,則是告訴init,System initial rc的script檔是/etc/rc(必須設為可執行屬性)。而後面則是定義登入的平台,在tty1~6,而且是在runlevel=2, 3, 4, 5,時才有這些登入平台。
熟悉DOS環境的人應該都知道config.sys這個裝置設定檔案。當DOS開機後,會找到這個檔案來處理有關裝置驅動程式的安裝,包括記憶體的驅動、光碟機驅動、以及音效卡驅動程式等等……。而某些系統參數,也是經由這個檔案來設定的。System initial rc這個shell script在Linux中所扮演的角色,確實和config.sys在DOS中所扮演的角色有些許雷同之處,不過System initial rc卻比config.sys強得多了,它包括了啟動許多server和daemon。
一個好的rc必須兼具有init切換runlevel時所必須執行或清除設定的能力,所以真正System initial rc這個script所做的事相當多的,而且rc檔牽涉到shell script programming,所以本節僅討論安裝module及開啟網路功能,而啟動各種Service和Server就不介紹了,讀者應該能自行體會。
#!/bin/sh /bin/mount -av # 掛載/etc/fstab所列之filesystem /bin/hostname mpp1 # 設定hostname為mpp1 # kernel內建(built-in)的網路自我回路,先行啟動 DEVICE=lo IPADDR=127.0.0.1 NETMASK=255.0.0.0 BROADCAST=127.255.255.255 /sbin/ifconfig $DEVICE $IPADDR netmask $NETMASK broadcast $BROADCAST up # 先安裝module,也就是網卡的driver,以ne相容為例, # 而ne.o所需的symbols必須由8390.o來提供。所以先安裝8390.o /sbin/insmod /lib/modules/8390.o /sbin/insmod /lib/modules/ne.o io=0x240 irq=11 # 變數設定 alias eth0 ne DEVICE=eth0 IPADDR=140.118.123.144 NETMASK=255.255.255.0 NETWORK=140.118.123.0 BROADCAST=140.118.123.255 GATEWAY=140.118.123.254 # 啟動網路卡 /sbin/ifconfig $DEVICE $IPADDR netmask $NETMASK broadcast $BROADCAST up # 設定路由及通訊閘 /sbin/route add -net $NETWORK netmask $NETMASK $DEVICE /sbin/route add default gw $GATEWAY $DEVICE
如果說System initial rc好比DOS的config.sys的話,那麼把/etc/profile說成是autoexec.bat也不為過了。在DOS環境中的autoexec.bat是用來設定系統相關的命令提示字元(command prompt)和路徑等參數的檔案。而在Linux中,/etc/profile也是掌管相同的工作。
在這裡筆者也不打算討論太多關於Bash Programming的事,而在/etc/profile最重要的是Bash的內建參數的設定。這些參數有的只有參考的功能,而有的則是和環境的設定習習相關,尤其是「PATH」和「PS1」更是如此。所以在這個地方我們會將部分重要的內建參數拿出來討論,而其他的,則留在Bash Programming那一章裡做介紹。
PATH
設定執行檔搜尋路徑。其格式是用冒號「:」來做分隔符號,如:PATH="$PATH:/usr/X11R6/bin"
HISTFILE
命令歷史記錄檔。內定是「~/.bash_history」。
HISTSIZE
命令歷史記錄個數的最大限制。內定是500。
HISTFILESIZE
命令歷史記錄檔的檔案最大限制。內定是500。
HOME
使用者家目錄。
PS1
提示字元設定,內定為"bash\$ "。這裡可以設定任何有趣的提示字元,而提示符號(Shell Prompt)的表示法如下所示:
\t 現在時間。
\d 現在日期。
\n 新行。
\s shell的名稱。
\w 目前工作目錄的完整路徑。
\W 目前工作目錄。
\u 使用者名稱。
\h 主機名稱。
\# 命令號碼。
\! 歷史號碼。
\$ 若EUID=0,則顯示「#」,否則顯示「$」。
\nnn 八進制字元。
\\ 「\」符號
\[ 開始一序列不可列印的字元。
\] 結束一序列不可列印的字元。
PS2
第二提示字元設定,內定為"> "。
PS3
select命令所使用的提示訊息。
PS4
執行追蹤時用的提示訊息設定,內定為"+ "。
另外,/etc/profile裡也會export(這也是Bash的內建命令)一些有用的參數提供Bash環境下,更多好用的參數和函數, 如:
USER=`id -un`
HOSTNAME=`hostname`
經過前面幾個小節的介紹,您是否觀念清楚了呢?如果還有些模糊,沒關係,在這個小節我們將利用前面所介紹的觀念來實作一個小型的root filesystem,而經過這個root filesystem的製作,您的觀念一定會更加的清楚。這是參考BootDisk HowTo所寫出來的,是一個最陽春的Linux系統,如果你用的版本越古老,整個size會更小喔。
7.6.1. 建立root filesystem
要建立這個root filesystem,首先當然要有一個乾淨的磁區囉。而讀者可以利用您多餘的硬碟分割區,或是利用軟碟機或是ramdisk。但是筆者建議使用loopback device,因為它直接就是一個檔案Image,做好之後可以壓縮起來使用,而其他的還要經過dd轉成檔案,而且容量也較沒有loopback device有彈性。如果您要使用的是loopback device,請確定您的系統上有/dev/loop0等裝置,如果沒有的話,請先建立一個loopback device:
# mknod /dev/loop0 b 7 0建立8MB的檔案以做為loopback device之用,「#」為root的提示字元:# dd if=/dev/zero of=image bs=1k count=8192 # mke2fs -m0 -i 2000 image # mount -t ext2 -o loop image /mnt為了增加壓縮的比例,所以要先使裝置內容清為0,尤其是當您做多次的檔案處理。因為當您刪除一個檔案時,檔案系統只會釋放這個block而不會清除這個block的內容,所以會影響壓縮比例。這就是為什麼在製作之前要用dd來清除個將要製作filesystem的裝置。
對於將被載入ramdisk的filesystem,Linux Kernel只能辨識兩種型態的檔案系統,即minix和ext2。而其中ext2是較被採納的,因為它可以隨意調整inode,而HowTo中則建議使用-i 2000的參數。而-m?的參數,是為了防止mke2fs保留root空間,這樣才能得到較大的使用空間。
用過Linux的人應該知道你的根目錄/下有什麼重要的基本東東吧…
/dev,/etc,/bin,/sbin,/lib,/tmp,/proc,/var,這些是必要的。而為了讓最高權限使用者root,能有目己的家目錄,所以我們可以再加一個/root。簡單吧,你完成第一步了喔。
7.6.2. /etc目錄裡的檔案
在這個目錄下該有的檔案如下:
/etc/passwd, 所有使用者的password設定和shell設定。 /etc/shadow, 只有root才能讀取的shadow password。 /etc/group, 使用者群組設定。 /etc/termcap, Terminal Capability DataBase /etc/profile, 共用環境設定檔。 /etc/gettydefs, Getty程式設定檔。 /etc/inittab, init設定檔 /etc/rc, System initial rc /etc/fstab, 在開機後將被掛載的filesystem建議直接copy後再修改的檔案:/etc/password, /etc/shadow, /etc/group, /etc/termcap, /etc/profile。而/etc/gettydefs容量不大,直接copy不用修改,而/etc/inittab, /etc/rc, /etc/fstab則可以自行動手撰寫。
其中的/etc/passwd裡,到少要有root,而如果您想有其他使用者,請別忘了設定家目錄和shell。這裡簡單介紹/etc/passwd的格式,要是您對security有興趣,也可以到官方網站www.linux.org看看User Authentication HowTo。
![]()
如上所示,/etc/passwd裡有root和Andy兩位使用者,而/etc/passwd的每一個設定都是用「:」來隔開。依次是,使用者帳號、密碼、使用者編號、群組編號、附註說明欄位、家目錄、shell設定。其中密碼的地方因為使用了shadow password所以只會出現「x」,而真正的密碼則存在/etc/shadow這個檔案內。
再來看看/etc/group,它的內容比較簡單。
![]()
/etc/termcap是終端機型態資料庫,這個檔案不小哦。而在這次的應用中,只要保留linux或linux-console部分的設定就可以了,其他的都可以刪除。有關termcap的細節,讀者可以查閱man page (man termcap)。以下是筆者刪減後的/etc/termcap內容。linux|linux-lat|linux console:\ :am:eo:mi:ms:ut:xn:xo:\ :co#80:it#8:li#25:\ :&7=^Z:@7=\E[4~:AL=\E[%dL:DC=\E[%dP:DL=\E[%dM:\ :F1=\E[23~:F2=\E[24~:F3=\E[25~:F4=\E[26~:F5=\E[28~:\ :F6=\E[29~:F7=\E[31~:F8=\E[32~:F9=\E[33~:FA=\E[34~:\ :IC=\E[%d@:K2=\E[G:al=\E[L:bl=^G:cd=\E[J:ce=\E[K:\ :ch=\E[%i%dG:cl=\E[H\E[J:cm=\E[%i%d;%dH:cr=^M:\ :cs=\E[%i%d;%dr:ct=\E[3g:cv=\E[%i%dd:dc=\E[P:dl=\E[M:\ :do=^J:ei=\E[4l:ho=\E[H:ic=\E[@:im=\E[4h:k1=\E[[A:\ :k2=\E[[B:k3=\E[[C:k4=\E[[D:k5=\E[[E:k6=\E[17~:\ :k7=\E[18~:k8=\E[19~:k9=\E[20~:k;=\E[21~:kB=\E[Z:\ :kD=\E[3~:kI=\E[2~:kN=\E[6~:kP=\E[5~:kb=^H:kd=\E[B:\ :kh=\E[1~:kl=\E[D:kr=\E[C:ku=\E[A:le=^H:mr=\E[7m:\ :nd=\E[C:nw=^M^J:r1=\Ec:rc=\E8:sc=\E7:sf=^J:sr=\EM:\ :st=\EH:ta=^I:u6=\E[%i%d;%dR:u7=\E[6n:u8=\E[?6c:\ :u9=\E[c:up=\E[A:vb=200\E[?5h\E[?5l:ve=\E[?25h:\ :vi=\E[?25l:tc=klone+sgr:tc=klone+color:tc=klone+acs:接下來,/etc/profile裡沒有太多東西要改,如下:PATH="$PATH" PS1="[\u@\h \w]\\$ " USER=`id -un` LOGNAME=$USER HOSTNAME=`/bin/hostname` HISTSIZE=1000 HISTFILESIZE=1000 export PATH PS1 HOSTNAME HISTSIZE HISTFILESIZE USER LOGNAME最後有三個檔案可以自己來撰寫。首先是/etc/inittab,示範如下:id:2:initdefault: si::sysinit:/etc/rc 1:2345:respawn:/sbin/getty tty1 2:2345:respawn:/sbin/getty tty2 3:2345:respawn:/sbin/getty tty3 4:2345:respawn:/sbin/getty tty4 5:2345:respawn:/sbin/getty tty5 6:2345:respawn:/sbin/getty tty6以上定了開機所進入的runlevel是2。而System initial rc檔為/etc/rc,最後再宣告6個平台,分別是tty1到tty6,而只有在runlevel是2,3,4,5時才存在這些平台。
再下來是/etc/rc的示範如下(請用chomd 755 /etc/rc改為可執行屬性):#!/bin/sh /bin/mount -av /bin/hostname Mpp1在下一章會介紹shell script的寫法,而在這裡我們簡單的寫了行,其中「#!/bin/sh」是不會執行的,只是用來判定這個檔案是shell script檔而已。雖然沒有這句還是可以執行,不過您下「file /etc/rc」指令時,它只把/etc/rc看成是English Text,而不是shell script。接下來的「/bin/mount -av」指令則把/etc/fstab裡定義的filesystem全部mount進來。最後,如果沒有設定hostname的話看起來會怪怪的,因為我們在之前/etc/profile裡定義的PS1="[\u@\h \w]\\$ "],其中的\h就是表示顯示hostname,所以我們就在這裡定義我們的hostname為Mpp1。
最後一個檔案是/etc/fstab,如前所說,/etc/rc其中的一句敘述「/bin/mount -av」就是要將/etc/fstab所列之filesystem掛載到系統上,所以這個檔就非寫不可了。/dev/ram0 / ext2 defaults none /proc proc defaults上式中,我們假設我開機後會建立ramdisk來當root。請注意/proc是一個很特殊的目錄,是系統用來記錄各種重要資料的資料庫,而且一定要掛在/proc上。
7.6.3. /dev目錄裡的檔案
這個目錄是個很特別的目錄,是用來放一些特殊的device檔,device檔有兩種,一種是character device(如/dev/tty1),另一種是block device(如/dev/hda1)。關於這部分的資訊,可以去看看有關device driver的書,它會教你這些檔案是怎麼做出來滴(偶正在K)。而我們在這裡,只要copy人家建立好的就可以啦,但是要注意參數喔。因為這些特殊的檔案,直接用的話,表示去動這些device,所以要copy檔案要下-dpR這個參數,系統才不會誤以為你要copy的是這個device內的東東。(ex. cp -dpR /dev/hda1 .)
這裡偶再提供幾個必要的device: console, kmem, mem, null, ram0, ram, tty0~7
如果你要的話,也可以把磁碟機的device弄進來: hda, hdb, hdc, hdd, fd0, fd1,...
7.6.4. /sbin目錄裡的檔案
這個地方只有兩個執行檔是必要的,就是init和getty。而getty會引發的login則被放在/bin。
7.6.5. /bin目錄裡的檔案
這個地方建議copy以下東東:login, sh, bash, mount, ls, id, hostname,
其中sh是一個link檔,link到bash,要copy的話要下-dpR的參數(ex. cp -dpR /bin/sh .),如果要自己來建立link,可以用下ln指令(ex. ln -s /bin/bash sh)。
如果空間還不算吃緊,還可以儘量加入您要的程式,如:cp, mv, rm, mkdir, rmdir, mknod, umount, gzip, tar, vi, 等等……
7.6.6. /lib目錄裡的檔案
因為Linux的很多程式在跑的時候咧,都會去找動態的library來做動態聯結。但是該怎麼知道哪個程式使用了哪個動態library呢?不用耽心,丫亮不是說過嗎:「凡走過必留下痕跡。」。所以我這次特蒐小組的任務就是……找到所有我們有用到的執行檔的動態Library。重點來了,怎麼找呢?這時候ldd指令就派上用場了。我們就做個使用ldd的範例。
# ldd /sbin/init /usr/share/chinese/xa+cv/wrap.so /lib/libutil.so.1 /lib/libc.so.6 /lib/libdl.so.2 /lib/ld-linux.so.2看到上面的結果你是不是被嚇到了呢?看起來很多,其實這裡面的/lib/libc.so.6, /lib/libdl.so.2, /lib/ld-linux.so.2是每的執行檔都會有的動態聯結。而真正的/sbin/init所用到的只有/usr/share/chinese/xa+cv/wrap.so和/lib/libutil.so.1而已啦。
有很多Library都是link檔,記得連link檔一起copy喔。而本來wrap.so不是在/lib目錄,但是因為要downsizing所以我們也把它copy到/lib下吧。讓我幫各位整理一下我們所用到的library吧:/usr/share/chinese/xa+cv/wrap.so /lib/libc.so.6 ==> /lib/libc-2.1.1.so /lib/libdl.so.2 ==> /lib/libdl-2.1.1.so /lib/ld-linux.so.2 ==> /lib/ld-2.1.1.so /lib/libutil.so.1 ==> /lib/libutil-2.1.1.so /lib/libtermcap.so.2 ==> /lib/libtermcap.so.2.08 /lib/libcrypt.so.1 ==> /lib/libcrypt-2.1.1.so /lib/libpam.so.0 ==> /lib/libpam.so.0.66 /lib/libpam_misc.so.0 ==> /lib/libpam_misc.so.0.667.6.7. PAM
因為版本不同,有的版本的Linux不一定會使用PAM(Pluggable Authentication Modules),但是如果我們使用ldd /sbin/login,有發現使用libpam.so.0和libpam_misc.so.0的,就表示有用到PAM,則必須做以下的步驟。如果你的版本的/sbin/login沒有用到這兩個library則可以略過這個步驟。
讓我解釋一下PAM是在做什麼的吧,PAM是一個控制使用者login的東東,有一些特定的使用者,我們會給他特定的登入程序,一般都是先打使用者名稱再輸入密碼。而我們為了達到downsizing的目的,所以假定只有root使用者,所以我們可以讓他不用打密碼就可以進來。而在設定中,如果找不到特定的使用者,則會被判定為OTHER。而 /lib/security/pam_permit.so 這個library就是不必打密而可以直接進入的設定(想知道更多的,可以去看PAM的HowTo)。
首先,編輯/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之後呢,因為我們只用到/lib/security/pam_permit.so所以記得把它copy到你的/lib/security下。
7.6.8. nsswitch
當系統引發一個位置(或檔案)的存取時,就一定會參考到這個檔,所以只好來個最陽春的設定好了。
首先,編輯/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之後呢,因為我們只用到files的service,所以只要複製/lib/libnss_files.so.?的檔就好。(其中的?=1表示for glibc2.0,?=2表示for glibc2.1)
而如果想擁有網路的性能,則必須查閱man nsswitch來編輯/etc/nsswitch.conf。而每增加一個service就必須存在著/lib/libnss_service.so的檔。
7.6.9. 最後要做的工作
做完以下的工作,一個最陽春的root-filesystem就完成嚕。
假設你的root-filesystem的partitioin被mount在/mnt的目錄,則:# mkdir -p /mnt/var/{log,run} # touch /mnt/var/run/utmp # ldconfig -r /mnt最後一道指令ldconfig -r /mnt的用意在建立/etc/ld.so.cache,這樣系統才能找得到動態連結的library。7.6.10. 啟用root-filesystem
之前提到過,製作root-filesystem時最好使用loopback device,因為這樣可以直接做成一個檔案。現在就假設您用的正是loopback device,而我們下一步要做的就是把這個檔案壓縮起來。記得要先umount這個loopback device。
而要使用這個root-filesystem的Image,必須符合幾項要求。首先,您使用的Kernel版本必須在1.3.73以上,有支援initrd和initial RAMDISK的版本。當然如果您覺得kernel太大,最根本的方法就是重新編譯一個kernel,因為在編譯Kernel時,可以針對這個小型的系統做最佳化的設定,對排除沒有必要的功能,以達到最小Kernel的目標。但還是別忘了支援ramdisk和ext2內建(build-in)。而在完成之後,更可以利用make zImage來得到壓縮後的Kernel。
有了Kernel Image和root-filesystem Image,事情就完成一大半了。接下來就是如何開機了,在之前也說了這麼多了,開機的方法好多種,可以用LILO或Loadlin,甚至連Loader都不需要也可以開機。而其中,最簡單的應該算是Loadlin了,只要有一個MSDOS的環境,加上有Loadlin.exe、KernelImage、以及root-filesystem的Image。就可以用以下指令開啟Linux:
![]()
以上的作法是將root-filesystem載入ramdisk。而且用ramdisk來當root。但,如前所說,如果您的root filesystem image太大的話,可以加上ramdisk_size的參數。如:C:\> Loadlin zImage root=/dev/ram initrd=rfImage.gz ramdisk_size=8192