Anaconda

2019年12月05日 阅读数:94
这篇文章主要向大家介绍Anaconda,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。


原文路径: http://www.cnblogs.com/htc-javaMe/archive/2012/02/27/2562443.html 

一、概述 

    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方式共享安装,能够将安装光盘先拷贝到网络服务器上保存为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工具实现服务器自动化安装的方法。经过生成的kickstart配置文件ks.cfg,服务器安装能够实现从裸机到全功能服务的的非交互式(无人值守式)安装配置;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软件包信息。它们分别对应高可用性、负载均衡、弹性存储、可扩展文件系统、以及服务器基础的软件包信息。每一个文件夹下都有一个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或Back按钮被单击时,调度器知道要跳转到哪一个屏幕,也知道根据相应的设置哪一步应该被忽略而直接跳过。每种安装模式都有它本身的要跳过或被添加返回的步骤集。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是一个随机字符串。
html

    文件的调用顺序为isolinux/vmlinuz--->isolinux/initrd.img--->/init--->/sbin/loader--->imagaes/install.img--->/usr/bin/anaconda。以最新的Fedora 15使用的Anaconda 15.31版本为例(注意RHEL 6.0使用的是比这老的版本,为了跟踪前沿,这里使用比较新的版本),Anaconda主程序的启动流程以下:前端

