本来想写篇关于Anaconda的文章,但看到这里写的这么详细,转,原文在这里:Linux安装程序Anaconda分析(续)

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

    1)gotoNext & gotoPrev:这两个接口分别从当前安装步骤前进(后退)到下一个(上一个)具有用户界面的安装步骤,在图形界面安装模式下,由InstallControlWindow类调用,在字符模式下,由InstallInterface类(在text.py和cmdline.py中)调用。这两个函数只是简单的设置安装方向,然后调用moveStep函数,其核心操作是moveStep。

2)moveStep:我们来重点分析movestep函数,代码如下:

    def moveStep(self):
if self.step == None:
self.step = self.firstStep
else:
if self.step >= len(installSteps):
return None log.info("leaving (%d) step %s" %(self._getDir(), installSteps[self.step][0]))
self.step = self.step + self._getDir() if self.step >= len(installSteps):
return None while self.step >= self.firstStep and self.step < len(installSteps) \
and (self.stepInSkipList(self.step) or self.stepIsDirect(self.step)): if self.stepIsDirect(self.step) and not self.stepInSkipList(self.step):
(stepName, stepFunc) = installSteps[self.step]
log.info("moving (%d) to step %s" %(self._getDir(), stepName))
log.debug("%s is a direct step" %(stepName,))
rc = stepFunc(self.anaconda)
if rc in [DISPATCH_BACK, DISPATCH_FORWARD]:
self._setDir(rc)
log.info("leaving (%d) step %s" %(self._getDir(), stepName))
# if anything else, leave self.dir alone self.step = self.step + self._getDir()
if self.step == len(installSteps): # 安装过程完成,退出循环
return None if (self.step < 0):
# pick the first step not in the skip list
self.step = 0
while self.skipSteps.has_key(installSteps[self.step][0]):
self.step = self.step + 1 # 步数加一,向前
elif self.step >= len(installSteps):
self.step = len(installSteps) - 1
while self.skipSteps.has_key(installSteps[self.step][0]):
self.step = self.step - 1
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调度字符用户界面时调用,这两个类通过该接口取得当前安装步骤的用户界面对应类及创建该用户界面类的实例所需的信息。

 	def currentStep(self):
if self.step == None:
self.gotoNext()
elif self.step >= len(installSteps):
return (None, None) stepInfo = installSteps[self.step]
step = stepInfo[0] return (step, self.anaconda)

另外,Dispatcher类的主要接口还有skipStep(self, stepToSkip, skip = 1, permanent = 0)是跳过安装步骤的函数。setStepList(self, *steps)是安装步骤设置函数,主要由安装类型实例调用,每个安装类型会根据自身的特点设置安装步骤。这些接口的实现逻辑都比较简单,这里不一一给出分析了。

    (2)gui.py: 核心是字符安装模式的InstallInterface类和图形安装模式InstallControlWindow类的实现。看InstallControlWindow中的接口。

1)数据结构stepTopClass: 该字典中记录了安装过程中所有的具有图形用户界面的安装步骤。

stepToClass = {
"language" : ("language_gui", "LanguageWindow"),
"keyboard" : ("kbd_gui", "KeyboardWindow"),
"filtertype" : ("filter_type", "FilterTypeWindow"),
"filter" : ("filter_gui", "FilterWindow"),
"zfcpconfig" : ("zfcp_gui", "ZFCPWindow"),
"partition" : ("partition_gui", "PartitionWindow"),
"parttype" : ("autopart_type", "PartitionTypeWindow"),
"cleardiskssel": ("cleardisks_gui", "ClearDisksWindow"),
"findinstall" : ("examine_gui", "UpgradeExamineWindow"),
"addswap" : ("upgrade_swap_gui", "UpgradeSwapWindow"),
"upgrademigratefs" : ("upgrade_migratefs_gui", "UpgradeMigrateFSWindow"),
"bootloader": ("bootloader_main_gui", "MainBootloaderWindow"),
"upgbootloader": ("upgrade_bootloader_gui", "UpgradeBootloaderWindow"),
"network" : ("network_gui", "NetworkWindow"),
"timezone" : ("timezone_gui", "TimezoneWindow"),
"accounts" : ("account_gui", "AccountWindow"),
"tasksel": ("task_gui", "TaskWindow"),
"group-selection": ("package_gui", "GroupSelectionWindow"),
"install" : ("progress_gui", "InstallProgressWindow"),
"complete" : ("congrats_gui", "CongratulationWindow"),
}

