Linux安装程序Anaconda分析(续)
本来想写篇关于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分析(续)的更多相关文章
- Linux安装程序Anaconda分析
1.概述 Anaconda是RedHat.CentOS.Fedora等Linux的安装管理程序.它能够提供文本.图形等安装管理方式,并支持Kickstart等脚本提供自己主动安装的功能.此外, ...
- linux 安装程序的方式
linux 安装程序的方式 通用二进制格式(绿色软件,打开即用) 软件包管理器(rpm) 软件包管理器的前端工具(yum) 源代码编译
- [linux笔记]理清linux安装程序用到的(configure, make, make install)
我作为一名经常和linux打交道的程序员,每次在linux安装软件都祈求可以用——apt-get,yum,brew等应用程序管理器安装,有的时候事与愿违,你只能自己编译安装-wtf,说好的美丽世界呢? ...
- linux驱动基础系列--linux spi驱动框架分析(续)
前言 这篇文章是对linux驱动基础系列--linux spi驱动框架分析的补充,主要是添加了最新的linux内核里设备树相关内容. spi设备树相关信息 如之前的文章里所述,控制器的device和s ...
- Linux挖矿程序kworkerds分析
0×00 背景概述 近日,同伴的一台Linux服务器中了kworkerds挖矿程序,随即对挖矿程序进行了处理与分析. 0×01服务器现状 进入服务器之后通过top命令,没有发现有占用CPU资源过高的进 ...
- ubuntu中的Linux安装程序的方法
Ubuntu: 1.下载.deb文件,下载后,cd到.deb文件目录,然后使用sudo dpkg -i xxx.deb dpkg=debian packager的缩写 -i=install ...
- linux 安装程序
tar文件打开 tar -xvf myfile.tar bz2文件打开
- Linux安装-kickstart无人值守安装
Linux安装-kickstart无人值守安装 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 大家做运维估计都避免不了时不时会去机房安装一台linux服务器,但是服务器启动的特别慢 ...
- RHEL64 缺少ISO 9660图像 安装程序试图挂载映像#1,在硬盘上无法找到该映像
用光盘安装Linux,很容易,按照提示一步一步就好.如果没有光驱,只好想办法用硬盘或者U盘安装了. 首先说说怎样用U盘启动Linux的安装程序:1.将ISO镜像文件拷贝到U盘中,并解压到U盘根目录.将 ...
随机推荐
- Builder(构造者)
Builder(构造者) <?php class Product { private $name; public function setName($name) { $this->name ...
- nginx反向代理,负载均衡,动静分离,rewrite地址重写介绍
一.rewrite地址重写 地址转发后客户端浏览器地址栏中的地址显示是不变的,而地址重写后地址栏中的地址会变成正确的地址. 在一次地址转发过程中只会产生一次网络请求,而一次地址重写产生两次请求. 地址 ...
- UITableView加载几种不同的cell
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...
- POJ——3126Prime Path(双向BFS+素数筛打表)
Prime Path Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 16272 Accepted: 9195 Descr ...
- BZOJ 3168 [Heoi2013]钙铁锌硒维生素 ——矩阵乘法 矩阵求逆
考虑向量ai能否换成向量bj 首先ai都是线性无关的,然后可以a线性表出bj c1*a1+c2*a2+...=bj 然后移项,得 c1/ci*a1+...-1/ci*bj+...=ai 所以当ci不为 ...
- [BZOJ1595] [Usaco2008 Jan]人工湖(单调栈)
传送门 好难的题..至少对我来说. 这题就是模拟从最低的平台注水,然后将最低的填满以后从最低的平台向两边扩展,每次找最近的最低的平台h,然后将水填到h高度. 栈里存的是向外扩展的时候,有时会遇到高度递 ...
- 洛谷P3758 - [TJOI2017]可乐
Portal Description 给出一张\(n(n\leq30)\)个点\(m(m\leq100)\)条边的无向图.初始时有一个可乐机器人在点\(1\),这个机器人每秒会做出以下三种行为之一:原 ...
- 关于java读取文件IO流学习总结(一)
IO流的分类: 1.根据流的数据对象来分: 高端流:所有的内存中的流都是高端流,比如:InputStreamReader 低端流:所有的外界设备中的流都是低端流,比如InputStream,Outpu ...
- iOS7 毛玻璃效果
转自:http://prolove10.blog.163.com/blog/static/138411843201391401054305/ 原图: 效果图: 实现:首先需要导入Accelerat ...
- Docker-PostgresSQL
Postgresql Docker安装运行 mac环境: 1.拉取官方镜像,并创建容器 zhoumatoMacBook-Pro:~ zhou$ docker search postgresql NA ...