代码阅读报告1_涂李傲.docx_第1页
代码阅读报告1_涂李傲.docx_第2页
代码阅读报告1_涂李傲.docx_第3页
代码阅读报告1_涂李傲.docx_第4页
代码阅读报告1_涂李傲.docx_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

代码阅读报告(1)涂李傲 2010013234软01目录1:阅读内容2:代码阅读2.1asm.h2.2bootasm.s2.3x86.h 2.4elf.h2.5bootmain.c3:操作系统启动引导的流程分析4:简答题5:阅读心得1:阅读内容本次阅读包含以下几个文件:asm.h:是bootasm.s汇编文件所需要的头文件,主要是一些与X86保护模式的段访问方式相关的宏定义。bootasm.s:定义并实现了bootloader最先执行的函数start,此函数进行了一定的初始化,完成了从实模式到保护模式的转换,并调用bootmain.c中的bootmain函数。types.h:包含一些无符号整型的缩写定义。x86.h:一些用GNU C 嵌入式汇编实现的C 函数。elf.h:定义了ELF 文件的结构。bootmain.c:定义并实现了bootmain 函数。2:代码阅读2.1.asm.h该文件是bootasm.s汇编文件所需要的头文件,主要是一些段描述符的宏定义。(1)#define SEG_NULLASM .word 0, 0; .byte 0, 0, 0, 0代码功能:把段(长度为8字节)基地址和大小都置为0(感觉有点像null),然后在bootasm.h中的gdt: SEG_NULLASM # null seg调用了这个函数,构建了空段。(2)#define SEG_ASM(type,base,lim) .word (lim) 12) & 0xffff), (base) & 0xffff); .byte (base) 16) & 0xff), (0x90 | (type), (0xC0 | (lim) 28) & 0xf), (base) 24) & 0xff)代码功能:定义了长度为8字节的一般段。(3)#define STA_X 0x8 / Executable segment#define STA_E 0x4 / Expand down (non-executable segments)#define STA_C 0x4 / Conforming code segment (executable only)#define STA_W 0x2 / Writeable (non-executable segments)#define STA_R 0x2 / Readable (executable segments)#define STA_A 0x1 / Accessed定义了段的六种类型2.2.bootasm.s该文件保存于硬盘设备的第一个扇区中,在开机的时候由BIOS加载到物理内存的0x7C000x7CFF处,然后在”real mode”下开始执行,此时有%CS=0%IP=0x7C00(1)start: cli # Disable interrupts此处开始进入start函数,先发出一条指令“cli”,那么它的用处是关闭中断。这里我的理解是BIOS就像一个小的CPU,它自己会产生中断,但是这个时候BIOS已经运行完了,如果放任这些中断存在的话就会给硬件提供“interrupt handlers”,但是这个时候存在“interrupt handlers”是不安全的,所以我们要关闭中断,等到x86准备好了再重新恢复中断。(2)xorw %ax,%ax # Segment number zero movw %ax,%ds # - Data Segment movw %ax,%es # - Extra Segment movw %ax,%ss # - Stack Segment由于在BIOS中寄存器的值是不可知的,所以为了后面能够正常运行,在这里需要将寄存器进行初始化,这样代码就很直观了。就是先将寄存器%ax置为0,再将0值赋给%ds,%es和%ss(3)seta20.1: inb $0x64,%al # Wait for not busy testb $0x2,%al jnz seta20.1 movb $0xd1,%al # 0xd1 - port 0x64 outb %al,$0x64seta20.2: inb $0x64,%al # Wait for not busy testb $0x2,%al jnz seta20.2 movb $0xdf,%al # 0xdf - port 0x60 outb %al,$0x60根据x86相关文档,segment:offset模式可以表示21位的物理地址,但是 Intel 8088 只支持 20位内存地址,但是通过段-偏移方式可以支持21位内存地址。这样一来 Intel 8088 会将自动丢弃最高位的数据从而出现错误。IBM为了避免这个问题,于是提供了一种兼容方式:当键盘控制器输出端口的第 2位为低位时, A20始终清零,而当其置为高位时,A20可以正常使用。在这段代码中通过检测0x64寄存器中的第二位是否为1来判断键盘缓存中是否有数据。当键盘空闲(第二位为0时)的时候向0x64寄存器中写入0xd1(表示下一个写入 0x60 端口的数据将被置于 8042 键盘控制器的输出端口),向0x60寄存器中写入0xdf(将键盘控制端口的第二位置为1)打开A20地址线,这样就可以使用21位地址啦!(4)lgdt gdtdescmovl %cr0, %eaxorl $CR0_PE, %eaxmovl %eax, %cr0第一行的作用是将GDT的地址设置为gdtdesc。一旦装载了GDT 寄存器,扇区就会通过将CR0_PE 装入寄存器cr0开启“protected mode”。后面三行的作用则是置%cr0 寄存器的 PE 位。$CR0_PE=1,因此,这三行是置%cr0 寄存器的第 0 位为 1,该位为 0 时,CPU 运行于“real mode”,为 1 时,CPU 运行于“protected mode”。当第四行执行完成后,CPU 就运行在保护模式下了。(5)ljmp $(SEG_KCODE3), $start32这一行因为有很长的注释,所以显得很直观。由于在进入“protected mode”的时候不会让系统进入32位模式,所以设置这么一个触发事件,将程序从16位转换到32位模式下。(6)start32:# Set up the protected-mode data segment registersmovw $(SEG_KDATA DS: Data Segmentmovw %ax, %es # - ES: Extra Segmentmovw %ax, %ss # - SS: Stack Segmentmovw $0, %ax # Zero segments not ready for usemovw %ax, %fs # - FSmovw %ax, %gs # - GS这一块的作用和bootasm.s最前面一块的作用基本是类似的,就不多说了。(7)movl $start, %esp call bootmain将$start的地址(0x7C00)赋值给%esp寄存器,将0x7C00设置为栈顶,之后栈指针就会从 0x7c00 开始递减,而不会覆盖掉启动扇区的代码。而后面那一行就要开始调用bootmain函数了,这个函数到目前为止还没有出现,所以我会在后面详细说。(8)movw $0x8a00, %ax # 0x8a00 - port 0x8a00movw %ax, %dxoutw %ax, %dxmovw $0x8ae0, %ax # 0x8ae0 - port 0x8a00outw %ax, %dxpin:jmp spin 这段代码从boot.pdf上来看好像是用来处理错误的,一般情况下我们不会调用,而一旦调用就会出现死循环,这个时候有这么一句话:“A real boot sector might attempt to print an error message first”。估计这样的直接结果就是重新启动计算机吧好的,现在我们已经把bootasm.s给过了一遍,感觉总体上和boot.pdf讲的差不多,接下来我们来看看几个头文件2.3x86.h 该头文件包含了嵌入式汇编的C函数,供 C程序使用汇编指令。由于在第一次作业中不是所有定义过的函数都出现了,所以下面我只介绍在bootmain.c中被用到的函数;(1)static inline ucharinb(ushort port) uchar data; asm volatile(in %1,%0 : =a (data) : d (port); return data;实现了将从外部I/O 设备通过port端口的输入数据(1 Byte)读入到 data 之中的功能。同时函数的最后返回了data。(2)static inline voidoutb(ushort port, uchar data) asm volatile(out %0,%1 : : a (data), d (port);这个感觉和inb非常的像,就是将顺序反了一下,将 data 数据通过port端口写入外部I/O 设备。(3)static inline voidinsl(int port, void *addr, int cnt) asm volatile(cld; rep insl : =D (addr), =c (cnt) : d (port), 0 (addr), 1 (cnt) : memory, cc);从端口 port 中读取 4cnt 个字节到起始地址为 addr 的内存中。实现了类似字符串读入的操作。(4)static inline voidstosb(void *addr, int data, int cnt) asm volatile(cld; rep stosb : =D (addr), =c (cnt) : 0 (addr), 1 (cnt), a (data) : memory, cc);实现了将 data 数据重复cnt次写向addr所指向的地址。2.4.elf.h该文件定义了 ELF 文件的结构。ELF 文件格式是一个开放的标准各种 UNIX 系统的可执行文件都采用 ELF 格式,它有三种不同的类型: 可重定位的目标文件 可执行文件 共享库在 xv6 的 ELF 文件格式中,仅定义了可执行文件这一种类型;本文件定义了 elfhdr 和prohdr 结构体,下面进行解释;(1)struct elfhdr uint magic; /前文所述的ELFchar95 MAGICuchar elf12; /Magic Number和其他信息ushort type; /目标文件类型,包括上述三种类型及核心文件等ushort machine; /结构体系,如 386 和 SPARC 等uint version; /目标文件版本uint entry; /入口程序的虚拟地址uint phoff; /Program Header Table偏移量uint shoff; /Section Header Table偏移量uint flags; /和处理器相关的一组标志位ushort ehsize; /ELF头部的字节数ushort phentsize;/Program Header Table的尺寸,所有表的尺寸都相同ushort phnum;/Program Header Table的数量ushort shentsize; /Section Header Table的尺寸,所有表的尺寸都相同ushort shnum; /Section Header Table的数量ushort shstrndx; /字符串表在 Section header table 中的索引;(2)struct proghdr uint type; /段的类型,例如可被加载、动态等uint offset; /段相对文件的偏移量uint va; /段在内存中的虚拟地址uint pa; /某些系统使用物理地址,则在此存放段的物理地址uint filesz; /段在文件中的字节数uint memsz;/段在内存中的字节数uint flags; /与段有关的标志位,包括可执行、可读写等uint align; /指定段在内存中的对齐方式;2.5.bootmain.c前文中的bootasm.S已经设置好32位保护模式并调用了bootmain函数,这里我们详细介绍bootmain的具体实现。这段 C代码是加载 xv6 内核程序的一部分,保存在启动盘第一个扇区中。bootmain 函数在系统启动保护模式后被调用,用于加载 xv6 内核。下面进行详细介绍;(1)voidwaitdisk(void) / Wait for disk ready. while(inb(0x1F7) & 0xC0) != 0x40) ;由于bootmain()中需要用到readseg()函数,所以我先分析后面三个函数,最后来说bootmain()。这段函数的功能从名字可以非常直观的看出来是等待磁盘就绪的,那么它的具体实现是先调用inb()函数从0x1F7(0号硬盘状态寄存器放在这个里面)中读取数据。因为在0号硬盘状态寄存器中最高两位为01时表示硬盘就绪,所以inb(0x1F7) & 0xC0的作用就是获取寄存器最高两位并且和0x40进行比较,如果相等,说明就绪了,跳出循环,如果不相等,说明还没有就绪,就一直等待直到磁盘就绪为止。(2)voidreadsect(void *dst, uint offset) / Issue command. waitdisk(); outb(0x1F2, 1); / count = 1 outb(0x1F3, offset); outb(0x1F4, offset 8); outb(0x1F5, offset 16); outb(0x1F6, (offset 24) | 0xE0); outb(0x1F7, 0x20); / cmd 0x20 - read sectors / Read data. waitdisk(); insl(0x1F0, dst, SECTSIZE/4);该函数的作用是将偏移为 offset 的扇区读入 dst 指向的内存单元。步骤如下;1. waitdisk();等待磁盘就绪。2. outb(0x1F2, 1);向 0x1F2 端口写入 1,表示要读取 1 个扇区。3. outb(0x1F3, offset); 分别向 0x1F3, 0x1F4, 0x1F5 端口写入 offset 的 0-7, 8-15, 16-23 位,表示LBA 地址的低 24 位。4. outb(0x1F6, (offset 24) | 0xE0); 向 0x1F6 端口写入 offset 的 24-27 位,表示 LBA 地址的高 4 位(LBA 地址共 28 位,因此 offset 的高 4 位为 0), 并置高 4 位为 0xE0=1110b,表示使用 0 号驱动器并使用 LBA 模式。5. outb(0x1F7, 0x20);向 0x1F7 端口写入 0x20。0x1F7 处是命令寄存器,0x20 表示读取指定的扇区。现在磁盘会将所请求的数据读入,并以 32 位为单位存放在端口 0x1F0的寄存器中。6. insl(0x1F0, dst, SECTSIZE/4);等待磁盘就绪。然后调用 insl 函数从 0x1F0 端口读入一个扇区(4 SECTSIZE / 4 = 512 个字节)的内容至 dst 处。这样readset()函数就算分析完了。(3)voidreadseg(uchar* va, uint count, uint offset) uchar* eva; eva = va + count; va -= offset % SECTSIZE; offset = (offset / SECTSIZE) + 1; for(; va magic != ELF_MAGIC判断是否为ELF格式(是否正常加载),如果没有就报错。如果正常加载了,那么就开始读取elfhdr中的值。(通过ph获得程序的起始地址。)接下来通过eph获得程序中段的个数信息。接着是一个for循环将ELF中所有代码放入到va中。最后将入口地址放在entry中并且通过entry()函数跳转到入口。至此,引导程序完成引导工作,内核程序将要开始执行。3:操作系统启动引导的流程分析恩,在前面比较随意的介绍了各个函数的作用(不一定正确来着),接下来我系统的梳理一遍;(1) 通电后CPU执行物理地址 0xFFFFFFF0的跳转指令。让BIOS进行初始化。(2) 关闭中断,防止出现硬盘读取系统不安全情况。(3) 对寄存器初始化。(4) 打开A20地址线跳到“protected mode”。(5) 设置%gdtr 寄存器。(6) 设置其它段寄存器的值,进入32位模式。(7) 创建堆栈,栈顶设在 0x7c00 处。(8) 加载bootmain.c函数。(9) 加载ELF头文件(并判断是否正确加载)。(10) 跳转去执行内核程序。4:简答题4.1.阅读Makefile,分析xv6.img是如何一步一步生成的。通过 Makefile 中文件生成的层次关系可以总结为下图:首先是由头文件(.h)与汇编文件(.S)或者 C 语言文件(.c)编译生成目标文件(.o),然后对.o文件进行编译生成3个文件bootlock,kernel,fs.img,然后这3个文件执行link操作得到 xv6.img。4.2. xv6如何做准备(建立GDT 表等)并进入保护模式的。这个问题在前面的代码分析中其实已经说得很明白了,这里再重复一下;(1):关闭中断,打开 A20 地址线。(2):建立 GDT 表并通过将CR0_PE 装入寄存器cr0开启保护模式。(3):对 3个段寄存器初始化为 0x10。以使虚拟内存和物理内存匹配。(4):进入保护模式。4.3.引导程序如何读取硬盘扇区的?又是如何加载ELF格式的OS的?(1)引导程序读取硬盘扇区过程: (i)引导程序在 bootmain中调用redseg(

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论