本文闡述如何以一個現有的 RedHat Linux系統安裝盤為藍本,定制符合需要的 Linux 系統安裝盤。 1 引言 通常由於某種實際應用,需要一個包含所有最近更新的RPM包的操作系統發布盤,以備在安裝時一次完成所有的更新操作,或者是想定制一個有自己特色的操作系統發布盤,如將自己開發的應用程序通過創建RPM包,加入到操作系統中,在系統安裝時一次完成,形成包含自己產品的操作系統發布盤。這些都需要重新生成安裝盤,而且生成安裝盤也是十分必要的,因為操作系統發布商在每一次正式發布後,總會對一些漏洞進行更新處理,有些還是與安全相關的,在重新生成安裝盤時就可以將這些bug修復添加進你自己定制的安裝盤中,對一些設備新開發的驅動程序提供支持也需要重新生成安裝盤。 在一些嵌入式具體應用中,由於其對操作系統的要求較具體,不需要當前操作系統安裝盤中自帶的那麼多的功能,如Fedora Core 2當前有4張安裝盤,它包含了許多其它的應用,如Office、娛樂和游戲等等,而一些具體的應用根本不需要這麼多的功能,因此,它們常常需要基於一個版本的操作系統,然後對之進行相應的裁減,使之能滿足具體應用的實際需要,而不需要其它的多余的功能。因此,通過操作系統安裝盤的定制,可以根據自己或實際的需要,選擇有用的軟件包,組成安裝盤,從而通過定制操作系統的安裝,滿足具體應用的需要。 我們在定制操作系統安裝盤之前,必須有一個藍本作為安裝盤的基礎,比如是Red Hat 9.0安裝盤或Fedora Core 2安裝盤,也可以是Red Hat 9.0或Fedora Core 2安裝盤的iso文件,這些我們可以從Red Hat的網站或其它一些網站上下載。現假設我們已經有了Fedora Core 2的安裝盤,下面我們先大略看一下Fedora Core 2的安裝盤裡面的內容。 在安裝盤中有一個目錄為Fedora,它包含了發布盤的核心內容,如下: drwxr-xr-x 2 root root 2048 May 13 2004 base drwxr-xr-x 2 root root 77824 May 13 2004 RPMS RPMS目錄包含Fedora Core 2發布盤的主要部分,它是一些RPM文件。RPM包通常包含二進制可執行文件、有關的配置文件和文檔,我們可以參考RPM幫助以獲得更多信息。 base 目錄中包含一些在安裝過程中所需要的文件,如comps.XML文件,它定義哪個組件包含哪些RPM包以及RPM包之間的依賴關系,需要注意的是,在comps.xml文件中表示哪個組件有哪些RPM包采用的是RPM包名,而不是包的文件名。比如perl-5.8.3-18.i386.rpm這個文件名,在comps.xml中所表示的RPM包名為perl。對於comps.xml文件,我們會在後面作進一步解釋。另一個重要的文件是hdlist文件,它包含了RPM目錄中的所有RPM包大部分的頭文件,這意味著在RPM包中相互依賴關系可以通過讀取hdlist文件而決定,而不需要讀所有的RPM包。hdlist文件的另一個作用是將包名映射到文件名,如將perl包名映射到perl-5.8.3-18.i386.rpm,這意味著如果你想更新RPM包或添加你自己的包到RPM目錄中,你就需要更新hdlist這個文件,這會在後面進行描述。 2 RPM操作 RPM(Redhat Package Management)是由RedHat開發的,在Linux系統下的系統包管理工具。它的目標是:使包的安裝和卸載過程更容易,它能夠證實一個包是否已經正確安裝了,可以簡化包的建立過程,可以從源代碼建立整個包,它能用於不同的體系結構。RPM系統已經成為現在Linux系統下包管理工具事實上的標准,並且它也移植到很多商業的unix系統之下。 RPM包由包標簽對它標識,包標簽包含軟件名,軟件版本,包的發行版本幾部分。在包的內部還包含包的建立時間,包的內容描述,安裝包的所有文件的大小,數字簽名以證實包的完整性等信息。RMP包還包含包內的文件信息,其中包括:每個文件的文件名,每個文件的權限,文件的屬組和擁有者,每個文件的md5校驗和,文件的內容等。 RPM包管理系統提供了下列功能:安裝新的包,卸載舊的包,將一個舊包升級為新的包,獲得已經安裝包的信息等。 Red Hat發布盤主要是由一些RPM包組成。RPM包的名字包含一個後綴:arch.rpm,arch 指的是體系結構,對於Intel平台的有i386、i586、i686等,你所安裝的包必須要與機器上的共享庫的版本相匹配。如果你發現某個RPM包沒有安裝,你可以自己安裝。任何時候,你都可以(必須是root用戶)安裝RPM包。RPM命令使用輕參考相關資料。 3 RPM包創建過程 為了完成RPM包的創建,需要執行以下步驟: 執行spec文件prep節的命令和宏; 檢查文件列表的內容; 執行spec文件build節的命令和宏; 執行spec文件install節的命令和宏,同時也執行文件列表中的宏; 創建二進制包文件; 創建源碼包。 為了執行打包的工作,RPM需要一系列目錄完成建立的工作。正常的目錄結構通常由一個頂級目錄和五個子目錄構成。這五個子目錄分別是: SOURCES------包含原始的源文件和補丁文件。 SPECS--------包含控制RPM包建立過程的spec文件。 BUILD--------包含源碼解包和軟件建立的目錄。 RPMS---------包含建立過程創建的二進制包文件。 SRPMS--------包含建立過程創建的源碼包文件。 除了上述這五個主要的目錄外,在RPMS或SRPMS目錄下通常還會有關於RPM包目標平台的目錄。例如,i386、i586、i686等代表與Intel兼容cpu的平台,noarch目錄下的RPM包代表可以在任何平台下執行。 3.1 SPEC文件 spec文件是整個RPM包建立過程的中心,它的作用就如同編譯程序時的Makefile文件。spec文件包含建立一個RPM包必需的信息,包括哪些文件是包的一部分以及它們安裝在哪個目錄下。這個文件一般分為如下的幾節: (1) Preamle(序言) 序言包含用戶請求包的信息時所顯示的內容。它可以包含包的功能描述、包的軟件版本、版權信息和所屬的包組等。Summary 是一行關於該軟件包的描述,Name 是該軟件包的基名,Version 是該軟件的版本號,Release 是 RPM 本身的版本號,如果修復了 spec 文件中的一個錯誤並發布了該軟件同一版本的新 RPM,就應該增加發行版號。License 應該給出一些許可術語(如:"GPL"、"Commercial"、"Shareware"),Group 標識軟件類型。那些試圖幫助人們管理 RPM 的程序通常按照組列出 RPM。您可以在usr/share/doc/rpm-4.0.4/GROUPS 文件看到一個 Red Hat 使用的組列表(假設您安裝的 RPM 版本是 4.0.4)。但是您還可以使用那些組名以外的名稱。Source0、Source1等等給這些源文件命名(通常為 tar.gz 文件)。%{name} 和 %{version} 是 RPM 宏,它們擴展成為頭中定義的 rpm 名稱和版本。 要注意的是,你不要在 Source 語句中包含任何路徑。缺省情況下,RPM 會在 /usr/src/redhat/SOURCES 中尋找文件,請將您的源文件復制或鏈接到那裡。(要使 spec 文件盡量可移植的話,應當盡量避免嵌入自己開發機器上的假想路徑。其他開發人員就可以指示 RPM 在別的目錄下查找源文件,而不用修改您的 spec 文件。) 接下來的部分從 %description 行開始。您應該在這裡提供該軟件更多的描述,這樣任何人使用 rpm -qi 查詢您的軟件包時都可以看到它。您可以解釋這個軟件包做什麼,描述任何警告或附加的配置指令,等等。 (2) Prep節 Prep節進行實際的打包准備工作,它是使用節前綴%prep表示的。一般而言,這一節的主要工作是檢查標簽語法是否正確,刪除舊的軟件源程序,對包含源程序的tar文件進行解碼。如果包含補丁(patch)文件,將補丁文件應用到解開的源碼中。它一般包含%setup與%patch兩個命令。%setup用於將軟件源碼包解開,執行%patch可將補丁文件加入解開的源程序中。 %setup -n newdir---------將壓縮的軟件源程序在newdir目錄下解開。 -c ---------------在解開源程序之前先創建目錄。 -b num------------在包含多個源程序時,將第num個源程序解壓縮。 -T----------------不使用缺省的解壓縮操作。 例如: %setup -T -b 0 /*解開第一個源程序文件。*/ %setup -c -n newdir /*創建目錄newdir,並在此目錄之下解開源程序。*/ %patch %patchN-------這裡N是數字,表示使用第N個補丁文件,等價於%patch -P N -p0-----------指定使用第一個補丁文件,-p1指定使用第二個補丁文件。 -s------------在使用補丁時,不顯示任何信息。 -b name-------在加入補丁文件之前,將源文件名上加入name。若為指定此參數,則缺省源文件加入.orig。 -T------------將所有打補丁時產生的輸出文件刪除。 (3) Build節 這一節主要用於編譯源碼,它是使用節前綴%build表示的。這一節一般由多個make命令組成。 (4) Install節 這一節主要用於完成實際安裝軟件必須執行的命令,它是使用節前綴%install表示的。這一節一般是由make install指令構成,但是有時也會包含cp、mv、install等指令。 這一節還能指定在用戶安裝的系統上,包安裝時運行的腳本。這樣的腳本稱為安裝(卸載)腳本。它可以指定包安裝前、包安裝後、包除去前、包除去後的系統必須運行的外殼程序段。在用戶安裝的系統上,為了驗證一個包是否已經成功安裝的驗證腳本也可由這一節指定。 (5) Clean節 這一節所描述的內容表示在完成包建立的工作之後,自動執行此節下的腳本進行附加的清除工作,它是使用節前綴%clean表示的。一般而言,這一節的內容是簡單地使用rm -rf $RPM_BUILD_ROOT命令,不需要指定此節的其它內容。 (6) 文件列表 這一節指定構成包的文件的列表,它是使用節前綴%files表示的。此外,它還包含一系列宏控制安裝後的文件屬性和配置信息。 %files 列出應該捆綁到 RPM 中的文件,並能夠可選地設置許可權和其它信息。在 %files 中,您可以使用 %defattr 來定義缺省的許可權、所有者和組;%defattr(-,root,root) 會安裝 root 用戶擁有的所有文件,使用當 RPM 從構建系統捆綁它們時它們所具有的任何許可權。 可以用 %attr(permissions,user,group) 覆蓋個別文件的所有者和許可權。可以在 %files 中用一行包括多個文件。可以通過在行中添加 %doc 或 %config 來標記文件。%doc 告訴 RPM 這是一個文檔文件,因此如果用戶安裝軟件包時使用 --excludedocs,將不安裝該文件。您也可以在 %doc 下不帶路徑列出文件名,RPM 會在構建目錄下查找這些文件並在 RPM 文件中包括它們,並把它們安裝到 /usr/share/doc/%{name}-%{version}。以 %doc 的形式包括 README 和 ChangeLog 這樣的文件是個好主意。 %config 告訴 RPM 這是一個配置文件。在升級時,RPM 將會試圖避免用 RPM 打包的缺省配置文件覆蓋用戶仔細修改過的配置。 注意:如果在 %files 下列出一個目錄名,RPM 會包括該目錄下的所有文件。通常這不是您想要的,特別對於 /bin 這樣的目錄。 (7) 改動日志 這一節主要描述軟件的開發記錄,它是使用節前綴%changlog表示的。這個段的內容是為了開發人員能詳細的了解該軟件的開發過程,對於包的維護極有好處。 3.2 創建RPM包 如果我們需要對RPM包作修改,那麼我們首先需要將源碼包取來,比如我們要修改內核,那麼我們可以從網上或光盤中取到內核的源代碼RPM包,如kernel-2.6.5-1.358.src.rpm,將源碼包解開:rpm -i kernel-2. 6.5-1.358.src.rpm,則該RPM中的內容將存放在目錄/usr/src/redhat/SOURCES和/usr/src/redhat/SPEC目錄中,前者存放的是源碼、補丁以及一些配置文件等,後者存放的是包對應的spec文件,如kernel-2.6.spec,現在你就可以對內核進行修改。假定我們想另外再對內核打一個補丁,比如說:mypatch-2.6.5.patch,你需要將這個補丁文件復制到/usr/src/redhat/SOURCES/目錄下,然後編輯kernel-2.6.spec文件。你需要先在定義補丁文件的最後加入對你補丁文件的初始定義,如: ………… Patch10000: linux-2.6.0-compile.patch # Patch10010: linux-2.6.0-module-license.patch Patch10030: mypatch-2.6.5.patch /*新加入的補丁文件的定義*/ # END OF PATCH DEFINITIONS ………… 然後在文件的後面加入對內核打補丁命令: ………… %patch10000 -p1 %patch10030 -p1 /*新加入的打補丁命令*/ # END OF PATCH APPLICATIONS ………… 如果你還想對內核做其它的修改,你可以修改相應的文件或添加相應的文件,然後修改kernel-2.6.spec文件。當spec文件修改完成之後,你只需要執行 rpmbuild -ba kernel-2.6.spec 就可以生成所需要的RPM包了。另外需要注意的是,以生成內核包為例,假如我們想生成kernel-smp-2.6.5-1.358.i686.rpm包,在kernel-2.6.spec文件中包含有一些開關選項,比如,在文件的開頭需要定義創建哪些內核的RPM包,如: %define buildup 1 %define buildsmp 0 %define buildsource 1 在通常情況下,在執行rpmbuild -ba kernel-2.6.spec 命令後,會創建一個kernel-2.6.5-1.358.i386.rpm、kernel-source-2.6.5-1.358.i386.rpm和源碼RPM包kernel-2.6.5-1.358.src.rpm。因此,當你需要創建支持SMP的內核的RPM包時,需要修改kernel-2.6.spec文件開頭時的定義為: %define buildup 1 %define buildsmp 1 %define buildsource 1 %define -target_cpu i686 此外,在文件的開頭還需要定義-target_cpu 為i686,從而創建i686的內核RPM包,並且需要對/usr/lib/rpm目錄下面的一些宏重新定義,比如當前目錄下面的macros文件,需要重新定義arch 和build_arch為i686。最後,執行命令 rpmbuild -ba kernel-2.6.spec --with smp 就可以。當然,如果對內核進行了相應的修改,就必須生成多個內核RPM包,以適用於多個arch,如kernel-2.4.18-3-i586-smp.rpm, kernel-2.4.18-3-athlon.rpm等。 4 操作系統安裝盤的定制過程 你需要將原來操作系統發布盤上的內容拷貝到本機硬盤中,根據有幾張發布盤而生成幾個目錄,比如Fedora Core 2有四張盤,你則需要在系統上生成四個目錄,如disc1、disc2、disc3、disc4,分別將這四張盤上內容拷貝到這四個目錄中,然後對相應的RPM包進行更新。 首先找到你想更新的RPM包,將新的RPM替換舊包。當然你也可以根據你的需要新增或刪除RPM包,需要注意的是,你需要在comps.xml文件中將新增加或刪除的RPM包名加入某個組件中,並且注意其與其它RPM包的依賴關系,也就是說你所放置的包的位置要恰當,否則它會依賴於在它之前而沒有加入系統的某個RPM包。 4.1 編輯comps.xml文件 在生成安裝盤之前,需要注意對comps.xml文件進行修改。這個文件用來告知安裝程序anaconda,用戶選擇了某個組是應該有哪些包需要安裝,定義了在安裝過程中,包是如何被捆綁在一起的。在Red Hat 8.0以前版本的發布盤中,對應的文件為comps,它只是一個簡單的文本文件,在Red Hat 8.0之後的版本中,用comps.xml代替了原來的comps文件。comps.xml是一個XML文件,易於對內容進行分析和說明。 comps.xml文件開始是說明xml的版本和DTD斷言,然後進入以標記開始的文件的主體內容。 comps.xml主要由三部分組成,首先是組列表,它描述了在安裝過程中需要的不同的組(或組件),包括組名、組的描述和包含的RPM包;其次是組的層次結構,它將組分成不同的類,並定義了組的一個順序,從而可以決定哪些組需要先安裝;最後為一系列RPM包以及它們之間的依賴關系。 下面分別介紹comps.xml文件的這三部分: (1) 組列表 在系統安裝時,需要用到一個組中的一些屬性,下面就是屬性列表以及它們如何使用。一個組被定義在和標記之內. 一個簡單的組定義可以是: somegroup Sample Group true false This is a silly sample group bash cpio 下面分別說明組定義中一些參數的含義: id:組的id僅僅是在comps.xml文件中作為該組的一個標識,這是必須的; name:表示用戶可以看到的組的名字,它也是必須的; default:它表示在系統安裝過程中,當選擇定制(custom)安裝時,該組是否在缺省情況下被選中。如果沒有說明,則缺省情況下為不選中。 uservisible:它表示該組在缺省情況下是否在安裝時可以看到,如果沒有說明,缺省設置為YES,為可以看到。 description:它表示對該組進行簡短的描述,這是必須的; packagelist:它說明在該組內的一系列安裝包,這也是必須的。 packagereq:包名 屬性: type:當進行安裝時,判定對應的包是否是組的"強制"部分、或"缺省"部分或"可選"部分。它可以是"mandatory"、"default"或"optional"之一。 requires:它說明只有當它所需要(依賴)的包也安裝情況下,此包才安裝進系統。 (2)組層次結構 它描述了組的樹狀層次結構,組層次結構定義在和標記之間,由定義的標記組成類。 一個簡單的組層次結構可以如下所述: Random Groups somegroup 一個類由下面這些屬性組成: name: 它表示類名,是必須的; subcategories: 它表示此類的一些子類,由一列表和 元素組成 subcategory: 前面定義的組的id (3)RPM包 此部分說明要安裝的RPM包,它定義在和標記之內。一個簡單的RPM包部分可以如: bash mktemp bash glibc liBTermcap name:它指的是RPM包名,是必須的。 dependencylist:它說明此包對應的依賴的RPM包。 dependency:此包需要的包的名字 4.2 產生完整的comps.xml文件 上述說明的comps.xml文件中的RPM包部分是是自動產生的,為了形成完全的comps.xml文件,需要在系統中安裝comps-extras RPM包,然後進行下面的操作: 將comps.xml文件中的原來的RPM包部分刪除;運行: /usr/share/comps-extras/getfullcomps.py comps.xml /path/to/tree arch >/root/filelist 在此,/path/to/tree是Red Hat Linux操作系統安裝盤內容存儲的地方,arch指的是體系結構,為i386。注意的是,假定comps.xml已經存放/path/to/tree/arch/RedHat/base/目錄下,將此輸出重定向到一個臨時文件,如/root/filelist。 將comps.xml文件中最後一行內容(為)刪除 將前面生成的臨時文件添加到comps.xml中 再將添加到comps.xml文件中 通過新增你的包到comps.xml文件,你可以根據你的需要做你自己的發布盤,確信你的包在缺省情況下會被安裝。需要注意的一件事是你更新的包與其它包的依賴關系,這是你需要處理的,要注意你更新的包所應該放置的位置。另外,不要在文件中隨意增加或刪除其余的空格。在修改comps.xml之前,也最好對最初的comps.xml做個備份,以備恢復使用。 4.3 重新編譯安裝程序,調整安裝階段 安裝程序是不可能一次就加載進來的,必須分階段進行,通常我們就稱為"stage"。第一個階段所用程序很小,只有這樣才能從一張軟盤、tFTP服務器等等上面加載。通常這個階段程序包含的只有一個精簡過的Linux內核和在後續步驟當中必要的一些驅動程序(比如SCSI)。 要采用一個新的RedHat安裝,就會需要很多的映像,最明顯的就是引導安裝盤本身(從軟驅或者光驅安裝)的boot.img,但是我們也需要對從硬盤、網絡文件系統等安裝方式提供支持。 RedHat就此提供了很好的腳本命令,只需一個簡單的操作就可以完成所有的操作。這些腳本的工作就是把某些RPM包的內容提取出來,然後用來生成各安裝步驟所用程序的映像。 所再強調的是,我們必須保證安裝了anaconda-runtime: #rpm -i anaconda-runtime-xxxxx-i386.rpm 接著進入目錄/usr/lib/anaconda-runtime,這裡我們會看到一些非常有用的腳本,比如: mk-images.i386:包涵有創建啟動磁盤時i386的專門設置(通常情況下,網絡和pcmcia)以及輔助磁盤驅動程序。在此您可以改變啟動映像中所包含的模塊,比如說在網絡啟動磁盤有: …… NETWORKMODULES="$COMMONMODULES nfs 3c59x eepro100 tulip pcnet32 ne2k-pci 8139too" …….. buildinstall 這是主要的。 #cd /usr/lib/anaconda-runtime #./buildinstall ~/disc1/ 這個腳本命令會在~/disc1/images目錄下更新一些的文件。 4.4 生成新的hdlist文件 當安裝時,安裝程序需要依賴光盤上的Fedora/base/hdlist文件,它包含的是所有可用的RPM包的必要信息,這些信息在安裝過程當中是用來顯示每一個包的用途以及解決用戶選擇軟件包後的依賴性問題。 用以生成hdlist文件的程序叫做genhdlist,它是由anaconda-runtime這個包產生的。現在的genhdlist多了一個新的參數:--withnumbers,是用來記錄hdlist文件中每個RPM包的媒介代號。 分步處理的過程如下: #rpm -i anaconda-runtime-xxxxx-i386.rpm #cd /usr/lib/anaconda-runtime #./genhdlist -- withnumbers ~/disc1 ~/disc2 ~/disc3 ~/disc4 整個過程只需要執行一個腳本,見附錄一:kernel-update.sh。 如果你在系統中添加了RPM包,那麼在生成安裝盤之前,最好將這四張盤上的內容復制到一個目錄下,然後修改附錄一的腳本文件,運行腳本,先網絡安裝一次,看是否存在包的依賴關系問題。如果沒有,則可以生成安裝盤。 5 生成iso映象 當前面系統進行網絡安裝成功後,則可以生成iso映象,然後進行刻盤,執行的操作如下: # build disk 1 cd ~/disc1 /*假設我們將第一張盤的內容放置在此外*/ mkisofs -R -J -T -no-emul-boot -boot-load-size 4 -boot-info-table -V fedora -b isolinux/isolinux.bin -c isolinux/boot.cat -o /iso/exm-disc1.iso . /*使用mkisofs工具生成iso映象,將生成的iso映象放在/iso目錄中*/ # build disk 2 cd ~/disc2 /*采用同樣的方法,生成第二個iso,依次。*/ mkisofs -R -J -T -V fedora -o /iso/exm-disc2.iso . 在生成iso映象之後,需要對它進行測試,你可以將它掛接到某個地方,比如: mount -o loop /iso/exm1-disc1.iso /mnt 在生成安裝iso(exm-disc1.iso)之後,我們可以將它復制到windows系統中,采用刻錄程序進行刻錄,然後可以從光盤安裝,進行安裝測試。 附錄一: kernel-update.sh #!/bin/sh # current working Directory BASE=`pwd` # generate hdlists mkdir -p $BASE/SOURCES echo echo Copying disc1 to SOURCES directory, please wait... cp -Rf $BASE/disc1/* $BASE/SOURCES echo Copying disc2 to SOURCES directory, please wait... cp -Rf $BASE/disc2/* $BASE/SOURCES echo Copying disc3 to SOURCES directory, please wait... cp -Rf $BASE/disc3/* $BASE/SOURCES echo Copying disc4 to SOURCES directory, please wait... cp -Rf $BASE/disc4/* $BASE/SOURCES echo echo Make sure anaconda, anaconda-runtime is installed... rpm -U $BASE/SOURCES/Fedora/RPMS/anaconda-*.rpm # generate hdlists* cd /usr/lib/anaconda-runtime ./genhdlist --withnumbers $BASE/disc1 $BASE/disc2 $BASE/disc3 $BASE/disc4 # generate the package ordering ./pkgorder $BASE/SOURCES i386 tee /root/pkgorder.txt ./buildinstall --pkgorder /root/pkgorder.txt --version 1 --prodUCt "Fedora" --release "Fedora" $BASE/SOURCES $BASE/SOURCES cp -apRf $BASE/SOURCES/images/* $BASE/disc1/images cp -apRf $BASE/SOURCES/isolinux/* $BASE/disc1/isolinux cp -apRf $BASE/SOURCES/RedHat/base/* $BASE/disc1/RedHat/base echo Cleaning up... rm -rf $BASE/SOURCES