anaconda安裝程序分析2

(1) disptach.py: 下面我們看一下Dispatcher類的主要接口。

    1)gotoNext & gotoPrev:這兩個接口分別從當前安裝步驟前進(後退)到下一個(上一個)具有用戶界面的安裝步驟,在圖形界面安裝模式下,由InstallControlWindow類調用,在字符模式下,由InstallInterface類(在text.py和cmdline.py中)調用。這兩個函數只是簡單的設置安裝方向,然後調用moveStep函數,其核心操作是moveStep。

    2)moveStep:我們來重點分析movestep函數,代碼如下:

  1. def moveStep(self):  
  2.     if self.step == None:  
  3.         self.step = self.firstStep  
  4.     else:  
  5.         if self.step >= len(installSteps):  
  6.             return None  
  7.   
  8.         log.info("leaving (%d) step %s" %(self._getDir(), installSteps[self.step][0]))  
  9.         self.step = self.step + self._getDir()  
  10.   
  11.         if self.step >= len(installSteps):  
  12.             return None  
  13.   
  14.     while self.step >= self.firstStep and self.step < len(installSteps) \  
  15.         and (self.stepInSkipList(self.step) or self.stepIsDirect(self.step)):  
  16.   
  17.         if self.stepIsDirect(self.step) and not self.stepInSkipList(self.step):  
  18.             (stepName, stepFunc) = installSteps[self.step]  
  19.             log.info("moving (%d) to step %s" %(self._getDir(), stepName))  
  20.             log.debug("%s is a direct step" %(stepName,))  
  21.             rc = stepFunc(self.anaconda)  
  22.             if rc in [DISPATCH_BACK, DISPATCH_FORWARD]:  
  23.                 self._setDir(rc)  
  24.             log.info("leaving (%d) step %s" %(self._getDir(), stepName))  
  25.             # if anything else, leave self.dir alone  
  26.   
  27.         self.step = self.step + self._getDir()  
  28.         if self.step == len(installSteps):  # 安裝過程完成,退出循環  
  29.             return None  
  30.   
  31.     if (self.step < 0):  
  32.         # pick the first step not in the skip list  
  33.         self.step = 0  
  34.         while self.skipSteps.has_key(installSteps[self.step][0]):  
  35.             self.step = self.step + 1   # 步數加一,向前  
  36.     elif self.step >= len(installSteps):  
  37.         self.step = len(installSteps) - 1  
  38.         while self.skipSteps.has_key(installSteps[self.step][0]):  
  39.             self.step = self.step - 1  
  40.     log.info("moving (%d) to step %s" %(self._getDir(), installSteps[self.step][0]))  
    我們重點看一下程序while循環體,首先看一下循環條件:當下一個安裝步驟是合法的,即在第一個安裝步驟和最後一個安裝步驟之間,並且(and)該步驟被跳過或者該步驟是一個無用戶界面的安裝步驟,即installSteps的條目的第二個元素是一個function,則進入循環體。進入循環後,Dispatcher直接調用該函數stepFunc執行安裝操作。如果下一個安裝步驟依然無用戶界面,則步數加一向前,繼續循環,直到下一個沒有被跳過的具有用戶界面的安裝步驟,對於圖形安裝模式,Dispatcher將控制權交給guid.py中的InstallControlWindow,對於字符安裝模式,Dispatcher將控制權交給InstallInterface。如果安裝過程完成則退出循環。
    3)currentStep:Dispatcher類的另一個主要接口,取得當前的安裝步驟及其相關信息返回給調用者。在圖形安裝模式下,該函數主要在InstallControlWindow調度圖形用戶界面類時調用,在字符模式下,主要在InstallInterface調度字符用戶界面時調用,這兩個類通過該接口取得當前安裝步驟的用戶界面對應類及創建該用戶界面類的實例所需的信息。
  1. def currentStep(self):  
  2.       if self.step == None:  
  3.           self.gotoNext()  
  4.       elif self.step >= len(installSteps):  
  5.           return (NoneNone)  
  6.   
  7.       stepInfo = installSteps[self.step]  
  8.       step = stepInfo[0]  
  9.   
  10.       return (step, self.anaconda)  
    另外,Dispatcher類的主要接口還有skipStep(self, stepToSkip, skip = 1, permanent = 0)是跳過安裝步驟的函數。setStepList(self, *steps)是安裝步驟設置函數,主要由安裝類型實例調用,每個安裝類型會根據自身的特點設置安裝步驟。這些接口的實現邏輯都比較簡單,這裏不一一給出分析了。
    (2)gui.py: 核心是字符安裝模式的InstallInterface類和圖形安裝模式InstallControlWindow類的實現。看InstallControlWindow中的接口。

    1)數據結構stepTopClass: 該字典中記錄了安裝過程中所有的具有圖形用戶界面的安裝步驟。

  1. stepToClass = {  
  2.     "language" : ("language_gui""LanguageWindow"),  
  3.     "keyboard" : ("kbd_gui""KeyboardWindow"),  
  4.     "filtertype" : ("filter_type""FilterTypeWindow"),  
  5.     "filter" : ("filter_gui""FilterWindow"),   
  6.     "zfcpconfig" : ("zfcp_gui""ZFCPWindow"),  
  7.     "partition" : ("partition_gui""PartitionWindow"),  
  8.     "parttype" : ("autopart_type""PartitionTypeWindow"),  
  9.     "cleardiskssel": ("cleardisks_gui""ClearDisksWindow"),  
  10.     "findinstall" : ("examine_gui""UpgradeExamineWindow"),  
  11.     "addswap" : ("upgrade_swap_gui""UpgradeSwapWindow"),  
  12.     "upgrademigratefs" : ("upgrade_migratefs_gui""UpgradeMigrateFSWindow"),  
  13.     "bootloader": ("bootloader_main_gui""MainBootloaderWindow"),  
  14.     "upgbootloader": ("upgrade_bootloader_gui""UpgradeBootloaderWindow"),  
  15.     "network" : ("network_gui""NetworkWindow"),  
  16.     "timezone" : ("timezone_gui""TimezoneWindow"),  
  17.     "accounts" : ("account_gui""AccountWindow"),  
  18.     "tasksel": ("task_gui""TaskWindow"),      
  19.     "group-selection": ("package_gui""GroupSelectionWindow"),  
  20.     "install" : ("progress_gui""InstallProgressWindow"),  
  21.     "complete" : ("congrats_gui""CongratulationWindow"),  
  22. }  
    每一個條目從左到右依次是安裝步驟名稱、圖形界面類所在模塊,圖形界面類的名稱。如language爲安裝步驟名稱,language_gui爲該步驟對應的圖形界面類所在模塊language_gui.py,LanguageWindow爲圖形界面對應的類名。
    2)run: 啓動圖形安裝界面的入口函數。該函數調用了setup_window接口,該接口調用gtk"繪製"圖形安裝界面的主窗體,然後控制權交給了gtk。
  1. def run (self):  
  2.     self.setup_theme()  
  3.     self.setup_window(False)  
  4.     gtk.main()  
    3)nextClicked & prevClicked:這兩個接口分別執行從當前圖形安裝界面向前(向後)到下一個圖形安裝界面的操作,我們可以想象安裝過程中當用戶點擊"下一步" 或"上一步"按鈕時,這兩個函數被調用。這兩個函數首先調用主流程控制Dispatcher實例向前(向後)前進到下一個圖形安裝界面,然後調用setScreen函數設置圖形界面。
  1.    def prevClicked (self, *args):  
  2.        try:  
  3.            self.currentWindow.getPrev ()  
  4.        except StayOnScreen:  
  5.            return  
  6.   
  7.        self.anaconda.dispatch.gotoPrev()  
  8.        self.setScreen ()  
  9.   
  10.    def nextClicked (self, *args):  
  11.        try:  
  12.            rc = self.currentWindow.getNext ()  
  13.        except StayOnScreen:  
  14.            return  
  15.   
  16.        self.anaconda.dispatch.gotoNext()  
  17.        self.setScreen ()  
  18. 4)setScreen: 用於設置圖形界面。代碼如下:  
  19.    def setScreen (self):  
  20.     # 取得當前安裝步驟信息  
  21.        (step, anaconda) = self.anaconda.dispatch.currentStep()  
  22.        if step is None:  
  23.            gtk.main_quit()  
  24.            return  
  25.   
  26.        if not stepToClass[step]:    # 不在其中,則直接跳到下一步  
  27.            if self.anaconda.dispatch.dir == DISPATCH_FORWARD:  
  28.                return self.nextClicked()  
  29.            else:  
  30.                return self.prevClicked()  
  31.   
  32.        (file, className) = stepToClass[step]    # 獲得圖形界面類所在模塊及其類名  
  33.        newScreenClass = None  
  34.   
  35.        while True:  
  36.            try:  
  37.                found = imp.find_module(file, iw.__path__)  
  38.                moduleName = 'pyanaconda.iw.%s' % file  
  39.                loaded = imp.load_module(moduleName, *found) # 載入該圖形界面模塊  
  40.                newScreenClass = loaded.__dict__[className]  
  41.                break  
  42.            except ImportError, e:  
  43.                stdout_log.error("loading interface component %s" % className)  
  44.                stdout_log.error(traceback.format_exc())  
  45.                win = MessageWindow(_("Error!"),  
  46.                                    _("An error occurred when attempting "  
  47.                                      "to load an installer interface "  
  48.                                      "component.\n\nclassName = %s")  
  49.                                    % (className,),  
  50.                                    type="custom", custom_icon="warning",  
  51.                                    custom_buttons=[_("_Exit"),  
  52.                                                    _("_Retry")])  
  53.                if not win.getrc():  
  54.                    msg =  _("The system will now reboot.")  
  55.                    buttons = [_("_Reboot")]  
  56.   
  57.                    MessageWindow(_("Exiting"),  
  58.                                  msg,  
  59.                                  type="custom",  
  60.                                  custom_icon="warning",  
  61.                                  custom_buttons=buttons)  
  62.                    sys.exit(0)  
  63.   
  64.        ics = InstallControlState (self)  
  65.     # 設置是否是可以返回上一步  
  66.        ics.setPrevEnabled(self.anaconda.dispatch.canGoBack())  
  67.        self.destroyCurrentWindow()      # 銷燬原來的圖形安裝界面  
  68.        self.currentWindow = newScreenClass(ics) # 創建新的圖形安裝界面並設爲當前界面  
  69.   
  70.        new_screen = self.currentWindow.getScreen(anaconda)      # 生成安裝步驟的界面  
  71.   
  72.        # If the getScreen method returned None, that means the screen did not  
  73.        # want to be displayed for some reason and we should skip to the next  
  74.        # step.  However, we do not want to remove the current step from the  
  75.        # list as later events may cause the screen to be displayed.  
  76.        if not new_screen:  
  77.            if self.anaconda.dispatch.dir == DISPATCH_FORWARD:  
  78.                self.anaconda.dispatch.gotoNext()  
  79.            else:  
  80.                self.anaconda.dispatch.gotoPrev()  
  81.   
  82.            return self.setScreen()  
  83.   
  84.        self.update (ics)  
  85.   
  86.        self.installFrame.add(new_screen)  
  87.        self.installFrame.show_all()  
  88.   
  89.        self.currentWindow.focus()  
  90.   
  91.        self.handle = gobject.idle_add(self.handleRenderCallback)  
  92.   
  93.        if self.reloadRcQueued:  
  94.            self.window.reset_rc_styles()  
  95.            self.reloadRcQueued = 0  
    前面的nextClicked和prevClicked函數已經通過Dispatcher將要進行的安裝步驟標記爲當前安裝步驟,所以該函數首先通過Dispatcher的currentStep從Dispatcher的數據結構installSteps中取得當前安裝步驟名稱及相關信息,接下來,做了一下判斷,如果Dispatcher的當前安裝步驟不在字典stepToClass中,則忽略該步驟,調用nextClicked或prevClicked繼續下一個圖形界面安裝步驟,直到下一個步驟在字典stepToClass中。驗證通過後,從字典stepToClass中取得當前圖形安裝界面對應的類及該類所在模塊,然後導入該模塊並創建圖形安裝界面的實例,銷燬前一個圖形安裝界面,並將新創建的圖形界面實例置爲當前安裝界面,調用圖形安裝界面實例的getScreen函數生成該安裝步驟的圖形用戶界面,然後顯示。
    至此,InstallControlWindow的主要邏輯已經分析完了,接下來涉及每個具體安裝界面及其安裝操作讀者可以到iw目錄下逐個深入分析。
    (3)anaconda主程序: 圖形環境運行是建立在X Server基礎上的,對於圖形模式,anaconda需要先運行X服務器,然後啓動圖形模式安裝過程。而對於字符模式,anaconda的主執行體就作了一件事,啓動字符模式安裝過程。
  1. if __name__ == "__main__":  
  2.     setupPythonPath()  
  3.   
  4.     # ......  
  5.   
  6.     # 解析啓動本腳本時傳入的參數  
  7.     (opts, args) = parseOptions()  
  8.     from pyanaconda.flags import flags  
  9.     if opts.images:  
  10.         flags.imageInstall = True  
  11.   
  12.     # 設置log  
  13.     import logging  
  14.     from pyanaconda import anaconda_log  
  15.     anaconda_log.init()  
  16.   
  17.     log = logging.getLogger("anaconda")  
  18.     stdoutLog = logging.getLogger("anaconda.stdout")  
  19.   
  20.     # ......  
  21.   
  22.     from pyanaconda import Anaconda  
  23.     anaconda = Anaconda()   # 創建主執行體實例  
  24.     warnings.showwarning = AnacondaShowWarning  
  25.     iutil.setup_translations(gettext)  
  26.   
  27.     # ......  
  28.   
  29.     # 檢測內存,現在只用在文本模式下  
  30.     check_memory(anaconda, opts, 't')  
  31.   
  32.     if opts.unsupportedMode:  
  33.         stdoutLog.error("Running anaconda in %s mode is no longer supported." % opts.unsupportedMode)  
  34.         sys.exit(0)  
  35.   
  36.     # ......  
  37.   
  38.     # kickstart文件解析  
  39.     if opts.ksfile:  
  40.         kickstart.preScriptPass(anaconda, opts.ksfile)  
  41.         anaconda.ksdata = kickstart.parseKickstart(anaconda, opts.ksfile)  
  42.         opts.rescue = opts.rescue or anaconda.ksdata.rescue.rescue  
  43.   
  44.     # ......  
  45.     # 如果沒有X server,使用文本模式  
  46.     if not flags.livecdInstall and not iutil.isS390() and not os.access("/usr/bin/Xorg", os.X_OK):  
  47.          stdoutLog.warning(_("Graphical installation is not available. "  
  48.                              "Starting text mode."))  
  49.          time.sleep(2)  
  50.          anaconda.displayMode = 't'  
  51.   
  52.     # ......  
  53.     # 啓動本地的X server  
  54.     if anaconda.displayMode == 'g' and not flags.preexisting_x11 and not flags.usevnc:  
  55.         try:  
  56.             # start X with its USR1 handler set to ignore.  this will make it send  
  57.             # us SIGUSR1 if it succeeds.  if it fails, catch SIGCHLD and bomb out.  
  58.   
  59.             def sigchld_handler(num, frame):  
  60.                 raise OSError(0"SIGCHLD caught when trying to start the X server.")  
  61.   
  62.             def sigusr1_handler(num, frame):  
  63.                 log.debug("X server has signalled a successful start.")  
  64.   
  65.             def preexec_fn():  
  66.                 signal.signal(signal.SIGUSR1, signal.SIG_IGN)  
  67.   
  68.             old_sigusr1 = signal.signal(signal.SIGUSR1, sigusr1_handler)  
  69.             old_sigchld = signal.signal(signal.SIGCHLD, sigchld_handler)  
  70.             xout = open("/dev/tty5""w")  
  71.   
  72.             # 啓動X server  
  73.             proc = subprocess.Popen(["Xorg""-br""-logfile""/tmp/X.log",  
  74.                                      ":1""vt6""-s""1440""-ac",  
  75.                                      "-nolisten""tcp""-dpi""96",  
  76.                                      "-noreset"],  
  77.                                      close_fds=True, stdout=xout, stderr=xout,  
  78.                                      preexec_fn=preexec_fn)  
  79.   
  80.             signal.pause()  
  81.   
  82.             os.environ["DISPLAY"] = ":1"  
  83.             doStartupX11Actions()  
  84.         except (OSError, RuntimeError) as e:  
  85.             stdoutLog.warning(" X startup failed, falling back to text mode")  
  86.             anaconda.displayMode = 't'  
  87.             graphical_failed = 1  
  88.             time.sleep(2)  
  89.         finally:  
  90.             signal.signal(signal.SIGUSR1, old_sigusr1)  
  91.             signal.signal(signal.SIGCHLD, old_sigchld)  
  92.   
  93.     set_x_resolution(opts.runres)  
  94.   
  95.     # ......  
  96.   
  97.     # 初始化UI界面  
  98.     anaconda.initInterface()  
  99.     anaconda.instClass.configure(anaconda)  
  100.   
  101.     # ......  
  102.   
  103.     # 啓動安裝過程  
  104.     try:  
  105.         anaconda.intf.run(anaconda)  
  106.     except SystemExit, code:  
  107.         anaconda.intf.shutdown()  
  108.   
  109.     if anaconda.ksdata and anaconda.ksdata.reboot.eject:  
  110.         for drive in anaconda.storage.devicetree.devices:  
  111.             if drive.type != "cdrom":  
  112.                 continue  
  113.   
  114.             log.info("attempting to eject %s" % drive.path)  
  115.             drive.eject()  
  116.   
  117.     del anaconda.intf  
    主要工作包括引用模塊路徑設置、參數解析、設置log、內存檢測、安裝類型設置,然後調用pyanaconda/__init__.py::Anaconda類創建主執行體實例anaconda,接着解析kickstart文件,調用/usr/bin/Xorg(位於解開後的install.img中)程序啓動X server,調用Anaconda類的initInterface()初始化界面,調用intf(是InstallInterface類的實例)的run()啓動安裝過程。對於字符安裝模式,是直接調用InstallInterface實例的run接口。而對於圖形安裝模式,則是由InstallInterface實例的run接口間接的調用installcontrolwindow實例的run接口,從而啓動圖形界面。
    (4)pyanaconda/__init__.py: 裏面有Anaconda類,負責具體的啓動安裝過程。前面說過,安裝的流程由Dispatcher控制,對於圖形模式,圖形模式的前端顯示及與用戶的交互由InstallControlWindow調度,而字符模式的前端顯示層由InstallInterface調度。因此,啓動安裝過程,實際就是創建主要控制類的實例,調用實例的接口,啓動安裝過程,然後再由這幾個主要的控制類的實例創建具體安裝界面,創建安裝行爲類的實例,調用具體的函數完成具體的安裝過程。
  1. class Anaconda(object):  
  2.     def __init__(self):  
  3.         import desktop, dispatch, firewall, security  
  4.         import system_config_keyboard.keyboard as keyboard  
  5.         from flags import flags  
  6.   
  7.         # ......  
  8.         # 創建dispatch實例  
  9.         self.dispatch = dispatch.Dispatcher(self)  
  10.         # ......  
  11.   
  12.     # ......  
  13.   
  14.     intf = property(_getInterface, _setInterface, _delInterface)  
  15.   
  16.     # ......  
  17.   
  18.     def initInterface(self):  
  19.         if self._intf:  
  20.             raise RuntimeError, "Second attempt to initialize the InstallInterface"  
  21.   
  22.         # 設置圖形模式需要的鏈接  
  23.         if self.displayMode == 'g':  
  24.             stdoutLog.info (_("Starting graphical installation."))  
  25.   
  26.             try:  
  27.                 from gui import InstallInterface  
  28.             except Exception, e:  
  29.                 from flags import flags  
  30.                 stdoutLog.error("Exception starting GUI installer: %s" %(e,))  
  31.                 # if we're not going to really go into GUI mode, we need to get  
  32.                 # back to vc1 where the text install is going to pop up.  
  33.                 if not flags.livecdInstall:  
  34.                     isys.vtActivate (1)  
  35.                 stdoutLog.warning("GUI installer startup failed, falling back to text mode.")  
  36.                 self.displayMode = 't'  
  37.                 if 'DISPLAY' in os.environ.keys():  
  38.                     del os.environ['DISPLAY']  
  39.                 time.sleep(2)  
  40.   
  41.         if self.displayMode == 't':  
  42.             from text import InstallInterface  
  43.             if not os.environ.has_key("LANG"):  
  44.                 os.environ["LANG"] = "en_US.UTF-8"  
  45.   
  46.         if self.displayMode == 'c':  
  47.             from cmdline import InstallInterface  
  48.   
  49.         self._intf = InstallInterface()     # 創建InstallInterface實例  
  50.         return self._intf  
    主要的工作包括創建dispatch實例,初始化界面,創建InstallInterface實例,它最後會創建InstallControlWindow實例,生成圖形界面。
    整個Anaconda的運行流程如下圖:

圖2 Anaconda運行流程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章