Linux安裝程序Anaconda分析


1、概述 
    Anaconda是RedHat、CentOS、Fedora等Linux的安裝管理程序。它可以提供文本、圖形等安裝管理方式,並支持Kickstart等腳本提供自動安裝的功能。此外,其還支持許多啟動參數,熟悉這些參數可為安裝帶來很多方便。該程序的功能是把位於光盤或其他源上的數據包,根據設置安裝到主機上。為實現該定制安裝,它提供一個定制界面,可以實現交互式界面供用戶選擇配置(如選擇語言,鍵盤,時區等信息)。Anaconda的大部分模塊用Python編寫,有少許的載入模塊用C編寫。
    Anaconda支持的管理模式: 
    (1)Kickstart提供的自動化安裝; 
    (2)對一個RedHat實施upgrade; 
    (3)Rescuse模式對不能啟動的系統進行故障排除。 
    要進入安裝步驟,需要先有一個引導程序引導啟動一個特殊的Linux安裝環境系統;引導有多種方式: 
    (1)基於網絡方式的小型引導鏡像,需要提供小型的引導鏡像; 
    (2)U盤引導,通過可引導存儲介質中的小型引導鏡像啟動安裝過程;  
    (3)基於PXE的網絡安裝方式,要提供PXE的完整安裝環境; 
    (4)其他bootloder引導(如GRUB)。 
    可用的安裝方式:本地CDROM、硬盤驅動器、網絡方式(NFS、FTP、HTTP)。 
    通過網絡方式安裝時,不論通過FTP、HTTP還是NFS方式共享安裝,可以將安裝光盤先拷貝到網絡服務器上保存為iso鏡像,然后loop掛載到共享目錄或網頁目錄(當然,拷貝鏡像中的所有文件到指定位置或直接掛載到共享目錄也可),而通過NFS方式時,可以直接將光盤的iso文件放到共享目錄即可,安裝程序掛載共享目錄后可以自動識別鏡像。
   注意思復制安裝光盤,並保存為一個 iso 映像文件的方法(對於 DVD/CD): 
# dd if=/dev/cdrom  of=/location/of/disk/space/RHEL.iso  bs=32k 
    注意拷貝時bs塊大小設置為32k,我實驗時設為1M,雖然減小了文件體積,但是安裝讀鏡像時會報錯。 
    對於Kickstart,它是一個利用Anconda工具實現服務器自動化安裝的方法。通過生成的kickstart配置文件ks.cfg,服務器安裝可以實現從裸機到全功能服務的的非交互式(無人值守式)安裝配置;ks.cfg是一個簡單的文本文件,文件包含Anconda在安裝系統及安裝后配置服務時所需要獲取的一些必要配置信息(如鍵盤設置,語言設置,分區設置等)。Anconda直接從該文件中讀取必要的配置,只要該文件信息配置正確無誤且滿足所有系統需求,就不再需要同用戶進行交互獲取信息,從而實現安裝的自動化。但是配置中如果忽略任何必需的項目,安裝程序會提示用戶輸入相關的項目的選擇,就象用戶在典型的安裝過程中所遇到的一樣。一旦用戶進行了選擇,安裝會以非交互的方式(unattended)繼續。使用kickstart可以實現流線化自動化的安裝、快速大量的裸機部署、強制建立的一致性(軟件包,分區,配置,監控,安全性)、以及減少人為的部署失誤。
    使用Kickstart方法安裝的過程包括創建一個kickstart文件、創建有kickstart文件的引導介質或者使這個文件在網絡上可用、籌備一個安裝樹、開始ks安裝(anconda自身啟動 -->選取ks安裝模式--> 從ks文件讀取配置 --> 最后安裝)。創建kickstart配置文件可以使用任何文本編輯器,也可以使用圖形化配置工具system-config-kickstat(需要安裝system-config-kickstart.noarch包)。注意配置文件生成后,推薦使用ksvalidator命令檢查配置文件語法及完整性錯誤,例如:
