收藏 分销(赏)

基于x86的linux.doc

上传人:仙人****88 文档编号:11987596 上传时间:2025-08-25 格式:DOC 页数:10 大小:236KB 下载积分:10 金币
下载 相关 举报
基于x86的linux.doc_第1页
第1页 / 共10页
基于x86的linux.doc_第2页
第2页 / 共10页


点击查看更多>>
资源描述
基于x86的linux2.6.26代码分析 一. 由Makefile分析bzImage的生成过程 内核配置完毕之后使用命令make bzImage生成内核镜像文件,这里使用的make规则默认是源代码目录linux-2.6.26(以后定为SOURCE_HOME).打开SOURCE_HOME/Makefile发现其中的target是vmlinux,没有发现命令中指定的目标bzImage.由于makefile 文件可以包含其他的make规则最后发现SOURCE_HOME/Makefile多处出现下面的一句话(431行) 图<1> 其中$SRCARCH是平台的架构这里是X86,这句话显然包含了SOURCE_HOME/arch/x86/Makefile,打开这个文件继续寻找目标bzImage有如下发现: 图<2> 212行规定生成的bzImage的位置在SOURCE_HOME/arch/x86/boot/下面. 215行发现bzImage生成依赖于vmlinux. Vmlinux的生成 显然vmlinux是SOURCE_HOME/Makefile中的规则负责生成的. 图<3> 这个vmlinux的生产依赖于后面的几个目标,这些目标如下 图<4> 图<5> 从上面的规则可以清楚看出vmlinux的生成用到了SOURCE_HOME下面的这些目录:kernel/,mm/,fs/,ipc/,security/, crypto/,kblock/,init/,lib/,net/,/drivers/, Vmlinux到最后直接依赖的是vmlinux.o,kallsyms.o,和上面所说那些目录下的built-in.o. built-in.o是如何生成的 图<6> 上图主Makefile838行调用了scripts/Makefile.build对保存在vmlinux-dirs目录中的内容进行编译 图<7> 可以看到vmlinux-dirs包括SOURCE_HOME下面的init,drivers,net,lib,kernel,mm,fs,ipc.security,crypto,block这些和体系结构无关的目录内容. 在scripts/Makefile.build83行看到每个目录生成的结构都保存在built-in.o中,这就生成了vmlinux所依赖的部分文件. 有了built-in.o还需要vmlinux.o和kallsyms.o vmlinux.o的生成方式 vmlinux.o依赖的vmlinux-main上一节讲过就是那些built-in.o.现在缺少的是$(modpost-init) 查找$(modpost-init)发现图<5>822行:modpost-init包括了vmlinux-init中除了init/built-in.o之外的那些文件. vmlinux-init := $(head-y) $(init-y) init-y是init/built-in.o,回头看图<5>822行这么做的原因可能是不想多次包含init/built-in.o. $(head-y)在主Makefile没找到,既然包含了x86/Makefile就去找吧: 在这里又发现其实主Makefile中定义的core-y,lib-y等又加入了和体系结构相关x86目录下的一些东西.而head-y如下: 图<8> 可见head-y包含了x86/kernel/下面的head_32.o,head32.o,init_task.o 至此vmlinux.o的生产条件已经满足了. 图<9> Kallsyms其实是script下的一个命令,主Makefile在757-759行生成了内核的全部符号表并且把这些符号交给scripts/kallsyms处理.处理的结果可以打开SOURCE_HOME下的..tmp_kallsymsX.o.cmd查看,这里的X代表1/2. 图<10> 从图中看到这个命令生成的是kallsyms.o依赖的.tmp_kallsymsX.o.至此kallsyms.o生成的条件完全满足了. 最后从主Makefile看出又分了三步一步一步修正vmlinux和kallsyms.o最终生成主目录下的vmlinux. 最后查看bzImage是如何生成的,方法是查看编译时的输出信息: 在上图的970行中vmlinux生成了.然而不是最终的bzImage. 从上图983-989,1007-1011行看到vmlinux显然又经过了一些变化,生成的目标都放在compress和boot目录下.到compress目录下查看发现几个命令文件,这些命令文件是编译时的输出,比较详细. 首先是.vmlinux.cmd文件他的目标又是一个vmlinux,目的地是本目录. cmd_arch/x86/boot/compressed/vmlinux := ld -m elf_i386 -T arch/x86/boot/compressed/vmlinux_32.lds arch/x86/boot/compressed/head_32.o arch/x86/boot/compressed/misc.o arch/x86/boot/compressed/piggy.o -o arch/x86/boot/compressed/vmlinux 可见他的输出文件是一个elf可执行文件,依赖于 vmlinux_32.lds这个文件本来就有; misc.o,和head_32.o是运行时进行解压缩和重定位的代码.目录中有同名命令生成.而piggy.o还要去找. 打开.piggy.o.cmd cmd_arch/x86/boot/compressed/piggy.o := ld -m elf_i386 -r --format binary --oformat elf32-i386 -T arch/x86/boot/compressed/vmlinux.scr arch/x86/boot/compressed/vmlinux.bin.gz -o arch/x86/boot/compressed/piggy.o 这里发现piggy.o是吧vmlinux.bin.gz链接而成的.在去找vmlinux.bin.gz的生成.打开.vimlnux.bin.gz.cmd cmd_arch/x86/boot/compressed/vmlinux.bin.gz := gzip -f -9 < arch/x86/boot/compressed/vmlinux.bin.all > arch/x86/boot/compressed/vmlinux.bin.gz 很显然他使用了gzip命令把vmlinux.bin.all压缩后存放到comprssed/vmlinux.bin.gz 在去找vmlinux.bin.all.同样打开同名文件.vmlinux.bin.all.cmd cmd_arch/x86/boot/compressed/vmlinux.bin.all := cat arch/x86/boot/compressed/vmlinux.bin arch/x86/boot/compressed/vmlinux.relocs > arch/x86/boot/compressed/vmlinux.bin.all 可见这个文件吧vmlinux.bin和vmlinux.relocs简单的合并成了vmlinux.bin.all 在查看.vmlinux.bin.cmd cmd_arch/x86/boot/compressed/vmlinux.bin := objcopy -R .comment -S vmlinux arch/x86/boot/compressed/vmlinux.bin 这个文件是用objcopy把原来主Makefile生成的vmlinux拷贝为二进制文件 打开.vmlinux.relocs发现 cmd_arch/x86/boot/compressed/vmlinux.relocs := arch/x86/boot/compressed/relocs vmlinux > arch/x86/boot/compressed/vmlinux.relocs;arch/x86/boot/compressed/relocs --abs-relocs vmlinux 说明vmlinux.relocs是把vmlinux的定位符提取出来,然后又用compressed/relocs把原来的vmlinux进行重定位了. 这样正推过去就找到了这个目录下vmlinux的生成方式 A>objcopy 主Makefile生成的位于SOURCE_HOME下的那个vmlinux生成arch/x86/compressed/vmlinux.bin B>arch/x86/compressed/relocs提取SOURCE_HOME/vmlinux的重定位信息输出到arch/x86/compressed/vmlinux.relocs. C>vmlinux.relocs和vmlinux.bin被简单的合并到一块儿.其中vmlinux.bin在头部,重定位信息在尾部.输出为vmlinux.bin.all D>用gzip命令把vmlinux.bin.all压缩为vmlinux.bin.gz E>vmlinux.src和vmlinux.bin.gz被链接为piggy.o. F>head_32.o和misc_32.o,piggy.o链接为vmlinux,格式为elf-i386. 依照创建文件的时间顺序排列位于x86/boot下的那些cmd文件得到 他们的顺序和编译时输出的顺序一样.分别打开他们会发现bzImage生成的后续步骤. G>boot/.vmlinux.bin.cmd使用objcopy把新生成的这个vmlinux拷贝为二进制文件boot/vmlinux.bin H>boot/.setup.elf.cmd生成elf文件setup.elf于当前目录 I>boot/.setup.bin.cmd把setup.elf 用objcopy命令拷贝为二进制文件setup.bin于当前目录. J>cmd_arch/x86/boot/bzImage := arch/x86/boot/tools/build -b arch/x86/boot/setup.bin arch/x86/boot/vmlinux.bin CURRENT > arch/x86/boot/bzImage 上面的命令负责生成boot目录下的bzImage.显然他使用boot/tools/build把boot目录下的vmlinux.bin和setup.bin合成为bzImage 这样bzImage就生成了!! 从上面的生成过程可以了解到bzImage由两部分组成:vmlinux代码段和跟特定体系结构机器启动紧密相关的setup代码段. Vmlinux包含了可以重定位和自解压的代码.setup 则包含了arch/x86/boot目录下的很多代码,可以断定她和启动的关系非常大. 通过以上的分析bzImage 的代码结构也就一目了然了. linux 的启动过程分析 一:BIOS启动阶段 Cpu上电时他的指令寄存器cs:ip总是指向一个固定的地址,这个地址保存在主板上的ROM里.这条指令是cpu执行的第一条指令的地址.x86启动之后进入到实模式下,他的地址线只有20位,因此可以寻址1MB的地址空间,段寄存器是16位.第一条指令的地址是0xffff0000.BIOS加载之后会按照自己的设置查找活动设备的可引导记录.以grub引导硬盘为例.grub在安装时会把自己的一部分写入到MBR中,MBR512字节.BIOS会把这块的内容加载到物理地址为0x7c00的地方.然后就到这个地方来执行.这里就是grub的代码.grub大于512B所以分为stage1和stage2.其中stage1放入MBR中,负责载入stage2的内容.stage2的代码会根据配置文件列出操作系统供选择,如果选择了linux则他就把bzImage载入到内存中.这之后grub转到linux的实模式代码setup的入口点_start. 这时内存布局如下图所示: Setup的入口点_start可以在arch/x86/boot/setup.ld里找到而_start的代码则定义在boot/header.S里 Header.S最后调转到C语言的/boot/main.c里运行 main.c里的main函数会把header.S收集到的参数拷贝一份到文件内的结构体boot_params里.这个结构体定义在include/asm-x86/bootparam.h中. 在boot_params里有个结构体很特别setup_header他里面的数据几乎就是header.S 的.header.S收集到的很多信息都被main函数调用的copy_boot_params拷贝到本文件定义的boot_params里的结构体setup_header里了.grub可以向内核传递参数,grub会把参数放在hdr的cmd_line_ptr里等待copy_boot_params去复制. Main的最后调用boot/pm.c中go_to_protected_mode函数进入保护模式. 这个函数中realmode_switch_hook运行保存在hdr中realmode_swtch钩子函数,关闭中断.最后setup_gdt在全局描述符表中建立临时的数据段和代码段供保护模式调用.最后跳转到一段汇编代码,两个参数第一个是x86保护模式的入口地址,在boot_params中有保存.第二个参数是传给内核的参数ds()<<4是对地址的修正. 保护模式的启动 从实模式调转之后来到了保护模式的代码,首先应该对代码进行解压缩.解压缩的代码是boot/compressed/head_32.S他把保护模式的内核部分解压缩之后调转到x86/kernel/head_32.S.从head_32.S的注释可以知道他做了以下几件事情:初始化段选择子,加载全局描述符表;把BSS区域初始化为0;拷贝传递给内核的参数;初始化中断;开启分页机制;初始化各个寄存器;这样保护模式的环境就基本建立了. 内核接着运行init/main.c里的init_kernel函数 这个函数又调用了很多其他的函数完成内核的初始化.打印内核版本信息,根据参数收集硬件信息复制参数,调度模块初始化,内存管理初始化,中断初始化,软中断初始化,时钟和控制台的初始化等等. 他的最后调用了rest_init这个函数.rest_init调用kernel_init产生了著名的1号进程init.调用关系如下: rest_init->kernel_init->init_post->产生init进程 到这里linux就启动成功了.
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 学术论文 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服