linux 216 启动流程分析Word文档下载推荐.docx

上传人:b****7 文档编号:22308175 上传时间:2023-02-03 格式:DOCX 页数:11 大小:20.70KB
下载 相关 举报
linux 216 启动流程分析Word文档下载推荐.docx_第1页
第1页 / 共11页
linux 216 启动流程分析Word文档下载推荐.docx_第2页
第2页 / 共11页
linux 216 启动流程分析Word文档下载推荐.docx_第3页
第3页 / 共11页
linux 216 启动流程分析Word文档下载推荐.docx_第4页
第4页 / 共11页
linux 216 启动流程分析Word文档下载推荐.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

linux 216 启动流程分析Word文档下载推荐.docx

《linux 216 启动流程分析Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《linux 216 启动流程分析Word文档下载推荐.docx(11页珍藏版)》请在冰豆网上搜索。

linux 216 启动流程分析Word文档下载推荐.docx

unsignedintperm;

param_set_fnset;

param_get_fnget;

void*arg;

};

__param这个段的声明有些平台是在arch/../../vmlinux.lds.S,而大多数平台是放到

kernel\include\asm-generic\vmlinux.lds.h中,定义如下:

__param:

AT(ADDR(__param)-LOAD_OFFSET){ 

VMLINUX_SYMBOL(__start___param)=.;

*(__param) 

VMLINUX_SYMBOL(__stop___param)=.;

}

内核启动时就会对字符串命令进行解析,在kernel\init\main.c中,内核启动函数start_kernel中

对外部数组进行了声明:

externstructkernel_param__start___param[],__stop___param[];

然后调用函数parse_args对数组进行解析:

parse_args("

Bootingkernel"

command_line,__start___param,

__stop___param-__start___param,

&

unknown_bootoption);

其中command_line就是要解析的字符串命令行,unknown_bootoption是函数指针,它用来获取指定参数的=右边的值。

parse_args就会在数组中找到和nousb名称一样的kernel_param变量,并调用它的set函数对其进行付值。

内核启动地址的确定

内核编译链接过程是依靠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.in

arch/arm/vmlinux.lds:

arch/arm/Makefile$(LD)\

$(wildcardinclude/config/cpu/32.h)\

$(wildcardinclude/config/cpu/26.h)\

$(wildcardinclude/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:

/*Initcodeanddata 

*/

_stext=.;

__init_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)

__initcall_end=.;

.=ALIGN(4096);

__init_end=.;

}

其中TEXTADDR就是内核启动的虚拟地址,定义在kernel/arch/arm/Makefile中:

ifeq($(CONFIG_CPU_32),y)

PROCESSOR 

=armv

TEXTADDR 

=0xC0008000

endif

需要注意的是这里是虚拟地址而不是物理地址。

一般情况下都在生成vmlinux后,再对内核进行压缩成为zImage,压缩的目录是kernel/arch/arm/boot。

下载到flash中的是压缩后的zImage文件,zImage是由压缩后的vmlinux和解压缩程序组成,如下图所示:

|-----------------|\ 

|-----------------|

 

|\ 

|

|decompresscode|

vmlinux 

\|-----------------| 

zImage

\| 

/|-----------------|

/

|/

|-----------------|/

zImage链接脚本也叫做vmlinux.lds,位于kernel/arch/arm/boot/compressed。

是由同一目录下的vmlinux.lds.in文件生成的,内容如下:

ENTRY(_start)

.=LOAD_ADDR;

_load_addr=.;

.=TEXT_START;

_text=.;

.text:

{

_start=.;

其中LOAD_ADDR就是zImage中解压缩代码的ram偏移地址,TEXT_START是内核ram启动的偏移地址,这个地址是物理地址。

在kernel/arch/arm/boot/Makefile文件中定义了:

ZTEXTADDR 

=0

ZRELADDR 

=0xa0008000

ZTEXTADDR就是解压缩代码的ram偏移地址,ZRELADDR是内核ram启动的偏移地址,这里看到指定ZTEXTADDR的地址为0,

明显是不正确的,因为我的平台上的ram起始地址是0xa0000000,在Makefile文件中看到了对该地址设置的几行注释:

#WenowhaveaPICdecompressorimplementation. 

Decompressorsrunning

#fromRAMshouldnotdefineZTEXTADDR. 

Decompressorsrunningdirectly

#fromROMorFlashmustdefineZTEXTADDR(preferablyviatheconfig)

他的意识是如果是在ram中进行解压缩时,不用指定它在ram中的运行地址,如果是在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.inMakefile$(TOPDIR)/arch/$(ARCH)/boot/Makefile$(TOPDIR)/.config

@sed"

$(SEDFLAGS)"

<

vmlinux.lds.in>

$@

以上就是我对内核启动地址的分析,总结一下内核启动地址的设置:

1、设置kernel/arch/arm/Makefile文件中的

TEXTADDR 

内核启动的虚拟地址

2、设置kernel/arch/arm/boot/Makefile文件中的

ZRELADDR 

内核启动的物理地址

如果需要从flash中启动还需要设置

ZTEXTADDR地址。

内核解压缩过程

内核压缩和解压缩代码都在目录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是压缩过的内核)压缩过的内核,就是由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/compressed/misc.c中,

decompress_kernel()又调用proc_decomp_setup(),arch_decomp_setup()进行设置,

然后使用在打印出信息“UncompressingLinux...”后,调用gunzip()。

将内核放于指定的位置。

以下分析head.S文件:

(1)对于各种ArmCPU的DEBUG输出设定,通过定义宏来统一操作。

(2)设置kernel开始和结束地址,保存architectureID。

(3)如果在ARM2以上的CPU中,用的是普通用户模式,则升到超级用户模式,然后关中断。

(4)分析LC0结构deltaoffset,判断是否需要重载内核地址(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)。

对于这样的结果依赖于,对内核解压的运行方式,也就是说,内核解压前是在内存(RAM)中还是在FLASH上,

因为这里,我们的BOOTLOADER将压缩内核(zImage)移到了RAM的0xA0008000位置,我们的压缩内核是在内存(RAM)从0xA0008000地址开始顺序排列,

因此我们的r0获得的偏移量是载入地址(0xA0008000)。

接下来的工作是要把内核镜像的相对地址转化为内存的物理地址,即重载内核地址。

(5)需要重载内核地址,将r0的偏移量加到BSSregion和GOTtable中。

(6)清空bss堆栈空间r2-r3。

(7)建立C程序运行需要的缓存,并赋于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类型值(processorID)

r7为系统类型值(architectureID)

(10)将reloc_start代码拷贝之kernel之后(r5+r0之后),首先清除缓存,而后执行reloc_start。

(11)reloc_start将r5开始的kernel重载于r4地址处。

(12)清除cache内容,关闭cache,将r7中architectureID赋于r1,执行r4开始的kernel代码。

下面简单介绍一下解压缩过程,也就是函数decompress_kernel实现的功能:

解压缩代码位于kernel/lib/inflate.c,inflate.c是从gzip源程序中分离出来的。

包含了一些对全局数据的直接引用。

在使用时需要直接嵌入到代码中。

gzip压缩文件时总是在前32K字节的范围内寻找重复的字符串进行编码,

在解压时需要一个至少为32K字节的解压缓冲区,它定义为window[WSIZE]。

inflate.c使用get_byte()读取输入文件,

它被定义成宏来提高效率。

输入缓冲区指针必须定义为inptr,inflate.c中对之有减量操作。

inflate.c调用flush_window()

来输出window缓冲区中的解压出的字节串,每次输出长度用outcnt变量表示。

在flush_window()中,还必

须对输出字节串计算CRC并且刷新crc变量。

在调用gunzip()开始解压之前,调用makecrc()初始化CRC计算表。

最后gunzip()返回0表示解压成功。

我们在内核启动的开始都会看到这样的输出:

UncompressingLinux...done,bootingthekernel.

这也是由decompress_kernel函数内部输出的,它调用了puts()输出字符串,

puts是在kernel/include/asm-arm/arch-pxa/uncompress.h中实现的。

执行完解压过程,再返回到head.S中,启动内核:

call_kernel:

bl 

cache_clean_flush

cache_off

movr0,#0

movr1,r7 

@restorearchitecturenumber

movpc,r4 

@callkernel

下面就开始真正的内核了。

汇编部分

(1)

在网上参考很多高手的文章,又加入了自己的一点儿内容,整理了一下,里面还有很多不明白的地方,而且也会有理解错误的地方,望高手指点,自己也会不断进行修改

当进入linux内核后,arch/arm/kernel/head-armv.S是内核最先执行的一个文件,包括从内核入口ENTRY(stext)到

start_kernel之间的初始化代码,下面以我所是用的平台intelpxa270为例,说明一下他的汇编代码:

.section"

.text.init"

#alloc,#execinstr

.type 

stext,#

/*内核入口点*/

3ENTRY(stext)

movr12,r0

/*程序状态,禁止FIQ、IRQ,设定SVC模式*/ 

movr0,#F_BIT|I_BIT|MODE_SVC 

@makesuresvcmode

msrcpsr_c,r0 

@andallirqsdisabled

/*判断CPU类型,查找运行的CPUID值与Linux编译支持的ID值是否支持*/

__lookup_processor_type

/*判断如果r10的值为0,则表示函数执行错误,跳转到出错处理,*/

/*出错处理函数__error的实现代码定义在debug-armv.S中,这里就不再作过多介绍了*/

teqr10,#0 

@invalidprocessor?

moveq 

r0,#'

p'

@yes,error'

10 

beq__error

/*判断体系类型,查看R1寄存器的ArchitectureType值是否支持*/

11 

__lookup_architecture_type

/*判断如果r7的值为0,则表示函数执行错误,跳转到出错处理,*/

12 

teqr7,#0 

@invalidarchitecture?

13 

a'

14 

/*创建核心页表*/

15 

__create_page_tables

16 

adrlr,__ret 

@returnaddress

17 

addpc,r10,#12 

@initialiseprocessor

@(returncontrolreg)

第5行,准备进入SVC工作模式,同时关闭中断(I_BIT)和快速中断(F_BIT)

第7行,查看处理器类型,主要是为了得到处理器的ID以及页表的flags。

第11行,查看一些体系结构的信息。

第15行,建立页表。

第17行,跳转到处理器的初始化函数,其函数地址是从__lookup_processor_type中得到的,

需要注意的是第16行,当处理器初始化完成后,会直接跳转到__ret去执行,

这是由于初始化函数最后的语句是movpc,lr。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 法律文书 > 辩护词

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1