Linux内核启动流程分析(一)
嵌入式linux系统的启动流程
嵌入式linux系统的启动流程
嵌入式Linux系统的启动流程一般包括以下几个步骤:
1.硬件初始化:首先会对硬件进行初始化,例如设置时钟、中
断控制等。
这一步骤通常是由硬件自身进行初始化,也受到系统的BIOS或Bootloader的控制。
2.Bootloader引导:接下来,系统会从存储介质(如闪存、SD
卡等)的Bootloader区域读取引导程序。
Bootloader是一段程序,可以从存储介质中加载内核镜像和根文件系统,它负责进行硬件初始化、进行引导选项的选择,以及加载内核到内存中。
3.Linux内核加载:Bootloader会将内核镜像从存储介质中加载到系统内存中。
内核镜像是包含操作系统核心的一个二进制文件,它由开发者编译并与设备硬件特定的驱动程序进行连接。
4.内核初始化:一旦内核被加载到内存中,系统会进入内核初
始化阶段。
在这个阶段,内核会初始化设备驱动程序、文件系统、网络协议栈等系统核心。
5.启动用户空间:在内核初始化完毕后,系统将启动第一个用
户空间进程(init进程)。
init进程会读取并解析配置文件(如
/etc/inittab)来决定如何启动其他系统服务和应用程序。
6.启动其他系统服务和应用程序:在用户空间启动后,init进
程会根据配置文件启动其他系统服务和应用程序。
这些服务和应用程序通常运行在用户空间,提供各种功能和服务。
以上是嵌入式Linux系统的基本启动流程,不同的嵌入式系统可能会有一些差异。
同时,一些特定的系统也可以添加其他的启动流程步骤,如初始化设备树、加载设备固件文件等。
Linux 系统引导过程
启动流程启动时要加载核心,让核心来驱动整个硬件。
整个启动过程:1.加载BIOS的硬件信息,并获得第一个启动设备的代号(CMOS中设定的启动项)。
2.读取第一个启动设备的MBR的引导加载程序(lilo、grub、spfdisk)3.加载核心操作系统的核心信息,核心开始解压缩,并且尝试驱动所有硬件设备。
4.核心执行init程序并获取运行信息。
5.Init执行/etc/rc.d/rc.sysinit文件6.启动核心的外挂模块(/etc/modprobe.conf)7.Init 执行各个批处理文件(根据运行级别)。
8.Init 执行/etc/rc.d/rc.local文件9.执行/bin/login程序,等待用户登录。
10.登录之后开始以shell控制主机。
引导加载程序与核心的载入主机读取BIOS,并且了解主要的主机硬件信息后,主机便开始尝试加载操作系统。
主机首先读取的就是硬盘的主引导记录(MBR),在MBR中装有引导加载程序(比如说grub)。
主机刚启动时是不认识磁盘文件系统的,这就需要引导加载程序帮忙。
但我们知道MBR是整个硬盘的第一个扇区,整个大小为一个扇区的大小(512KB),而我们的加载程序却远远大于这个容量。
怎么办?引导加载程序分成两个阶段来执行:1,执行引导加载程序的主程序,这个主程序放在MBR或超级块中。
2,载入引导加载程序的所有设置文件与相关的环境参数文件。
一般来说,设置文件都放在/boot目录下。
引导加载程序必须能做到:●引导加载程序可以直接指定并取用核心文件,并加载到主存储器中。
●也可以将加载程序的控制权移交给下一个加载程序(超级块中的引导加载程序)。
grub是如何被载入的呢?grub有几个重要文档,stage1,stage2,以及stage1.5,这些文档都在/boot/grub下,grub被载入时有以下几个步骤。
Stage1阶段装载基本的引导程式(stage1),这也是安装在MBR中的内容,大小为512字节,但这并不意味着占用的空间为512字节,这还要看块的大小以及inode控制的块数。
Linux 内核启动分析
Linux 内核启动分析1. 内核启动地址1.1. 名词解释ZTEXTADDR解压代码运行的开始地址。
没有物理地址和虚拟地址之分,因为此时MMU处于关闭状态。
这个地址不一定时RAM的地址,可以是支持读写寻址的flash等存储中介。
Start address of decompressor. here's no point in talking about virtual or physical addresses here, since the MMU will be off at the time when you call the decompressor code. Y ou normally call the kernel at this address to start it booting. This doesn't have to be located in RAM, it can be in flash or other read-only or read-write addressable medium.ZRELADDR内核启动在RAM中的地址。
压缩的内核映像被解压到这个地址,然后执行。
This is the address where the decompressed kernel will be written, and eventually executed. The following constraint must be valid:__virt_to_phys(TEXTADDR) == ZRELADDRThe initial part of the kernel is carefully coded to be position independent.TEXTADDR内核启动的虚拟地址,与ZRELADDR相对应。
一般内核启动的虚拟地址为RAM的第一个bank地址加上0x8000。
arm版本linux系统的启动流程
arm版本linux系统的启动流程ARM架构是一种常见的处理器架构,被广泛应用于嵌入式设备和移动设备中。
在ARM版本的Linux系统中,启动流程是非常重要的,它决定了系统如何从开机到正常运行。
本文将详细介绍ARM版本Linux系统的启动流程。
一、引导加载程序(Bootloader)引导加载程序是系统启动的第一阶段,它位于系统的固化存储器中,比如ROM或Flash。
在ARM版本的Linux系统中,常用的引导加载程序有U-Boot和GRUB等。
引导加载程序的主要功能是加载内核镜像到内存中,并将控制权转交给内核。
二、内核初始化引导加载程序将内核镜像加载到内存后,控制权被转交给内核。
内核初始化是系统启动的第二阶段,它主要完成以下几个步骤:1. 设置异常向量表:ARM架构中,异常是指硬件产生的中断或故障,比如系统调用、中断请求等。
内核需要设置异常向量表,以便正确处理异常。
2. 初始化处理器:内核对处理器进行初始化,包括设置页表、启用缓存、初始化中断控制器等。
3. 启动第一个进程:内核创建第一个用户进程(一般是init进程),并将控制权转交给它。
init进程是系统中所有其他进程的父进程,负责系统的初始化工作。
三、设备树(Device Tree)设备树是ARM版本Linux系统中的一种机制,用于描述硬件设备的相关信息。
在内核初始化过程中,内核会解析设备树,并建立设备树对象,以便后续的设备驱动程序使用。
设备树描述了硬件设备的类型、地址、中断等信息,以及设备之间的连接关系。
它使得内核能够在运行时自动识别和配置硬件设备,大大提高了系统的可移植性和灵活性。
四、启动初始化(Init)启动初始化是系统启动的第三阶段,它是用户空间的第一个进程(init进程)接管系统控制权后的操作。
启动初始化主要完成以下几个任务:1. 挂载根文件系统:启动初始化会挂载根文件系统,使得用户可以访问文件系统中的文件和目录。
2. 加载系统服务:启动初始化会加载并启动系统服务,比如网络服务、日志服务、时间同步服务等。
嵌入式系统第七讲 嵌入式Linux系统启动分析
嵌入式LINUX内核的版本控制 (2)
嵌入式Linux的版本号后面还会加一个后缀,如 “rmk4-mx1bsp0.3.6” ,该后缀往往表示针对某 个开发平台的补丁。几个常用的后缀:
– – –
–
rmk:表示由Russell King维护的ARM Linux; np:表示由Nicolas Pitre维护的基于StrongARM和 Xscale的ARM Linux; ac:表示由Alan Cox(Alan Cox是仅次于Linus的 Linux维护人员,主要负责网络部分和OSS等的维护 工作)维护的Linux代码; hh : 表 示 由 网 站 发 布 的 ARM Linux代码,主要是基于Xscale的,它包括工具链 、内核补丁、嵌入式图形系统等。
Bootloader空间位置
在flash中的典型空间分配情况
BootLoader 的主要功能(1/2)
初始化系统在启动阶段必需的硬件设备; 准备后续软件系统(如操作系统)运行所需 的软件环境,如复制操作系统内核代码到 RAM中等。 向内核传递启动参数; [可选]配置系统各种参数;
BootLoader 的主要功能(2/2)
BootLoader的特点
BootLoader是操作系统内核运行前的核心程 序,它具有如下特点: 代码量大; 由C语言写成,大多数时候需要嵌入式汇编 语言; 运行于SDRAM等随机存储器 由于它是启动内核前运行的最后一个程序, 它必须把控制权交给内核,因此它最后是一 条跳转到系统内核的语句。
嵌入式LINUX的内核源代码结构 (1)
COPYING
– GPL版权申明。
CREDITS
– 光荣榜。对Linux做出过重大贡献的人员信息。
Linux启动过程详解_MBR和GRUB概述
MBR和GRUB概述Linux 的启动流程目前比较流行的方式主要是以下步骤:1、引导器(例如 GRUB)启动;2、内核启动;3、系统进程启动与配置。
本文以 GRUB 为研究对象,对 GRUB 启动与内核启动两个部分进行描述,关于系统进程的进一步启动与配置将用另一篇文章来说明。
常见的目录结构(以 CentOS 5.3 为例):/boot|-- System.map-2.6.18-128.el5|-- System.map-2.6.18-128.el5xen|-- config-2.6.18-128.el5|-- config-2.6.18-128.el5xen|-- initrd-2.6.18-128.el5.img|-- initrd-2.6.18-128.el5xen.img|-- lost+found|-- memtest86+-1.65|-- message|-- symvers-2.6.18-128.el5.gz|-- symvers-2.6.18-128.el5xen.gz|-- vmlinuz-2.6.18-128.el5|-- vmlinuz-2.6.18-128.el5xen|-- xen-syms-2.6.18-128.el5|-- xen.gz-2.6.18-128.el5`-- grub|-- device.map|-- e2fs_stage1_5|-- fat_stage1_5|-- ffs_stage1_5|-- grub.conf|-- iso9660_stage1_5|-- jfs_stage1_5|-- menu.lst -> ./grub.conf|-- minix_stage1_5|-- reiserfs_stage1_5|-- splash.xpm.gz|-- stage1|-- stage2|-- ufs2_stage1_5|-- vstafs_stage1_5`-- xfs_stage1_5图一: CentOS 5.3 的 /boot 目录目录分作两大部分,一个是 /boot 目录下除 grub 目录以外的所有文件,这些是 Linux 的内核以及内核启动相关的一些文件;另一个就是 grub 下的所有文件, GRUB 引导器启动所需要的所有文件都在 grub 目录下。
linux_mips启动流程_存储相关
Linux-mips启动流程-存储相关linux内核启动的第一个阶段是从/arch/mips/kernel/head.s文件开始的。
而此处正是内核入口函数kernel_entry(),该函数定义在/arch/mips/kernel/head.s文件里。
kernel_entry()函数是体系结构相关的汇编语言,它首先初始化内核堆栈段,来为创建系统中的第一个进程进行准备,接着用一段循环将内核映像的未初始化数据段(bss段在_edata和_end之间)清零,最后跳转到/arch/mips/kernel/setup.c 中的start_kernel()初始化硬件平台相关的代码。
下面讲述start_kernel() 函数。
在这个函数中跟内存初始化的函数是setup_arch()。
第一部分:以函数调用关系为线索下面是函数之间调用关系的框图:第一章:start_kenel()->setup_arch()setup_arch(&command_line);每种体系结构都有自己的setup_arch() 函数,这些是体系结构相关的。
【如何确定编译那个体系结构的setup_arch() 函数呢?主要由linux 源码树顶层Makefile 中ARCH 变量来决定的。
例如:MIPS 体系结构的。
SUBARCH := mipsARCH ?= $(SUBARCH)】。
void __init setup_arch(char **cmdline_p){cpu_probe();调用函数cpu_probe(),该函数通过MIPS CPU的PRID寄存器来确定CPU类型,从而确定使用的指令集和其他一些CPU参数,如TLB等prom_init();prom_init() 函数是和硬件相关的,做一些低层的初始化,接受引导装载程序传给内核的参数,确定mips_machgroup,mips_machtype 这两个变量,这两个变量分别对应着相应的芯片组合开发板;cpu_report();打印cpu_probe() 函数检测到的CPU 的Processor ID。
Linux内核分析_课程设计
Linux内核分析_课程设计计算机科学与⼯程学院课程设计报告题⽬全称:Linux内核初起代码分析学⽣学号:姓名:指导⽼师:职称:指导⽼师评语:签字:课程设计成绩:⽬录摘要 (2)第⼀章引⾔ (1)1.1 问题的提出 (1)1.2任务与分析 (1)第⼆章代码分析 (2)2.1系统初始化过程流程 (2)2.2数据结构 (2)2.3常量和出错信息的意义 (4)2.4调⽤关系图 (4)2.5各模块/函数的功能及详细框图 (5)2.5.1 static void time_init(void)分析 (6)2.5.2 void main(void)分析 (6)2.5.3 pause()分析 (8)2.5.4 static int printf(const char *fmt, ...)分析 (8)2.5.5 void init(void)分析 (9)第三章内核调试 (12)3.1运⾏环境 (12)3.2编译内核过程 (12)第四章总结与体会 (15)致谢 (16)参考⽂献 (17)摘要随着计算机的普及,计算机发挥着越来越重要的作⽤,计算机的使⽤也越来越普遍,所以让更多的⼈能够更好的使⽤和掌握⼀些计算机⽅法显得⼗分重要。
充分发挥计算机的作⽤也显得⼗分重要。
操作系统应运⽽⽣。
操作系统是⼀种软件,⽤来帮助其他的程序控制计算机并和⽤户进⾏交互。
因⽽,对操作系统的研究是很有必要的。
操作系统包含了多个部分或者组件,最核⼼的部分是内核。
其他的部分⽤来帮助内核完成计算机资源的管理和应⽤程序的控制。
Linux操作系统是使⽤很⼴泛的,⾼质量的⼀个操作系统。
此次起始代码分析,我分析了init/main.c⽂件中的main()、init()以及编译内核代码。
main()中主要是关于起始的调⽤和设备和系统信息初始化,以及创建进程。
此时中断仍被禁⽌着,做完必要的设置后就将其开启init()是创建进程,并检测是否出错,出错则再次创建执⾏并打印出出错信息。
arm-linux 启动代码分析——stage1 (1)
arm-linux 启动代码分析——stage1 (1)本文针对arm linux, 从kernel的第一条指令开始分析,一直分析到进入start_kernel()函数.我们当前以linux-2.6.19内核版本作为范例来分析,本文中所有的代码,前面都会加上行号以便于和源码进行对照.例:在文件init/main.c中:00478: asmlinkage void __init start_kernel(void)前面的"00478:" 表示478行,冒号后面的内容就是源码了.在分析代码的过程中,我们使用缩进来表示各个代码的调用层次.由于启动部分有一些代码是平台特定的,虽然大部分的平台所实现的功能都比较类似,但是为了更好的对code进行说明,对于平台相关的代码,我们选择at91(ARM926EJS)平台进行分析.另外,本文是以uncompressed kernel开始讲解的.对于内核解压缩部分的code,在arch/arm/boot/compressed中,本文不做讨论.一. 启动条件通常从系统上电到执行到linux kenel这部分的任务是由bootloader来完成.关于boot loader的内容,本文就不做过多介绍.这里只讨论进入到linux kernel的时候的一些限制条件,这一般是bootloader在最后跳转到kernel之前要完成的:1.CPU必须处于SVC(supervisor)模式,并且IRQ和FIQ中断都是禁止的;2. MMU(内存管理单元)必须是关闭的, 此时虚拟地址对物理地址;3. 数据cache(Data cache)必须是关闭的4. 指令cache(Instructioncache)可以是打开的,也可以是关闭的,这个没有强制要求;5. CPU 通用寄存器0 (r0)必须是0;6. CPU 通用寄存器1 (r1)必须是ARM Linux machinetype (关于machine type, 我们后面会有讲解)7. CPU 通用寄存器2 (r2) 必须是kernel parameterlist 的物理地址(parameter list 是由bootloader传递给kernel,用来描述设备信息属性的列表,详细内容可参考"Booting ARM Linux"文档).二. starting kernel首先,我们先对几个重要的宏进行说明(我们针对有MMU的情况): 宏 位置 默认值 说明KERNEL_RAM_ADDR arch/arm/kernel/head.S+26 0xc0008000 kernel在RAM中的的虚拟地址PAGE_OFFSET include/asm-arm/memeory.h+50 0xc0000000 内核空间的起始虚拟地址TEXT_OFFSET arch/arm/Makefile+137 0x00008000 内核相对于存储空间的偏移TEXTADDR arch/arm/kernel/head.S+49 0xc0008000 kernel的起始虚拟地址PHYS_OFFSET include/asm-arm/arch-xxx/memory.h 平台相关 RAM的起始物理地址内核的入口是stext,这是在arch/arm/kernel/vmlinux.lds.S中定义的:00011: ENTRY(stext)对于vmlinux.lds.S,这是ldscript文件,此文件的格式和汇编及C程序都不同,本文不对ldscript作过多的介绍,只对内核中用到的内容进行讲解,关于ld的详细内容可以参考这里的ENTRY(stext) 表示程序的入口是在符号stext.而符号stext是在arch/arm/kernel/head.S中定义的:下面我们将arm linuxboot的主要代码列出来进行一个概括的介绍,然后,我们会逐个的进行详细的讲解.在arch/arm/kernel/head.S中72 - 94 行,是armlinux boot的主代码:00072:ENTRY(stext) 00073: msr cpsr_c,#PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode00074: @and irqsdisabled 00075: mrc p15, 0,r9, c0, c0 @ getprocessorid 00076:bl __lookup_processor_type @r5=procinfor9=cpuid 00077: movs r10,r5 @invalid processor (r5=0)?00078:beq __error_p @yes, error‘p’ 00079:bl __lookup_machine_type @r5=machinfo 00080: movs r8,r5 @invalid machine (r5=0)?00081:beq __error_a @yes, error‘a’ 00082:bl __create_page_tables 00083: 00084: 00091: ldr r13,__switch_data @address to jump to after00092: @mmu has beenenabled 00093: adr lr,__enable_mmu @return (PIC)address 00094: add pc,r10,#PROCINFO_INITFUNC 其中,73行是确保kernel运行在SVC模式下,并且IRQ和FIRQ中断已经关闭,这样做是很谨慎的.arm linux boot的主线可以概括为以下几个步骤:1. 确定processortype (75 - 78行)2. 确定machinetype (79 - 81行)3.创建页表 (82行) 4.调用平台特定的__cpu_flush函数 (在struct proc_info_list中) (94行) 5.开启mmu (93行)6. 切换数据 (91行)最终跳转到start_kernel (在__switch_data的结束的时候,调用了b start_kernel)下面,我们按照这个主线,逐步的分析Code.1. 确定processor type arch/arm/kernel/head.S中:00075: mrc p15, 0,r9, c0, c0 @ getprocessorid 00076:bl __lookup_processor_type @r5=procinfor9=cpuid 00077: movs r10,r5 @invalid processor (r5=0)?00078:beq __error_p @yes, error‘p’ 75行: 通过cp15协处理器的c0寄存器来获得processor id的指令.关于cp15的详细内容可参考相关的arm手册76行:跳转到__lookup_processor_type.在__lookup_processor_type中,会把processortype 存储在r5中77,78行: 判断r5中的processor type是否是0,如果是0,说明是无效的processortype,跳转到__error_p(出错)__lookup_processor_type 函数主要是根据从cpu中获得的processorid和系统中的proc_info进行匹配,将匹配到的proc_info_list的基地址存到r5中,0表示没有找到对应的processor type.下面我们分析__lookup_processor_type函数arch/arm/kernel/head-common.S中:00145:.type __lookup_processor_type,%function00146: __lookup_processor_type:00147: adr r3,3f00148: ldmda r3,{r5 - r7}00149: sub r3, r3,r7 @get offset between virt&phys00150: add r5, r5,r3 @convert virt addresses to00151: add r6, r6,r3 @physical address space00152: 1: ldmia r5,{r3,r4} @value, mask00153: and r4, r4,r9 @mask wanted bits00154: teq r3,r400155:beq 2f00156: add r5, r5,#PROC_INFO_SZ @sizeof(proc_info_list)00157: cmp r5,r600158:blo 1b00159: mov r5,#0 @unknown processor00160: 2: mov pc,lr00161:00162:00165: ENTRY(lookup_processor_type)00166: stmfd sp!,{r4 - r7, r9, lr}00167: mov r9,r000168:bl __lookup_processor_type00169: mov r0,r500170: ldmfd sp!,{r4 - r7, r9, pc}00171:00172:00176:.long __proc_info_begin00177:.long __proc_info_end00178:3: .long .00179:.long __arch_info_begin00180:.long __arch_info_end 145, 146行是函数定义147行: 取地址指令,这里的3f是向前symbol名称是3的位置,即第178行,将该地址存入r3. 这里需要注意的是,adr指令取址,获得的是基于pc的一个地址,要格外注意,这个地址是3f 处的"运行时地址",由于此时MMU还没有打开,也可以理解成物理地址(实地址).(详细内容可参考arm指令手册) 148行: 因为r3中的地址是178行的位置的地址,因而执行完后: r5存的是176行符号__proc_info_begin的地址; r6存的是177行符号__proc_info_end的地址; r7存的是3f处的地址. 这里需要注意链接地址和运行时地址的区别. r3存储的是运行时地址(物理地址),而r7中存储的是链接地址(虚拟地址). __proc_info_begin和__proc_info_end是在arch/arm/kernel/vmlinux.lds.S中: 00031: __proc_info_begin= .; 00032: *(.init) 00033: __proc_info_end= .; 这里是声明了两个变量:__proc_info_begin 和__proc_info_end,其中等号后面的"."是locationcounter(详细内容请参考) 这三行的意思是: __proc_info_begin 的位置上,放置所有文件中的".init"段的内容,然后紧接着是__proc_info_end 的位置. kernel 使用struct proc_info_list来描述processor type. 在include/asm-arm/procinfo.h 中: 00029: struct proc_info_list { 00030: unsignedint cpu_val; 00031: unsignedint cpu_mask; 00032: unsignedlong __cpu_mm_mmu_flags; 00033: unsignedlong __cpu_io_mmu_flags; 00034: unsignedlong __cpu_flush; 00035: constchar *arch_name; 00036: constchar *elf_name; 00037: unsignedint elf_hwcap; 00038: constchar *cpu_name; 00039: structprocessor *proc; 00040: structcpu_tlb_fns *tlb; 00041: structcpu_user_fns *user; 00042: structcpu_cache_fns *cache; 00043: }; 我们当前以at91为例,其processor是926的. 在arch/arm/mm/proc-arm926.S 中: 00464: .section ".init", #alloc,#execinstr 00465: 00466:.type __arm926_proc_info,#object 00467: __arm926_proc_info: 00468:.long 0x41069260 @ARM926EJ-S (v5TEJ) 00469:.long 0xff0ffff0 00470:.long PMD_TYPE_SECT | \ 00471: PMD_SECT_BUFFERABLE| \ 00472: PMD_SECT_CACHEABLE| \ 00473: PMD_BIT4 |\ 00474: PMD_SECT_AP_WRITE| \ 00475: PMD_SECT_AP_READ 00476:.long PMD_TYPE_SECT | \ 00477: PMD_BIT4 |\ 00478: PMD_SECT_AP_WRITE| \ 00479: PMD_SECT_AP_READ 00480:b __arm926_setup 00481:.long cpu_arch_name 00482:.long cpu_elf_name 00483:.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_VFP|HW CAP_EDSP|HWCAP_JA V A 00484:.long cpu_arm926_name 00485:.long arm926_processor_functions 00486:.long v4wbi_tlb_fns 00487:.long v4wb_user_fns 00488:.long arm926_cache_fns 00489:.size __arm926_proc_info,. - __arm926_proc_info 从464行,我们可以看到__arm926_proc_info 被放到了".init"段中. 对照struct proc_info_list,我们可以看到__cpu_flush的定义是在480行,即__arm926_setup.(我们将在"4.调用平台特定的__cpu_flush函数"一节中详细分析这部分的内容.) 从以上的内容我们可以看出: r5中的__proc_info_begin是proc_info_list的起始地址,r6中的__proc_info_end是proc_info_list的结束地址.149行:从上面的分析我们可以知道r3中存储的是3f处的物理地址,而r7存储的是3f处的虚拟地址,这一行是计算当前程序运行的物理地址和虚拟地址的差值,将其保存到r3中.150行: 将r5存储的虚拟地址(__proc_info_begin)转换成物理地址151行: 将r6存储的虚拟地址(__proc_info_end)转换成物理地址152行: 对照structproc_info_list,可以得知,这句是将当前proc_info的cpu_val和cpu_mask分别存r3,r4中153行: r9中存储了processorid(arch/arm/kernel/head.S中的75行),与r4的cpu_mask进行逻辑与操作,得到我们需要的值154行: 将153行中得到的值与r3中的cpu_val进行比较155行: 如果相等,说明我们找到了对应的processor type,跳到160行,返回156行: (如果不相等) , 将r5指向下一个proc_info,157行: 和r6比较,检查是否到了__proc_info_end.158行: 如果没有到__proc_info_end,表明还有proc_info配置,返回152行继续查找159行: 执行到这里,说明所有的proc_info都匹配过了,但是没有找到匹配的,将r5设置成0(unknownprocessor)160行: 返回2. 确定machine type arch/arm/kernel/head.S中:00079:bl __lookup_machine_type @r5=machinfo 00080: movs r8,r5 @invalid machine (r5=0)?00081:beq __error_a @yes, error ‘a’79行: 跳转到__lookup_machine_type函数,在__lookup_machine_type中,会把structmachine_desc的基地址(machine type)存储在r5中80,81行: 将r5中的machine_desc的基地址存储到r8中,并判断r5是否是0,如果是0,说明是无效的machinetype,跳转到__error_a(出错)__lookup_machine_type 函数下面我们分析__lookup_machine_type 函数: arch/arm/kernel/head-common.S中: 00176:.long __proc_info_begin00177:.long __proc_info_end00178:3: .long .00179:.long __arch_info_begin00180:.long __arch_info_end00181:00182: 00193:.type __lookup_machine_type,%function00194: __lookup_machine_type:00195: adr r3,3b00196: ldmia r3,{r4, r5, r6}00197: sub r3, r3,r4 @get offset between virt&phys00198: add r5, r5,r3 @convert virt addresses to00199: add r6, r6,r3 @physical address space00200: 1: ldr r3,[r5, #MACHINFO_TYPE] @ get machinetype00201: teq r3,r1 @matches loader number?00202:beq 2f @found00203: add r5, r5,#SIZEOF_MACHINE_DESC @ nextmachine_desc00204: cmp r5,r600205:blo 1b00206: mov r5,#0 @unknown machine00207: 2: mov pc,lr193, 194行: 函数声明195行: 取地址指令,这里的3b是向后symbol名称是3的位置,即第178行,将该地址存入r3. 和上面我们对__lookup_processor_type 函数的分析相同,r3中存放的是3b处物理地址.196行: r3是3b处的地址,因而执行完后: r4存的是3b处的地址 r5存的是__arch_info_begin 的地址 r6存的是__arch_info_end 的地址 __arch_info_begin 和__arch_info_end是在arch/arm/kernel/vmlinux.lds.S中: 00034: __arch_info_begin= .; 00035: *(.init) 00036: __arch_info_end= .; 这里是声明了两个变量:__arch_info_begin 和__arch_info_end,其中等号后面的"."是locationcounter(详细内容请参考) 这三行的意思是: __arch_info_begin 的位置上,放置所有文件中的".init"段的内容,然后紧接着是__arch_info_end 的位置. kernel 使用struct machine_desc 来描述machine type. 在include/asm-arm/mach/arch.h 中: 00017: struct machine_desc { 00018: 00022: unsignedint nr; 00023: unsignedint phys_io; 00024: unsignedint io_pg_offst; 00026: 00027: constchar *name; 00028: unsignedlong boot_params; 00029: 00030: unsignedint video_start; 00031: unsignedint video_end; 00032: 00033: unsignedint reserve_lp0:1; 00034: unsignedint reserve_lp1:1; 00035: unsignedint reserve_lp2:1; 00036: unsignedint soft_reboot:1; 00037:void (*fixup)(structmachine_desc *, 00038: struct tag *, char **, 00039: struct meminfo *); 00040:void (*map_io)(void); 00041:void (*init_irq)(void); 00042: structsys_timer *timer; 00043:void (*init_machine)(void); 00044: }; 00045: 00046: 00050: #defineMACHINE_START(_type,_name) \ 00051: static const struct machine_desc__mach_desc_##_type \ 00052:__attribute_used__ \ 00053:__attribute__((__section__(".init"))) ={ \ 00054:.nr =MACH_TYPE_##_type, \ 00055:.name =_name, 00056: 00057: #defineMACHINE_END \ 00058:}; 内核中,一般使用宏MACHINE_START来定义machine type. 对于at91, 在arch/arm/mach-at91rm9200/board-ek.c 中: 00137: MACHINE_START(AT91RM9200EK, "Atmel AT91RM9200-EK") 00138: 00139: .phys_io =AT91_BASE_SYS, 00140:.io_pg_offst =(AT91_VA_BASE_SYS >> 18)& 0xfffc, 00141:.boot_params =AT91_SDRAM_BASE + 0x100, 00142:.timer =&at91rm9200_timer, 00143:.map_io =ek_map_io, 00144: .init_irq =ek_init_irq, 00145:.init_machine =ek_board_init, 00146: MACHINE_END197行:r3中存储的是3b处的物理地址,而r4中存储的是3b处的虚拟地址,这里计算处物理地址和虚拟地址的差值,保存到r3中198行: 将r5存储的虚拟地址(__arch_info_begin)转换成物理地址 199行:将r6存储的虚拟地址(__arch_info_end)转换成物理地址 200行: MACHINFO_TYPE 在arch/arm/kernel/asm-offset.c 101行定义, 这里是取struct machine_desc中的nr(architecture number) 到r3中201行: 将r3中取到的machine type 和r1中的machine type(见前面的"启动条件")进行比较202行: 如果相同,说明找到了对应的machine type,跳转到207行的2f处,此时r5中存储了对应的structmachine_desc的基地址203行: (不相同), 取下一个machine_desc的地址204行: 和r6进行比较,检查是否到了__arch_info_end.205行: 如果不相同,说明还有machine_desc,返回200行继续查找.206行: 执行到这里,说明所有的machind_desc都查找完了,并且没有找到匹配的, 将r5设置成0(unknownmachine).207行: 返回3. 创建页表通过前面的两步,我们已经确定了processor type 和machine type.此时,一些特定寄存器的值如下所示:r8 = machineinfo (struct machine_desc的基地址)r9 = cpuid (通过cp15协处理器获得的cpu id)r10 =procinfo (struct proc_info_list的基地址)创建页表是通过函数__create_page_tables 来实现的.这里,我们使用的是arm的L1主页表,L1主页表也称为段页表(section page table)L1 主页表将4 GB 的地址空间分成若干个1 MB的段(section),因此L1页表包含4096个页表项(sectionentry). 每个页表项是32 bits(4 bytes)因而L1主页表占用4096 *4 = 16k的内存空间. 对于ARM926,其L1 section entry的格式为:(可参考arm926EJS TRM): 。
linux启动顺序讲解
linux启动顺序讲解⼀、简单介绍RHEL开机时的先后顺序BIOS —> MBR —> Kernel —> init1、当电脑⼀打开电源时电脑就会进⼊BIOS(BIOS的⼯作主要是检测⼀些硬件设备);2、检测完后会进⼊MBR也就是boot loader(MBR位于硬盘的第⼀个扇区总共512bytes,其中前446bytes⾥⾯的编码是在选择引导分区也就是决定要由哪个分区来引导);3、载⼊系统的Kernel(核⼼),在Kernel⾥主要是载⼊电脑设备的驱动程序,以便可以控制电脑上的设备,并且以只读⽅式来挂载根⽬录,也就是⼀开始只能读取到根⽬录所对应的那个分区,所以/etc、/bin、/sbin、/dev、/lib这五个⽬录必须同根⽬录在⼀个分区中;4、最后启动init这个程序,所以init这个程序的进程编号为1,是Linux中第⼀个执⾏的程序;init这个程序会根据Run level来执⾏以下这些程序:·/etc/rc.d/rc.sysinit;·/etc/rc.d/rc 和etc/rc.d/rc?.d/·/etc/rc.d/rc.local·如果有适当的图形界⾯管理程序⼆、BIOS初始化时主要的三个任务BIOS(B asic I nput/O utput S ystem)1、电脑周边设备的检测,加电⾃检POST (Power on self test);2、BIOS会选择要由哪⼀个设备来开机,例如:软盘启动、光盘启动、⽹络启动、最常见的从硬盘启动;3、选择好由哪个设备开机后,就开始读取这个设备的MBR 引导扇区;三、介绍Boot Loader中的主要⼯作1、Boot Loader可以安装在两个地⽅:·安装在硬盘的MBR中;·当有时候MBR中被其他开机管理程序占⽤就可以将Boot Loader 安装在硬盘中的其中⼀个分区的引导扇区上,;2、Boot Loader的程序码分为两个阶段:(1)Boot Loader第⼀阶段的程序码⾮常⼩,只有446bytes,可以存⼊在MBR或是某⼀个分区的引导扇区⾥,(2)Boot Loader第⼀阶段的程序码是从boot 分区来载⼊的,就是说Boot Loader 第⼆阶段程序码存放在/boot 这个分区中;3、下⾯来看三个Boot Loader 的开机流程范例,如在⼀块硬盘中安装了两个系统分别为:windows 2003 和Red hat linux当电脑开机后,会先载⼊MBR通过第⼀阶段程序码来载⼊第⼆阶段程序码,进⼊GRUB开机菜单这⾥选择哪个系统就会载⼊相应的核⼼;四、介绍GRUB和grub.conf 这个配置⽂件的内容其实从MBR载⼊Boot Loader开始,载⼊Kernel,载⼊init这些程序之间都是由GRUB这个多重开机管理程序所负责的。
计算机及Linux操作系统开机启动过程详解
计算机及Linux操作系统开机启动过程详解从按下开机键开始的计算机启动过程:(主要包括从主板加载BIOS并执⾏、从磁盘加载启动区并执⾏、从磁盘加载操作系统并执⾏三步,是依次递进的,详情参阅)加载BIOS:按下开机键,主板ROM的BIOS被(被谁?)加载到到内存0xffff0处,CPU 将 PC 寄存器的值强制初始化为 0xffff0(⼀跳)。
执⾏BIOS代码:阶段1(0xffff0 处的内容):该⼊⼝地址处存的是⼀个跳转指令,跳转的⽬的地是内存0xfe05b位置,该位置存了BIOS的真正内容。
执⾏该跳转(⼆跳)。
阶段2(0xfe05b 处的内容):执⾏硬件检测、硬件初始化、建⽴中断向量表等⼯作后,找到磁盘上的启动区(或称引导区)加载到内存0x7c00位置,并跳转到该位置(三跳)。
执⾏启动区代码(0x7c00 处的内容):从磁盘加载OS内核到内存,与上⾯不同这⾥内存位置不是固定的了,并跳转到OS内核代码处(四跳)。
执⾏OS内核代码:包括开启分段机制、进⼊保护模式、开启中断机制等,执⾏完后系统由OS接⼿管理。
具体过程见下⽂“操作系统启动过程”部分。
整体过程概要:补充:BIOS位于主板ROM,启动时被加载到内存;启动区、OS位于磁盘,被先后加载到内存。
BIOS、启动区在内存的位置是固定的(为啥是这三个值?早期定死的);⽽OS在内存位置不是固定的。
启动区:若⼀个磁盘上0盘0道1扇区的内容(512B)的末两个字节为0x55、0xaa,则这该扇区会被BIOS识别为启动区,该磁盘会被当做可启动盘。
往⼀个磁盘烧录OS后之所以可以当做启动盘就是因为往该位置写⼊了这些特殊数据。
若装了多系统,则启动时会列出并让⽤户选择要启动的系统,这些系统就是根据上述条件被识别得到。
可见,⼀个程序只要其虚拟内存以0x7c00作为段地址,且按上述条件烧录到磁盘,则就可以被BIOS识别为启动区加载到内存执⾏。
因此,如果该程序逻辑中不是去加载OS⽽是直接输出数据,则该程序⾃⾝就是⼀个简洁的"操作系统"。
ARMlinux启动分析-Nathan.Yu的专栏-CSDN博客
ARMlinux启动分析-Nathan.Yu的专栏-CSDN博客linux启动分析(1)---bootloader启动内核过程我分析的是2.4.19的内核版本,是xscale的平台,参考了网上很多有价值的帖子,也加入了自己的一些看法,陆续总结成文字,今天是第一篇:内核一般是由bootloader来引导的,通过bootloader启动内核一般要传递三个参数,第一个参数放在寄存器0中,一般都为0,r0 = 0;第二个参数放在寄存器1中,是机器类型id,r1 = Machine Type Number;第三个参数放在寄存器2中,是启动参数标记列表在ram中的起始基地址;bootloader首先要将ramdisk(如果有)和内核拷贝到ram当中,然后可以通过c语言的模式启动内核:void (*startkernel)(int zero, int arch, unsigned int params_addr) = (void(*)(int, int, unsigned int))KERNEL_RAM_BASE;startkernel(0, ARCH_NUMBER, (unsigned int)kernel_params_start);其中KERNEL_RAM_BASE为内核在ram中启动的地址,ARCH_NUMBER是Machine Type Number,kernel_params_start 是参数在ram的偏移地址。
这时候就将全力交给了内核。
linux启动分析(2)---内核启动地址的确定内核编译链接过程是依靠vmlinux.lds文件,以arm为例vmlinux.lds文件位于kernel/arch/arm/vmlinux.lds,但是该文件是由vmlinux-armv.lds.in生成的,根据编译选项的不同源文件还可以是vmlinux-armo.lds.in,vmlinux-armv-xip.lds.in。
Linux操作系统启动流程图文详解
Linux操作系统启动流程图⽂详解理解Linux操作系统启动流程,能有助于后期在企业中更好的维护Linux服务器,能快速定位系统问题,进⽽解决问题。
上图为Linux操作系统启动流程1.加载BIOS计算机电源加电质检,⾸先加载基本输⼊输出系统(Basic Input Output System,BIOS),BIOS中包含硬件CPU、内存、硬盘等相关信息,包含设备启动顺序信息、硬盘信息、内存信息、时钟信息、即插即⽤(Plug-and-Play,PNP)特性等。
加载完BIOS信息,计算机将根据顺序进⾏启动。
2.读取MBR读取完BIOS信息,计算机将会查找BIOS所指定的硬盘MBR引导扇区,将其内容复制到0x7c00地址所在的物理内存中。
被复制到物理内存的内容是Boot Loader,然后进⾏引导。
3.GRUB引导GRUB启动引导器是计算机启动过程中运⾏的第⼀个软件程序,当计算机读取内存中的GRUB配置信息后,会根据其配置信息来启动硬盘中不同的操作系统。
4.加载Kernel计算机读取内存映像,并进⾏解压缩操作,屏幕⼀般会输出“Uncompressing Linux”的提⽰,当解压缩内核完成后,屏幕输出“OK, booting the kernel”。
系统将解压后的内核放置在内存之中,并调⽤start_kernel()函数来启动⼀系列的初始化函数并初始化各种设备,完成Linux核⼼环境的建⽴。
5.设定Inittab运⾏等级内核加载完毕,会启动Linux操作系统第⼀个守护进程init,然后通过该进程读取/etc/inittab⽂件,/etc/inittab⽂件的作⽤是设定Linux的运⾏等级,Linux常见运⾏级别如下:•0:关机模式•1:单⽤户模式•2:⽆⽹络⽀持的多⽤户模式•3:字符界⾯多⽤户模式•4:保留,未使⽤模式•5:图像界⾯多⽤户模式•6:重新引导系统,重启模式6.加载rc.sysinit读取完运⾏级别,Linux系统执⾏的第⼀个⽤户层⽂件/etc/rc.d/rc.sysinit,该⽂件功能包括:设定PATH运⾏变量、设定⽹络配置、启动swap分区、设定/proc、系统函数、配置Selinux等。
简要分析linux系统的启动过程
简要分析linux系统的启动过程接触linux系统运维已经好⼏年了,常常被问到linux系统启动流程问题,刚好今天有空来梳理下这个过程:⼀般来说,所有的操作系统的启动流程基本就是:总的来说,linux系统启动流程可以简单总结为以下⼏步:1)开机BIOS⾃检,加载硬盘。
2)读取MBR,进⾏MBR引导。
3)grub引导菜单(Boot Loader)。
4)加载内核kernel。
5)启动init进程,依据inittab⽂件设定运⾏级别6)init进程,执⾏rc.sysinit⽂件。
7)启动内核模块,执⾏不同级别的脚本程序。
8)执⾏/etc/rc.d/rc.local9)启动mingetty,进⼊系统登陆界⾯。
linux系统安装时,如果要想设置开启启动项,可以:开机到BIOS提醒界⾯,按键F11(Dell服务器的做法)进⼊BIOS设置BOOT MENU,继⽽设置启动项:硬盘HD启动,光盘CD/DVD启动,还是U盘USB启动。
下⾯就linux操作系统的启动过程做⼀详细解析记录:加载内核操作系统接管硬件以后,⾸先读⼊ /boot ⽬录下的内核⽂件。
[root@bastion-IDC ~]# ll /boot/total 21668-rw-r--r--. 1 root root 105195 Nov 22 2013 config-2.6.32-431.el6.x86_64drwxr-xr-x. 3 root root 1024 Aug 22 16:31 efidrwxr-xr-x. 2 root root 1024 Aug 22 16:32 grub-rw-------. 1 root root 15217153 Aug 22 16:32 initramfs-2.6.32-431.el6.x86_64.imgdrwx------. 2 root root 12288 Aug 22 16:24 lost+found-rw-r--r--. 1 root root 193758 Nov 22 2013 symvers-2.6.32-431.el6.x86_64.gz-rw-r--r--. 1 root root 2518236 Nov 22 2013 System.map-2.6.32-431.el6.x86_64-rwxr-xr-x. 1 root root 4128368 Nov 22 2013 vmlinuz-2.6.32-431.el6.x86_64启动初始化进程内核⽂件加载以后,就开始运⾏第⼀个程序 /sbin/init,它的作⽤是初始化系统环境。
(整理)BeagleBone开发板研究心得.
精品文档1 linux 的启动 1.1 linux 的启动过程1.1.1 Linux 启动过程的三个部分Linux 启动过程如 fig 1 所示。
(1) Bootloader 1) 提供基本的硬件初始化; 2) 调用 linux 内核和传递启动参数; (2)Kernel 1)初始化系统和设备; 2)管理系统资源; 3)为用户程序提供服务。
(3)文件系统 1)单一文件系统(/root); 2)存储所有的系统文件; 3)init 进程启动,初始化其它信息; 4)linux 内核通过启动参数确定文件系统的位置。
fig 11.1.2 linux 的启动过程Linux 启动过程如 fig 2 所示。
(1)启动 uboot:初始化部分硬件;传递启动参数给内核; 精品文档精品文档 (2)启动 linux 内核:初始化硬件; (3)启动文件系统:启动 init 进程和其它一些初始化、登录。
fig 21.2 beagleBone 开发板的启动过程beagleBone 开发板启动过程如 fig 3 所示。
(1)x-loader(MLO)是一级引导程序,系统上电后由 CPU 内部 firmware 自动拷贝到 内部 RAM 并执行。
主要作用为初始化 CPU,拷贝 u-boot 到内存中,然后把控制权交给 u-boot; (2)u-boot 是二级引导程序,主要用于和用户进行交互,提供映像更新、引导内核等 功能; (3)内核启动。
精品文档精品文档 fig 31.3 网络式保护 dtu 的启动过程(计划中)网络式保护 dtu 的启动过程如 fig 4 所示。
(1) 系统上电后,在内部 ram 中启动一级引导程序 x-loader; (2) 拷贝 uboot 到内存中,在内存中启动; (3) 拷贝内核到内存中,在内存中启动。
fig 4 Nand 布局如 fig5 所示。
fig 5 精品文档精品文档2 开发环境的搭建 2.1 windows 下开发板的使用和程序恢复过程2.1.1 windows 下开发板的使用方法(1)通过小 USB 接口连接比格尔开发板; (2)下载开发板驱动 BONE_DRV.exe,并安装; (3)通过串口工具,可以看到开发运行过程,如 fig 6 所示。
linux2.6内核启动分析--李枝果(不看是你的损失^_^)
S h e n z h e n F a. lizhiguo0532@ 2010-6-041Linux 2.6lizhiguo0532@ 2010-6-04----------------------------------------------------------------------------------------------------------------------/sz_farsight---------------------------------------------------------------------------------------------------------------------- ^_^SDMakefile uImageMakefile uImage *.o1. arm-linux-gnu-ld arch/arm/kernel/vmlinux.ldsarch/arm/kernel/head.o arch/arm/kernel/init_task.oS h e nz h e n F a r silizhiguo0532@ 2010-6-042 vmlinux.lds2. 3. 4. piggy.gz5.S h e ns i gh t In c . lizhiguo0532@ 2010-6-043piggy.gz piggy.o ld6. arm-linux-gnu-ld arch/arm/boot/compressed/piggy.o27 *(.piggydata) piggydata piggydata Image piggy.gzvmlinux.ldsS h e n zc . lizhiguo0532@ 2010-6-0447.8.uboot arch/arm/boot/compressed/piggy.gz- arch/arm/boot/compressed/piggy.o 0xc0008000 (arch/arm/boot/compressed/vmlinux- arch/arm/boot/zImage) 0x0 0x00000000 0x0 0x30008000 Image vmlinux 0xc0008000S h e n z h e nF a r s i g h t In c . lizhiguo0532@ 2010-6-0450x0 arch/arm/boot/compressed/head.s misc.c 1. uboot thekernelr0—>r8,r1- r7.2. LC00x0 0x300080003. 0x00x30008000 CONFIG_ZBOOT_ROM r2, r3 r5, r6, ip, sp r6 ip got4. clear bss5. cache 4K.align.section ".stack", "w"user_stack: .space 4096S h eh tI nc.lizhiguo0532@ 2010-6-0466.Zreladdr vmlinuxarch/arm/mach-s3c2410/Makefile.bootarch/arm/boot/MakefileS h e nz h e n F a r si gh t I n c .lizhiguo0532@ 2010-6-047 arch/arm/boot/compressed/Makefile ZRELADDR vmlinux ImageuImage load zImage load uImage zImage uboot zImage load entryuImage zImage mkimage uImage -a data load -e entry arch/arm/boot/Makefile0x30008000S h e n z h e n F a r s i g h t In c . lizhiguo0532@ 2010-6-048 r4 Image 0x30008000 r5 zImage r2 zImager4>=r2, r4=0x30800000 Image r2=0x30008000+(zImage+bss size)+stack size 4K + malloc size 64K zImage Image 0x30800000r4+4M<r5, r5=0x30800000 r4=30008000 zImage r4r4+4M>r5, r4=r5=0x30008000 0x30008000@ r0 = malloc end or decompress space,@ r1 = sp end or malloc begin,@ r2 = malloc end ,@ r3 = architecture IDdecompress_kernelmalloc 0x300080007. decompress_kernel in arch/arm/boot/compressed/misc.cS h e nn F a r s i gh t I n c .lizhiguo0532@ 2010-6-049Gunzip() lib/inflate.c gunzip 8. 128add r0, r0, #127bic r0, r0, #127 @ align the kernel length9 head.S 0x30008000R1 128 r2 reloc_start r3 reloc_end head.Scache_clean_flush cache cache reloc_startzImage gdb9. reloc_startS h e n z h enF a r s i g h t I n c . lizhiguo0532@ 2010-6-0410* r0 = decompressed kernel length * r1-r3 = unused* r4 = kernel execution address* r5 = decompressed kernel start* r6 = processor ID* r7 = architecture ID* r8-r14 = unused0x30008000 cache r0 r1 pc 0x30008000 /node/3VMLINUX arch/arm/kernel/head.S init/Main.c 0x0 0xC0008000Mmu I Cache D Cache r0=0 r1=architecture ID arch/arm/kernel/vmlinux.lds stext1. SVC FIR IRQ2. __lookup_processor_type cp15 cpuid .init proc_info_list cpu3. __lookup_machine_type uboot machinearchitecture number .init machine number machine_descS h e n z h n F a r si g h t I n c .lizhiguo0532@ 2010-6-0411 ……………………arch-arm-kernel-head.Sarch-arm-kernel-head/node/4start_kernel in init/Main.c1. printk(linux_banner)2. a. setup_processor()proc_info_list list cpu_name cpuname idproc_arch system_utsname= list->arch_name armv4telf_platform= list->elf_name v4 elf_hwcap = list->elf_hwcap;/* 1|2|4 */ cpu_proc_init()Cpu-single.h#define cpu_proc_init __cpu_fn(CPU_NAME,_proc_init)#define __cpu_fn(name,x) __catify_fn(name,x)#define __catify_fn(name,x) name##xCPU_NAME = cpu_arm920cpu_proc_init(); cpu_arm920_proc_init()proc_arm920.S b. mdesc = setup_machine(machine_arch_type).init machine_desclist list-namec. machine_name = mdesc->name machine_named. tags = phys_to_virt(mdesc->boot_params)uboot 0x300001000xc0000100e.if (tags->hdr.tag == ATAG_CORE) {if (meminfo.nr_banks != 0) /* meminfo defined in setup.c */squash_mem_tags(tags);parse_tags(tags);}static struct meminfo meminfo __initdata = { 0, };in steup.cparse_tags(tags) in steup.cS h e n z h e n F a r s i g h t In clizhiguo0532@ 2010-6-0412Steup_arch()- parse_tags()- parse_tag(), all function in steup.c __tagtable_begin, __tagtable_end arch/arm/kernel/vmlinux.ldsparse_tag()parseIgnoring unrecognised tag 0x%08x\n ubootf. struct mm_struct init_mm = INIT_MM(init_mm);init_mm.start_code = (unsigned long) &_text;init_mm.end_code = (unsigned long) &_etext;init_mm.end_data = (unsigned long) &_edata;init_mm.brk = (unsigned long) &_end;_text, _etext, _edata, _end arch/arm/kernel/vmlinux.ldsg. parse_cmdline(cmdline_p, from) uboot commond_linefrom command_linecommand_linestart_kernerl mem initrdstart_kernel()->parse_option() mem initrdcommand_line *cmdline_pstart_kernelmem initrdh. paging_init(&meminfo, mdesc);/**/ in arch/arm/mm/init.cmemtable_init(mi)/bbstcon,board,Embedded,reid,1165977462.html0xffff0000mdesc->map_io() smdk2410arch/arm/mach-s3c2410/mach-smdk2410.c Linux , smdk2410_map_io() 1 iotable_init(s3c_iodesc, GPIO,IRQ,MEMCTRL,UARTS h e nz h e n F a r s i gh t In c . lizhiguo0532@ 2010-6-0413 2 (cpu->map_io)(mach_desc, size) LCD, map.h S3C2410_ADDR(x) ((void __iomem *)0xF0000000 + (x)) IO 0xF0000000 1M 1. GPIO IRQ UART MEMCRTL WATCHDOG USB 2. kmalloc 0x30008000 phys_to_virt virt_to_ phys 3. mmu TTB 0x30004000 16K mmu arch/arm/kernel/head.S 4M mmu sector Uarth.request_standard_resources(&meminfo, mdesc) memory kernel_text kernel_data video_ram new new resource NULLi. cpu_init() in arch/arm/kernel/setup.ccpu cpu id cache IRQ ABT UND stack 12 svci. __mach_desc_SMDK2410_typeinit_arch_irq init_machine system_timer3. sched_init()init_idle (current, smp_processor_id()) idle4. preempt_disable()5. (zone) build_all_zonelists()6. printk(KERN_NOTICE "Kernel command line: %s\n", saved_command_line); uboot command_line setup_arch7. parse_early_param()S h e n z h e n F a r s i g h t In c . lizhiguo0532@ 2010-6-0414 __setup setup_arch &command_line command_linesaved_command_line8. parse_args("Booting kernel", command_line, __start___param,__stop___param - __start___param,&unknown_bootoption);parse_early_param(); setup paramcommand_line setup_arch__start___param param System.map__stop___param - __start___paramunknown_bootoption paramparse_one()parse_one() param __setupLinux bootargs9. sort_main_extable()__start___ex_table __stop___ex_table *(__ex_table) struct exception_table_entry insn10. setup_arch- paging_init- memtable_initinit_maps, alloc_bootmem_low_pages ARM 0xFFFF0000 0xFFFF0000trap_init .Lcvectors 0xffff0000 __stubs_start __stubs_end 0xffff0200 0xffff0500 0xffff0000 cache DOMAIN_USER DOMAIN_MANAGER DOMAIN_CLIENT11.rcu_init() cpu struct rcu_dataper_cpu_rcu_data per_cpu_rcu_bh_data.12.init_IRQstruct irqdesc irq_desc[NR_IRQS], irq_desc[n] bad_irq_desc pendS h e nz h e n F a r s i gh t I n c . lizhiguo0532@ 2010-6-0415 init_arch_irq setup_arch smdk2410_init_irq in mach-smdk2410.cs3c24xx_init_irq do_level_IRQ do_edge_IRQ __do_irquart ADC13.pidhash_init()pid_hash hash pidhash_shift pidhash_shift min 12 hash hash pid_hash[n](n=1~3), hash hash struct hlist_head first NULL14.init_timers()struct tvec_t_base_s per_cpu_tvec_bases, per_cpu_tvec_bases lock15.softirq_initS h e n z h e n F a r s i gh t I n c .lizhiguo0532@ 2010-6-041616.time_init() timersetup_archtime_init if s3c2410_timer_init timer417.console_initprintk log_bufa. tty_register_ldisc TTYtty ttyttytty ttyS h e n z h e n F ar s i g h t I n c . lizhiguo0532@ 2010-6-0417 ppp tty tty b. s3c24xx_serial_initconsole /*vmlinux.lds.S__con_initcall_start = .;*(.con_initcall.init)__con_initcall_end = .;con_initcall.initfn .con_initcall.init :#define console_initcall(fn) \static initcall_t __initcall_##fn \__attribute_used____attribute__((__section__(".con_initcall.init")))=fn*///:console_initcall(s3c24xx_serial_initconsole);//:start_kernel->console_init->s3c24xx_serial_initconsolecall = __con_initcall_start; /* console_initcall */while (call < __con_initcall_end) {(*call)();call++;}18.profile_init()/* *///profile// bootargs profile/*profile menuconfig profiling support1. profileprofile profile=1 profile=schedule 12. /proc/profile readprofilereadprofile -m /proc/kallsyms | sort -nr > ~/cur_profile.log,readprofile -r -m /proc/kallsyms |sort -nr,readprofile -r && sleep 1 && readprofile -m /proc/kallsymsS h e n z h e n F a r s i g h t In c . lizhiguo0532@ 2010-6-0418|sort -nr >~/cur_profile.log 3. /proc/profile profile profile=?profile=schedule ? schedule schedule*/19.local_irq_enable()IRQ20.mem_init()alloc_bootmem(),alloc_bootmem_low(),alloc_bootmem_pages()21.kmem_cache_init()slab22. numa_policy_init();if (late_time_init)late_time_init();calibrate_delay();// BogMIPS23. pidmap_init();pgtable_cache_init();prio_tree_init();/*index_bits_to_maxindex[BITS_PER_LONG]index_bits_to_maxindex[n] -1index_bits_to_maxindex[BITS_PER_LONG-1] ~0UL*/24 anon_vma_init();/*kmem_cache_creat() struct anon_vmakmem_cache_t anon_vma ,void anon_vma_ctor NULLkmem_cache_t anon_vma_chachepS h e n z h e n F a r s i g h tlizhiguo0532@ 2010-6-0419 */ 25. fork_init(num_physpages);/* */ 26. proc_caches_init();buffer_init();/*kmem_cache_create("buffer_head",sizeof(struct buffer_head), 0,SLAB_RECLAIM_ACCOUNT|SLAB_PANIC, init_buffer_head, NULL) struct buffer_head kmem_cache_t */27. security_init();/* */28. vfs_caches_init(num_physpages);radix_tree_init();signals_init();kmem_cache_create("sigqueue",sizeof(struct sigqueue),__alignof__(struct sigqueue),SLAB_PANIC, NULL, NULL) struct sigqueue kmem_cache_t sigqueue cache line kmem_cache_t sihqueue_cachep.29. page_writeback_init()buffer_pages.30. proc_root_init();/* proc CONFIG_PROC_FS */31. check_bugs();/* arm */32 rest_init()initlinux :0 rest_init()a. in arch/arm/kernel/process.cinitb. schedule() idle schedulec. cpu_idle()0S h e n z h e n F a r s i g h t In c . lizhiguo0532@ 2010-6-0420 init in main.c a. lock_kernel() lock b. smpc. populate_rootfs() initcallsd. do_basic_setup()#define module_init(x) __initcall(x);#define __initcall(fn) device_initcall(fn)#define core_initcall(fn) __define_initcall("1",fn)#define postcore_initcall(fn) __define_initcall("2",fn) #define arch_initcall(fn) __define_initcall("3",fn) #define subsys_initcall(fn) __define_initcall("4",fn) #define fs_initcall(fn) __define_initcall("5",fn) #define device_initcall(fn) __define_initcall("6",fn) #define late_initcall(fn) __define_initcall("7",fn)#define __define_initcall(level,fn) \static initcall_t __initcall_##fn __attribute_used__ \__attribute__((__section__(".initcall" level ".init"))) = fn include/linux/init.harch_initcall initcall module_init init 1 do_basic_setup do_initcalls()S h e n z h e n F a r s i gh t I n c . lizhiguo0532@ 2010-6-0421 Start_kernel * -- rest_init()* -- kernel_thread()* init 1 -- Init* -- populate_rootfs() -- do_basic_setup()* initcall modlue_init -- init_workqueues() -- usermodehelper_init() khelper-- driver_init()-- sysctl_init()-- sock_init() socket-- do_initcalls()*-- (*call)()* initcall-- prepare_namespace()-- name_to_dev_t()root bootargs root=/dev/** root=31:03 /dev/mtdblock3 root /dev/ 31-- mount_root();/* */-- free_initmem() init:Freeing init memory: 116K-- sys_open() and sys_dup(0) 0 1 2-- run_init_process(execute_command) init=/initrd command_line-- cpu_idle() 0init 1 …… run_init_process("/sbin/init");run_init_process("/etc/init");run_init_process("/bin/init");run_init_process("/bin/sh");……S h n n F a r s i g h t In c . lizhiguo0532@ 2010-6-0422 ARM Linux -- -PXA255--- Linux bootargs/u3/99423/article.html/bbstcon,board,Embedded,reid,1165977462.html/node/4。
CentOS系列--linux启动顺序
系统加电之后,首先进行的硬件自检,然后是bootloader对系统的初始化,加载内核。
内核被加载到内存中之后,就开始执行了。
一旦内核启动运行,对硬件的检测就会决定需要对哪些设备驱动程序进行初始化。
从这里开始,内核就能够挂装根文件系统(这个过程类似于Windows识别并存取C盘的过程)。
内核挂装了根文件系统,并已初始化所有的设备驱动程序和数据结构等之后,就通过启动一个叫init的用户级程序,完成引导进程。
系统首先启动init进程,该进程先会执行/etc/rc.d/rc.sysinit,然后去读/etc/inittab文件决定运行模式,根据默认运行模式读取/etc/rc.d/rc $RUNLEVEL文件夹下的执行程序连接,这些文件以K或S开头,它们都是指向init.d/的一些软连接。
Init进程是系统启动之后的第一个用户进程,所以它的pid(进程编号)始终为1。
init进程上来首先做的事是去读取/etc/目录下inittab文件中initdefault id值,这个值称为运行级别(run-level)。
它决定了系统启动之后运行于什么级别。
运行级别决定了系统启动的绝大部分行为和目的。
这个级别从0到6 ,具有不同的功能。
不同的运行级定义如下:# 0 - 停机(千万别把initdefault设置为0,否则系统永远无法启动)# 1 - 单用户模式# 2 - 多用户,没有NFS# 3 - 完全多用户模式(标准的运行级)# 4 –系统保留的# 5 - X11 (x window)# 6 - 重新启动(千万不要把initdefault 设置为6,否则将一直在重启)rc.d的内容如下:init.d/ :各种服务器和程序的二进制文件存放目录。
rc $RUNLEVEL.d/: 各个启动级别的执行程序连接目录。
里头的东西都是指向init.d/的一些软连接。
具体的后边叙述。
还有三个脚本:rc.sysinit, rc, rc.local如图:redhat的启动方式和执行次序是:加载内核执行init程序/etc/rc.d/rc.sysinit # 由init执行的第一个脚本/etc/rc.d/rc $RUNLEVEL # $RUNLEVEL为缺省的运行模式/etc/rc.d/rc.local/sbin/mingetty # 等待用户登录在Redhat中,/etc/rc.d/rc.sysinit主要做在各个运行模式中相同的初始化工作,包括:调入keymap以及系统字体启动swapping设置主机名设置NIS域名检查(fsck)并mount文件系统打开quota装载声卡模块设置系统时钟等等。
linux启动流程
centos6启动流程•硬件启动阶段o Power on打开电源o BIOS▪POST: power on self test开机自检•初始化硬件设备,检测系统外围主要设备:cpu,memory,硬盘、显卡等▪确定启动设备•启动设备:硬盘、网络、U盘、光盘•如果启动设备是硬盘,则读取硬盘第一个扇区MBR,512字节•控制权交给bootloadero MBR▪MBR512字节• 1.446字节bootloadero启动加载器bootloader▪windows: ntloader仅仅启动os▪GRUB: GRand Unified Bootloader,CentOS 6 GRUB 0.97: GRUBLegacy, CentOS 7 以后使用GRUB 2.02• 2.64字节分区信息o16字节x4 partitions• 3.55aa标志位表示分区是否有效•grub启动阶段o stage1▪ 1.446字节bootloader•这是二进制的0101,写在MBR扇区的前446字节。
o单纯为了找到1.5阶段生成的bootloader上o stage1.5▪第一阶段446字节的bootloader无法存放内核位置,/boot/grub/grub.config里面有内核位置,但是/boot的文件系统需要加载,即想办法识别/boot的文件系统来读取config文件加载内核。
MBRbootloader太小无法容下文件系统的驱动代码,而且文件系统的类型有很多种,比如ext2 xfsext4 fat32等,MBRbootloader无法容下这么多种文件系统的驱动,所以,只能提供一个中间的过度bootloader即stage1_5 bootloader▪ 1.5阶段是MBR后面的分区,grub-install 时候,会将/boot/grub所在文件系统类型对应的stage1_5硬编码到MBR扇区后第一个分区前15个扇区中,因此这段空间位于MBR分区后,第一个分区之前。
zynq linux操作基本流程
zynq linux操作基本流程下载温馨提示:该文档是我店铺精心编制而成,希望大家下载以后,能够帮助大家解决实际的问题。
文档下载后可定制随意修改,请根据实际需要进行相应的调整和使用,谢谢!并且,本店铺为大家提供各种各样类型的实用资料,如教育随笔、日记赏析、句子摘抄、古诗大全、经典美文、话题作文、工作总结、词语解析、文案摘录、其他资料等等,如想了解不同资料格式和写法,敬请关注!Download tips: This document is carefully compiled by theeditor.I hope that after you download them,they can help yousolve practical problems. The document can be customized andmodified after downloading,please adjust and use it according toactual needs, thank you!In addition, our shop provides you with various types ofpractical materials,such as educational essays, diaryappreciation,sentence excerpts,ancient poems,classic articles,topic composition,work summary,word parsing,copy excerpts,other materials and so on,want to know different data formats andwriting methods,please pay attention!Zynq SoC启动Linux操作系统的基本流程详解Zynq System-on-Chip (SoC) 是Xilinx公司的一款高度集成的器件,它结合了ARM处理器和可编程逻辑(FPGA)部分,广泛应用于嵌入式系统设计。
linux-mips启动分析
linux-mips启动分析(1)系统加电起动后,MIPS 处理器默认的程序入口是0xBFC00000,此地址在无缓存的KSEG1的地址区域内,对应的物理地址是0x1FC00000,即CPU从0x1FC00000开始取第一条指令,这个地址在硬件上已经确定为FLASH的位置,Bootloader将Linux 内核映像拷贝到RAM 中某个空闲地址处,然后一般有个内存移动操作,目的地址在arch/mips/Makefile 内指定:load-$(CONFIG_MIPS_PB1550) += 0xFFFFFFFF80100000,则最终bootloader定会将内核移到物理地址0x00100000 处。
上面Makefile 里指定的的load 地址,最后会被编译系统写入到arch/mips/kernel/vmlinux.lds 中:OUTPUT_ARCH(mips)ENTRY(kernel_entry)jiffies = jiffies_64;SECTIONS{. = 0xFFFFFFFF80100000;/* read-only */_text = .; /* Text and read-only data */.text : {*(.text)...这个文件最终会以参数-Xlinker --script -Xlinker vmlinux.lds 的形式传给gcc,并最终传给链接器ld 来控制其行为。
ld 会将 .text 节的地址链接到0xFFFFFFFF80100000 处。
关于内核ELF 文件的入口地址(Entry point),即bootloader 移动完内核后,直接跳转到的地址,由ld 写入ELF的头中,其会依次用下面的方法尝试设置入口点,当遇到成功时则停止:a. 命令行选项-e entryb. 脚本中的ENTRY(symbol)c. 如果有定义start 符号,则使用start符号(symbol)d. 如果存在 .text 节,则使用第一个字节的地址。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
很久以前分析的,一直在电脑的一个角落,今天发现贴出来和大家分享下。
由于是word直接粘过来的有点乱,敬请谅解!S3C2410 Linux 2.6.35.7启动分析(第一阶段) arm linux 内核生成过程1. 依据arch/arm/kernel/vmlinux.lds 生成linux内核源码根目录下的vmlinux,这个vmlinux属于未压缩,带调试信息、符号表的最初的内核,大小约23MB;命令:arm-linux-gnu-ld -o vmlinux -T arch/arm/kernel/vmlinux.ldsarch/arm/kernel/head.oinit/built-in.o--start-grouparch/arm/mach-s3c2410/built-in.okernel/built-in.omm/built-in.ofs/built-in.oipc/built-in.odrivers/built-in.onet/built-in.o--end-group .tmp_kallsyms2.o2. 将上面的vmlinux去除调试信息、注释、符号表等内容,生成arch/arm/boot/Image,这是不带多余信息的linux内核,Image的大小约3.2MB;命令:arm-linux-gnu-objcopy -O binary -S vmlinux arch/arm/boot/Image3.将 arch/arm/boot/Image 用gzip -9 压缩生成arch/arm/boot/compressed/piggy.gz大小约1.5MB;命令:gzip -f -9 < arch/arm/boot/compressed/../Image > arch/arm/boot/compressed/piggy.gz 4. 编译arch/arm/boot/compressed/piggy.S 生成arch/arm/boot/compressed/piggy.o大小约1.5MB,这里实际上是将piggy.gz通过piggy.S编译进piggy.o文件中。
而piggy.S文件仅有6行,只是包含了文件piggy.gz;命令:arm-linux-gnu-gcc -o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/piggy.S5. 依据arch/arm/boot/compressed/vmlinux.lds 将arch/arm/boot/compressed/目录下的文件head.o 、piggy.o 、misc.o链接生成arch/arm/boot/compressed/vmlinux,这个vmlinux是经过压缩且含有自解压代码的内核,大小约1.5MB;命令:arm-linux-gnu-ld zreladdr=0x30008000 params_phys=0x30000100 -T arch/arm/boot/compressed/vmlinux.lds a rch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/misc.o -o arch/arm/boot/compressed/vmlinux6.将arch/arm/boot/compressed/vmlinux去除调试信息、注释、符号表等内容,生成arch/arm/boot/zImage 大小约1.5MB;这已经是一个可以使用的linux内核映像文件了;命令:arm-linux-gnu-objcopy -O binary -S arch/arm/boot/compressed/vmlinux arch/arm/boot/zImage7. 将arch/arm/boot/zImage添加64Bytes的相关信息打包为arch/arm/boot/uImage大小约1.5MB;命令: ./mkimage -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -n 'Linux-2.6.35.7' -d arch/arm/ boot/zImage arch/arm/boot/uImage内核启动分析:本文着重分析S3C2410 linux-2.6.35.7内核启动的详细过程,主要包括: zImage 解压缩阶段、 vmlinux 启动汇编阶段、 startkernel 到创建第一个进程阶段三个部分,一般将其称为 linux 内核启动一、二、三阶段,本文也将采用这种表达方式。
对于 zImage 之前的启动过程,本文不做表述,可参考前面正亮讲得“ u-boot的启动过程分析”。
本文中涉及到的术语约定如下:基本内核映像:即内核编译过程中最终在内核源代码根目录下生成的 vmlinux 映像文件,并不包含任何内核解压缩和重定位代码;zImage 内核映像:包含了内核piggy.o及解压缩和重定位代码,通常是目标板 bootloader 加载的对象;zImage 下载地址:即 bootloader 将 zImage 下载到目标板内存的某个地址或者 nand read 将 zImage 读到内存的某个地址;zImage 加载地址:由 Linux 的 bootloader 完成的将 zImage 搬移到目标板内存的某个位置所对应的地址值,默认值 0x30008000 。
1、Linux 内核启动第一阶段:内核解压缩和重定位该阶段是从u-boot引导进入内核执行的第一阶段,我们知道u-boot引导内核启动的最后一步是:通过一个函数指针thekernel()带三个参数跳转到内核(zImage)入口点开始执行,此时,u-boot的任务已经完成,控制权完全交给内核(zImage)。
稍作解释,在u-boot的文件arch\arm\lib\bootm.c(uboot-2010.9)中定义了thekernel,并在do_bootm_linux的最后执行thekernel.定义如下:void (*theKernel)(int zero, int arch, uint params);theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);//hdr->ih_ep----Entry Point Address uImage 中指定的内核入口点,这里是0x30008000。
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);其中第二个参数为机器ID,第三参数为u-boot传递给内核参数存放在内存中的首地址,此处是0x30000100。
由上述zImage的生成过程我们可以知道,第一阶段运行的内核映像实际就是arch/arm/boot/compressed/vmlinux,而这一阶段所涉及的文件也只有三个:(1)arch/arm/boot/compressed/vmlinux.lds(2)arch/arm/boot/compressed/head.S(3)arch/arm/boot/compressed/misc.c下面的图是使用64MRAM时,通常的内存分布图:下面我们的分析集中在 arch/arm/boot/compressed/head.S, 适当参考 vmlinux.lds 。
从linux/arch/arm/boot/compressed/vmlinux.lds文件可以看出head.S的入口地址为ENTRY(_start),也就是head.S汇编文件的_start标号开始的第一条指令。
下面从head.S中得_start 标号开始分析。
(有些指令不影响初始化,暂时略去不分析)代码位置在/arch/arm/boot/compressed/head.S中:start:.type start,#function /*uboot跳转到内核后执行的第一条代码*/.rept 8 /*重复定义8次下面的指令,也就是空出中断向量表的位置*/mov r0, r0 /*就是nop指令*/.endrb 1f @ 跳转到后面的标号1处.word 0x016f2818 @ 辅助引导程序的幻数,用来判断镜像是否是zImage.word start @ 加载运行zImage的绝对地址,start表示赋的初值.word _edata @ zImage结尾地址,_edata是在vmlinux.lds.S中定义的,表示init,text,data三个段的结束位置1: mov r7, r1 @ save architecture ID 保存体系结构ID 用r1保存mov r8, r2 @ save atags pointer 保存r2寄存器参数列表,r0始终为0mrs r2, cpsr @ get current mode 得到当前模式tst r2, #3 @ not user?,tst实际上是相与,判断是否处于用户模式bne not_angel @ 如果不是处于用户模式,就跳转到not_angel标号处/*如果是普通用户模式,则通过软中断进入超级用户权限模式*/mov r0, #0x17 @ angel_SWIreason_EnterSVC,向SWI中传递参数swi 0x123456 @ angel_SWI_ARM这个是让用户空间进入SVC空间not_angel: /*表示非用户模式,可以直接关闭中断*/mrs r2, cpsr @ turn off interrupts to 读出cpsr寄存器的值放到r2中orr r2, r2, #0xc0 @ prevent angel from running关闭中断msr cpsr_c, r2 @ 把r2的值从新写回到cpsr中/*读入地址表。
因为我们的代码可以在任何地址执行,也就是位置无关代码(PIC),所以我们需要加上一个偏移量。
下面有每一个列表项的具体意义。
LC0是表的首项,它本身就是在此head.s中定义的.type LC0, #objectLC0: .word LC0 @ r1 LC0表的起始位置.word __bss_start @ r2 bss段的起始地址在vmlinux.lds.S中定义.word _end @ r3 zImage(bss)连接的结束地址在vmlinux.lds.S中定义.word zreladdr @ r4 zImage的连接地址,我们在arch/arm/mach-s3c2410/makefile.boot中定义的.word _start @ r5 zImage的基地址,bootp/init.S中的_start函数,主要起传递参数作用.word _got_start @ r6 GOT(全局偏移表)起始地址,_got_start是在compressed/vmlinux.lds.in中定义的.word _got_end @ ip GOT结束地址.word user_stack+4096 @ sp 用户栈底 user_stack是紧跟在bss段的后面的,在compressed/vmlinux.lds.in中定义的@ 在本head.S的末尾定义了zImag的临时栈空间,在这里分配了4K的空间用来做堆栈。