共找到2條詞條名為Anaconda的結果 展開

Anaconda

槍支

anaconda,作為槍支的名字,由柯爾特公司生產,該槍重量很大,在手把處加入了鉛塊,持續射擊容易炸膛,該槍運用於美國警備部門使用。在網路遊戲反恐精英online中以左輪軍魂的身份出現。此外有同名電影《anaconda》,在一些其他領域anaconda也有出現。

引言


anaconda 是 RedHat 的安裝程序。
Linux的安裝過程可以分為兩個階段:
第一階段:載入內核,創建供後續安裝過程使用的系統環境。
第二階段:載入系統安裝程序,執行具體的安裝過程。
對於第一階段,有不少資料作了比較詳細的介紹,而對於第二階段,也就是具體的安裝過程,卻鮮有資料介紹,這裡針對 redhat9.0 的安裝程序 anaconda 作分析。
anaconda 是用 python 語言寫的,所以最好具備一些 python 語言的基礎和面向對象的基本知識,如果有過圖形界面程序設計的經驗,則無論是對圖形安裝模式還是字元安裝模式安裝過程的理解都會有很大幫助。

概述


系統啟動,載入啟動映像,在內存中建立了 linux 系統環境后,解析安裝程序映像文件,將安裝程序載入內存,執行主執行程序 anaconda ,該程序是具體安裝程序的一個入口,負責啟動具體的安轉過程,完成 linux 系統的安裝。
首先看一下安裝映像文件。進入 redhat9.0 第一張系統盤的 redhat/base 目錄,其中 stage2.img 就是當安裝介質為 CD-ROM時的安裝映像文件。採用如下命令解析該映像文件(注意:命令中的掛載點 /mnt/stage2 要根據實際情況修改):
mount -o loop /mnt/cdrom/Redhat/base/stage2.img /mnt/stage2
解析后可以看到,安裝程序的主執行體 anaconda 在 /mnt/stag2/usr/bin 目錄下,其它安裝腳本模塊均在 /mnt/stage2/usr/lib/anaconda 目錄下,今後的行文中,除非是 anaconda 主執行體,其餘均指 /mnt/stage2/usr/lib/anaconda 目錄下的模塊,並將該目錄簡稱為 anaconda 主目錄。

anaconda 主目錄結構


installclasses子目錄中的各個模塊定義了在安裝過程中用戶可選擇的安裝類型。redhat9.0 下包含了四個文件 workstation.py,server.py,custom.py 和 personal_desktop.py 。
workstation.py 描述了工作站安裝類型
server.py 描述了伺服器安裝類型
custom.py 描述了用戶自定義安裝類型
personal_desktop.py 描述了個人桌面安裝類型。
每個安裝類型描述文件根據相應安裝類型的特點,分別對安裝步驟、分區策略以及安裝包的取捨給出了不同的方案。
Iw 子目錄下包含所有安裝圖形界面類所在的模塊,每個圖形界面對應一個類,負責相應安裝步驟圖形界面的具體外觀顯示及與用戶的交互,(可能)調用 anaconda 主目錄下的相關安裝行為模塊完成具體的安裝操作。
textw 子目錄和iw 子目錄含義是一致的,只是包含的是字元安裝模式的前端字元用戶界面類所在的模塊,每個字元用戶界面對應一個類,負責與用戶的交互,字元界面的採用了 python 的 snack 庫。
如果說用戶界面類是處理安裝程序外觀的話,則 anaconda 主目錄下的各 python 模塊則執行每個安裝界面背後具體的安裝行為,包括那些無用戶界面安裝步驟的安裝操作。
Python 的許多內置模塊在目錄 /mnt/stage2/usr/lib/pythonXX 下,其中 XX 指版本號。另外可參考 redhat 的 anaconda 源碼包,所有的相關工具都在源碼包里,在理解了安裝過程的原理后,可修改源碼,重新編譯,然後搭建測試環境進行測試。

總體分析


