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方式共享安裝,可以將安裝光盤先複製到網絡server上保存爲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工具實現server本身主動化安裝的方法。經過生成的kickstart配置文件ks.cfg,server安裝可以實現從裸機到全功能服務的的非交互式(無人值守式)安裝配置;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軟件包信息。它們分別相應高可用性、負載均衡、彈性存儲、可擴展文件系統、以及server基礎的軟件包信息。每個文件夾下都有一個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或Backbutton被單擊時,調度器知道要跳轉到哪一個屏幕,也知道依據對應的設置哪一步應該被忽略而直接跳過。每種安裝模式都有它本身的要跳過或被加入返回的步驟集。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是一個隨機字符串。

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

/init (loader/init.c)
	--->setupEnv()		環境變量設置
	--->mount("/proc", "/proc", "proc",...)		掛載/proc文件系統
	--->mount("/dev", "/dev", "tmpfs",...)		建立/dev文件系統
	--->execl("/sbin/udevd", "/sbin/udevd",...)		啓動udev
	--->mount("/sys", "/sys", "sysfs",...)		掛載/sys文件系統
	--->open("/dev/console", O_WRONLY)		打開控制檯設備
	--->open("/dev/tty1", O_RDWR, 0)		打開tty1控制檯,用於執行安裝
	--->禁用Ctrl+Z、Ctrl+C等
	--->mount("/", "/", "ext2",...)		嘗試又一次掛載rootfs
	--->mount("none", "/tmp", "tmpfs",...)		掛載/tmp文件系統
	--->execve("/sbin/loader", argvc, env)	  依據選項參數執行loader程序,替換init進程

/sbin/loader (loader/loader.c:main())
	--->解析執行loader的選項參數
	--->pyanaconda/isys/log.c:openLog()		打開log
		--->fopen("/dev/tty3","a")		在tty3上顯示硬件相關消息
		--->fopen("/tmp/anaconda.log", "a")
		--->fopen("/tmp/program.log", "a") 
	--->loader.c:parseCmdLineFlags()	解析/proc/cmdline中的內核命令行參數,以獲取ks文件的nfs路徑
		--->readNetInfo()		讀取/tmp/s390net文件裏的網絡配置信息(假設存在的話)
	--->driverdisk.c:readModuleInfo(arg, modInfo,...)		讀取/lib/modules/module-info中的模塊信息
	--->loader.c:checkForRam(-1)		檢查內存容量是否足夠
		--->pyanaconda/isys/mem.c:totalMemory() 
			--->open("/proc/meminfo", O_RDONLY)	獲取meminfo中"MemTotal"一行中的數據(kB爲單位)
	--->loader.c:loadScsiDhModules()	載入SCSI模塊
					(讀取/lib/modules/<ver>/kernel/drivers/scsi/device_handler/下的模塊)
		--->modules.c:mlLoadModuleSet(modNames)		載入用:分隔的模塊列表
			--->modules.c:_doLoadModule()
	--->modules.c:mlSaveModuleState()		保存預載入的模塊狀態
		--->modules.c:processModuleLines()		一行一行地處理每個模塊
			--->fopen("/proc/modules", "r")		讀取/proc/modules中的所有模塊信息
			--->modules.c:cb_savestate()		保存當前模塊狀態
	--->hardware.c:busProbe()		探測總線以載入所有知道的硬件設備
		--->hardware.c:detectHardware()
			-->execv("/sbin/udevadm", args)		執行udevadm來載入設備
	--->driverdisk.c:loadDriverDiskFromPartition()	  載入本身主動檢測到的第三方Driver Disk(假設有的話)
		--->loadDriverDisk(loaderData, "/tmp/drivers")		載入DD
	--->pyanaconda/isys/iface_start_NetworkManager()		啓動NetworkManager
		--->execl(NETWORKMANAGER, NETWORKMANAGER,...) 
	--->kickstart.c:getKickstartFile(&loaderData)		獲取Kickstart文件並拷貝到/tmp/ks.cfg
        	################################### NFS 方式 #####################################
		--->nfsinstall.c:kickstartFromNfs(c+4, loaderData)		從NFS獲取ks文件
			--->nfsinstall.c:getFileFromNfs()
				--->net.c:kickstartNetworkUp()		啓動網卡
				--->pyanaconda/isys/iface.c:iface_ip2str()		獲取系統IP
				--->nfsinstall.c:parseNfsHostPathOpts()		解析NFS的url路徑
				--->拷貝kickstart文件到本地
        	################################## CD 方式 #######################################
		--->cdinstall.c:kickstartFromCD()		從光盤上獲取ks文件ks.cfg
			--->kickstart.c:getKickstartFromBlockDevice()
				--->method.c:getFileFromBlockDevice()
					--->pyanaconda/isys/imount.c:doPwMount()	掛載光盤
						--->pyanaconda/isys/imount.c:mountCommandWrapper()
							--->execl("/bin/mount",...)
       	 	######################################################################################
	--->kickstart.c:runKickstart()		執行Kickstart
		--->導入一些python庫,如pykickstart.parser
		--->getObject()		建立KickstartParser對象
		--->preprocessKickstart()  預處理,執行與pykickstart.parser.preprocessKickstart類似的任務
		--->readKickstart()
			-->PyObject_CallMethodObjArgs()	  處理kickstart文件,解析各個屬性並設置到parser對象上
		--->處理Kickstart數據                                  
			--->loadKickstartModule()		載入kickstart模塊
			--->setKickstartNfs()		解析NFS路徑中的主機IP、路徑名、及選項參數
			--->setDisplayMode()		解析安裝模式(文本、命令行、或圖形模式)
			--->處理其它各項數據
	--->net.c:kickstartNetworkUp()		再次啓動網卡
		--->chooseNetworkInterface(loaderData)		選擇並啓動可以使用的網卡(假設有多個網卡)
		--->writeEnabledNetInfo()	把網卡信息寫入/etc/tsysconfig/network-scripts/ifcfg-DEVICE
	--->loader.c:doLoaderMain()
		--->cdinstall.c:findInstallCD()		掛載光盤,載入images/install.img等(光盤安裝狀況)
			--->pyanaconda/isys/imount.c:doPwMount("/mnt/source","iso9660",...)
		--->STEP_LANG和STEP_KBD		設置安裝語言和鍵映射(若從CD/DVD安裝則跳過這兩步)
			--->lang.c:setLanguage()和pyanaconda/isys/lang.c:isysLoadKeymap()
		--->STEP_METHOD和STEP_DRIVER等	  命令行模式下設置安裝方法、驅動等(若爲圖形安裝模式則跳過)
	--->selinux.c:loadpolicy()		載入SELinux策略
		--->execl("/sbin/load_policy",...)
	--->loader.c:spawnShell()		在tty2上打開Shell,這樣在安裝時可在tty2上登陸
		--->open("/dev/tty2",...)
		--->execl("/bin/sh",...)
	--->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,例如如下所看到的:
installSteps = [
    ("language", ),
    ("keyboard", ),
    ("betanag", betaNagScreen, ),
    ("filtertype", ),
    ("filter", ),
    ("storageinit", storageInitialize, ),
    ("findrootparts", findRootParts, ),
    ("findinstall", ),
    ("network", ),
    ("timezone", ),
    ("accounts", ),
    ("setuptime", setupTimezone, ),
    ("parttype", ),
    ("cleardiskssel", ),
    ("autopartitionexecute", doAutoPartition, ),
    ("partition", ),
    ("upgrademount", upgradeMountFilesystems, ),
    ("restoretime", restoreTime, ),
    ("upgradecontinue", queryUpgradeContinue, ),
    ("upgradeswapsuggestion", upgradeSwapSuggestion, ),

    # ......

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

    # ......

    if anaconda.storage.doAutoPart:
       (disks, devs) = _createFreeSpacePartitions(anaconda)
        if disks == []:
            if anaconda.ksdata:
                msg = _("Could not find enough free space for automatic "
                        "partitioning.  Press 'OK' to exit the installer.")
            else:
                msg = _("Could not find enough free space for automatic "
                        "partitioning, please use another partitioning method.")

            anaconda.intf.messageWindow(_("Error Partitioning"), msg,
                                        custom_icon='error')

            if anaconda.ksdata:
                sys.exit(0)

            anaconda.storage.reset()
            return DISPATCH_BACK

        _schedulePartitions(anaconda, disks)

    # sanity check the individual devices
    log.warning("not sanity checking devices because I don't know how yet")

    # run the autopart function to allocate and grow partitions

    try:
        doPartitioning(anaconda.storage
        if anaconda.storage.doAutoPart:
            _scheduleLVs(anaconda, devs)

        # grow LVs
        growLVM(anaconda.storage)
    except PartitioningWarning as msg:
        if not anaconda.ksdata:
            anaconda.intf.messageWindow(_("Warnings During Automatic "
                                          "Partitioning"),
                           _("Following warnings occurred during automatic "
                           "partitioning:\n\n%s") % (msg,),
                           custom_icon='warning')
        else:
            log.warning(msg)
    except PartitioningError as msg:
        # restore drives to original state
        anaconda.storage.reset()
        if not anaconda.ksdata:
            extra = ""

            if anaconda.displayMode != "t":
                anaconda.dispatch.skipStep("partition", skip = 0)
        else:
            extra = _("\n\nPress 'OK' to exit the installer.")
        anaconda.intf.messageWindow(_("Error Partitioning"),
               _("Could not allocate requested partitions: \n\n"
                 "%(msg)s.%(extra)s") % {'msg': msg, 'extra': extra},
               custom_icon='error')

        if anaconda.ksdata:
            sys.exit(0)
        else:
            return DISPATCH_BACK

    # now do a full check of the requests
    (errors, warnings) = anaconda.storage.sanityCheck()
    if warnings:
        for warning in warnings:
            log.warning(warning)
    if errors:
        errortxt = "\n".join(errors)
        if anaconda.ksdata:
            extra = _("\n\nPress 'OK' to exit the installer.")
        else:
            extra = _("\n\nPress 'OK' to choose a different partitioning option.")

        anaconda.intf.messageWindow(_("Automatic Partitioning Errors"),
                           _("The following errors occurred with your "
                             "partitioning:\n\n%(errortxt)s\n\n"
                             "This can happen if there is not enough "
                             "space on your hard drive(s) for the "
                             "installation. %(extra)s")
                           % {'errortxt': errortxt, 'extra': extra},
                           custom_icon='error')
        #
        # XXX if in kickstart we reboot
        #
        if anaconda.ksdata:
            anaconda.intf.messageWindow(_("Unrecoverable Error"),
                               _("The system will now reboot."))
            sys.exit(0)
        anaconda.storage.reset()
        return DISPATCH_BACK
    主要執行的步驟包含建立空暇分區,執行autopart分配或增加分區容量,分區完整性檢查等。
相關文章
相關標籤/搜索