[html]  view plain copy
  1. /init (loader/init.c)  
  2.     --->setupEnv()       设置环境变量  
  3.     --->mount("/proc", "/proc", "proc",...)      挂载/proc文件系统  
  4.     --->mount("/dev", "/dev", "tmpfs",...)       建立/dev文件系统  
  5.     --->execl("/sbin/udevd", "/sbin/udevd",...)      启动udev  
  6.     --->mount("/sys", "/sys", "sysfs",...)       挂载/sys文件系统  
  7.     --->open("/dev/console", O_WRONLY)       打开控制台设备  
  8.     --->open("/dev/tty1", O_RDWR, 0)     打开tty1控制台,用于执行安装  
  9.     --->禁用Ctrl+Z、Ctrl+C等  
  10.     --->mount("/", "/", "ext2",...)      尝试从新挂载rootfs  
  11.     --->mount("none", "/tmp", "tmpfs",...)       挂载/tmp文件系统  
  12.     --->execve("/sbin/loader", argvc, env)     根据选项参数运行loader程序,替换init进程  
  13.   
  14. /sbin/loader (loader/loader.c:main())  
  15.     --->解析运行loader的选项参数  
  16.     --->pyanaconda/isys/log.c:openLog()      打开log  
  17.         --->fopen("/dev/tty3","a")       在tty3上显示硬件相关消息  
  18.         --->fopen("/tmp/anaconda.log", "a")  
  19.         --->fopen("/tmp/program.log", "a")   
  20.     --->loader.c:parseCmdLineFlags() 解析/proc/cmdline中的内核命令行参数,以获取ks文件的nfs路径  
  21.         --->readNetInfo()        读取/tmp/s390net文件中的网络配置信息(若是存在的话)  
  22.     --->driverdisk.c:readModuleInfo(arg, modInfo,...)        读取/lib/modules/module-info中的模块信息  
  23.     --->loader.c:checkForRam(-1)     检查内存容量是否足够  
  24.         --->pyanaconda/isys/mem.c:totalMemory()   
  25.             --->open("/proc/meminfo", O_RDONLY)  获取meminfo中"MemTotal"一行中的数据(kB为单位)  
  26.     --->loader.c:loadScsiDhModules() 加载SCSI模块  
  27.                     (读取/lib/modules/<ver>/kernel/drivers/scsi/device_handler/下的模块)  
  28.         --->modules.c:mlLoadModuleSet(modNames)      加载用:分隔的模块列表  
  29.             --->modules.c:_doLoadModule()  
  30.     --->modules.c:mlSaveModuleState()        保存预加载的模块状态  
  31.         --->modules.c:processModuleLines()       一行一行地处理每一个模块  
  32.             --->fopen("/proc/modules", "r")      读取/proc/modules中的全部模块信息  
  33.             --->modules.c:cb_savestate()     保存当前模块状态  
  34.     --->hardware.c:busProbe()        探测总线以加载全部知道的硬件设备  
  35.         --->hardware.c:detectHardware()  
  36.             -->execv("/sbin/udevadm", args)      运行udevadm来加载设备  
  37.     --->driverdisk.c:loadDriverDiskFromPartition()     加载自动检测到的第三方Driver Disk(若是有的话)  
  38.         --->loadDriverDisk(loaderData, "/tmp/drivers")       加载DD  
  39.     --->pyanaconda/isys/iface_start_NetworkManager()     启动NetworkManager  
  40.         --->execl(NETWORKMANAGER, NETWORKMANAGER,...)   
  41.     --->kickstart.c:getKickstartFile(&loaderData)        获取Kickstart文件并复制到/tmp/ks.cfg  
  42.             ################################### NFS 方式 #####################################  
  43.         --->nfsinstall.c:kickstartFromNfs(c+4, loaderData)       从NFS获取ks文件  
  44.             --->nfsinstall.c:getFileFromNfs()  
  45.                 --->net.c:kickstartNetworkUp()       启动网卡  
  46.                 --->pyanaconda/isys/iface.c:iface_ip2str()       获取系统IP  
  47.                 --->nfsinstall.c:parseNfsHostPathOpts()      解析NFS的url路径  
  48.                 --->拷贝kickstart文件到本地  
  49.             ################################## CD 方式 #######################################  
  50.         --->cdinstall.c:kickstartFromCD()        从光盘上获取ks文件ks.cfg  
  51.             --->kickstart.c:getKickstartFromBlockDevice()  
  52.                 --->method.c:getFileFromBlockDevice()  
  53.                     --->pyanaconda/isys/imount.c:doPwMount() 挂载光盘  
  54.                         --->pyanaconda/isys/imount.c:mountCommandWrapper()  
  55.                             --->execl("/bin/mount",...)  
  56.             ######################################################################################  
  57.     --->kickstart.c:runKickstart()       运行Kickstart  
  58.         --->导入一些python库,如pykickstart.parser  
  59.         --->getObject()      建立KickstartParser对象  
  60.         --->preprocessKickstart()  预处理,执行与pykickstart.parser.preprocessKickstart相似的任务  
  61.         --->readKickstart()  
  62.             -->PyObject_CallMethodObjArgs()    处理kickstart文件,解析各个属性并设置到parser对象上  
  63.         --->处理Kickstart数据                                    
  64.             --->loadKickstartModule()        载入kickstart模块  
  65.             --->setKickstartNfs()        解析NFS路径中的主机IP、路径名、及选项参数  
  66.             --->setDisplayMode()     解析安装模式(文本、命令行、或图形模式)  
  67.             --->处理其余各项数据  
  68.     --->net.c:kickstartNetworkUp()       再次启动网卡  
  69.         --->chooseNetworkInterface(loaderData)       选择并启动可以使用的网卡(若是有多个网卡)  
  70.         --->writeEnabledNetInfo()    把网卡信息写入/etc/tsysconfig/network-scripts/ifcfg-DEVICE  
  71.     --->loader.c:doLoaderMain()  
  72.         --->cdinstall.c:findInstallCD()      挂载光盘,加载images/install.img等(光盘安装状况)  
  73.             --->pyanaconda/isys/imount.c:doPwMount("/mnt/source","iso9660",...)  
  74.         --->STEP_LANG和STEP_KBD       设置安装语言和键映射(若从CD/DVD安装则跳过这两步)  
  75.             --->lang.c:setLanguage()和pyanaconda/isys/lang.c:isysLoadKeymap()  
  76.         --->STEP_METHOD和STEP_DRIVER等   命令行模式下设置安装方法、驱动等(若为图形安装模式则跳过)  
  77.     --->selinux.c:loadpolicy()       加载SELinux策略  
  78.         --->execl("/sbin/load_policy",...)  
  79.     --->loader.c:spawnShell()        在tty2上打开Shell,这样在安装时可在tty2上登陆  
  80.         --->open("/dev/tty2",...)  
  81.         --->execl("/bin/sh",...)  
  82.     --->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,以下所示:
[python]  view plain copy
  1. installSteps = [  
  2.     ("language", ),  
  3.     ("keyboard", ),  
  4.     ("betanag", betaNagScreen, ),  
  5.     ("filtertype", ),  
  6.     ("filter", ),  
  7.     ("storageinit", storageInitialize, ),  
  8.     ("findrootparts", findRootParts, ),  
  9.     ("findinstall", ),  
  10.     ("network", ),  
  11.     ("timezone", ),  
  12.     ("accounts", ),  
  13.     ("setuptime", setupTimezone, ),  
  14.     ("parttype", ),  
  15.     ("cleardiskssel", ),  
  16.     ("autopartitionexecute", doAutoPartition, ),  
  17.     ("partition", ),  
  18.     ("upgrademount", upgradeMountFilesystems, ),  
  19.     ("restoretime", restoreTime, ),  
  20.     ("upgradecontinue", queryUpgradeContinue, ),  
  21.     ("upgradeswapsuggestion", upgradeSwapSuggestion, ),  
  22.   
  23.     # ......  
  24.   
  25.     ]  
    installSteps中记录了有序排列的整个安装过程当中全部可能的安装步骤,在生成具体的Dispatcher实例时,会根据安装类型制定对此进行相应裁减。installSteps中的条目(item)有两种格式,即( name )或( name, Function )。name表明安装步骤的名称,Function指安装操做的具体执行函数,这个函数会直接由Dispatcher调用。全部的安装步骤都把anaconda对象做为惟一的参数,当咱们调用这个Function时,这个参数就会传进去。
    咱们假设当前安装步骤为autopartitionexecute,若是熟悉linux安装过程,你应该能够猜出这个安装步骤是分区方式选择,对应该安装步骤的函数为storage/partitioning.py中的doAutoPartition函数,代码片段以下:
[python]  view plain copy
  1. def doAutoPartition(anaconda):  
  2.   
  3.     # ......  
  4.   
  5.     if anaconda.storage.doAutoPart:  
  6.        (disks, devs) = _createFreeSpacePartitions(anaconda)  
  7.         if disks == []:  
  8.             if anaconda.ksdata:  
  9.                 msg = _("Could not find enough free space for automatic "  
  10.                         "partitioning.  Press 'OK' to exit the installer.")  
  11.             else:  
  12.                 msg = _("Could not find enough free space for automatic "  
  13.                         "partitioning, please use another partitioning method.")  
  14.   
  15.             anaconda.intf.messageWindow(_("Error Partitioning"), msg,  
  16.                                         custom_icon='error')  
  17.   
  18.             if anaconda.ksdata:  
  19.                 sys.exit(0)  
  20.   
  21.             anaconda.storage.reset()  
  22.             return DISPATCH_BACK  
  23.   
  24.         _schedulePartitions(anaconda, disks)  
  25.   
  26.     # sanity check the individual devices  
  27.     log.warning("not sanity checking devices because I don't know how yet")  
  28.   
  29.     # run the autopart function to allocate and grow partitions  
  30.   
  31.     try:  
  32.         doPartitioning(anaconda.storage  
  33.         if anaconda.storage.doAutoPart:  
  34.             _scheduleLVs(anaconda, devs)  
  35.   
  36.         # grow LVs  
  37.         growLVM(anaconda.storage)  
  38.     except PartitioningWarning as msg:  
  39.         if not anaconda.ksdata:  
  40.             anaconda.intf.messageWindow(_("Warnings During Automatic "  
  41.                                           "Partitioning"),  
  42.                            _("Following warnings occurred during automatic "  
  43.                            "partitioning:\n\n%s") % (msg,),  
  44.                            custom_icon='warning')  
  45.         else:  
  46.             log.warning(msg)  
  47.     except PartitioningError as msg:  
  48.         # restore drives to original state  
  49.         anaconda.storage.reset()  
  50.         if not anaconda.ksdata:  
  51.             extra = ""  
  52.   
  53.             if anaconda.displayMode != "t":  
  54.                 anaconda.dispatch.skipStep("partition", skip = 0)  
  55.         else:  
  56.             extra = _("\n\nPress 'OK' to exit the installer.")  
  57.         anaconda.intf.messageWindow(_("Error Partitioning"),  
  58.                _("Could not allocate requested partitions: \n\n"  
  59.                  "%(msg)s.%(extra)s") % {'msg': msg, 'extra': extra},  
  60.                custom_icon='error')  
  61.   
  62.         if anaconda.ksdata:  
  63.             sys.exit(0)  
  64.         else:  
  65.             return DISPATCH_BACK  
  66.   
  67.     # now do a full check of the requests  
  68.     (errors, warnings) = anaconda.storage.sanityCheck()  
  69.     if warnings:  
  70.         for warning in warnings:  
  71.             log.warning(warning)  
  72.     if errors:  
  73.         errortxt = "\n".join(errors)  
  74.         if anaconda.ksdata:  
  75.             extra = _("\n\nPress 'OK' to exit the installer.")  
  76.         else:  
  77.             extra = _("\n\nPress 'OK' to choose a different partitioning option.")  
  78.   
  79.         anaconda.intf.messageWindow(_("Automatic Partitioning Errors"),  
  80.                            _("The following errors occurred with your "  
  81.                              "partitioning:\n\n%(errortxt)s\n\n"  
  82.                              "This can happen if there is not enough "  
  83.                              "space on your hard drive(s) for the "  
  84.                              "installation. %(extra)s")  
  85.                            % {'errortxt': errortxt, 'extra': extra},  
  86.                            custom_icon='error')  
  87.         #  
  88.         # XXX if in kickstart we reboot  
  89.         #  
  90.         if anaconda.ksdata:  
  91.             anaconda.intf.messageWindow(_("Unrecoverable Error"),  
  92.                                _("The system will now reboot."))  
  93.             sys.exit(0)  
  94.         anaconda.storage.reset()  
  95.         return DISPATCH_BACK  
    主要执行的步骤包括建立空闲分区,运行autopart分配或增加分区容量,分区完整性检查等。

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

    2)moveStep:咱们来重点分析movestep函数,代码以下:java

[python]  view plain copy
  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调度字符用户界面时调用,这两个类经过该接口取得当前安装步骤的用户界面对应类及建立该用户界面类的实例所需的信息。
[python]  view plain copy
  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: 该字典中记录了安装过程当中全部的具备图形用户界面的安装步骤。python

[python]  view plain copy
  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。
[python]  view plain copy
  1. def run (self):  
  2.     self.setup_theme()  
  3.     self.setup_window(False)  
  4.     gtk.main()  
    3)nextClicked & prevClicked:这两个接口分别执行从当前图形安装界面向前(向后)到下一个图形安装界面的操做,咱们能够想象安装过程当中当用户点击"下一步" 或"上一步"按钮时,这两个函数被调用。这两个函数首先调用主流程控制Dispatcher实例向前(向后)前进到下一个图形安装界面,而后调用setScreen函数设置图形界面。
[python]  view plain copy
  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的主执行体就做了一件事,启动字符模式安装过程。
[python]  view plain copy
  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调度。所以,启动安装过程,实际就是建立主要控制类的实例,调用实例的接口,启动安装过程,而后再由这几个主要的控制类的实例建立具体安装界面,建立安装行为类的实例,调用具体的函数完成具体的安装过程。
[python]  view plain copy
  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