圖形安裝界面採用 python + Gtk 開發,各個模塊(類)之間的邏輯關係可用下圖表示:
anaconda
anaconda
安裝程序邏輯上可劃分為三個層次,先看最主要的一層:調度中心。可以說,Dispatcher類和InstallControlWindow類控制了整個安裝程序的運轉,理解這兩個核心類的功能及其與用戶界面類和安裝行為類的邏輯關係,就基本上理解了整個安裝程序。
Dispatcher類在anaconda主目錄下的dispatch.py模塊中,負責整個安裝流程的控制,在安裝過程中,某些安裝步驟有的需要前置安裝操作,有的又需要後置安裝操作,而某些安裝操作則是沒有用戶界面的,統稱這些安裝操作為無用戶界面的安裝操作,那麼,這些沒有用戶界面的安裝操作由誰來調度運行呢?答案就是Dispatcher。
InstallControlWindow 類在 anaconda 主目錄下的 gui.py模塊中,控制安裝過程中前端圖形界面的顯示,總體調度各個安裝圖形界面類,InstallControlWindow 建立圖形界面的主窗體,每個具體的圖形安裝界面可視為其子窗體。InstallControlWindow 調用 Dispatcher 控制整個安裝流程。
安裝過程中,每個具體的圖形安裝界面均對應一個具體的類,由其對應的具體的類完成具體的安裝任務,將這些圖形界面類歸為前端顯示層,位於iw目錄下的模塊中。
anaconda將某些用戶界面類中的具體安裝操作分離出來,放到了另外一些模塊中,這些模塊放在了anaconda的主目錄下。由Dispatcher直接調用的沒有用戶界面的安裝步驟對應的函數也放在了anaconda的主目錄下,將這些模塊歸為安裝行為層。
圖中虛線表達的含義是"可能調用"。如在安裝過程中,負責前端顯示的package_gui.py模塊可能要調用負責安裝行為的packages.py模塊或其它負責安裝行為模塊中的類或函數。

主要控制類分析