每一个条目从左到右依次是安装步骤名称、图形界面类所在模块,图形界面类的名称。如language为安装步骤名称,language_gui为该步骤对应的图形界面类所在模块language_gui.py,LanguageWindow为图形界面对应的类名。

    2)run: 启动图形安装界面的入口函数。该函数调用了setup_window接口,该接口调用gtk"绘制"图形安装界面的主窗体,然后控制权交给了gtk

    def run (self):
self.setup_theme()
self.setup_window(False)
gtk.main()

3)nextClicked & prevClicked:这两个接口分别执行从当前图形安装界面向前(向后)到下一个图形安装界面的操作,我们可以想象安装过程中当用户点击"下一步" 或"上一步"按钮时,这两个函数被调用。这两个函数首先调用主流程控制Dispatcher实例向前(向后)前进到下一个图形安装界面,然后调用setScreen函数设置图形界面。

    def prevClicked (self, *args):
try:
self.currentWindow.getPrev ()
except StayOnScreen:
return self.anaconda.dispatch.gotoPrev()
self.setScreen () def nextClicked (self, *args):
try:
rc = self.currentWindow.getNext ()
except StayOnScreen:
return self.anaconda.dispatch.gotoNext()
self.setScreen ()
4)setScreen: 用于设置图形界面。代码如下:
def setScreen (self):
# 取得当前安装步骤信息
(step, anaconda) = self.anaconda.dispatch.currentStep()
if step is None:
gtk.main_quit()
return if not stepToClass[step]: # 不在其中,则直接跳到下一步
if self.anaconda.dispatch.dir == DISPATCH_FORWARD:
return self.nextClicked()
else:
return self.prevClicked() (file, className) = stepToClass[step] # 获得图形界面类所在模块及其类名
newScreenClass = None while True:
try:
found = imp.find_module(file, iw.__path__)
moduleName = 'pyanaconda.iw.%s' % file
loaded = imp.load_module(moduleName, *found) # 载入该图形界面模块
newScreenClass = loaded.__dict__[className]
break
except ImportError, e:
stdout_log.error("loading interface component %s" % className)
stdout_log.error(traceback.format_exc())
win = MessageWindow(_("Error!"),
_("An error occurred when attempting "
"to load an installer interface "
"component.\n\nclassName = %s")
% (className,),
type="custom", custom_icon="warning",
custom_buttons=[_("_Exit"),
_("_Retry")])
if not win.getrc():
msg = _("The system will now reboot.")
buttons = [_("_Reboot")] MessageWindow(_("Exiting"),
msg,
type="custom",
custom_icon="warning",
custom_buttons=buttons)
sys.exit(0) ics = InstallControlState (self)
# 设置是否是可以返回上一步
ics.setPrevEnabled(self.anaconda.dispatch.canGoBack())
self.destroyCurrentWindow() # 销毁原来的图形安装界面
self.currentWindow = newScreenClass(ics) # 创建新的图形安装界面并设为当前界面 new_screen = self.currentWindow.getScreen(anaconda) # 生成安装步骤的界面 # If the getScreen method returned None, that means the screen did not
# want to be displayed for some reason and we should skip to the next
# step. However, we do not want to remove the current step from the
# list as later events may cause the screen to be displayed.
if not new_screen:
if self.anaconda.dispatch.dir == DISPATCH_FORWARD:
self.anaconda.dispatch.gotoNext()
else:
self.anaconda.dispatch.gotoPrev() return self.setScreen() self.update (ics) self.installFrame.add(new_screen)
self.installFrame.show_all() self.currentWindow.focus() self.handle = gobject.idle_add(self.handleRenderCallback) if self.reloadRcQueued:
self.window.reset_rc_styles()
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的主执行体就作了一件事,启动字符模式安装过程。

