1、概述
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方式共享安装,能够将安装光盘先复制到网络server上保存为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工具实现server自己主动化安装的方法。通过生成的kickstart配置文件ks.cfg,server安装能够实现从裸机到全功能服务的的非交互式(无人值守式)安装配置;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安装光盘。
2、RedHat企业版6.0光盘的安装树介绍
(1)Packages文件夹:包括安装所需的全部二进制RPM包。
(2)HighAvailability、LoadBalancer、ResilientStorage、ScalableFileSystem、Server文件夹:五个文件夹包括了安装所需的全部RPM软件包信息。它们分别相应高可用性、负载均衡、弹性存储、可扩展文件系统、以及server基础的软件包信息。每一个文件夹下都有一个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文件夹等。
3、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或Backbutton被单击时,调度器知道要跳转到哪个屏幕,也知道依据对应的设置哪一步应该被忽略而直接跳过。每种安装模式都有它自己的要跳过或被加入�返回的步骤集。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。
4、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是一个随机字符串。
文件的调用顺序为isolinux/vmlinuz--->isolinux/initrd.img--->/init--->/sbin/loader--->imagaes/install.img--->/usr/bin/anaconda。以最新的Fedora 15使用的Anaconda 15.31版本号为例(注意RHEL 6.0使用的是比这老的版本号,为了跟踪前沿,这里使用比較新的版本号),Anaconda主程序的启动流程例如以下:
/init (loader/init.c)
--->setupEnv() 环境变量设置
--->mount("/proc", "/proc", "proc",...) 挂载/proc文件系统
--->mount("/dev", "/dev", "tmpfs",...) 创建/dev文件系统
--->execl("/sbin/udevd", "/sbin/udevd",...) 启动udev
--->mount("/sys", "/sys", "sysfs",...) 挂载/sys文件系统
--->open("/dev/console", O_WRONLY) 打开控制台设备
--->open("/dev/tty1", O_RDWR, 0) 打开tty1控制台,用于执行安装
--->禁用Ctrl+Z、Ctrl+C等
--->mount("/", "/", "ext2",...) 尝试又一次挂载rootfs
--->mount("none", "/tmp", "tmpfs",...) 挂载/tmp文件系统
--->execve("/sbin/loader", argvc, env) 依据选项參数执行loader程序,替换init进程
/sbin/loader (loader/loader.c:main())
--->解析执行loader的选项參数
--->pyanaconda/isys/log.c:openLog() 打开log
--->fopen("/dev/tty3","a") 在tty3上显示硬件相关消息
--->fopen("/tmp/anaconda.log", "a")
--->fopen("/tmp/program.log", "a")
--->loader.c:parseCmdLineFlags() 解析/proc/cmdline中的内核命令行參数,以获取ks文件的nfs路径
--->readNetInfo() 读取/tmp/s390net文件里的网络配置信息(假设存在的话)
--->driverdisk.c:readModuleInfo(arg, modInfo,...) 读取/lib/modules/module-info中的模块信息
--->loader.c:checkForRam(-1) 检查内存容量是否足够
--->pyanaconda/isys/mem.c:totalMemory()
--->open("/proc/meminfo", O_RDONLY) 获取meminfo中"MemTotal"一行中的数据(kB为单位)
--->loader.c:loadScsiDhModules() 载入SCSI模块
(读取/lib/modules/<ver>/kernel/drivers/scsi/device_handler/下的模块)
--->modules.c:mlLoadModuleSet(modNames) 载入用:分隔的模块列表
--->modules.c:_doLoadModule()
--->modules.c:mlSaveModuleState() 保存预载入的模块状态
--->modules.c:processModuleLines() 一行一行地处理每一个模块
--->fopen("/proc/modules", "r") 读取/proc/modules中的全部模块信息
--->modules.c:cb_savestate() 保存当前模块状态
--->hardware.c:busProbe() 探測总线以载入全部知道的硬件设备
--->hardware.c:detectHardware()
-->execv("/sbin/udevadm", args) 执行udevadm来载入设备
--->driverdisk.c:loadDriverDiskFromPartition() 载入自己主动检測到的第三方Driver Disk(假设有的话)
--->loadDriverDisk(loaderData, "/tmp/drivers") 载入DD
--->pyanaconda/isys/iface_start_NetworkManager() 启动NetworkManager
--->execl(NETWORKMANAGER, NETWORKMANAGER,...)
--->kickstart.c:getKickstartFile(&loaderData) 获取Kickstart文件并拷贝到/tmp/ks.cfg
################################### NFS 方式 #####################################
--->nfsinstall.c:kickstartFromNfs(c+4, loaderData) 从NFS获取ks文件
--->nfsinstall.c:getFileFromNfs()
--->net.c:kickstartNetworkUp() 启动网卡
--->pyanaconda/isys/iface.c:iface_ip2str() 获取系统IP
--->nfsinstall.c:parseNfsHostPathOpts() 解析NFS的url路径
--->拷贝kickstart文件到本地
################################## CD 方式 #######################################
--->cdinstall.c:kickstartFromCD() 从光盘上获取ks文件ks.cfg
--->kickstart.c:getKickstartFromBlockDevice()
--->method.c:getFileFromBlockDevice()
--->pyanaconda/isys/imount.c:doPwMount() 挂载光盘
--->pyanaconda/isys/imount.c:mountCommandWrapper()
--->execl("/bin/mount",...)
######################################################################################
--->kickstart.c:runKickstart() 执行Kickstart
--->导入一些python库,如pykickstart.parser
--->getObject() 创建KickstartParser对象
--->preprocessKickstart() 预处理,执行与pykickstart.parser.preprocessKickstart相似的任务
--->readKickstart()
-->PyObject_CallMethodObjArgs() 处理kickstart文件,解析各个属性并设置到parser对象上
--->处理Kickstart数据
--->loadKickstartModule() 载入kickstart模块
--->setKickstartNfs() 解析NFS路径中的主机IP、路径名、及选项參数
--->setDisplayMode() 解析安装模式(文本、命令行、或图形模式)
--->处理其它各项数据
--->net.c:kickstartNetworkUp() 再次启动网卡
--->chooseNetworkInterface(loaderData) 选择并启动可使用的网卡(假设有多个网卡)
--->writeEnabledNetInfo() 把网卡信息写入/etc/tsysconfig/network-scripts/ifcfg-DEVICE
--->loader.c:doLoaderMain()
--->cdinstall.c:findInstallCD() 挂载光盘,载入images/install.img等(光盘安装情况)
--->pyanaconda/isys/imount.c:doPwMount("/mnt/source","iso9660",...)
--->STEP_LANG和STEP_KBD 设置安装语言和键映射(若从CD/DVD安装则跳过这两步)
--->lang.c:setLanguage()和pyanaconda/isys/lang.c:isysLoadKeymap()
--->STEP_METHOD和STEP_DRIVER等 命令行模式下设置安装方法、驱动等(若为图形安装模式则跳过)
--->selinux.c:loadpolicy() 载入SELinux策略
--->execl("/sbin/load_policy",...)
--->loader.c:spawnShell() 在tty2上打开Shell,这样在安装时可在tty2上登录
--->open("/dev/tty2",...)
--->execl("/bin/sh",...)
--->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文件及各个软件包都写到这个文件夹去。
5、Anaconda各模块分析
从没计角度看,整个安装程序分为三层,从上层往下层依次是前端显示、调度中心、安装行为。例如以下图所看到的。
图1 Anaconda体系结构
Dispatcher类在主文件夹pyanaconda下的dispatch.py模块中,负责整个安装流程的控制,在安装过程中,某些安装步骤有的须要前置安装操作,有的又须要后置安装操作,而某些安装操作则是没实用户界面的,我们统称这些安装操作为无用户界面的安装操作,那么,这些没实用户界面的安装操作由谁来调度执行呢?答案就是Dispatcher。InstallControlWindow类控制安装过程中前端图形界面的显示,整体调度各个安装图形界面类,InstallControlWindow建立图形界面的主窗口,每一个详细的图形安装界面可视为其子窗口。InstallControlWindow调用 Dispatcher控制整个安装流程。安装过程中,每一个详细的图形安装界面均相应一个详细的类,由其相应的详细的类完毕详细的安装任务,我们将这些图形界面类归为前端显示层,位于iw文件夹下的模块中。
anaconda将某些用户界面类中的详细安装操作分离出来,放到了另外一些模块中,这些模块放在了anaconda的主文件夹pyanaconda下。由Dispatcher直接调用的没实用户界面的安装步骤相应的函数也放在了anaconda的主文件夹下,我们将这些模块归为安装行为层。
dispatch.py模块中有一个序列(sequence)数据结构:installSteps,例如以下所看到的:
installSteps = [
("language", ),
("keyboard", ),
("betanag", betaNagScreen, ),
("filtertype", ),
("filter", ),
("storageinit", storageInitialize, ),
("findrootparts", findRootParts, ),
("findinstall", ),
("network", ),
("timezone", ),
("accounts", ),
("setuptime", setupTimezone, ),
("parttype", ),
("cleardiskssel", ),
("autopartitionexecute", doAutoPartition, ),
("partition", ),
("upgrademount", upgradeMountFilesystems, ),
("restoretime", restoreTime, ),
("upgradecontinue", queryUpgradeContinue, ),
("upgradeswapsuggestion", upgradeSwapSuggestion, ),
# ......
]
installSteps中记录了有序排列的整个安装过程中全部可能的安装步骤,在生成详细的Dispatcher实例时,会依据安装类型制定对此进行对应裁减。installSteps中的条目(item)有两种格式,即( name )或( name, Function )。name代表安装步骤的名称,Function指安装操作的详细运行函数,这个函数会直接由Dispatcher调用。全部的安装步骤都把anaconda对象作为唯一的參数,当我们调用这个Function时,这个參数就会传进去。
我们如果当前安装步骤为autopartitionexecute,如果熟悉linux安装过程,你应该能够猜出这个安装步骤是分区方式选择,相应该安装步骤的函数为storage/partitioning.py中的doAutoPartition函数,代码片断例如以下:
def doAutoPartition(anaconda):
# ......
if anaconda.storage.doAutoPart:
(disks, devs) = _createFreeSpacePartitions(anaconda)
if disks == []:
if anaconda.ksdata:
msg = _("Could not find enough free space for automatic "
"partitioning. Press 'OK' to exit the installer.")
else:
msg = _("Could not find enough free space for automatic "
"partitioning, please use another partitioning method.")
anaconda.intf.messageWindow(_("Error Partitioning"), msg,
custom_icon='error')
if anaconda.ksdata:
sys.exit(0)
anaconda.storage.reset()
return DISPATCH_BACK
_schedulePartitions(anaconda, disks)
# sanity check the individual devices
log.warning("not sanity checking devices because I don't know how yet")
# run the autopart function to allocate and grow partitions
try:
doPartitioning(anaconda.storage
if anaconda.storage.doAutoPart:
_scheduleLVs(anaconda, devs)
# grow LVs
growLVM(anaconda.storage)
except PartitioningWarning as msg:
if not anaconda.ksdata:
anaconda.intf.messageWindow(_("Warnings During Automatic "
"Partitioning"),
_("Following warnings occurred during automatic "
"partitioning:\n\n%s") % (msg,),
custom_icon='warning')
else:
log.warning(msg)
except PartitioningError as msg:
# restore drives to original state
anaconda.storage.reset()
if not anaconda.ksdata:
extra = ""
if anaconda.displayMode != "t":
anaconda.dispatch.skipStep("partition", skip = 0)
else:
extra = _("\n\nPress 'OK' to exit the installer.")
anaconda.intf.messageWindow(_("Error Partitioning"),
_("Could not allocate requested partitions: \n\n"
"%(msg)s.%(extra)s") % {'msg': msg, 'extra': extra},
custom_icon='error')
if anaconda.ksdata:
sys.exit(0)
else:
return DISPATCH_BACK
# now do a full check of the requests
(errors, warnings) = anaconda.storage.sanityCheck()
if warnings:
for warning in warnings:
log.warning(warning)
if errors:
errortxt = "\n".join(errors)
if anaconda.ksdata:
extra = _("\n\nPress 'OK' to exit the installer.")
else:
extra = _("\n\nPress 'OK' to choose a different partitioning option.")
anaconda.intf.messageWindow(_("Automatic Partitioning Errors"),
_("The following errors occurred with your "
"partitioning:\n\n%(errortxt)s\n\n"
"This can happen if there is not enough "
"space on your hard drive(s) for the "
"installation. %(extra)s")
% {'errortxt': errortxt, 'extra': extra},
custom_icon='error')
#
# XXX if in kickstart we reboot
#
if anaconda.ksdata:
anaconda.intf.messageWindow(_("Unrecoverable Error"),
_("The system will now reboot."))
sys.exit(0)
anaconda.storage.reset()
return DISPATCH_BACK
主要执行的步骤包含创建空暇分区,执行autopart分配或增长分区容量,分区完整性检查等。