Dispatcher
該類在anaconda主目錄下的dispatch.py模塊中。無論是圖形模式安裝還是字元模式安裝,都由該類來控制安裝流程,所以,分析該類時將圖形用戶界面和字元用戶界面統稱為用戶界面,涉及到字元安裝模式的敘述,讀者暫時可略過,讀到字元安裝模式分析時,再回頭來看。dispatch.py模塊中有一個序列(sequence)數據結構:installSteps。installSteps中記錄了有序排列的整個安裝過程中所有可能的安裝步驟,在生成具體的Dispatcher實例時,會根據安裝類型制定對此進行相應裁減。
installSteps中的條目(item)有如下兩種格式,第一種格式:( name, tuple)
這種格式表示有用戶界面的安裝步驟。其中,name代表安裝步驟的名稱,tuple(元組,python的一種內置數據類型)存放創建相應安裝步驟的用戶界面的參數。
第二種格式:( name, Function, tuple)
這種格式表示沒有用戶界面的安裝步驟,其中,name代表安裝步驟的名稱,Function指安裝操作的具體執行函數,tuple存放的是傳遞給Function的參數。該安裝步驟直接由Dispatcher調度進行。
下面結合具體的程序具體地解釋一下上面的敘述,加深對安裝流程的理解。
首先看下面從序列installSteps中截取的一個程序片斷:
installSteps = [
……
("partitionmethod", ("id.partitions", "id.instClass")),
("partitionobjinit", partitionObjectsInitialize, ("id.diskset","id.partitions","dir", "intf")),
("partitionmethodsetup", partitionMethodSetup, ("id.partitions","dispatch")),
("autopartition", ("id.diskset", "id.partitions", "intf", "dispatch")),
("autopartitionexecute", doAutoPartition, ("dir", "id.diskset","id.partitions", "intf",
"id.instClass", "dispatch")),
("fdisk", ("id.diskset", "id.partitions", "intf")),
……
]
假設當前安裝步驟為partitionmethod,根據其條目的數據格式可以看出該安裝步驟具有用戶界面,根據安裝步驟名稱,如果熟悉linux安裝過程,應該可以猜出這個安裝步驟是分區方式選擇,對應該安裝步驟的用戶界面的類是PartitionMethodWindow,該類屬於前端顯示層,在iw目錄下partitionmethod_gui.py模塊中。看一下該類的部分代碼:
class PartitionMethodWindow(InstallWindow):
def getNext(self):
if self.useAuto.get_active():
self.partitions.useAutopartitioning = 1
else:
self.partitions.useAutopartitioning = 0
……
可以想象當用戶在用戶界面上點擊"下一步"按鈕時getNext函數被調用,分析該函數,可以看出,當用戶選擇自動分區后,該函數設置partitions.useAutopartitioning = 1,如果選擇手動分區,則設置partitions.useAutopartitioning = 0。
本步驟完成,安裝過程進行下一步,Dispatcher首先在installSteps中取下一個條目"partitionobjinit",該條目中含有function,說明此安裝步驟沒有用戶界面,Dispatcher直接調用函數(Function)partitionObjectsInitialize,從字面上我們就可以看出,該函數是對分區操作進行相關初始化方面的操作,執行完該安裝步驟后,Dispatcher繼續調用下一個安裝操作"partitionmethodsetup",該安裝步驟也沒有用戶界面,Dispatcher直接調用函數partitionMethodSetup,函數partitionMethodSetup和partitionObjectsInitialize都屬於安裝行為層,在anaconda主目錄下的partitioning.py模塊中。看一下函數partitionMethodSetup的部分代碼:
def partitionMethodSetup(partitions, dispatch):
dispatch.skipStep("autopartition", skip = not partitions.useAutopartitioning)
dispatch.skipStep("autopartitionexecute", skip = not partitions.useAutopartitioning)
……
可以看出,如果用戶選擇手動分區,即partitions.useAutopartitioning = 0,則not partitions.useAutopartitioning) = 1,即skip = 1,那麼安裝流程式控制制實例Dispatcher就會略過(skipsteip)安裝步驟"autopartition"和"autopartitionexecute"繼續下一個安裝步驟,下一個安裝步驟有用戶界面。
再分析另一個從installSteps中截取的片斷:
installSteps = [
……
("bootloaderadvanced", ("dispatch", "id.bootloader", "id.fsset", "id.diskset")),
("networkdevicecheck", networkDeviceCheck, ("id.network", "dispatch")),
("network", ("id.network", "dir", "intf")),
……
]
假設當前安裝步驟為"bootloaderadvanced",其後的另一個具有用戶界面的安裝步驟為網路配置"network",而進行網路配置的前置條件就是安裝的主機要具有網路設備,因此,系統安裝程序在"network"之前插入了步驟"networkdevicecheck"。假設安裝過程從當前安裝步驟"bootloaderadvanced"進行到下一個安裝步驟"networkdevicecheck",該安裝步驟沒有用戶界面,Dispatcher直接調用函數networkDeviceCheck,看一下該函數的代碼(network.py):
def networkDeviceCheck(network, dispatch):
devs = network.available()
if not devs:
dispatch.skipStep("network")
可以看到,該函數首先檢測是否存在網路設備,如果沒有網路設備,Dispatcher則忽略安裝步驟"network"。這裡,可以說函數networkDeviceCheck是有用戶界面的安裝步驟"network"的前置操作。
通過上面的分析,不難理解為什麼Dispatcher被稱為"主要安裝流程式控制制類"了。其它的安裝步驟控制基本相同。
下面看一下Dispatcher的主要介面。
gotoNext & gotoPrev:這兩個介面分別從當前安裝步驟前進(後退)到下一個(上一個)具有用戶界面的安裝步驟,在圖形界面安裝模式下,由installcontrolwindow調用,在字元模式下,由InstallInterface調用。這兩個函數只是簡單的設置安裝方向,然後調用movestep函數,其核心操作是movestep。這裡來重點分析movestep函數:
def moveStep(self):
……
if self.step == None:
self.step = self.firstStep
else:
self.step = self.step + self.dir
while ((self.step >= self.firstStep and self.step < len(installSteps))
and (self.skipSteps.has_key(installSteps[self.step][0])
or (type(installSteps[self.step][1]) == FunctionType))):
info = installSteps[self.step]
if ((type(info[1]) == FunctionType) and (not self.skipSteps.has_key(info[0]))):
(func, args) = info[1:]
rc = apply(func, self.bindArgs(args))
if rc == DISPATCH_BACK:
self.dir = -1
elif rc == DISPATCH_FORWARD:
self.dir = 1
self.step = self.step + self.dir
if self.step == len(installSteps):
return None
重點看一下程序while循環體,首先看一下循環條件:當下一個安裝步驟是合法的,即在第一個安裝步驟和最後一個安裝步驟之間,並且(and)該步驟被裁減了或者該步驟是一個無用戶界面的安裝步驟,即installSteps的條目的第二個元素是一個function,則進入循環體。進入循環后,Dispatcher直接調用該函數執行安裝操作,其中bindArgs是Dispatcher類的一個函數,負責參數解析,這裡的apply是python的的一個內置方法,用來執行函數,apply介面的第一個參數是要運行的函數名稱,第二個參數是傳給該函數的參數。如果下一個安裝步驟依然無用戶界面,則繼續循環,直到下一個沒有被裁減的具有用戶界面的安裝步驟,對於圖形安裝模式,Dispatcher將控制權交給InstallControlWindow,對於字元安裝模式,Dispatcher將控制權交給InstallInterface。如果安裝過程完成則退出循環。
currentStep:Dispatcher類的另一個主要介面,取得當前的安裝步驟及其相關信息返回給調用者。在圖形安裝模式下,該函數主要由InstallControlWindow調度圖形用戶界面類時調用,在字元模式下,主要由InstallInterface調度字元用戶界面時調用,這兩個類通過該介面取得當前安裝步驟的用戶界面對應的類及創建該用戶界面類的實例所需的信息。
另外,Dispatcher類的主要介面還有skipStep(self, stepToSkip, skip = 1, permanent = 0)是裁減安裝步驟函數。setStepList(self, *steps)是安裝步驟設置函數,主要由安裝類型實例調用,每個安裝類型會根據自身的特點設置安裝步驟。這些介面的實現邏輯都比較簡單,這裡不一一給出分析了。
至此,Dispatcher的主體基本分析完了,看完下面InstallControlWindow以及後面字元安裝模式的InstallInterface類的分析,對Dispatcher將會有更好的理解。

