您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 其它行业文档 > 基于x86体系结构分析linux-2626的启动过程
实验报告:基于x86体系结构分析linux-2.6.26的启动过程仅考虑32位体系结构不考虑多核多处理器要求1:分析流程按照开机--BIOS--grub--Linux的顺序进行,到start_kernel结束要求2:结合几个关键的Makefile文件和缺省编译linux的输出信息,给出Linux映像的make过程分析,说明编译链接的结果有哪些?要求3:结合GRUB和几个链接描述文件,说明GRUB是如何加载bzImage的,GRUB将跳转到内存的哪个地址上开始运行Linux,该地址的代码对应于哪个Linux源文件中的哪处?要求4:给出Linux中的启动相关的几个关键源文件的执行顺序,分析setup和内核解压缩前后这三个阶段的主要流程(直到start_kernel)。一.编译内核过程分析首先分析linux-2.6.26的顶层Makefile文件,我们发现这里没有bzImage,不过有如下语句其中的include$(srctree)arch/$(ARCH)/Makefile,对于x86架构而言,ARCH将会展开成x86,由于bzImage目标在当前的Makefile中并未找到,因此会到该Makefile中包含的子Makefile中寻找。进入/linux-2.6.26/arch/x86/Makefile中分析代码从图中我们可以看到bzImage是依赖于目标vmlinux的,于是下文我们将分两部分分析1.vmlinux的生成A.依赖分析首先在/linux-2.6.26/Makefile中,我们找到vmlinux的定义我们可以看出vmlinux依赖于vmlinux-lds,vmlinux-init,vmlinux-main和kallsysm.o变量。然后我们继续查找这些变量可看到:从这里可以看出vmlinux-init依赖于(head-y)$(init-y),vmlinux-main依赖于$(core-y)$(libs-y)$(drivers-y)$(net-y)接下来我们需要分析这些是什么.我们可以找到相关的定义,如下图由图中可以看出,首先这些*-y变量代表着不用的文件夹地址(kernel/init/net/等等等)经过patsubst是实现匹配替换的,在这里将文件夹的尾部替换为'/built-in.o'。例如,init-y初始值为init/经过patsubst替换为“init/built-in.o”在内核根目录有以下子目录:init内核初始化代码。提供main.c,包含startkernel函数。kernel内核管理代码。mm内存管理代码。ipc进程间通讯代码。net网络部分代码。lib与体系结构无关的内核库代码。drivers设备驱动代码。每类设备有相应的子目录,如char、block、netfs文件系统代码。每个支持文件系统有相应的子目录,如ext2、proc等。通过上述分析,我们发现,vmlinux的生成是依赖于这些文件夹内部的built-in.o的,然后最终这些.o文件经过vmlinux-lds的链接描述规则,连接成为vmlinux.至此,vmlinux生成依赖分析完毕。B.生成规则重新找到vmlinux的生成规则其中$(callifchangedrule,vmlinux)即调用rule_vmlnux__找到rule_vmlinux__的相关定义,如下根据定义,可以看出它的作用是调用cmd_vmlinux__最终生成并检验System.map找到cmd_vmlinux__可以分析出,它定义了根据vmlinux-lds的规定了链接vmlinux-init以及vmlinux-main生成vmlinux的规则。2.bzImage的生成首先找到/arch/x86/Makefile中bzImage的规则,如下可以观察得知它只是一个链接,链接到文件夹下找到/arch/x86/boot/Makefile的中bzImage根据规则可以看出来bzImage依赖于setup.bin以及vimlinux.bin.而vmlinux.bin有指的是/compressed/vmlinux,我们需要找到compressed目录下的Makefile,如下:根据规则可以看出来这里的vmlinux依赖于以下文件vmlinux_32.lds(本来就有),head_32.o,misc.o以及piggy.o。其中,head_32.o以及misc.o是用于解压缩和重定位的代码,piggy.o我们可以追溯编译时的console输出情况,最终发现,piggy.o是由vmliux经过一系列过程产生的。具体过程如下Aobjcopy主Makefile生成的位于SOURCE_HOME下的那个vmlinux生成arch/x86/compressed/vmlinux.binBarch/x86/compressed/relocs提取SOURCE_HOME/vmlinux的重定位信息输出到arch/x86/compressed/vmlinux.relocs.Cvmlinux.relocs和vmlinux.bin被简单的合并到一块儿.其中vmlinux.bin在头部,重定位信息在尾部.输出为vmlinux.bin.allD用gzip命令把vmlinux.bin.all压缩为vmlinux.bin.gzEvmlinux.src和vmlinux.bin.gz被链接为piggy.o.综上所述,我们/arch/x86/boot目录下的bzImage是由setup.bin以及一个具有自解压缩的vmlinux.bin组成的。二.Linux启动分析整体启动流程图如下图所示:1.BIOS启动阶段BIOS调用Bootloader来把操作系统的内核映像加载到系统RAM中。(1)当PC的电源打开后,80x86架构的cpu将自动进入实模式,并从地址0xFFFF0(CS:0xFFFF,IP:0x0)开始自动执行程序代码,这个地址通常是BIOS的地址。(2)BIOS的首先进行POST(PowerOnSelfTest即加电后自检),检测系统中一些关键设备是否存在和能否正常工作,例如内存和显卡等设备。此时显卡还没有初始化,如果发现了一些致命错误,例如没有找到内存或者内存有问题(此时只会检查640K常规内存),BIOS会直接控制喇叭发声来报告错误,声音的长短和次数代表了错误的类型。(3)然后物理地址0处开始初始化中断向量(注意:这个BIOS的中断向量很重要,后边的很多和硬盘等的交互都是通过此中断向量完成的)。(4)此后,BIOS将启动设备的第一个扇区(第0磁道第一个扇区被称为MBR即主引导记录,它的大小是512字节,里面存放了用汇编语言编写的预启动信息、分区表信息、魔数0x55AA),读入内存绝对地址0x7C00处,并跳转到这个地址并执行。其实被复制到物理内存0x7C00处的内容就是BootLoader,对于较早的内核不靠grub启动的,它就是bootsect.S程序,而对于现在PC多数使用grub引导启动的,就是lilo或者grub了。2.Bootloader(以grub为例)启动过程经过BIOS启动阶段,此时我们所需要的bootloader已经被载入到0x7C00并执行程序。我们分为2个版本讨论grub的启动过程,grub以及grub2:(1)grub:早期版本的grub分为以下3个阶段启动:stage1,stage1_5,stage2stage1grub的stage1存放在MBR(masterbootrecord,主引导记录),也就是说存放在MBR是磁盘的0柱面,0磁道,1扇区(扇区从1开始计数)。具体存储结构如下图所示:在图中我们可以观察到,stage1写在了MBR的前446个字节中,剩下的60来个字节用来放分区表。stage1的主要作用是:stage1判断磁盘处于CHS或者LBR模式并运行相应代码,将stage1.5装载到内存并运行stage1.5。stage1_5stage1_5负责识别文件系统和加载stage2,所以stage1_5往往有多个,以支持不同文件系统的读取。在存储方面,stage1.5在“MBRgap”中,也就是MBR与第一个分区之间的那点儿间隙,因为现在的磁盘空间都很大,为了提高性能,好的分区工具默认都是兆字节以上对齐的,所以这个间隙常常在1M以上,但怕的就是例外,所以stage1.5一般保持在31Kb以下。stage2stage2存放在文件系统中(这主要得益于stage1_5能够识别文件系统并访问)stage2负责显示启动菜单和提供用户交互接口,并根据用户选择或默认配置加载操作系统内核。(2)grub2新的grub启动分两个阶段:boot.img、cdboot.img、pexboot.img这些是一个阶段。和之前版本相比,这个阶段相应于stage1阶段。core.img阶段.和原来的grub相比较,core.img对应于stage1.5阶段,因为它们储存的位置是一样的(当然,core.img也可以安装在其它地方),但是,新的grub在core.img阶段之后,基本的功能已经有了,即使所有的分区全部格式化了,还能抛出一个minishell。这就是相对健壮的原因。经过这个阶段,我们的Bootloader已经把所选择的内核代码加载到了ram内存中,加载Linux内核的内存布局图如下所示(基于X86)我们可以发现bzImage被分成了3部分装载到ram中:bootsector(512字节)setupprotected-modekernel。bootsector被装载到0x90000处,setup被装载到0x90200处操作系统protected-modekernel则被装载到:对于小内核:0x10000(即64K处),称为低装载对于大内核:0x100000(即1M处),称为高装载装载完毕之后,grub将跳转到0x90200处运行代码,也就是我们setup部分的代码。setup用于体系结构相关的硬件初始化工作,在arch目录中的每个系统机构的平台相关都有类似功能的代码。在32位的X86平台中setup的入口时arch/x86/boot/header.s中的_start。3.实模式setup阶段查看_start代码我们可以看到_start的主要功能是:跳转到startofsetup检查setup的signature清除BSS段跳转到main执行其中,Main用来初始化硬件设备并为内核程序的执行建立环境象内存检测、键盘、视频等等arch/x86/boot/main.c中,main函数最后调用go_to_protected_mode函数,如下4.保护模式阶段在setup.bin中,系统从实模式到了保护模式,在这里我们会调用一个compressed/head_32.S的startup_32函数这个函数作用是:初始化段寄存器和一个临时堆栈初始化BSS段解压缩decompresskernel无论高装载或低装载解压缩后,都在物理地址0x100000(1MB)处跳转到0x100000处下面是startup_32的截图解压的内核镜像以包含在arch/x86/kernel/head.S中的另一个startup_32()函数开始。这两个函数使用相同的名字不会产生任何问题因为这两个函数会跳转到自己的起始物理地址去执行。第二个startup_32()函数为第一个linux进程(进程0)建立执行环境。该函数执行以下操作:A.初始化段寄存器B设置页目录和页表,分页C.建立进程0的内核堆栈D.SetupidtE.拷贝系统参数F.识别处理器G.GDT、IDTH.跳转到i386start_kernel下面是arch/x86/kernel/head.S中的另一个startup_32的截图start_kernel()函数完成linux内核的初始化工作。几乎每天内核部件都是由这个函数进行初始化的,我们只提及其中的少部分。调用sched_init()函数来初始化调度程序。调用bui
本文标题:基于x86体系结构分析linux-2626的启动过程
链接地址:https://www.777doc.com/doc-2572966 .html