[root@bogon ~]# ksvalidator ks.cfg 
not enough arguments for format string 
    Kickstart文件的語法及參數含義可參考 http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Installation_Guide/s1-kickstart2-options.html。
    我們以RHEL 6.0的安裝為例來分析Anaconda。為緊跟新版本,anaconda源碼則使用較新的在Fedora 15中使用的版本。先從Fedora的下載站點鏡像列表http://mirrors.fedoraproject.org/publiclist/中選擇一個站點,例如上海交大鏡像站點的/fedora/linux/releases/15/Everything/source/SRPMS/目錄中下載用於Fedora 15的最新版anaconda源碼包anaconda-15.31-1.fc15.src.rpm,還要准備好RHEL 6.0的DVD安裝光盤。 
    2、RedHat企業版6.0光盤的安裝樹介紹 
    (1)Packages目錄:包含安裝所需的所有二進制RPM包。 
    (2)HighAvailability、LoadBalancer、ResilientStorage、ScalableFileSystem、Server目錄:五個文件夾包含了安裝所需的所有RPM軟件包信息。它們分別對應高可用性、負載均衡、彈性存儲、可擴展文件系統、以及服務器基礎的軟件包信息。每個文件夾下都有一個Packages目錄,它只是一個鏈接,指向頂級的../Packages目錄。還有一個repodata目錄,其中的類似於*-comps-rhel6-HighAvailability.xml的XML文件定義了對應特性的軟件包信息,這些軟件包被分成不同的組,每個組有一個ID。這樣的repodata精確描述各個RPM包的詳細信息,如依賴關系,包含文件,校驗碼信息等。可以通過在Kickstart文件的%packages段中指定組ID來安裝相應組中的軟件包。
    (3)EFI目錄:用於64位的基於EFI的系統引導。其中BOOT目錄下的BOOTX64.conf為grub的配置文件,用於顯示引導菜單。 
    (4)TRANS.TBL文件:記錄當前目錄的列表,用mkisofs的-T參數重新生成,主要是為了長文件名稱。 
    (5).discinfo文件是安裝介質的識別信息。.treeinfo文件記錄不同安裝方式安裝程序所在的目錄結構,如PXE方式時,內核kernel=images/pxeboot/vmlinuz,根文件系統initrd=images/pxeboot/initrd.img。
    (6)isolinux目錄:有開機引導系統安裝的內核(vmlinuz)及RAM鏡像(initrd.img),在引導系統時會載入內存,給系統的安裝提供一個Linux安裝引導平台,文件夾中還有在不同模式下顯示信息的boot.msg文件,splash.jpg是特殊格式的引導過程背景圖片(640*480)。安裝時這個畫面上的引導菜單內容在isolinux.cfg文件中指定。按Enter會自動進入圖形界面安裝模式,若按Esc,會顯示"boot: "命令提示符,進入用戶交互模式,界面上會有各種模式操作提示。鍵入"linux text",會進入文本安裝模式。 
    (7)images目錄:包含有各種引導鏡像。最重要的是引導第二階段安裝需要用到的鏡像文件install.img(rhel 5中是stage2.img),anaconda程序就在這個鏡像文件中。另外還有用於制作微型啟動光盤的boot.iso(為節省空間rhel 6.0中已經刪除了,在rhel 5中是有的),有可放置於USB或其他大容量可引導介質的VFAT分區上,制作引導工具的鏡像diskboot.img(rhel 5中有),也有用於制作PXE安裝方式引導介質的pxeboot文件夾等。
    3、Anaconda程序目錄結構和源代碼包概覽 
    先用file命令查看install.img的文件系統類型,可知是suqashfs,用mount -o loop -t squashfs install.img ./img/的方式掛載出來。 除了主執行體/usr/bin/anaconda,其它安裝腳本模塊均在/usr/lib/anaconda主目錄下。我們看一下整個anaconda主目錄的結構:
    /usr/bin/anaconda: 主程序,是python腳本。 
    /usr/lib/anaconda/installclasses: 定義了在安裝過程中用戶可選擇的安裝類型。每個安裝類型描述文件根據相應安裝類型的特點,分別對安裝步驟、分區策略以及安裝包的取舍給出了不同的方案。里面有兩個文件fedora.py和rhel.py,分別針對fedora和rhel的安裝類型。其他的Linux發行版可以定義它們自己的安裝類型。
    /usr/lib/anaconda/iw: 圖形安裝模式的模塊。包含所有圖形界面類所在的模塊,每個圖形界面對應一個類,負責相應安裝步驟圖形界面的具體外觀顯示及與用戶的交互,(可能)調用anaconda主目錄下的相關安裝行為模塊完成具體的安裝操作。
    /usr/lib/anaconda/storage: 存儲配置的響應目錄(如分區,FCOE, iSCSI, RAID, ZFCP的配置等)。 
    /usr/lib/anaconda/textw: 文本安裝模式的模塊。和iw子目錄含義是一致的,只是包含的是文本安裝模式的前端字符用戶界面類所在的模塊,每個字符用戶界面對應一個類,負責與用戶的交互,字符界面的采用了python的snack庫。
    /usr/lib/anaconda-runtime: 有init和loader程序。這是兩個靜態編譯的程序,不依賴其他庫,就是編譯anaconda源代碼目錄下的loader目錄下的C代碼得到。這兩個程序會放在最后用來啟動安裝過程的Linux initrd image里面。
    /usr/anaconda主目錄:如果說用戶界面類是處理安裝程序外觀的話,則anaconda主目錄下的各python模塊執行每個安裝界面背后具體的安裝行為,包括那些無用戶界面安裝步驟的安裝操作。
    由此可見,主執行體/usr/bin/anaconda調用的大量例程分布在/usr/lib/anaconda目錄下,安裝過程要用到的資源文件(例如背景圖片)則分布在/usr/share/anaconda目錄下。Python的許多內置模塊則在目錄/usr/lib/pythonXX下,其中XX指版本號。
    上面分析的是已經編譯好的anaconda目錄結構,現在概覽一下anaconda源代碼包的結構。Anaconda主要用Python編寫,圖形界面前端用pyGtk庫(參考http://www.pygtk.org/)和Glade界面描述文件(參考http://glade.gnome.org/)編寫。用來啟動環境、加載模塊、加載anaconda主體的loader程序用C編寫,一些其他的硬件相關的部分也是用C編寫。另外,bash和python腳本還用在一些管理性的任務中。
    Aanaconda核心的源代碼主要有: 
    (1)界面(Interface) 
pyanaconda/cmdline.py 
pyanaconda/gui.py 
pyanaconda/installinterfacebase.py 
pyanaconda/text.py 
    這些文件處理用戶界面。anaconda支持3種用戶界面,即圖形、文本、命令行模式。每種模式的相應實現文件都包含用來畫出各種窗體的類。 
data/ui 
pyanaconda/iw/ 
pyanaconda/textw/  
    iw目錄包含圖形界面屏幕的python文件,textw目錄包含文件界面屏幕的python文件,ui目錄包含圖形模式所需的glade界面描述文件。通常開發者盡可能多地移除文本模式,把更多地東西移到圖形界面,以便能使用glade。
pyanaconda/dispatch.py 
    調度器類Dispatcher是一個狀態機,用來控制安裝器中各步驟的移動。當一個Next或Back按鈕被單擊時,調度器知道要跳轉到哪個屏幕,也知道根據相應的設置哪一步應該被忽略而直接跳過。每種安裝模式都有它自己的要跳過或被添加返回的步驟集。Install類也可以指定需要跳過或需要添加的步驟。各種其他的與機器相關的細節,如anaconda檢測和用戶選擇能夠修改步驟集。
pyanaconda/vnc.py 
    用於控制對VNC進行設置(當在安裝過程中請求了VNC時)。之后,安裝進入圖形模式。 
    (2)分區(Partitioning) 
pyanaconda/storage/dasd.py 
pyanaconda/storage/devicelibs/ 
pyanaconda/storage/fcoe.py 
pyanaconda/storage/iscsi.py 
pyanaconda/storage/zfcp.py 
    這些文件處理探測、配置、啟動和停止anaconda支持的高級存儲系統。這既包括硬件設備(FCOE, iSCSI, RAID, ZFCP等),也包含軟件抽象(加密,邏輯卷管理lvm等)。其中LVM和RAID使用最普遍。
pyanaconda/storage/formats/  
    這個目錄下的文件用來將一些文件系統或類似於文件系統的抽象寫到存儲設備。你可以把它看作是在pyanaconda/storage/devicelibs/之上的一層。類似於文件系統的抽象包括硬盤卷標、加密、與機器相關的引導分區、交換分區。
pyanaconda/partIntfHelpers.py 
    用來進行錯誤檢查、輸入驗證、顯示錯誤消息。圖形和文本模式都要用到它。 
pyanaconda/storage/__init__.py 
pyanaconda/storage/errors.py 
pyanaconda/storage/miscutils.py 
pyanaconda/storage/size.py 
pyanaconda/storage/storage_log.py 
pyanaconda/storage/udev.py 
    這些文件形成存儲模塊的一個支持庫,完成不適合分在其他組中的一些細小任務。從文件名即可看出其功能。__init__.py完成大部分的工作,包括讀寫存儲相關的配置文件、檢測現存的安裝、各存儲動作的協作、聚焦不同存儲對象的數據、執行完整性檢查。
pyanaconda/storage/deviceaction.py 
pyanaconda/storage/devices.py 
pyanaconda/storage/devicetree.py 
pyanaconda/storage/partitioning.py 
pyanaconda/storage/partspec.py 
    這組文件實現分區邏輯。它們使用DeviceTree抽象來存放現存的分區和請求、定義把存儲請求寫到硬盤的行為、處理自動分區(為默認方式)、並且知道怎么調整分區大小,以符合給定的分區容量。硬盤上分區的創建使用pyparted包來完成。
    (3)引導器(Bootloader) 
pyanaconda/bootloader.py 
pyanaconda/booty/ 
    這些文件控制把bootloader寫到安裝后的系統里。每種類型的機器有它自己的bootloader格式,因此在booty/目錄下有相應的文件,bootloader.py則把它們粘合到一起。這對新的安裝和更新非常有用。
    (4)配置(Configuration) 
pyanaconda/desktop.py 
pyanaconda/firewall.py 
pyanaconda/language.py 
pyanaconda/network.py 
pyanaconda/security.py 
pyanaconda/timezone.py 
pyanaconda/users.py 
    這些文件處理相關配置,這些配置步驟可以通過圖形界面或kickstart進入。某種程度上它們影響安裝過程(例如語言和鍵盤設置)。但是它們的主要目的是在安裝過程的最后把這些配置寫到安裝后的系統里去。
    (5)軟件包安裝(Package Installation) 
pyanaconda/compssort.py 
pyanaconda/backend.py 
pyanaconda/image.py 
pyanaconda/sortedtransaction.py 
pyanaconda/yuminstall.py 
    這些文件控制軟件包的安裝。anaconda允許在后端安裝多個軟件包,雖然在安裝樹中某一時刻真正只有一個在使用yum、寫入安裝包的配置。 
    (6)安裝類型(Installation Classes) 
pyanaconda/installclass.py 
pyanaconda/installclasses/ 
pyanaconda/product.py 
    安裝類型定義形成一種安裝輪廓的配置。這包括要顯示或跳過的步驟、產品名稱、安裝方法、激活的庫、配置設置等。這里主要用它來創建針對Fedora和RHEL的不同安裝類型,其他的項目或ISV可以定義它們自己的安裝類型。
    (7)特殊模式(Special Modes) 
pyanaconda/kickstart.py 
    Kickstart是一種通過給anaconda提供一個文件以實現自動化安裝的方式。這個文件包含用戶通過UI需要提供的所有數據。這個文件是解析器(在pykickstart包中)和anaconda內部構件之間的接口。它主要提供在anaconda期望的地方保存設置的方法。
data/icons 
data/liveinst 
liveinst/ 
pyanaconda/livecd.py 
    這些文件實現從Live CD安裝。它們提供一種特殊的安裝方法、一個特殊的軟件包安裝后端、以及一些用來從Live CD桌面上加載安裝器的文件。 
pyanaconda/rescue.py 
pyanaconda/upgrade.py 
    這些文件提供與恢復模式和更新相關的方法。 
    (8)庫(Library) 
pyanaconda/__init__.py 
pyanaconda/anaconda_log.py 
pyanaconda/backend_log.py 
pyanaconda/baseudev.py 
pyanaconda/constants.py 
pyanaconda/errors.py 
pyanaconda/exception.py 
pyanaconda/flags.py 
pyanaconda/installmethod.py 
pyanaconda/isys/ 
pyanaconda/iutil.py 
pyanaconda/packages.py 
pyanaconda/platform.py 
pyanaconda/pyudev.py 
pyanaconda/simpleconfig.py 
pyanaconda/sitecustomize.py 
pyanaconda/xutils.c 
    這組文件提供在安裝器中使用的各種雜項功能,包括日志框架、硬件檢測(通過udev接口)、進程控制、異常處理,以及其他任務。 
    (9)主程序(The Main Program) 
anaconda 
    這是anaconda主程序,在源代碼包的頂級目錄中。它處理大量的環境設置、激活更新(如果存在)、讀取kickstart文件、設置VNC,等等。當所有這些任務完成后,它把控制權交給dispatcher,以處理其余的安裝過程。
    (10)安裝映像文件的構建(Image Building) 
data/bootdisk/ 
data/command-stubs/ 
data/fonts/ 
scripts/ 
utils/ 
    這些目錄下的代碼用來控制怎么建立安裝環境,這包括創建初始ramdisk和stage2映像,添加某些必需命令的基本版,分割安裝樹為媒介大小的塊,以及其他一些雜項任務。
    (11)anaconda加載器(Loader) 
loader/ 
    該目錄下的C代碼實現initrd.img中的/init程序(實為指向/sbin/init程序)和/sbin/loader程序,loader程序用來加載主程序anaconda。
    4、Anaconda啟動分析 
    從“Linux內核啟動過程分析”一節中我們知道,當開機從OS光盤啟動,會先加載isolinux下可執行的內核映像vmlinuz,在內存中建立一個虛擬的根文件系統(rootfs),然后內核加載初始RAM磁盤initrd.img,建立一個安裝Linux所需要的系統環境,這就是所謂的第一階段。內核最后會運行initrd.img中的/init程序,由它來啟動第二階段的安裝過程,即加載系統安裝程序anaconda,執行具體的安裝過程。注意如果通過網絡方式安裝(如NFS方式),則會根據安裝樹的NFS路徑,通過mount把vmlinuz和initrd.img掛載到本地,像訪問本地文件一樣訪問遠程文件,以建立安裝環境(在具體運行某個文件時會從網絡下載到本地)。
    initrd.img通常是一個用gzip壓縮的cpio歸檔文件,需要加上.gz后綴並用gunzip解壓成新的initrd.img,然后用cpio -i --make-directories < initrd.img釋放其內容,生成一個小型的根文件系統。可以看到/init程序指向的是/sbin/init程序,里面還有loader程序,這就是編譯anaconda源碼時生成的兩個程序。可見這個initrd.img中的/sbin/init程序是專門為anaconda定制的,常被稱為installer類型的init。/sbin/loader程序可以看作是真正的anaconda自己的"init"程序,它由/init程序調用。
    總結anaconda的兩個階段: 
    (1)第一階段:加載安裝樹的isolinux目錄下的內核映像vmlinuz和初始RAM磁盤initrd.img,建立安裝環境。initrd.img中的/init程序調用/sbin/loader程序,loader加載kickstart文件,最后運行/usr/bin/anaconda主程序,進入第二階段。
    (2)第二階段:anaconda程序加載各python和bash模塊,執行各個安裝步驟。 
    OK,分析的起點從loader/init.c的main函數開始。可以結合系統安裝完后的anaconda log來分析,在/var/log下,主要有一般性的anaconda消息anaconda.log,由anaconda運行的所有外部程序信息anaconda.program.log,可擴展的存儲模塊信息anaconda.storage.log,網絡接口配置相關信息anaconda.ifcfg.log,yum安裝軟件包的信息anaconda.yum.log,硬件相關的系統信息anaconda.syslog。注意如果系統安裝失敗,則這些文件的信息會一起放在一個anaconda-tb-identifier文件中,這里identifier是一個隨機字符串。

    文件的調用順序為isolinux/vmlinuz--->isolinux/initrd.img--->/init--->/sbin/loader--->imagaes/install.img--->/usr/bin/anaconda。以最新的Fedora 15使用的Anaconda 15.31版本為例(注意RHEL 6.0使用的是比這老的版本,為了跟蹤前沿,這里使用比較新的版本),Anaconda主程序的啟動流程如下:

[html]  view plain copy
 
  1. /init (loader/init.c)  
  2.     --->setupEnv()       設置環境變量  
  3.     --->mount("/proc", "/proc", "proc",...)      掛載/proc文件系統  
  4.     --->mount("/dev", "/dev", "tmpfs",...)       創建/dev文件系統  
  5.     --->execl("/sbin/udevd", "/sbin/udevd",...)      啟動udev  
  6.     --->mount("/sys", "/sys", "sysfs",...)       掛載/sys文件系統  
  7.     --->open("/dev/console", O_WRONLY)       打開控制台設備  
  8.     --->open("/dev/tty1", O_RDWR, 0)     打開tty1控制台,用於執行安裝  
  9.     --->禁用Ctrl+Z、Ctrl+C等  
  10.     --->mount("/", "/", "ext2",...)      嘗試重新掛載rootfs  
  11.     --->mount("none", "/tmp", "tmpfs",...)       掛載/tmp文件系統  
  12.     --->execve("/sbin/loader", argvc, env)     根據選項參數運行loader程序,替換init進程  
  13.   
  14. /sbin/loader (loader/loader.c:main())  
  15.     --->解析運行loader的選項參數  
  16.     --->pyanaconda/isys/log.c:openLog()      打開log  
  17.         --->fopen("/dev/tty3","a")       在tty3上顯示硬件相關消息  
  18.         --->fopen("/tmp/anaconda.log", "a")  
  19.         --->fopen("/tmp/program.log", "a")   
  20.     --->loader.c:parseCmdLineFlags() 解析/proc/cmdline中的內核命令行參數,以獲取ks文件的nfs路徑  
  21.         --->readNetInfo()        讀取/tmp/s390net文件中的網絡配置信息(如果存在的話)  
  22.     --->driverdisk.c:readModuleInfo(arg, modInfo,...)        讀取/lib/modules/module-info中的模塊信息  
  23.     --->loader.c:checkForRam(-1)     檢查內存容量是否足夠  
  24.         --->pyanaconda/isys/mem.c:totalMemory()   
  25.             --->open("/proc/meminfo", O_RDONLY)  獲取meminfo中"MemTotal"一行中的數據(kB為單位)  
  26.     --->loader.c:loadScsiDhModules() 加載SCSI模塊  
  27.                     (讀取/lib/modules/<ver>/kernel/drivers/scsi/device_handler/下的模塊)  
  28.         --->modules.c:mlLoadModuleSet(modNames)      加載用:分隔的模塊列表  
  29.             --->modules.c:_doLoadModule()  
  30.     --->modules.c:mlSaveModuleState()        保存預加載的模塊狀態  
  31.         --->modules.c:processModuleLines()       一行一行地處理每個模塊  
  32.             --->fopen("/proc/modules", "r")      讀取/proc/modules中的所有模塊信息  
  33.             --->modules.c:cb_savestate()     保存當前模塊狀態  
  34.     --->hardware.c:busProbe()        探測總線以加載所有知道的硬件設備  
  35.         --->hardware.c:detectHardware()  
  36.             -->execv("/sbin/udevadm", args)      運行udevadm來加載設備  
  37.     --->driverdisk.c:loadDriverDiskFromPartition()     加載自動檢測到的第三方Driver Disk(如果有的話)  
  38.         --->loadDriverDisk(loaderData, "/tmp/drivers")       加載DD  
  39.     --->pyanaconda/isys/iface_start_NetworkManager()     啟動NetworkManager  
  40.         --->execl(NETWORKMANAGER, NETWORKMANAGER,...)   
  41.     --->kickstart.c:getKickstartFile(&loaderData)        獲取Kickstart文件並復制到/tmp/ks.cfg  
  42.             ################################### NFS 方式 #####################################  
  43.         --->nfsinstall.c:kickstartFromNfs(c+4, loaderData)       從NFS獲取ks文件  
  44.             --->nfsinstall.c:getFileFromNfs()  
  45.                 --->net.c:kickstartNetworkUp()       啟動網卡  
  46.                 --->pyanaconda/isys/iface.c:iface_ip2str()       獲取系統IP  
  47.                 --->nfsinstall.c:parseNfsHostPathOpts()      解析NFS的url路徑  
  48.                 --->拷貝kickstart文件到本地  
  49.             ################################## CD 方式 #######################################  
  50.         --->cdinstall.c:kickstartFromCD()        從光盤上獲取ks文件ks.cfg  
  51.             --->kickstart.c:getKickstartFromBlockDevice()  
  52.                 --->method.c:getFileFromBlockDevice()  
  53.                     --->pyanaconda/isys/imount.c:doPwMount() 掛載光盤  
  54.                         --->pyanaconda/isys/imount.c:mountCommandWrapper()  
  55.                             --->execl("/bin/mount",...)  
  56.             ######################################################################################  
  57.     --->kickstart.c:runKickstart()       運行Kickstart  
  58.         --->導入一些python庫,如pykickstart.parser  
  59.         --->getObject()      創建KickstartParser對象  
  60.         --->preprocessKickstart()  預處理,執行與pykickstart.parser.preprocessKickstart類似的任務  
  61.         --->readKickstart()  
  62.             -->PyObject_CallMethodObjArgs()    處理kickstart文件,解析各個屬性並設置到parser對象上  
  63.         --->處理Kickstart數據                                    
  64.             --->loadKickstartModule()        載入kickstart模塊  
  65.             --->setKickstartNfs()        解析NFS路徑中的主機IP、路徑名、及選項參數  
  66.             --->setDisplayMode()     解析安裝模式(文本、命令行、或圖形模式)  
  67.             --->處理其他各項數據  
  68.     --->net.c:kickstartNetworkUp()       再次啟動網卡  
  69.         --->chooseNetworkInterface(loaderData)       選擇並啟動可使用的網卡(如果有多個網卡)  
  70.         --->writeEnabledNetInfo()    把網卡信息寫入/etc/tsysconfig/network-scripts/ifcfg-DEVICE  
  71.     --->loader.c:doLoaderMain()  
  72.         --->cdinstall.c:findInstallCD()      掛載光盤,加載images/install.img等(光盤安裝情況)  
  73.             --->pyanaconda/isys/imount.c:doPwMount("/mnt/source","iso9660",...)  
  74.         --->STEP_LANG和STEP_KBD       設置安裝語言和鍵映射(若從CD/DVD安裝則跳過這兩步)  
  75.             --->lang.c:setLanguage()和pyanaconda/isys/lang.c:isysLoadKeymap()  
  76.         --->STEP_METHOD和STEP_DRIVER等   命令行模式下設置安裝方法、驅動等(若為圖形安裝模式則跳過)  
  77.     --->selinux.c:loadpolicy()       加載SELinux策略  
  78.         --->execl("/sbin/load_policy",...)  
  79.     --->loader.c:spawnShell()        在tty2上打開Shell,這樣在安裝時可在tty2上登錄  
  80.         --->open("/dev/tty2",...)  
  81.         --->execl("/bin/sh",...)  
  82.     --->execv(anacondaArgs[0], anacondaArgs) 開始運行/usr/bin/anaconda,替換當前進程  

    執行到/usr/bin/anaconda,就是安裝程序的主體了,這是一個python腳本。可見/init程序會初始化console,/dev文件系統,mount相應的目錄等等。然后調用loader程序,就開始了安裝過程。loader程序中會打開log、進行network interface的配置等相關工作,如果指定了網絡上的kickstart文件,他也會下載下來保存為/tmp/ks.cfg ,然后從kickstart配置文件中獲取到install.img所在的位置(如光盤或者NFS上)。如果install.img是在光盤上的話,會被掛載到/mnt/source目錄下。    install.img里面的anaconda程序運行所需要的很多python支持庫、*.so文件等也都是在這時mount過來,一般是復制到/lib 目錄下,然后配置好LD_LIBRARY_PATH環境變量,這樣主安裝程序運行時候就可以找到庫了。當然硬盤也會被mount到一個目錄下,這樣安裝程序才能把OS文件及各個軟件包都寫到這個目錄去。
    5、Anaconda各模塊分析 
    從沒計角度看,整個安裝程序分為三層,從上層往下層依次是前端顯示、調度中心、安裝行為。如下圖所示。

圖1 Anaconda體系結構
    Dispatcher類在主目錄pyanaconda下的dispatch.py模塊中,負責整個安裝流程的控制,在安裝過程中,某些安裝步驟有的需要前置安裝操作,有的又需要后置安裝操作,而某些安裝操作則是沒有用戶界面的,我們統稱這些安裝操作為無用戶界面的安裝操作,那么,這些沒有用戶界面的安裝操作由誰來調度運行呢?答案就是Dispatcher。InstallControlWindow類控制安裝過程中前端圖形界面的顯示,總體調度各個安裝圖形界面類,InstallControlWindow建立圖形界面的主窗體,每個具體的圖形安裝界面可視為其子窗體。InstallControlWindow調用 Dispatcher控制整個安裝流程。安裝過程中,每個具體的圖形安裝界面均對應一個具體的類,由其對應的具體的類完成具體的安裝任務,我們將這些圖形界面類歸為前端顯示層,位於iw目錄下的模塊中。
    anaconda將某些用戶界面類中的具體安裝操作分離出來,放到了另外一些模塊中,這些模塊放在了anaconda的主目錄pyanaconda下。由Dispatcher直接調用的沒有用戶界面的安裝步驟對應的函數也放在了anaconda的主目錄下,我們將這些模塊歸為安裝行為層。
    dispatch.py模塊中有一個序列(sequence)數據結構:installSteps,如下所示:

[python]  view plain copy
 
  1. installSteps = [  
  2.     ("language", ),  
  3.     ("keyboard", ),  
  4.     ("betanag", betaNagScreen, ),  
  5.     ("filtertype", ),  
  6.     ("filter", ),  
  7.     ("storageinit", storageInitialize, ),  
  8.     ("findrootparts", findRootParts, ),  
  9.     ("findinstall", ),  
  10.     ("network", ),  
  11.     ("timezone", ),  
  12.     ("accounts", ),  
  13.     ("setuptime", setupTimezone, ),  
  14.     ("parttype", ),  
  15.     ("cleardiskssel", ),  
  16.     ("autopartitionexecute", doAutoPartition, ),  
  17.     ("partition", ),  
  18.     ("upgrademount", upgradeMountFilesystems, ),  
  19.     ("restoretime", restoreTime, ),  
  20.     ("upgradecontinue", queryUpgradeContinue, ),  
  21.     ("upgradeswapsuggestion", upgradeSwapSuggestion, ),  
  22.   
  23.     # ......  
  24.   
  25.     ]  

    installSteps中記錄了有序排列的整個安裝過程中所有可能的安裝步驟,在生成具體的Dispatcher實例時,會根據安裝類型制定對此進行相應裁減。installSteps中的條目(item)有兩種格式,即( name )或( name, Function )。name代表安裝步驟的名稱,Function指安裝操作的具體執行函數,這個函數會直接由Dispatcher調用。所有的安裝步驟都把anaconda對象作為唯一的參數,當我們調用這個Function時,這個參數就會傳進去。
    我們假設當前安裝步驟為autopartitionexecute,如果熟悉linux安裝過程,你應該可以猜出這個安裝步驟是分區方式選擇,對應該安裝步驟的函數為storage/partitioning.py中的doAutoPartition函數,代碼片斷如下:

[python]  view plain copy
 
  1. def doAutoPartition(anaconda):  
  2.   
  3.     # ......  
  4.   
  5.     if anaconda.storage.doAutoPart:  
  6.        (disks, devs) = _createFreeSpacePartitions(anaconda)  
  7.         if disks == []:  
  8.             if anaconda.ksdata:  
  9.                 msg = _("Could not find enough free space for automatic "  
  10.                         "partitioning.  Press 'OK' to exit the installer.")  
  11.             else:  
  12.                 msg = _("Could not find enough free space for automatic "  
  13.                         "partitioning, please use another partitioning method.")  
  14.   
  15.             anaconda.intf.messageWindow(_("Error Partitioning"), msg,  
  16.                                         custom_icon='error')  
  17.   
  18.             if anaconda.ksdata:  
  19.                 sys.exit(0)  
  20.   
  21.             anaconda.storage.reset()  
  22.             return DISPATCH_BACK  
  23.   
  24.         _schedulePartitions(anaconda, disks)  
  25.   
  26.     # sanity check the individual devices  
  27.     log.warning("not sanity checking devices because I don't know how yet")  
  28.   
  29.     # run the autopart function to allocate and grow partitions  
  30.   
  31.     try:  
  32.         doPartitioning(anaconda.storage  
  33.         if anaconda.storage.doAutoPart:  
  34.             _scheduleLVs(anaconda, devs)  
  35.   
  36.         # grow LVs  
  37.         growLVM(anaconda.storage)  
  38.     except PartitioningWarning as msg:  
  39.         if not anaconda.ksdata:  
  40.             anaconda.intf.messageWindow(_("Warnings During Automatic "  
  41.                                           "Partitioning"),  
  42.                            _("Following warnings occurred during automatic "  
  43.                            "partitioning:\n\n%s") % (msg,),  
  44.                            custom_icon='warning')  
  45.         else:  
  46.             log.warning(msg)  
  47.     except PartitioningError as msg:  
  48.         # restore drives to original state  
  49.         anaconda.storage.reset()  
  50.         if not anaconda.ksdata:  
  51.             extra = ""  
  52.   
  53.             if anaconda.displayMode != "t":  
  54.                 anaconda.dispatch.skipStep("partition", skip = 0)  
  55.         else:  
  56.             extra = _("\n\nPress 'OK' to exit the installer.")  
  57.         anaconda.intf.messageWindow(_("Error Partitioning"),  
  58.                _("Could not allocate requested partitions: \n\n"  
  59.                  "%(msg)s.%(extra)s") % {'msg': msg, 'extra': extra},  
  60.                custom_icon='error')  
  61.   
  62.         if anaconda.ksdata:  
  63.             sys.exit(0)  
  64.         else:  
  65.             return DISPATCH_BACK  
  66.   
  67.     # now do a full check of the requests  
  68.     (errors, warnings) = anaconda.storage.sanityCheck()  
  69.     if warnings:  
  70.         for warning in warnings:  
  71.             log.warning(warning)  
  72.     if errors:  
  73.         errortxt = "\n".join(errors)  
  74.         if anaconda.ksdata:  
  75.             extra = _("\n\nPress 'OK' to exit the installer.")  
  76.         else:  
  77.             extra = _("\n\nPress 'OK' to choose a different partitioning option.")  
  78.   
  79.         anaconda.intf.messageWindow(_("Automatic Partitioning Errors"),  
  80.                            _("The following errors occurred with your "  
  81.                              "partitioning:\n\n%(errortxt)s\n\n"  
  82.                              "This can happen if there is not enough "  
  83.                              "space on your hard drive(s) for the "  
  84.                              "installation. %(extra)s")  
  85.                            % {'errortxt': errortxt, 'extra': extra},  
  86.                            custom_icon='error')  
  87.         #  
  88.         # XXX if in kickstart we reboot  
  89.         #  
  90.         if anaconda.ksdata:  
  91.             anaconda.intf.messageWindow(_("Unrecoverable Error"),  
  92.                                _("The system will now reboot."))  
  93.             sys.exit(0)  
  94.         anaconda.storage.reset()  
  95.         return DISPATCH_BACK  

    主要執行的步驟包括創建空閑分區,運行autopart分配或增長分區容量,分區完整性檢查等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM