bootstrap-xv6

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

代码阅读报告——bootstrap  一、源代码中重要函数或语句及关键技术的代码分析和注释  bootasm.S:  .code16                       //在实模式下运行的代码  .globl start    //设定为全局函数,可以从外部进行调用  start:     //首先执行的函数    cli      //使硬件不能进行中断                             xorw    %ax,%ax            //将ax寄存器初始化为零    movw    %ax,%ds  //初始化数据              seta20.1:    inb     $0x64,%al               //等待硬件空闲      movb  $0xd1, %al  //将0xd1输出到第0x64号I\O端口 seta20.2: //以上代码的作用是打开A20地址线.在默认的情况下,第20根地址线一直为0,这样做的目的是为了向下兼容早期的PC.由早期的PC仅仅只是在实模式下进行寻址,这样所可能理论上可以寻到的最大地址应该是0xFFF0+0xFFFF.这看上去超过了1MB的地址空间,然而因为早期的PC 只有20根地址线,于是相当于最高位的进位时被忽略了,地址最终还是在1MB以内.所以当PC 有了32根地址线并且能够在保护模式下寻址4G的地址空间后,为了向下兼容,在默认情况下将第20根地址线一直置零,这样就可以让仅在实模式下运行的程序不会出现最高为的进位,相当于还是只有20根地址线在起作用.    lgdt    gdtdesc        //将GDT 表的首地址加载到GDTR    movl    %cr0, %eax      //将cr0寄存器的最低位置置1,    orl     $CR0_PE, %eax      //标志进入保护模式    movl    %eax, %cr0      //   ljmp    $(SEG_KCODE3), $start32  //跳转到32位模式,并执行32位模式的代码 .code32                       //32位模式的代码  start32:     //32位函数     call    bootmain  //调用main.c中的bootmain spin:     //无限循环,但是理论上不会返回.    jmp     spin  .p2align 2                                //GDT表4字节对齐 gdt:        //定义GDT表 bootmain.c:  #define SECTSIZE  512  //每个磁盘片为512字节  void readseg(uchar*, uint, uint);  //函数声明  void bootmain(void) {   struct elfhdr *elf;       //指向elf的指针    elf = (struct elfhdr*)0x10000;     // 指向地址0x10000   readseg((uchar*)elf, 4096, 0);    //首先读入4096字节,确保全部都读进去   if(elf‐magic != ELF_MAGIC)     return;          // 通过查看magic来确定elf是否正确组织.   ph = (struct proghdr*)((uchar*)elf + elf‐phoff);  //指针指向程序头表的首地址   eph = ph + elf‐phnum;     //明确文件段的个数   for(; ph  eph; ph++) {     va = (uchar*)(ph‐va & 0xFFFFFF);   //由虚拟地址映射为物理地址     readseg(va, ph‐filesz, ph‐offset);   //将文件的每一段读入内存中相应的位置     if(ph‐memsz  ph‐filesz)       stosb(va + ph‐filesz, 0, ph‐memsz – ph‐filesz);   //如果memsz   }        //的大小大于fiesz,则将多余地址均赋为0      entry = (void(*)(void))(elf‐entry & 0xFFFFFF); //将内核加载到内存中后转移      //到内核入口转移到内核入口地址处执行,并且不会再返回.   entry(); }  void waitdisk(void) {   while((inb(0x1F7) & 0xC0) != 0x40)  //等待硬盘空闲,否则循环.     ; }  void readsect(void *dst, uint offset)    //读入一个磁盘分区 {   outb(0x1F2, 1);   // count = 1    //把1输出到端口0x1F2   outb(0x1F3, offset);      //将offset输出到0x1F3   outb(0x1F4, offset  8);        outb(0x1F5, offset  16);   outb(0x1F6, (offset  24) | 0xE0);   outb(0x1F7, 0x20);         // 以上为将参数输出到端口   waitdisk(); } void readseg(uchar* va, uint count, uint offset)   //从硬盘读文件 {   uchar* eva;    eva = va + count;      //找到内存中加载地址的最末端   va ‐= offset % SECTSIZE;    //将链接地址转换成加载地址   offset = (offset / SECTSIZE) + 1;   //将在硬盘中的偏移字节由字节数换成扇区数,      //由于内核可执行程序是从磁盘的第二扇区开始存储的,所以要加1   for(; va  eva; va += SECTSIZE, offset++)     readsect(va, offset);     //一扇区一扇区的读取文件 二、操作系统启动引导的流程分析 1.PC启动时,首先进入的是实模式,并且开始执行位于地址0xFFFF0处得代码,也就是BIOS的起始位置的代码。 2.BIOS先进行一系列的系统自检,然后初始化位于地址0的中断向量表。最后BIOS将启动盘的第一个扇区装入到0x7C00,并开始执行此处的代码。 3.先初始化寄存器值,然后打开A20地址线。转换为保护模式,跳转执行32位代码。 4.启动保护模式数据段寄存器,初始值化寄存器。,调用bootmain函数。 5.建立elf指针,指向0x10000,读入指向地址后的4096个字节。并验证elf是否组织良好。 6.将elf中的内核文件从磁盘中读入到内存。 7.将控制权交给内核。 三、 简答题目的简要回答 (1)仔细阅读Makefile,分析xv6.img 是如何一步一步生成的。1.先生成bio.o console.o    exec.o file.o  fs.o   ide.o   ioapic.o  kalloc.o  kbd.o   lapic.o main.o    mp.o  picirq.o pipe.o   proc.o spinlock.o   string.o   swtch.o syscall.o sysfile.o sysproc.o    timer.o trapasm.o     trap.o uart.o    vectors.o vm.o 2.定义TOOLPREFIX,并判断是否存在,如果存在侧在正确的端口中输出,不存在在错误的端口中输出。 3.定义QEMU,并判断是否存在,如果存在侧在正确的端口中输出,不存在在错误的端口中输出。 4.编译bootmain.c生成bootmain.o,编译bootasm.S生成bootmain.o,链接bootmain.o ,bootasm.S生成bootblock.o,并将程序入口函数定位start,令start的内存地址为0x7C00。将bootblock.o中的内容复制到bootblock中,并且声明不复制重分配和符号信息。 5.编译bootother.S生成bootother.o,链接bootother.o生成bootother.out,并将程序入口函数定位start,令start的内存地址为0x7C00。将bootother.out中的内容复制到bootother中,并且声明不复制重分配和符号信息。 6.编译initcode.S生成initcode.o,链接initcode.o生成initcode.out,并将程序入口函数定位start,令start的内存地址为0。将initcode.out中的内容复制到initcode中,并且声明不复制重分配和符号信息。 7.生成tags,vectors.S,通过规则生成ulib.o usys.o printf.o umalloc.o,通过规则生成_forktest。 8.通过mkfs.c  fs.h生成mkfs。 9.将/dev/zero中的1000字节输出到xv6.img,将bootblock不间断的输出到xv6.img,将kernel从磁盘1不间断的输出到xv6.img,生成xv6.img。 (2)xv6如何做准备(建立GDT表等进入保护模式的)  1.  初始化寄存器数据  2. 建立GDT表。  3. 将cr0寄存器的最低位置置1,标识进入保护模式  4. 跳转执行32位代码,进入保护模式。 (3)引导程序如何读取硬盘扇区的?又是如何加载ELF 格式的OS的?1.确定读的文件数,文件首地址和偏置后,通过 readseg(),readsect()函数读取硬盘扇区。具体实现,见第一部分的readseg()和readsect()函数的分析。 2. 通过定义elf型的指针,指向0x10000.加载前4096个字节,并判断magic是否组织良好,这样来加载elf的OS。 四、对本部分原理和技术的阅后心得 本部分旨在将控制权交给内核之前的引导工作。读后,对操作系统内核是怎样被加载到内存中的有了一定了解。看见了软件与硬件结合的部分,对代码的运行机制和软件与硬件的联系有了一定的了解。感觉,想要彻底的了解操作系统,还需要部分的硬件知识,感觉以后应该多修修硬件课,这样可能会对以后的软件编写有大大的益处。 

1 / 4
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功