狠狠撸
Submit Search
础颈蝉补苍耻虫安装光盘分析
?
0 likes
?
777 views
Guangyao Cheng
Follow
础颈蝉补苍耻虫光盘结构,颈苍颈迟谤诲,补苍补肠辞苍诲补源码浅析。
Read less
Read more
1 of 32
More Related Content
础颈蝉补苍耻虫安装光盘分析
1.
Asianux 安装光盘分析 中科院软件所基础软件中心 程光瑶
2.
架构 ? 以Asianux 3
sp2 一张dvd的发行版为例: ? 安装光盘的框架: -Asianux <-安装向导镜像及rpm包 -Documents <-License协议及相关说明文档 -dosutils <-为Dos下特殊安装准备的工具 -images <-其他引导img镜像及额外驱动 -isolinux <-光盘引导程序及msg提示信息 -repodata <-精确描述一个 rpm 包的详细信息,如依赖关 系,包含文件,校验码信息 --TRANS.TBL <-当前目录的列表,用mkisofs的-T参数重新 生成 --.discinfo <-安装程序anaconda用于检验光盘正确性的文件 --.treeinfo <-不同安装方式安装程序所在目录结构,如:内 核kernel = images/pxeboot/vmlinuz;根文件系统initrd = images/pxeboot/initrd.img
3.
/Asianux /base包含了在安装过程中要用到的描述组织结构和安装行为的所 有文件,其中comps,hdlist和hdlist2是描述RPM包组织结构的文件 --comps.xml 把各个RPM包按一定的原则组织成若干组,即 components,这样在安装过程中就不必对每一个包做出取舍,而以组 为单位。如:kde-desktop,development 等。 --hdlist,hdlist2
这两个文件维护从RPM包名到真实包文件名 的映射过程。这两个文件是用genhdlist生成的,无法用简单的方法察 看其中的内容和结构。 /RPMS包含了Asianux发布的主要部分,即以RPM包的形式将 Asianux系统中的二进制可执行文件,配置文件,文档等等组织在一 起,形成能完成一定功能的比较独立的软件包。这个目录就是把这些 软件包都集合在一起,形成了Asianux发布。
4.
肠辞尘辫蝉.虫尘濒格式
5.
/images 包含了制作启动盘的映像文件,以及从一些非常规硬件上 加载安装程序所需的驱动程序盘映像 ? boot.iso
当安装介质光盘时,负责引导系统的映像文件 ? bootefi.img 用于Intel EFI引导方式的机器 ? diskboot.img 安装引导盘,可以写入U盘或其他大容量 可引导存储介质中,用来引导从本地光盘、网络、硬盘或 PCMCIA设备的安装 ? stage2.img,minstg2.img 光盘引导的镜像文件, 在定制Live CD的时候,需要修改它 ? /pxeboot 用于PXE安装方式
6.
boot.img结构 boot.img
|----vmlinuz Linux内核 |----ldlinux.sys 引导Linux的系统文件 |----syslinux.cfg Linux内核引导参数配置文件 |----initrd.img 内存虚拟文件系统映像文件 |----*.msg文件 引导时的各种提示信息文件 其中,initrd.img为Linux ext2文件系统,构成如下: initrd.img |----/bin |----/dev |----/etc |----/module |----/sbin ------ loader安装程序装载器 |----/tmp |----/var 可执行文件/sbin/loader的任务是判断安装介质的有效性,并从中执行安装 程序。其实正是boot.img,在系统启动时被执行,经解析之后在内存建立起 了Linux内核,并根据配置文件syslinux.cfg装载虚拟文件系统,形成了完整的 LinuxSystem,为后续的工作提供了必要的操作系统环境。
7.
stage2.img结构 stage2.img |----/etc |----/modules |----/proc |----/usr----/bin----anaconda
安装程序主执行文件 |-------------/lib-----/anaconda安装程序脚本文件目录 | |----/installclasses 安装类型分类目录 | |----/iw 安装各步骤响应目录 | |----/texttw 字符界面各步骤响应目录 | |----*.py |-------------/share---/anaconda安装程序资源文件目录 | |----/help 安装过程帮助系统目录 | |----/pixmaps 安装时图片存储的位置 如上所示,stage2.img映像文件中的主要部分是安装程序 anaconda,它的主执行体是/usr/bin下的anaconda,由其调用的 大量例程分布在/usr/lib/anaconda下,而安装过程中要用到的资源 文件分布在/usr/share/anaconda下。
8.
initrd(boot loader initialized
RAM disk) ? 定义: 由 boot loader 初始化的内存盘。在 linux内核启动前, boot loader 会将存储介 质中的 initrd文件加载到内存,内核启动时会在访问真正的根文件系统前 先访问该内存中的 initrd 文件系统。在 boot loader 配置了 initrd的情况 下,内核启动被分成了两个阶段,第一阶段先执行 initrd文件系统中的文 件,完成加载驱动模块等任务,第二阶段才会执行真正的根文件系统中 的 /sbin/init 进程。 ? cpio-initrd 的处理流程: 1. boot loader 把内核以及 initrd 文件加载到内存的特定位置。 2. 内核判断initrd的文件格式,如果是cpio格式。 3. 将initrd的内容释放到rootfs中。 4. 执行initrd中的/init文件,执行到这一点,内核的工作全部结束,完全交给 /init文件处理。
9.
initrd处理流程 initrd相关代码的调用层次关系图 (内核的初始化代码位于 init/main.c 中的
static int init(void * unused)函数中。同initrd的处理相关 部分函数调用层次如下图 )
10.
相关概念 ? rootfs: 一个基于内存的文件系统,是linux在初
始化时加载的第一个文件系统。 ? initramfs: initramfs同本文的主题关系不是很 大,Initramfs是在 kernel 2.5中引入的技术。在 内核镜像中附加一个cpio包,这个cpio包中包含 了一个小型的文件系统,当内核启动时,内核 将这个 cpio包解开,并且将其中包含的文件系 统释放到rootfs中,内核中的一部分初始化代码 会放到这个文件系统中,作为用户层进程来执 行。 ? cpio-initrd: 指linux内核使用的cpio格式的initrd。 ? image-initrd: 指传统的文件镜像格式的initrd。 ? realfs: 用户最终使用的真正的文件系统。
11.
代码分析( unused函数 ) static
int init(void * unused){ [1] populate_rootfs(); [2] if (sys_access((const char __user *) "/init", 0) == 0) execute_command = "/init"; else prepare_namespace(); [3] if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) printk(KERN_WARNING "Warning: unable to open an initial console.n"); (void) sys_dup(0); (void) sys_dup(0); [4] if (execute_command) run_init_process(execute_command); run_init_process("/sbin/init"); run_init_process("/etc/init"); run_init_process("/bin/init"); run_init_process("/bin/sh"); panic("No init found. Try passing init= option to kernel."); }
12.
代码分析( unused函数 ) ?
代码[1]:populate_rootfs函数负责加载initramfs 和cpio-initrd。 ? 代码[2]:如果rootfs的根目录下中包含/init进程, 则赋予execute_command,在init函数的末尾会 被执行。否则执行prepare_namespace函数, initrd是在该函数中被加载的。 ? 代码[3]:将控制台设置为标准输入,后续的两 个sys_dup(0),则复制标准输入为标准输出 和标准错误输出。 ? 代码[4]:如果rootfs中存在init进程,就将后续 的处理工作交给该init进程。其实这段代码的含 义是如果加载了cpio-initrd则交给cpio-initrd中的 /init处理,否则会执行realfs中的init.
13.
代码分析( populate_rootfs ) 对cpio-initrd的处理位于populate_rootfs函数中 void
__init populate_rootfs(void){ [1] char *err = unpack_to_rootfs(__initramfs_start, __initramfs_end - __initramfs_start, 0); [2] if (initrd_start) { [3] err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start, 1); [4] if (!err) { printk(" it isn"); unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start, 0); free_initrd_mem(initrd_start, initrd_end); return; } [5] fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 700); if (fd >= 0) { sys_write(fd, (char *)initrd_start, initrd_end - initrd_start); sys_close(fd); free_initrd_mem(initrd_start, initrd_end); } }
14.
代码分析( populate_rootfs ) ?
代码[1]:加载initramfs,initramfs位于地址 __initramfs_start处,是内核在编译过程中生成的, initramfs的是作为内核的一部分而存在的,不是boot loader加载的。 ? 代码[2]:判断是否加载了initrd.无论哪种格式的initrd, 都会被boot loader加载到地址initrd_start处。 ? 代码[3]:判断加载的是不是cpio-initrd.实际上 unpack_to_rootfs有两个功能一个是释放cpio包,另一个 就是判断是不是cpio包, 这是通过最后一个参数来区分 的,0-释放 1-查看。 ? 代码[4]:如果是cpio-initrd则将其内容释放出来到rootfs 中。 ? 代码[5]:如果不是cpio-initrd,则认为是一个image- initrd,将其内容保存到/initrd.image中。在后面的 image-initrd的处理代码中会读取/initrd.image.
15.
代码分析(prepare_namespace) 对image-initrd的处理在prepare_namespace函数里,包含了对image- initrd进行处理的代码 void _init prepare_namespace(void){ [1]
if (initrd_load()) goto out; out: umount_devfs("/dev"); [2] sys_mount(".", "/", NULL, MS_MOVE, NULL); sys_chroot("."); security_sb_post_mountroot(); mount_devfs_fs (); 代码[1]:执行initrd_load函数,将initrd载入,如果载入成功的话 initrd_load函数会将realfs的根设置为当前目录。 代码[2]:将当前目录即realfs的根mount为Linux VFS的根。initrd_load函 数执行完后,将真正的文件系统的根设置为当前目录。
16.
代码分析( initrd_load函数 ) initrd_load函数负责载入image-initrd int
__init initrd_load(void) { [1] if (mount_initrd) { create_dev("/dev/ram", Root_RAM0, NULL); [2] if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) { sys_unlink("/initrd.image"); handle_initrd(); return 1; } } sys_unlink("/initrd.image"); return 0; } 代码[1]:如果加载initrd则建立一个ram0设备 /dev/ram。 代码[2]:/initrd.image文件保存的就是image-initrd,rd_load_image函数 执行具体的加载操作,将 image-nitrd的文件内容释放到ram0里。判断 ROOT_DEV!=Root_RAM0的含义是,如果你在grub或者lilo里配置了 root=/dev/ram0 ,则实际上真正的根设备就是initrd了,所以就不把它作为 initrd处理,而是作为realfs处理。
17.
代码分析(handle_initrd) handle_initrd()函数负责对initrd进行具体的处理
static void __init handle_initrd(void){ [1] real_root_dev = new_encode_dev(ROOT_DEV); [2] create_dev("/dev/root.old", Root_RAM0, NULL); mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY); [3] sys_mkdir("/old", 0700); root_fd = sys_open("/", 0, 0); old_fd = sys_open("/old", 0, 0); /* move initrd over / and chdir/chroot in initrd root */ [4] sys_chdir("/root"); sys_mount(".", "/", NULL, MS_MOVE, NULL); sys_chroot("."); mount_devfs_fs (); [5] pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD); if (pid > 0) { while (pid != sys_wait4(-1, &i, 0, NULL)) yield(); } /* move initrd to rootfs' /old */ sys_fchdir(old_fd); sys_mount("/", ".", NULL, MS_MOVE, NULL); /* switch root and cwd back to / of rootfs */ [6] sys_fchdir(root_fd); sys_chroot("."); sys_close(old_fd); sys_close(root_fd); umount_devfs("/old/dev");
18.
代码分析(handle_initrd) [7]
if (new_decode_dev(real_root_dev) == Root_RAM0) { sys_chdir("/old"); return; } [8] ROOT_DEV = new_decode_dev(real_root_dev); mount_root(); [9] printk(KERN_NOTICE "Trying to move old root to /initrd ... "); error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); if (!error) printk("okayn"); else { int fd = sys_open("/dev/root.old", O_RDWR, 0); printk("failedn"); printk(KERN_NOTICE "Unmounting old rootn"); sys_umount("/old", MNT_DETACH); printk(KERN_NOTICE "Trying to free ramdisk memory ... "); if (fd < 0) { error = fd; } else { error = sys_ioctl(fd, BLKFLSBUF, 0); sys_close(fd); } printk(!error ? "okayn" : "failedn"); }
19.
代码分析(handle_initrd) ? handle_initrd函数的主要功能是执行initrd的linuxrc文
件,并且将realfs的根目录设置为当前目录 ? 代码[1]:real_root_dev,是一个全局变量保存的是realfs的设备号。 ? 代码[2]:调用mount_block_root函数将initrd文件系统挂载到了VFS 的/root下。 ? 代码[3]:提取rootfs的根的文件描述符并将其保存到root_fd.它的作 用就是为了在chroot到initrd的文件系统,处理完initrd之后要,还 能够返回rootfs.返回的代码参考代码[7]. ? 代码[4]:chroot进入initrd的文件系统。前面initrd已挂载到了rootfs 的/root目录。 ? 代码[5]:执行initrd的linuxrc文件,等待其结束。 ? 代码[6]:initrd处理完之后,重新chroot进入rootfs. ? 代码[7]:如果real_root_dev在 linuxrc中重新设成Root_RAM0,则 initrd就是最终的realfs了,改变当前目录到initrd中,不作后续处理 直接返回。 ? 代码[8]:在linuxrc执行完后,realfs设备已经确定,调用 mount_root函数将realfs挂载到root_fs的 /root目录下,并将当前目 录设置为/root. ? 代码[9]:收尾并释放内存盘。
20.
anaconda目录结构 installclasses子目录中的各个模块定义了在安装过程中用户可选择 的安装类型。 例如:workstation.py,server.py,custom.py和 personal_desktop.py。其中,workstation.py描述了工作站安装类 型,server.py描述了服务器安装类型,custom.py描述了用户自定 义安装类型,personal_desktop.py描述了个人桌面安装类型。每个 安装类型描述文件根据相应安装类型的特点,分别对安装步骤、 分区策略以及安装包的取舍给出了不同的方案。 iw子目录下包含所有安装图形界面类所在的模块,每个图形界面 对应一个类,负责相应安装步骤图形界面的具体外观显示及与用 户的交互,调用anaconda主目录下的相关安装行为模块完成具体的 安装操作。 textw子目录和iw子目录含义是一致的,只是包含的是字符安装模 式的前端字符用户界面类所在的模块,每个字符用户界面对应一 个类,负责与用户的交互。 如果说用户界面类是处理安装程序外观的话,则anaconda主目录下 的各python模块则执行每个安装界面背后具体的安装行为,包括那 些无用户界面安装步骤的安装操作。
21.
各模块间逻辑关系
22.
Dispatcher类 作用: 该类在anaconda主目录下的dispatch.py模 块中。无论是图形模式安装还是字符模 式安装,都由该类来控制安装流程。
23.
installSteps ? dispatch.py模块中有一个序列(sequence)数据
结构:installSteps。installSteps中记录了有序排 列的整个安装过程中所有可能的安装步骤,在 生成具体的Dispatcher实例时,会根据安装类型 制定对此进行相应裁减。 ? installSteps中的条目(item)有如下两种格式 – 第一种格式:( name, tuple):这种格式表示有用户界 面的安装步骤。其中,name代表安装步骤的名称, tuple(元组,python的一种内置数据类型)存放创建相 应安装步骤的用户界面的参数。 – 第二种格式:( name, Function, tuple):这种格式表示 没有用户界面的安装步骤,其中,name代表安装步 骤的名称,Function指安装操作的具体执行函数, tuple存放的是传递给Function的参数。该安装步骤直 接由Dispatcher调度进行。
24.
Dispatcher的主要接口 ? gotoNext &
gotoPrev:这两个接口分别从当前安装步骤 前进(后退)到下一个(上一个)具有用户界面的安 装步骤,在图形界面安装模式下,由 installcontrolwindow调用,在字符模式下,由 InstallInterface调用。这两个函数只是简单的设置安装 方向,然后调用 movestep函数,其核心操作是movestep。 ? currentStep:Dispatcher类的另一个主要接口,取得当前 的安装步骤及其相关信息返回给调用者。在图形安装 模式下,该函数主要由 InstallControlWindow调度图形 用户界面类时调用,在字符模式下,主要由 InstallInterface调度字符用户界面时调用,这两个类通 过该接口取得当前安装步骤的用户界面对应的类及创 建该用户界面类的实例所需的信息。 ? skipStep(self, stepToSkip, skip = 1, permanent = 0)是裁减 安装步骤函数。setStepList(self, *steps)是安装步骤设置 函数,主要由安装类型实例调用,每个安装类型会根 据自身的特点设置安装步骤。
25.
movestep def moveStep(self): …… if self.step
== None: self.step = self.firstStep else: self.step = self.step + self.dir while ((self.step >= self.firstStep and self.step < len(installSteps)) and (self.skipSteps.has_key(installSteps[self.step][0]) or (type(installSteps[self.step][1]) == FunctionType))): info = installSteps[self.step] if ((type(info[1]) == FunctionType) and (not self.skipSteps.has_key(info[0]))): (func, args) = info[1:] rc = apply(func, self.bindArgs(args)) if rc == DISPATCH_BACK: self.dir = -1 elif rc == DISPATCH_FORWARD: self.dir = 1 self.step = self.step + self.dir if self.step == len(installSteps): return None
26.
movestep ? 首先看一下循环条件:当下一个安装步骤是合法的,
即在第一个安装步骤和最后一个安装步骤之间,且该 步骤被裁减了或者该步骤是一个无用户界面的安装步 骤,即installSteps的条目的第二个元素是一个function, 则进入循环体。 ? 进入循环后,Dispatcher直接调用该函数执行安装操 作,其中bindArgs是Dispatcher类的一个函数,负责参 数解析,这里的apply是python的的一个内置方法,用 来执行函数,apply接口的第一个参数是要运行的函数 名称,第二个参数是传给该函数的参数。 ? 如果下一个安装步骤依然无用户界面,则继续循环, 直到下一个没有被裁减的具有用户界面的安装步骤, 对于图形安装模式,Dispatcher将控制权交给 InstallControlWindow,对于字符安装模式,Dispatcher 将控制权交给InstallInterface。如果安装过程完成则退 出循环。
27.
InstallControlWindow ? 控制安装过程中前端图形界面的显示,
该类在anaconda主目录下的gui.py模块中。 ? 启动图形安装界面的入口函数run,该函 数调用了setup_window接口,该接口调用 gtk"绘制"图形安装界面的主窗体,然后 控制权交给了gtk。 def run (self, runres, configFileData): self.configFileData = configFileData self.setup_window(runres) gtk.main()
28.
数据结构stepToClass gui.py模块中的一个数据结构:stepToClass记录了安装过 程中所有的具有图形用户界面的安装步骤。 stepToClass
= { "language" : ("language_gui", "LanguageWindow"), "keyboard" : ("keyboard_gui", "KeyboardWindow"), …… } 每一个条目从左到右依次是安装步骤名称、图形界面对应的类所在的 模块,图形界面类的名称。如language为安装步骤名称, language_gui为该步骤对应的图形界面类所在的模块 language_gui.py,LanguageWindow为图形界面对应的类名。
29.
nextClicked & prevClicked 作用: 这两个接口分别执行从当前图形安装界 面向前(向后)到下一个图形安装界面 的操作。这两个函数首先调用主流程控 制Dispatcher实例向前(向后)前进到下 一个图形安装界面,然后调用setScreen函 数,setScreen是设置图形界面的。
30.
setScreen def setScreen (self):
(step, args) = self.dispatch.currentStep() if not stepToClass[step]: if self.dir == 1: return self.nextClicked() else: return self.prevClicked() (file, className) = stepToClass[step] newScreenClass = None s = "from %s import %s; newScreenClass = %s" % (file, className, className) while 1: exec s break self.destroyCurrentWindow() self.currentWindow = newScreenClass(ics) new_screen = apply(self.currentWindow.getScreen, args) self.installFrame.add(new_screen) self.installFrame.show_all() ……
31.
InstallControlWindow 流程 ? 前面的nextClicked和
prevClicked函数已经通过 Dispatcher将要进行的安装步骤标记为当前安装步骤, 所以该函数首先通过Dispatcher的 currentStep从 Dispatcher的数据结构installSteps中取得当前安装步骤名 称及相关信息 ? 做了一下判断,如果 Dispatcher的当前安装步骤不在字 典stepToClass中,则忽略该步骤,调用nextClicked或 prevClicked继续下一个图形界面安装步骤,直到下一个 步骤在字典stepToClass中 ? 从字典stepToClass中取得当前图形安装界面对应的类及 该类所在模块,然后导入该模块并创建图形安装界面 的实例,销毁前一个图形安装界面,并将新创建的图 形界面实例置为当前安装界面,调用图形安装界面实 例的 getScreen函数生成该安装步骤的图形用户界面并 显示。
32.
Q&A The end 谢谢