InstallControlWindow


該類在anaconda主目錄下的gui.py模塊中。首先先看一下啟動圖形安裝界面的入口函數run:
def run (self, runres, configFileData):
self.configFileData = configFileData
self.setup_window(runres)
gtk.main()
該函數調用了setup_window介面,該介面調用gtk"繪製"圖形安裝界面的主窗體,然hou控制權交給了gtk。
nextClicked & prevClicked:這兩個介面分別執行從當前圖形安裝界面向前(向後)到下一個圖形安裝界面的操作,可以想象安裝過程中當用戶點擊"下一步"或"上一步"按鈕時,這兩個函數被調用。這兩個函數首先調用主流程式控制制Dispatcher實例向前(向後)前進到下一個圖形安裝界面,然後調用setScreen函數,從函數名稱的字面上看,setScreen是設置圖形界面的,那麼接下來我們看一看該函數的具體功能,在分析其之前,要首先看一下gui.py模塊中的一個字典數據結構:stepToClass。該字典中記錄了安裝過程中所有的具有圖形用戶界面的安裝步驟。
stepToClass = {
"language" : ("language_gui", "LanguageWindow"),
"keyboard" : ("keyboard_gui", "KeyboardWindow"),
……
}
每一個條目從左到右依次是安裝步驟名稱、圖形界面對應的類所在的模塊,圖形界面類的名稱。如language為安裝步驟名稱,language_gui為該步驟對應的圖形界面類所在的模塊language_gui.py,LanguageWindow為圖形界面對應的類名。理解了該數據結構后,開始分析setScreen函數:
def setScreen (self):
(step, args) = self.dispatch.currentStep()
if not stepToClass[step]:
if self.dir == 1:
return self.nextClicked()
else:
return self.prevClicked()
(file, className) = stepToClass[step]
newScreenClass = None
s = "from %s import %s; newScreenClass = %s" % (file, className, className)
while 1:
exec s
break
self.destroyCurrentWindow()
self.currentWindow = newScreenClass(ics)
new_screen = apply(self.currentWindow.getScreen, args)
self.installFrame.add(new_screen)
self.installFrame.show_all()
……
前面的nextClicked和prevClicked函數已經通過Dispatcher將要進行的安裝步驟標記為當前安裝步驟,所以該函數首先通過Dispatcher的currentStep從Dispatcher的數據結構installSteps中取得當前安裝步驟名稱及相關信息,接下來,做了一下判斷,如果Dispatcher的當前安裝步驟不在字典stepToClass中,則忽略該步驟,調用nextClicked或prevClicked繼續下一個圖形界面安裝步驟,直到下一個步驟在字典stepToClass中。驗證通過後,從字典stepToClass中取得當前圖形安裝界面對應的類及該類所在模塊,然後導入該模塊並創建圖形安裝界面的實例,銷毀前一個圖形安裝界面,並將新創建的圖形界面實例置為當前安裝界面,調用圖形安裝界面實例的getScreen函數生成該安裝步驟的圖形用戶界面,然後顯示。
至此,InstallControlWindow的主要邏輯已經分析完了,接下來涉及每個具體安裝界面及其安裝操作讀者可以到iw目錄下逐個深入分析。

主執行程序anaconda分析