if __name__ == "__main__":
setupPythonPath() # ...... # 解析启动本脚本时传入的参数
(opts, args) = parseOptions()
from pyanaconda.flags import flags
if opts.images:
flags.imageInstall = True # 设置log
import logging
from pyanaconda import anaconda_log
anaconda_log.init() log = logging.getLogger("anaconda")
stdoutLog = logging.getLogger("anaconda.stdout") # ...... from pyanaconda import Anaconda
anaconda = Anaconda() # 创建主执行体实例
warnings.showwarning = AnacondaShowWarning
iutil.setup_translations(gettext) # ...... # 检测内存,现在只用在文本模式下
check_memory(anaconda, opts, 't') if opts.unsupportedMode:
stdoutLog.error("Running anaconda in %s mode is no longer supported." % opts.unsupportedMode)
sys.exit(0) # ...... # kickstart文件解析
if opts.ksfile:
kickstart.preScriptPass(anaconda, opts.ksfile)
anaconda.ksdata = kickstart.parseKickstart(anaconda, opts.ksfile)
opts.rescue = opts.rescue or anaconda.ksdata.rescue.rescue # ......
# 如果没有X server,使用文本模式
if not flags.livecdInstall and not iutil.isS390() and not os.access("/usr/bin/Xorg", os.X_OK):
stdoutLog.warning(_("Graphical installation is not available. "
"Starting text mode."))
time.sleep(2)
anaconda.displayMode = 't' # ......
# 启动本地的X server
if anaconda.displayMode == 'g' and not flags.preexisting_x11 and not flags.usevnc:
try:
# start X with its USR1 handler set to ignore. this will make it send
# us SIGUSR1 if it succeeds. if it fails, catch SIGCHLD and bomb out. def sigchld_handler(num, frame):
raise OSError(0, "SIGCHLD caught when trying to start the X server.") def sigusr1_handler(num, frame):
log.debug("X server has signalled a successful start.") def preexec_fn():
signal.signal(signal.SIGUSR1, signal.SIG_IGN) old_sigusr1 = signal.signal(signal.SIGUSR1, sigusr1_handler)
old_sigchld = signal.signal(signal.SIGCHLD, sigchld_handler)
xout = open("/dev/tty5", "w") # 启动X server
proc = subprocess.Popen(["Xorg", "-br", "-logfile", "/tmp/X.log",
":1", "vt6", "-s", "1440", "-ac",
"-nolisten", "tcp", "-dpi", "96",
"-noreset"],
close_fds=True, stdout=xout, stderr=xout,
preexec_fn=preexec_fn) signal.pause() os.environ["DISPLAY"] = ":1"
doStartupX11Actions()
except (OSError, RuntimeError) as e:
stdoutLog.warning(" X startup failed, falling back to text mode")
anaconda.displayMode = 't'
graphical_failed = 1
time.sleep(2)
finally:
signal.signal(signal.SIGUSR1, old_sigusr1)
signal.signal(signal.SIGCHLD, old_sigchld) set_x_resolution(opts.runres) # ...... # 初始化UI界面
anaconda.initInterface()
anaconda.instClass.configure(anaconda) # ...... # 启动安装过程
try:
anaconda.intf.run(anaconda)
except SystemExit, code:
anaconda.intf.shutdown() if anaconda.ksdata and anaconda.ksdata.reboot.eject:
for drive in anaconda.storage.devicetree.devices:
if drive.type != "cdrom":
continue log.info("attempting to eject %s" % drive.path)
drive.eject() 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调度。因此,启动安装过程,实际就是创建主要控制类的实例,调用实例的接口,启动安装过程,然后再由这几个主要的控制类的实例创建具体安装界面,创建安装行为类的实例,调用具体的函数完成具体的安装过程。

class Anaconda(object):
def __init__(self):
import desktop, dispatch, firewall, security
import system_config_keyboard.keyboard as keyboard
from flags import flags # ......
# 创建dispatch实例
self.dispatch = dispatch.Dispatcher(self)
# ...... # ...... intf = property(_getInterface, _setInterface, _delInterface) # ...... def initInterface(self):
if self._intf:
raise RuntimeError, "Second attempt to initialize the InstallInterface" # 设置图形模式需要的链接
if self.displayMode == 'g':
stdoutLog.info (_("Starting graphical installation.")) try:
from gui import InstallInterface
except Exception, e:
from flags import flags
stdoutLog.error("Exception starting GUI installer: %s" %(e,))
# if we're not going to really go into GUI mode, we need to get
# back to vc1 where the text install is going to pop up.
if not flags.livecdInstall:
isys.vtActivate (1)
stdoutLog.warning("GUI installer startup failed, falling back to text mode.")
self.displayMode = 't'
if 'DISPLAY' in os.environ.keys():
del os.environ['DISPLAY']
time.sleep(2) if self.displayMode == 't':
from text import InstallInterface
if not os.environ.has_key("LANG"):
os.environ["LANG"] = "en_US.UTF-8" if self.displayMode == 'c':
from cmdline import InstallInterface self._intf = InstallInterface() # 创建InstallInterface实例
return self._intf

主要的工作包括创建dispatch实例,初始化界面,创建InstallInterface实例,它最后会创建InstallControlWindow实例,生成图形界面。

    整个Anaconda的运行流程如下图:

图2 Anaconda运行流程

