linux2.6启动流程分析.doc

上传人:hw****26 文档编号:3551901 上传时间:2019-06-04 格式:DOC 页数:12 大小:63.50KB
下载 相关 举报
linux2.6启动流程分析.doc_第1页
第1页 / 共12页
linux2.6启动流程分析.doc_第2页
第2页 / 共12页
linux2.6启动流程分析.doc_第3页
第3页 / 共12页
linux2.6启动流程分析.doc_第4页
第4页 / 共12页
linux2.6启动流程分析.doc_第5页
第5页 / 共12页
点击查看更多>>
资源描述

1、linux 2.6 启动流程分析linux2.6 启动传递命令行分析内核在启动时可以传递一个字符串命令行,来控制内核启动的过程,例如:“console=ttyS2,115200 mem=64M0xA0000000“这里指定了控制台是串口 2,波特率是 115200,内存大小是 64M,物理基地址是 0xA0000000。另外我们可以在内核中定义一些全局变量,使用这些全局变量控制内核的配置,例如 usb驱动中定义了static int nousb; /* Disable USB when built into kernel image */这个变量为 1,则 整个 usb驱动不初始化,如果想将其

2、置 1,可在字符串命令行中添加 nousb=1。在操作该变量之前, 还要让系统知道 该变量,方法是:_module_param_call(“,nousb,param_set_bool,param_get_bool,_module_param_call这个宏定义在 kernelincludelinuxmoduleparam.h原型如下:#define _module_param_call(prefix, name, set, get, arg, perm) static char _param_str_#name = prefix #name; static struct kernel_para

3、m const _param_#name _attribute_used_ _attribute_ (unused,_section_ (“_param“),aligned(sizeof(void *) = _param_str_#name, perm, set, get, arg 它定义了一个 kernel_param类型的变量,这个变量被放到了段_param,kernel_param结构体的定义是:struct kernel_param const char *name;unsigned int perm;param_set_fn set;param_get_fn get;void *ar

4、g;_param这个段的声明有些平台是在 arch/././vmlinux.lds.S,而大多数平台是放到kernelincludeasm-genericvmlinux.lds.h 中,定义如下:_param : AT(ADDR(_param) - LOAD_OFFSET) VMLINUX_SYMBOL(_start_param) = .; *(_param) VMLINUX_SYMBOL(_stop_param) = .; 内核启动时就会对字符串命令进行解析,在 kernelinitmain.c 中,内核启动函数 start_kernel 中对外部数组进行了声明:extern struct

5、kernel_param _start_param, _stop_param;然后调用函数 parse_args对数组进行解析:parse_args(“Booting kernel“, command_line, _start_param,_stop_param - _start_param,其中 command_line 就是要解析的字符串命令行, unknown_bootoption 是函数指针,它用来 获取指定参数的=右边的值。parse_args 就会在数组中找到和 nousb 名称一样的 kernel_param变量,并调用它的 set 函数对其进行付值。内核启动地址的确定内核编译链

6、接过程是依靠 vmlinux.lds 文件,以 arm为例 vmlinux.lds 文件位于 kernel/arch/arm/vmlinux.lds,但是该文件是由 vmlinux-armv.lds.in 生成的,根据编译选项的不同源文件还可以是 vmlinux-armo.lds.in,vmlinux-armv-xip.lds.in。vmlinux-armv.lds 的生成过程在 kernel/arch/arm/Makefile 中LD = arch/arm/vmlinux-armv.lds.inarch/arm/vmlinux.lds: arch/arm/Makefile $(LD) $(w

7、ildcard include/config/cpu/32.h) $(wildcard include/config/cpu/26.h) $(wildcard include/config/arch/*.h)echo Generating $sed s/TEXTADDR/$(TEXTADDR)/;s/DATAADDR/$(DATAADDR)/ $(LD) $vmlinux-armv.lds.in 文件的内容:OUTPUT_ARCH(arm)ENTRY(stext)SECTIONS. = TEXTADDR;.init : /* Init code and data */_stext = .;_i

8、nit_begin = .;*(.text.init)_proc_info_begin = .;*(.proc.info)_proc_info_end = .;_arch_info_begin = .;*(.arch.info)_arch_info_end = .;_tagtable_begin = .;*(.taglist)_tagtable_end = .;*(.data.init). = ALIGN(16);_setup_start = .;*(.setup.init)_setup_end = .;_initcall_start = .;*(.initcall.init)_initcal

9、l_end = .;. = ALIGN(4096);_init_end = .;其中 TEXTADDR 就是内核启动的虚拟地址,定义在 kernel/arch/arm/Makefile 中:ifeq ($(CONFIG_CPU_32),y)PROCESSOR = armvTEXTADDR = 0xC0008000LD = arch/arm/vmlinux-armv.lds.inendif需要注意的是这里是虚拟地址而不是物理地址。一般情况下都在生成 vmlinux 后,再对内核进行压缩成为 zImage,压缩的目录是 kernel/arch/arm/boot。下载到 flash 中的是压缩后的

10、zImage 文件,zImage 是由压缩后的 vmlinux 和解压缩程序组成,如下图所示:|-| |-| | | | | | decompress code | vmlinux | |-| zImage| | | | | | | | | | | | | | | /|-| | /| | /| | /|-|/zImage链接脚本也叫做 vmlinux.lds,位于 kernel/arch/arm/boot/compressed。是由同一目录下的 vmlinux.lds.in 文件生成的,内容如下:OUTPUT_ARCH(arm)ENTRY(_start)SECTIONS. = LOAD_ADD

11、R;_load_addr = .;. = TEXT_START;_text = .;.text : _start = .;其中 LOAD_ADDR 就是 zImage 中解压缩代码的 ram 偏移地址,TEXT_START 是内核 ram 启动的偏移地址,这 个地址是物理地址。在 kernel/arch/arm/boot/Makefile 文件中定义了:ZTEXTADDR =0ZRELADDR = 0xa0008000ZTEXTADDR 就是解压缩代码的 ram 偏移地址,ZRELADDR 是内核 ram 启动的偏移地址, 这里看到指定 ZTEXTADDR 的地址为 0,明显是不正确的,因 为

12、我的平台上的 ram 起始地址是 0xa0000000,在 Makefile 文件中看到了对该地址设置的几行注释:# We now have a PIC decompressor implementation. Decompressors running# from RAM should not define ZTEXTADDR. Decompressors running directly# from ROM or Flash must define ZTEXTADDR (preferably via the config)他的意识是如果是在 ram 中进行解压缩时,不用指定它在 ram 中

13、的运行地址,如果是在 flash 中就必须指定他的地址。所以这里将 ZTEXTADDR 指定为 0,也就是没有真正指定地址。在 kernel/arch/arm/boot/compressed/Makefile 文件有一行脚本:SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/LOAD_ADDR/$(ZRELADDR)/;s/BSS_START/$(ZBSSADDR)/使得 TEXT_START = ZTEXTADDR,LOAD_ADDR = ZRELADDR。这样 vmlinux.lds 的生成过程如下:vmlinux.lds: vmlinux.lds.in Ma

14、kefile $(TOPDIR)/arch/$(ARCH)/boot/Makefile $(TOPDIR)/.configsed “$(SEDFLAGS)“ $以上就是我对内核启动地址的分析,总结一下内核启动地址的设置:1、设 置 kernel/arch/arm/Makefile 文件中的TEXTADDR = 0xC0008000内核启动的虚拟地址2、设 置 kernel/arch/arm/boot/Makefile 文件中的ZRELADDR = 0xa0008000内核启动的物理地址如果需要从 flash 中启动还需要设置ZTEXTADDR 地址。内核解压缩过程内核压缩和解压缩代码都在目录

15、kernel/arch/arm/boot/compressed,编译完成后将产生 vmlinux、head.o、misc.o、head-xscale.o、piggy.o这几个文件,head.o 是内核的头部文件, 负责初始 设置;misc.o 将主要负责内核的解压工作,它在 head.o 之后;head-xscale.o 文件主要针对 Xscale 的初始化,将在链接时与 head.o 合并;piggy.o 是一个中间文件,其 实是一个 压缩的内核(kernel/vmlinux),只不 过没有和初始化文件及解压文件链接而已;vmlinux 是(没有lw:zImage 是压缩过的内核)压缩过的内

16、核,就是由 piggy.o、head.o、misc.o、head-xscale.o组成的。在 BootLoader 完成系统的引导以后并将 Linux 内核调入内存之后,调用 bootLinux(),这个函数将跳转到 kernel 的起始位置。如果 kernel 没有压缩,就可以启动了。如果 kernel压缩过,则要进行解压 ,在 压缩过的 kernel头部有解压 程序。压缩过得 kernel 入口第一个文件源码位置在 arch/arm/boot/compressed/head.S。它将调用函数 decompress_kernel(),这个函数在文件 arch/arm/boot/compres

17、sed/misc.c 中,decompress_kernel()又调用 proc_decomp_setup(),arch_decomp_setup()进行设置,然后使用在打印出信息“Uncompressing Linux.”后,调用 gunzip()。将内核放于指定的位置。以下分析 head.S 文件:(1)对于各种 Arm CPU 的 DEBUG输出设定,通过定义宏来统一操作。(2)设置 kernel 开始和结束地址,保存 architecture ID。(3)如果在 ARM2 以上的 CPU 中,用的是普通用 户模式, 则升到超级用 户模式,然后关中断。(4)分析 LC0结构 delta

18、offset,判断是否需要重 载内核地址(r0 存入偏移量,判断 r0 是否为零)。这里是否需要重载内核地址,我以为主要分析 arch/arm/boot/Makefile、arch/arm/boot/compressed/Makefile和 arch/arm/boot/compressed/vmlinux.lds.in 三个文件,主要看 vmlinux.lds.in链接文件的主要段的位置,LOAD_ADDR(_load_addr)0xA0008000,而对于 TEXT_START(_text、_start)的位置只设为 0,BSS_START(_bss_start)ALIGN(4)。对于这样的

19、结果依赖于,对内核解 压的运行方式,也就是 说,内核解压前是在内存(RAM)中还是在FLASH 上,因为这里,我们的 BOOTLOADER 将压缩内核(zImage)移到了 RAM 的 0xA0008000 位置,我 们的压缩内核是在内存(RAM)从 0xA0008000 地址开始顺序排列,因此我们的 r0获得的偏移量是载入地址(0xA0008000)。接下来的工作是要把内核镜像的相对地址转化为内存的物理地址,即重载 内核地址。(5)需要重载内核地址,将 r0 的偏移量加到 BSS region 和 GOT table 中。(6)清空 bss 堆栈空间 r2r3。(7)建立 C 程序运行需要的

20、缓存,并赋于 64K 的栈空间。(8)这时 r2 是缓存的结束地址, r4 是 kernel 的最后执行地址, r5 是 kernel 境象文件的开始地址。检查是否地址有冲突。将 r5 等于 r2,使 decompress 后的 kernel 地址就在 64K 的栈之后。(9)调用文件 misc.c 的函数 decompress_kernel(),解压内核于缓存结束的地方(r2 地址之后)。此 时各寄存器值有如下变化:r0为解压后 kernel 的大小r4为 kernel执行时的地址r5为解压后 kernel 的起始地址r6为 CPU类型值(processor ID)r7为系统类型值(arch

21、itecture ID)(10)将 reloc_start 代码拷贝之 kernel 之后(r5+r0 之后),首先清除缓存,而后 执行 reloc_start。(11)reloc_start 将 r5 开始的 kernel 重载于 r4 地址处。(12)清除 cache 内容,关 闭 cache,将 r7 中 architecture ID赋于 r1,执行 r4 开始的 kernel 代码。下面简单介绍一下解压缩过程,也就是函数 decompress_kernel实现的功能:解压缩代码位于 kernel/lib/inflate.c,inflate.c 是从 gzip 源程序中分离出来的。包含

22、了一些对全局数据的直接引用。在使用时需要直接嵌入到代码中。gzip 压缩文件时总是在前 32K 字节的范围内寻找重复的字符串进行编码, 在解压时需要一个至少为 32K 字节的解压缓冲区,它定义为 windowWSIZE。inflate.c 使用 get_byte()读取输入文件,它被定义成宏来提高效率。输入缓 冲区指针必须定义为 inptr,inflate.c 中对之有减量操作。inflate.c调用 flush_window()来输出 window缓冲区中的解压出的字节串,每次输出长度用 outcnt变量表示。在 flush_window()中,还 必 须对输出字节串计算 CRC 并且刷新

23、crc变量。在调用 gunzip()开始解压之前, 调用 makecrc()初始化CRC计算表。最后 gunzip()返回 0 表示解压成功。我们在内核启动的开始都会看到这样的输出:Uncompressing Linux.done, booting the kernel.这也是由 decompress_kernel 函数内部输出的,它调用了 puts()输出字符串,puts 是在 kernel/include/asm-arm/arch-pxa/uncompress.h 中实现的。执行完解压过程,再返回到 head.S 中,启 动内核:call_kernel: bl cache_clean_fl

24、ushbl cache_offmov r0, #0mov r1, r7 restore architecture numbermov pc, r4 call kernel下面就开始真正的内核了。汇编部分(1)在网上参考很多高手的文章,又加入了自己的一点儿内容,整理了一下,里面还有很多不明白的地方,而且也会有理解错误的地方,望高手指点,自己也会不断进行修改当进入 linux 内核后,arch/arm/kernel/head-armv.S 是内核最先执行的一个文件,包括从内核入口 ENTRY(stext)到start_kernel 之间的初始化代码,下面以我所是用的平台 intel pxa270为例, 说明一下他的汇编代码:1 .section “.text.init“,#alloc,e xecinstr

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育教学资料库 > 精品笔记

Copyright © 2018-2021 Wenke99.com All rights reserved

工信部备案号浙ICP备20026746号-2  

公安局备案号:浙公网安备33038302330469号

本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。