前面比較詳細的分析了圖形模式和字元模式的安裝過程,對於圖形安裝過程,圖形環境運行是建立在X Server基礎上的,因此在進行圖形安裝時,必須運行X。因此,簡單的說,對於圖形模式,anaconda的主執行體就作了兩件事:1、運行X伺服器(如果X沒有運行)。2、啟動圖形模式安裝過程。而對於字元模式,anaconda的主執行體就作了一件事,啟動字元模式安裝過程。
啟動X server
如果是圖形模式安裝,則anaconda首先要啟動X server。anaconda首先檢測圖形正常顯示所需的硬體資源。Python提供了許多內置模塊實現相關硬體的操作。這些模塊在目錄pythonXX/rhpl下。
Videocard.py、Monitor.py、Mouse.py、keyboard.py分別是顯示卡、監視器、滑鼠和鍵盤的相關操作和配置數據。xserver.py - 圖形模式下X server啟動初始化方面的工作,包括相關硬體的探測和X server的啟動,xserver為videcard、monitor和mouse的探測提供了統一個介面,實際調用的是相應硬體模塊的介面。
下邊的程序片段探測顯示卡、監視器和滑鼠、鍵盤,並且利用xhwstate記錄該配置狀態,供後續安裝過程使用。
import rhpl.xserver as xserver
(videohw, monitorhw, mousehw) = xserver.probeHW(skipDDCProbe=skipddcprobe,
skipMouseProbe = skipmouseprobe)
kbd = keyboard.Keyboard()
if keymap:
kbd.set(keymap)
xcfg = xhwstate.XF86HardwareState(defcard=videohw, defmon=monitorhw)
硬體檢測完成後,啟動X server的條件已就緒,如果X server沒有啟動,並且安裝模式為圖形模式,則啟動X server:
xsetup_failed = xserver.startXServer(videohw, monitorhw, mousehw, kbd,runres,
xStartedCB=doStartupX11Actions,
xQuitCB=doShutdownX11Actions,
logfile=xlogfile)
該調用除了傳遞相關硬體的配置信息外,還傳遞了兩個函數。
doStartupX11Actions函數在X server啟動時調用,該函數主要是啟動窗口管理器,設置相關資源。
doShutdownX11Actions函數在X server退出時調用,關閉窗口管理器。
啟動安裝過程
前面分析安裝過程原理時指出:安裝的流程由Dispatcher控制,對於圖形模式,圖形模式的前端顯示及與用戶的交互由InstallControlWindow調度,而字元模式的前端顯示層由InstallInterface調度。因此,啟動安裝過程,實際就是創建主要控制類的實例,調用實例的介面,啟動安裝過程,然後再由這幾個主要的控制類的實例創建具體安裝界面,創建安裝行為類的實例,調用具體的函數完成具體的安裝過程。
創建實例
1. 創建dispatch實例
dispatch = dispatch.Dispatcher(intf, id, methodobj, rootPath)
2. 創建InstallInterface實例
對於字元模式,前端字元用戶界面由InstallInterface調度。而對於圖形模式,我們在前面分析時,沒有提到InstallInterface類,其實在圖形模式下,也有這麼一個同名類,在模塊gui.py中,但在圖形安裝模式下,這個類的作用很小,除了封裝了創建常用對話框(包括messageWindow,progressWindow,exceptionWindow等)的介面外,一個主要的作用就是為創建installcontrolwindow實例提供了一個介面,為什麼不在anaconda主執行程序中直接創建installcontrolwindow實例呢?個人認為可能是為了保證代碼的一致性。
if (display_mode == 'g'):
from gui import InstallInterface
if (display_mode == 't'):
from text import InstallInterface
if display_mode == "t":
intf = InstallInterface ()
else:
intf = InstallInterface ()
3. 創建installcontrolwindow實例
installcontrolwindow實例在gui.py模塊中的InstallInterface類的介面run中創建,該介面創建了InstallControlWindow實例后運行InstallControlWindow實例的run介面啟動圖形安裝界面:
def run(self, id, dispatch, configFileData):
……
self.icw = InstallControlWindow (self, self.dispatch, lang)
self.icw.run (self.runres, configFileData)
啟動安裝過程
安裝控制類的實例創建完成後,調用其相應介面啟動安裝過程。對於字元安裝模式,則直接調用InstallInterface實例的run介面。而對於圖形安裝模式,則是由InstallInterface實例的run介面間接的調用installcontrolwindow實例的run介面。
intf.run(id, dispatch, configFileData)
另外,在啟動安裝過程前,anacoanda的主執行程序作了大量的繁瑣的準備工作,主要包括引用模塊路徑設置、參數解析、內存檢測、安裝類型設置、創建安裝介質實例等等。