Linux安装程序Anaconda分析(续)的更多相关文章

  1. Linux安装程序Anaconda分析

    1.概述     Anaconda是RedHat.CentOS.Fedora等Linux的安装管理程序.它能够提供文本.图形等安装管理方式,并支持Kickstart等脚本提供自己主动安装的功能.此外, ...

  2. linux 安装程序的方式

    linux 安装程序的方式 通用二进制格式(绿色软件,打开即用) 软件包管理器(rpm) 软件包管理器的前端工具(yum) 源代码编译

  3. [linux笔记]理清linux安装程序用到的(configure, make, make install)

    我作为一名经常和linux打交道的程序员,每次在linux安装软件都祈求可以用——apt-get,yum,brew等应用程序管理器安装,有的时候事与愿违,你只能自己编译安装-wtf,说好的美丽世界呢? ...

  4. linux驱动基础系列--linux spi驱动框架分析(续)

    前言 这篇文章是对linux驱动基础系列--linux spi驱动框架分析的补充,主要是添加了最新的linux内核里设备树相关内容. spi设备树相关信息 如之前的文章里所述,控制器的device和s ...

  5. Linux挖矿程序kworkerds分析

    0×00 背景概述 近日,同伴的一台Linux服务器中了kworkerds挖矿程序,随即对挖矿程序进行了处理与分析. 0×01服务器现状 进入服务器之后通过top命令,没有发现有占用CPU资源过高的进 ...

  6. ubuntu中的Linux安装程序的方法

    Ubuntu: 1.下载.deb文件,下载后,cd到.deb文件目录,然后使用sudo dpkg -i xxx.deb      dpkg=debian packager的缩写  -i=install ...

  7. linux 安装程序

    tar文件打开 tar -xvf myfile.tar bz2文件打开

  8. Linux安装-kickstart无人值守安装

    Linux安装-kickstart无人值守安装 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 大家做运维估计都避免不了时不时会去机房安装一台linux服务器,但是服务器启动的特别慢 ...

  9. RHEL64 缺少ISO 9660图像 安装程序试图挂载映像#1,在硬盘上无法找到该映像

    用光盘安装Linux,很容易,按照提示一步一步就好.如果没有光驱,只好想办法用硬盘或者U盘安装了. 首先说说怎样用U盘启动Linux的安装程序:1.将ISO镜像文件拷贝到U盘中,并解压到U盘根目录.将 ...

随机推荐

  1. spring junit4 单元测试运行正常,但是数据库并无变化

    解决方案 http://blog.csdn.net/molingduzun123/article/details/49383235 原因:Spring Juint为了不污染数据,对数据的删除和更新操作 ...

  2. Java-确定字符串是否包含子字符串

    利用String自带的函数和正则来实现 package com.tj; public class MyClass implements Cloneable { public static void m ...

  3. 用openrowset函数操作远程数据库

    OPENROWSET 包含访问 OLE DB 数据源中的远程数据所需的全部连接信息.当访问链接服务器中的表时,这种方法是一种替代方法,并且是一种使用 OLE DB 连接并访问远程数据的一次性的.特殊的 ...

  4. iphone丢了以后发现关机了怎么办?

    有好几个办法都可以尝试一下: 1. "ICCID法",但目前这个办法只能寻找苹果iPhone手机,而对于安卓手机,则不能采取相同的方法进行寻找.之所以能采取该方法寻找苹果 iPho ...

  5. i++和++i的区别,及其线程安全问题

    i++和++i都是i=i+1的意思,但是过程有些许区别: i++:先赋值再自加.(例如:i=1:a=1+i++:结果为a=1+1=2,语句执行完后i再进行自加为2) ++i:先自加再赋值.(例如:i= ...

  6. hdu2084

    老题目了 #include <stdio.h> int main(){ ][]; int i,j,max; int c,n; scanf("%d",&c); w ...

  7. 【树状数组区间修改单点查询+分组】HDU 4267 A Simple Problem with Integers

    http://acm.hdu.edu.cn/showproblem.php?pid=4267 [思路] 树状数组的区间修改:在区间[a, b]内更新+x就在a的位置+x. 然后在b+1的位置-x 树状 ...

  8. jsp 详解request对象

    request对象 客户端的请求信息被封装在request对象中,通过它才能了解到客户的需求,然后做出响应.它是HttpServletRequest类的实例. 序号 方 法 说 明 1  object ...

  9. redmine与SVN的Https方式整合问题

    尼玛啊!这个SVN的整合搞了一晚上,今天早上终于搞定了,FUCK!!! 进入话题: 可以先在bitnami redmine stack的命令行环境下手工运行svn,看是否能取到数据, svn list ...

  10. OI 数论整理

    1.素数: 质数(prime number)又称素数,有无限个.一个大于1的自然数,除了1和它本身外,不能被其他自然数整除,换句话说就是该数除了1和它本身以外不再有其他的因数;否则称为合数. 2016 ...