Linux启动过程分析

上传人:san****019 文档编号:16508562 上传时间:2020-10-04 格式:PPT 页数:47 大小:298.31KB
返回 下载 相关 举报
Linux启动过程分析_第1页
第1页 / 共47页
Linux启动过程分析_第2页
第2页 / 共47页
Linux启动过程分析_第3页
第3页 / 共47页
点击查看更多>>
资源描述
Linux内核分析与实例精讲,大连理工大学软件学院 邱铁 综合楼413,Tel:0411-87571632 E_mail:,第2.2章 Linux的启动,源代码简介 启动代码简介 Linux内核代码组成分析 Linux的启动层次 Linux的启动分析,Linux系统的引导过程主要引导阶段,bootload加载阶段 LINUX启动阶段,在嵌入式系统中,一般的环境初始化都是在bootload中完成的。由bootload完成基本硬件环境的初始化之后,会将kernel image加载到一个区域. 在x86中,开机之后的环境初始化是由bios提供的功能来完成的.然后跳转到活动分区对应的引导程序。,首先来看kernel image : Linux的系统映像其实是一个引导层加上kernel代码映像构成.不妨去查看一下关于make bzimage的过程.它是通过linux-2.6.25/arch/x86/boot/tools/build.c生成的build工具,由linux-2.6.25/arch/x86/boot/head.s生成的文件将kenel压缩链接在一起。,启动过程要从head.s部分跳转到kernel code部份,因此需要将kernel code加载到一个固定的地址.对于压缩的kernel.会加载到0 x1000.对于完成的kernel.会将其加载到0 x100000.,对于head.S生成之后的文件,包含自带的一段启动程序和一段初始化代码. 启动程序段位于head.S生成文件的前512 B的bootload会跳转到加载位置的512偏移处开始执行. head.S的链接脚本内容如下: linux-2.6.25/arch/x86/boot/setup.ld 14 .bsdata : *(.bsdata) 15 16 . = 497; 17 .header : *(.header) 18 .inittext : *(.inittext) ,linux-2.6.25/arch/x86/boot/header.S .section .header, a .globlhdr hdr: setup_sects:.byte SETUPSECTS root_flags:.word ROOT_RDONLY ,#下面的代码是初始化hdr的成员.程序的执行流程会在_start通过jump的机器码跳转出去 hdr: setup_sects: .byte SETUPSECTS root_flags: .word ROOT_RDONLY syssize: .long SYSSIZE ram_size: .word RAMDISK vid_mode: .word SVGA_MODE root_dev: .word ROOT_DEV boot_flag: .word 0 xAA55,# offset 512, entry point #这里是偏移512字节的地方。bootload加载kernel之后的入口 .globl _start _start: #这里实际上是jmp的操作码. .byte 0 xeb # short (2-byte) jump .byte start_of_setup-1f ,.header的偏移是在497处.那512的偏移刚好到了_start.这也就是从bootload跳转进来的入口. .hdr中存放的就是引导的各项参数.对应着一个struct setup_header 结构 ,_start通过jmp的操作码跳转到了start_of_setup.,#跳转后的入口 start_of_setup: #如果定义了SAFE_RESET_DISK_CONTROLLER 重启磁盘控制器 #ifdef SAFE_RESET_DISK_CONTROLLER # Reset the disk controller. movw $0 x0000, %ax # Reset disk controller movb $0 x80, %dl # All disks int $0 x13 #endif,#设置es寄存器值为ds的内容 # Force %es = %ds movw %ds, %ax movw %ax, %es cld #为了调用高级语言作准备,接下来要call c fuction.先设置好堆栈 # Apparently some ancient versions of LILO invoked the kernel with %ss != %ds, # which happened to work by accident for the old code. Recalculate the stack # pointer if %ss is invalid. Otherwise leave it alone, LOADLIN sets up the # stack behind its own code, so we cant blindly put it directly past the heap. movw %ss, %dx cmpw %ax, %dx # %ds = %ss? movw %sp, %dx ,# We will have entered with %cs = %ds+0 x20, normalize %cs so # it is on par with the other segments. pushw %ds pushw $6f lretw 6: #判断setup_sig 与$0 x5a5aaa55是否相等,在link的时候,会将setup_sig设为$0 x5a5aaa55 # Check signature at end of setup cmpl $0 x5a5aaa55, setup_sig jne setup_bad,#清空BSS # Zero the bss movw $_bss_start, %di movw $_end+3, %cx xorl %eax, %eax subw %di, %cx shrw $2, %cx rep; stosl #跳转到main # Jump to C code (should not return) calll main,设置好了堆栈之后,call main,跳转到了用C写的函数里.在这个函数里会初始化一部份硬件环境. Main的代码如下: linux-2.6.25/arch/x86/boot/main.c void main(void) /* First, copy the boot header into the zeropage */ copy_boot_params(); /* End of heap check */ init_heap();,/* Make sure we have all the proper CPU support */ /验证CPU 是否有效 if (validate_cpu() puts(Unable to boot - please use a kernel appropriate for your CPU.n); die(); /* Tell the BIOS what CPU mode we intend to run in. */ /设置CPU的工作模式 set_bios_mode();,/* Detect memory layout */ /调用int 0 x15 向bios 了解当前的内存布局 detect_memory(); /* Set keyboard repeat rate (why?) */ keyboard_set_repeat(); /* Query MCA information */ /检查IBM 微通道总线 query_mca(); ,/* Set the video mode */ set_video(); /* Do the last things and invoke protected mode */ /通过这个跳转到保护模式了 go_to_protected_mode(); 在copy_boot_params()中,会将hdr的值copy到一个全局变量boot_params中. static void copy_boot_params(void) memcpy( ,在detect_memory()中会调用0 x15完成对内存的初步探测.并将其保存在boot_params.e820_map Main()最终会调用go_to_protected_mode(). 这个函数会将其转换到保护模式. void go_to_protected_mode(void) /* Hook before leaving real mode, also disables interrupts */ /禁用中断 realmode_switch_hook();,/* Move the kernel/setup to their final resting places */ /移动kernel到0 x10000 move_kernel_around(); /* Enable the A20 gate */ /置位键盘的a20 引脚 if (enable_a20() puts(A20 gate not responding, unable to boot.n); die(); ,/* Reset coprocessor (IGNNE#) */ /重置协处理器 reset_coprocessor(); /* Mask all interrupts in the PIC */ /在pic中屏弊掉所有中断 mask_all_interrupts(); /* Actual transition to protected mode. */ /建立临时的idt 和gdt 并将IDT清空 setup_idt(); setup_gdt();,/跳转到内核的起点处即header.S /对于压缩的kernel来说,这里还是从0 x1000处运行,并没有跳转到转移中运行 protected_mode_jump(boot_params.hdr.code32_start, (u32) ,调用setup_idt()和setup_gdt()建立一个临时的IDT和GDT.代码如下: static void setup_idt(void) static const struct gdt_ptr null_idt = 0, 0; asm volatile(lidtl %0 : : m (null_idt); 可以看到,这个临时的IDT是空的.,static void setup_gdt(void) /* There are machines which are known to not boot with the GDT being 8-byte unaligned. Intel recommends 16 byte alignment. */ static const u64 boot_gdt _attribute_(aligned(16) = /* CS: code, read/execute, 4 GB, base 0 */ GDT_ENTRY_BOOT_CS = GDT_ENTRY(0 xc09b, 0, 0 xfffff), /* DS: data, read/write, 4 GB, base 0 */ GDT_ENTRY_BOOT_DS = GDT_ENTRY(0 xc093, 0, 0 xfffff), /* TSS: 32-bit tss, 104 bytes, base 4096 */ /* We only have a TSS here to keep Intel VT happy; we dont actually use it for anything. */ GDT_ENTRY_BOOT_TSS = GDT_ENTRY(0 x0089, 4096, 103), ;,从实模式到保护模式的切换是在protected_mode_jump中完成的. linux-2.6.25/arch/x86/boot/ pmjump.S protected_mode_jump: #edx:存放第二个参数,即bootparams movl %edx, %esi # Pointer to boot_params table xorl %ebx, %ebx movw %cs, %bx shll $4, %ebx addl %ebx, 2f,#设置CX - _BOOT_DS , di - _BOOT_TSS movw $_BOOT_DS, %cx movw $_BOOT_TSS, %di #将CR0 的PE位置1. 开启了保护模式 movl %cr0, %edx orb $X86_CR0_PE, %dl # Protected mode movl %edx, %cr0 jmp 1f ,in_pm32: # Set up data segments for flat 32-bit mode #设置段寄存器 movl %ecx, %ds movl %ecx, %es movl %ecx, %fs movl %ecx, %gs movl %ecx, %ss # The 32-bit code sets up its own stack, but this way we do have a valid stack if some debugging hack wants to use it. addl %ebx, %esp,# Clear registers to allow for future extensions to the # 32-bit boot protocol #清除普通寄存器 xorl %ecx, %ecx xorl %edx, %edx xorl %ebx, %ebx xorl %ebp, %ebp xorl %edi, %edi # Set up LDTR to make Intel VT happy lldt %cx /跳转到指定的入口了 jmpl *%eax # Jump to the 32-bit entrypoint,protected_mode_jump函数是用寄存器来传值的,第一个参数放eax,第二个参数在edx中. 这个函数的两个参数如下示: protected_mode_jump(boot_params.hdr.code32_start, (u32),一个是转换到保护模式下要跳转到的地址,在压缩的情况下,这个值是0 x1000.末压缩情况下,这个值是0 x10000.另一个值是引导参数. 这个函数在将引导参数移到esi后,置位段寄存器,清空普通寄存器,然后主跳转到arch/boot/kernel/head_32.S 中,在分析这段代码之后,我们先来看下它的链接脚本: linux-2.6.25/arch/x86/kernel/vmlinux_32.lds.S #define LOAD_OFFSET _PAGE_OFFSET #include #include #include #include #include OUTPUT_FORMAT(elf32-i386, elf32-i386, elf32-i386) OUTPUT_ARCH(i386) ENTRY(phys_startup_32) jiffies = jiffies_64;,PHDRS text PT_LOAD FLAGS(5); /* R_E */ data PT_LOAD FLAGS(7); /* RWE */ note PT_NOTE FLAGS(0); /* _ */ SECTIONS . = LOAD_OFFSET + LOAD_PHYSICAL_ADDR; phys_startup_32 = startup_32 - LOAD_OFFSET; .text.head : AT(ADDR(.text.head) - LOAD_OFFSET) _text = .; /* Text and read-only data */ *(.text.head) :text = 0 x9090 ,所有的SECTIONS是从LOAD_OFFSET + LOAD_PHYSICAL_ADDR开始的. LOAD_OFFSET就是PAGE_OFFSET. LOAD_PHYSICAL_ADDR在没有压缩kernel的情况就是0 x100000.,arch/boot/kernel/head_32.S的代码: # 低端页面总数 132 / 112 LOW_PAGES = 1(32-PAGE_SHIFT_asm) /* * To preserve the DMA pool in PAGEALLOC kernels, well allocate * pagetables from above the 16MB DMA limit, so well have to set * up pagetables 16MB more (worst-case): */ #ifdef CONFIG_DEBUG_PAGEALLOC LOW_PAGES = LOW_PAGES + 0 x1000000 #endif,#if PTRS_PER_PMD 1 #PTD和PMD所占空间 PAGE_TABLE_SIZE = (LOW_PAGES / PTRS_PER_PMD) + PTRS_PER_PGD #else #PTD所占的页面数(每个PTE占一个页面) PAGE_TABLE_SIZE = (LOW_PAGES / PTRS_PER_PGD) #endif /用位来表示页面的数组大小 BOOTBITMAP_SIZE = LOW_PAGES / 8 ALLOCATOR_SLOP = 4,/总共所占空间的大小 INIT_MAP_BEYOND_END = BOOTBITMAP_SIZE + (PAGE_TABLE_SIZE + ALLOCATOR_SLOP)*PAGE_SIZE_asm 这部份计算页面位图与PTE,PMD所占空间.在这里之所以不要保存PGD所占空间是因为,PGD的区域是在kernel中链接的时候指定的,属于静态区域,2: /* * Clear BSS first so that there are no surprises. */ #清空BSS cld xorl %eax,%eax movl $pa(_bss_start),%edi movl $pa(_bss_stop),%ecx subl %edi,%ecx shrl $2,%ecx rep ; stosl,在这里重新设置GDT,清空BSS段 #esi中已经存放了boot_parms的值 movl $pa(boot_params),%edi movl $(PARAM_SIZE/4),%ecx cld rep,/将esi中的值copy到edi中,也就是boot_params对应的内存空间处 movsl movl pa(boot_params) + NEW_CL_POINTER,%esi andl %esi,%esi jz 1f # No comand line movl $pa(boot_command_line),%edi movl $(COMMAND_LINE_SIZE/4),%ecx rep movsl 将引导参数保存到boot_params.将command_line保存到boot_command_line,#_PAGE_OFFSET对应的页目录索引 page_pde_offset = (_PAGE_OFFSET 20); #pg0:临时的页表项。映射前面4M 内存空间大小 movl $pa(pg0), %edi movl $pa(swapper_pg_dir), %edx movl $PTE_ATTR, %eax 10: #edi中存放了pg0的地址。PDE_ATTR(%edi):会成生一个PDE项 leal PDE_ATTR(%edi),%ecx /* Create PDE entry */,#将生成的PDE设为PGD的第0项 movl %ecx,(%edx) /* Store identity PDE entry */ #将生成的PDE设为PGD的page_pde_offset项即0 x300 movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */ #即edx 指向pgd的第二项 addl $4,%edx #接下来就是设置pg0的值了. #设置循环次数 movl $1024, %ecx,11: #将eax- edi . edi存放的是pg0的地址 stosl addl $0 x1000,%eax #eax = eax +0 x1000 (0 x1000 = 4K) loop 11b #经过上面的循环之后,pg0中的内容依次被设置为:0 x007, 0 x1007,0 x2007.0 x3FF007 #这次从线性地址0开始的第一个PGD项和从_PAGE_OFFSET开始的第一个PGD都可以对前4M 进行寻址了,/* * End condition: we must map up to and including INIT_MAP_BEYOND_END * bytes beyond the end of our own page tables; the +0 x007 is * the attribute bits */ # 注意这里要一直映射到INIT_MAP_BEYOND_END leal (INIT_MAP_BEYOND_END+PTE_ATTR)(%edi),%ebp,#判断INIT_MAP_BEYOND_END是否有映射,如果没有映射关系,就跳转到10.建立映射关系 cmpl %ebp,%eax jb 10b #将最后的页表项存入init_pg_tables_end movl %edi,pa(init_pg_tables_end) 接下来:初始化映射区 、开启分页等等,/* Set up the stack pointer */ /建立内核态堆栈 lss stack_start,%esp 在这里将stack_start作为堆栈段,也就是对应系统第一个kernel进程 /建立idt call setup_idt ,之后跳转到start_kernel中, 第一阶段的启动完成 /跳转到start_kernel jmp start_kernel,
展开阅读全文
相关资源
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!