Linux安裝程序Anaconda分析

一、概述 
    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安裝光盤。 
    二、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文件夾等。
    三、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。
    四、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是一個隨機字符串。html

    文件的調用順序爲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文件及各個軟件包都寫到這個目錄去。
    五、Anaconda各模塊分析 
    從沒計角度看,整個安裝程序分爲三層,從上層往下層依次是前端顯示、調度中心、安裝行爲。以下圖所示。

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

[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函數,代碼片段以下:linux

[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分配或增加分區容量,分區完整性檢查等。後端

相關文章
相關標籤/搜索