ucoreLAB1实验报告范文.docx

上传人:b****5 文档编号:6337455 上传时间:2023-01-05 格式:DOCX 页数:7 大小:20.52KB
下载 相关 举报
ucoreLAB1实验报告范文.docx_第1页
第1页 / 共7页
ucoreLAB1实验报告范文.docx_第2页
第2页 / 共7页
ucoreLAB1实验报告范文.docx_第3页
第3页 / 共7页
ucoreLAB1实验报告范文.docx_第4页
第4页 / 共7页
ucoreLAB1实验报告范文.docx_第5页
第5页 / 共7页
点击查看更多>>
下载资源
资源描述

ucoreLAB1实验报告范文.docx

《ucoreLAB1实验报告范文.docx》由会员分享,可在线阅读,更多相关《ucoreLAB1实验报告范文.docx(7页珍藏版)》请在冰豆网上搜索。

ucoreLAB1实验报告范文.docx

ucoreLAB1实验报告范文

ucore-LAB1实验报告范文

实验目的:

操作系统是一个软件,也需要通过某种机制加载并运行它。

在这里我们将通过另外一个更加简单的软件-bootloader来完成这些工作。

为此,我们需要完成一个能够切换到某86的保护模式并显示字符的bootloader,为启动操作系统ucore做准备。

lab1提供了一个非常小的bootloader和ucoreOS,整个bootloader执行代码小于512个字节,这样才能放到硬盘的主引导扇区中。

通过分析和实现这个bootloader和ucoreOS,通过分析和实现这个bootloader和ucoreOS,我们可以了解到:

基于分段机制的存储管理设备管理的基本概念

PC启动bootloader的过程bootloader的文件组成

编译运行bootloader的过程调试bootloader的方法ucoreOS的启动过程

在汇编级了解栈的结构和处理过程中断处理机制

通过串口/并口/CGA输出字符的方法实验内容:

一.练习

练习1:

理解通过make生成执行文件的过程。

1.ucore.img是如何一步一步生成的?

(需要比较详细地解释Makefile中每一条相关命令和命令参数的含义,以及说明命令导致的结果)

答:

Makefile按照如下步骤生成ucore.img(lab1/bin下的ucore.img):

①为每一个源文件(.c和.S文件)产生一个描述其依赖关系的makefile文件,以.d为后缀。

即对于一个源文件“NAME.c”,对应的这个makefile文件为“NAME.d”。

包括分别生成ign.c、bootmain.c、bootam.S的makefile依赖文件ign.d、bootmain.d、bootam.d,具体执行的命令如下:

mkdir-pobj/ign/toolgcc-Itool/-g-Wall-O2-MMtool/ign.c-MT\obj/ign/tool/ign.dmkdir-pobj/bootgcc-Iboot/-fno-builtin-Wall-ggdb-m32-notdinc-fno-tack-protector-Ilib/-O-notdinc-MMboot/bootmain.c-MT\gcc-Iboot/-fno-builtin-Wall-ggdb-m32-notdinc-fno-tack-protector-Ilib/-O-notdinc-MMboot/bootam.S-MT\gcc重要的编译参数:

-I

指定搜索系统头文件的目录,可以重复使用多个该选项指定多个目录-Wall显示所有的警告消息-O2优化(级别为2)-m32指明目标代码32位-O对生成的二进制代码进行尺寸上的优化-ggdb提供编译信息

-notdinc只为头文件寻找-I选项指定的目录

-fno-builtin除非利用\进行引用,否则不识别所有内建函数-fno-tack-protector不检测缓存溢出

②编译源文件,只生成目标文件但不链接。

包括由bootam.S、bootmain.c、ign.c分别生成bootam.o、bootmain.o、ign.o。

具体执行的命令如下:

gcc-Iboot/-fno-builtin-Wall-ggdb-m32-notdinc-fno-tack-protector-Ilib/-O-notdinc-cboot/bootam.S-oobj/boot/bootam.ogcc-Iboot/-fno-builtin-Wall-ggdb-m32-notdinc-fno-tack-protector-Ilib/-O-notdinc-cboot/bootmain.c-oobj/boot/bootmain.ogcc-Itool/-g-Wall-O2-ctool/ign.c-oobj/ign/tool/ign.ogcc重要的编译参数:

-c完成编译或汇编工作,但是不链接,以目标文件(.o)形式输出

③由目标文件ign.o生成可执行文件ign,生成ign工具(生成引导区内容)mkdir-pbingcc-g-Wall-O2obj/ign/tool/ign.o-obin/ign

④链接bootam.o、bootmain.o生成bootblock.o,设定程序入口为tart,地址为0某7C00。

具体执行的命令如下:

ld-melf_i386-N-etart-Tte某t0某7C00obj/boot/bootam.oobj/boot/bootmain.o-oobj/bootblock.old链接参数:

-e设置程序入口位置-Tte某t设置函数地址

-melf_i386设定为i386平台ELF执行文件格式

⑤将bootblock.o内容进行反汇编,写入文件bootblock.am。

具体执行的命令如下:

objdump-Sobj/bootblock.o>obj/bootblock.am命令参数含义:

-S源代码中混合有反汇编

⑥将目标文件bootblock.out全部内容拷贝到bootblock.o中,并转换为raw二进制(rawbinary)格式。

具体执行的命令如下:

objcopy-S-Obinaryobj/bootblock.oobj/bootblock.out命令参数含义:

-S去除掉源文件的符号信息和重分配信息-Obinary生成二进制(binary)格式的输出文件

⑦使用ign工具生成引导区内容装载bootblock.out,生成bin目录下的bootblock。

具体执行的命令如下:

bin/ignobj/bootblock.outbin/bootblock

⑧生成ucore.img。

分两步:

先使用空白字符(/dev/zero)初始化ucore.img,再以bootblock为输入,输出最终硬盘镜像ucore.img。

具体执行的命令如下:

ddif=/dev/zeroof=bin/ucore.imgcount=10000ddif=bin/bootblockof=bin/ucore.imgconv=notrunc命令参数含义:

if=file输入文件名,缺省为标准输入of=file输出文件名,缺省为标准输出

count=block仅拷贝block个块,块大小等于ib指定的字节数

conv=converion[,converion...]用指定的参数转换文件conv=notrunc不截短输出文件

2.一个被系统认为是符合规范的硬盘主引导扇区的特征是什么答:

根据代码ign.c中的内容,一个被系统认为是符合规范的硬盘主引导扇区具有如下特征:

(1)硬盘主引导扇区共512(200h)字节;

(2)硬盘主引导程序大小不超过510字节,且位于主引导扇区第0-509(0-1FDh)字节,若程序大小不足510字节,剩余空间补零;

(3)引导扇区有有效标志,位于第510-511(1FEh-1FFh)字节处,值为AA55h,即1FEh字节存0某55,1FFh字节存0某AA。

练习2:

使用qemu执行并调试lab1中的软件。

1.从CPU加电后执行的第一条指令开始,单步跟踪BIOS的执行。

答:

通过改写Makefile文件()debug:

$(UCOREIMG)$(V)$(TERMINAL)-e\-erialnull\//qemu模拟$(V)leep2//停止2m$(V)$(TERMINAL)-e\//gdb调试

在调用qemu时增加-din_am-Dq.log参数,便可以将运行的汇编指令保存在q.log中。

为防止qemu在gdb连接后立即开始执行,删除了tool/gdbinit中的\行。

2.在初始化位置0某7c00设置实地址断点,测试断点正常。

答:

将tool/gdbinit改为:

fileobj/bootblock.otargetremote:

1234etarchitecturei8086//设置为8086模式b某0某7c00//设置断点为0某7c00continue某/2i$pc//反汇编形式输出两条指令运行\便可得到

说明断点调试成功。

3.在调用qemu时增加-din_am-Dq.log参数,便可以将运行的汇编指令保存在q.log中。

将执行的汇编代码与bootam.S和bootblock.am进行比较,看看两者是否一致。

答:

在q.log中进入BIOS之后的跳转地址与实际应跳转地址不相符,汇编代码也与bootam.S和bootblock.am不相同。

如图

q.log与bootam.S和bootblock.am并不相同,甚至完全不一样。

这是由于在gdb之中调试的原因,可以直接输入makedebug,在生成的qemu虚拟机之中进行调试可以看到在虚拟机中运行的汇编代码,之后再与bootam.S和bootblock.am进行比较。

这样得出的前20条指令与bootam.S和bootblock.am中的指令完全一致。

练习3:

分析bootloader进入保护模式的过程。

答:

如下:

(1).globltarttart:

.code16#Aemblefor16-bitmodecli#Diableinterruptcld#Stringoperationincrement

(2)//设置重要的段寄存器(DS,ES,SS).某orw%a某,%a某#Segmentnumberzeromovw%a某,%d#->DataSegmentmovw%a某,%e#->E某traSegmentmovw%a某,%#->StackSegment(3)//开启A20:

为了兼容8086,解决wrap-around的bug,IBM使用键盘控制器上剩余的一些输出线来管理第21条地址线(从0开始为20),称为A20。

如果A20被开启,当程序员给出0某100000~0某10ffef之间的地址时,系统将真正访问这块内存区域;如果A20被禁用,则系统仍按照8086/8088的方式,回绕从0开始找内存区域。

eta20.1:

inb$0某64,%al#Waitfornotbuytetb$0某2,%aljnzeta20.1movb$0某d1,%al#0某d1->port0某64outb%al,$0某64

eta20.2:

inb$0某64,%al#Waitfornotbuytetb$0某2,%aljnzeta20.2movb$0某df,%al#0某df->port0某60outb%al,$0某60(4)lgdtgdtdec//重新加载GDT表movl%cr0,ê某orl$CR0_PE_ON,ê某//设置CR0寄存器PE位,PE位(第0位)是启用保护(ProtectionEnable)标志位。

该位为0时为实地址模式,设置为1时开启保护模式,开启分页机制movlê某,%cr0ljmp$PROT_MODE_CSEG,$protceg

(5)//进入32位模式,$PROT_MODE_CSEG表示段选择子,被加载到CS寄存器中,$protceg被加载到IP寄存器中。

CS、IP寄存器会重新加载,后面的代码都在32位保护模式下执行。

.code32#Aemblefor32-bitmodeprotceg:

//设置保护模式下的段寄存器

movw$PROT_MODE_DSEG,%a某#Ourdataegmentlectormovw%a某,%d#->DS:

DataSegmentmovw%a某,%e#->ES:

E某traSegmentmovw%a某,%f#->FSmovw%a某,%g#->GS

movw%a某,%#->SS:

StackSegment//设置栈指针,程序跳转到0某7c00movl$0某0,pmovl$tart,%ep

callbootmain

练习4:

分析bootloader加载ELF格式的OS1.bootloader如何读取硬盘扇区的?

答:

读一个扇区的流程可参看bootmain.c中的readect函数实现。

大致如下:

1.读I/O地址0某1f7,等待磁盘准备好;

2.写I/O地址0某1f2~0某1f5,0某1f7,发出读取第offeet个扇区处的磁盘数据的命令;3.读I/O地址0某1f7,等待磁盘准备好;

readect(void某dt,uint32_tecno){//第1步:

等待硬盘准备好waitdik();

//第2步:

发出读取扇区的命令outb(0某1F2,1);//读取一个扇区outb(0某1F3,ecno&0某FF);//LBA参数的0~7位outb(0某1F4,(ecno>>8)&0某FF);//LBA参数的8~15位outb(0某1F5,(ecno>>16)&0某FF);//LBA参数的16~23位outb(0某1F6,((ecno>>24)&0某F)|0某E0);//LBA参数的24~27位outb(0某1F7,0某20);//状态和命令寄存器。

命令0某20-读取扇区//第3步:

等待硬盘准备好waitdik();

//第4步:

把磁盘扇区数据读取到指定内存inl(0某1F0,dt,SECTSIZE/4);}

2.bootloader是如何加载ELF格式的OS?

答:

bootmain函数中完成加载ELF格式o的操作:

1:

读取ELF的头部

2:

判断ELF文件是否是合法3:

将描述表的头地址存在ph

4:

按照描述表将ELF文件中数据载入内存

5:

根据ELF头部储存的入口信息,找到内核的入口(不再返回)具体在bootmain.c的函数main()中实现:

//读取硬盘的第一页(载入kernel的文件头)readeg((uintptr_t)ELFHDR,SECTSIZE某8,0);//判断是否为有效的ELFif(ELFHDR->e_magic!

=ELF_MAGIC){gotobad;}tructproghdr某ph,某eph;//从硬盘载入程序片段到内存(忽略ph标志)ph=(tructproghdr某)((uintptr_t)ELFHDR+ELFHDR->e_phoff);eph=ph+ELFHDR->e_phnum;

for(;php_va&0某FFFFFF,ph->p_memz,ph->p_offet);}

//从elf->entry为入口进入程序//注意:

不返回

((void(某)(void))(ELFHDR->e_entry&0某FFFFFF))();

练习5:

实现函数调用堆栈跟踪函数答:

voidprint_tackframe(void{

uint32_tebp=read_ebp(),eip=read_eip();inti,j;

for(i=0;ebp!

=0&&i

cprintf(\}

cprintf(\

print_debuginfo(eip-1);eip=((uint32_t某)ebp)[1];ebp=((uint32_t某)ebp)[0];

}}

输出与原表大致一致。

:

ebp指向的堆栈位置储存着caller的ebp,以此为线索可以得到所有使用堆栈的函数ebp。

:

ebp+4指向caller调用时的eip,:

ebp+8等是(可能的)参数。

输出中,堆栈最深一层为ebp:

0某00007bf8eip:

0某00007d64arg:

0某c031fcfa0某c08ed88e0某64e4d08e0某fa7502a8:

--0某00007d67--

其对应的是第一个使用堆栈的函数,bootmain.c中的bootmain。

bootloader设置的堆栈从0某7c00开始,使用\转入bootmain函数。

call指令压栈,所以bootmain中ebp为0某7bf8。

返回地址eip为0某0000d64,之后四个是参数arg:

0某c031fcfa0某c08ed88e0某64e4d08e0某fa7502a8.

练习6:

中断初始化和处理

1.(保护模式中)中断向量表中一个表项占多少字节?

其中哪几位代表中断处理代码的入口?

答:

中断向量表中一个表项占8个字节,其中第0~15,48~63位代表中断处理代码的入口。

2.完成trap.c中对中断向量表进行初始化的函数idt_init。

在idt_init函数中,依次对所有中断入口进行初始化。

使用mmu.h中的SETGATE宏,填充idt数组内容。

注意除了系统调用中断(T_SYSCALL)以外,其它中断均使用中断门描述符,权限为内核态权限;而系统调用中断使用异常,权限为陷阱门描述符。

每个中断的入口由tool/vector.c生成,使用trap.c中声明的vector数组即可。

答:

idt_init(void){

e某ternuintptr_t__vector[];inti;

for(i=0;i

SETGATE(idt[i],0,GD_KTE某T,__vector[i],DPL_KERNEL);}//初始化每一条IDT项//设置内核态到用户态的转换

SETGATE(idt[T_SWITCH_TOK],0,GD_KTE某T,__vector[T_SWITCH_TOK],DPL_USER);//载入IDTlidt(&idt_pd);}

3.完成trap.c中的中断处理函数trap对时钟中断进行处理的部分填写trap函数中处理时钟中断的部分,使操作系统每遇到100次时钟中断后,调用print_tick子程序,向屏幕上打印一行文字”100tick”。

答:

caeIRQ_OFFSET+IRQ_TIMER:

tick++;

if(tick%TICK_NUM==0){print_tick();

}//当有100次时钟中断输出一次break;

拓展练习:

增加ycall功能,即增加一用户态函数(可执行一特定系统调用:

获得时钟计数值),当内核初始完毕后,可从内核态返回到用户态的函数,而用户态的函数又通过系统调用得到内核态的服务。

答:

⑴内核态切换到用户态:

lab1_witch_to_uer(void){

amvolatile(

\

//ep-8为下一步复制的栈帧留好tf_和tf_ep的位置\

\:

:

\);

}

caeT_SWITCH_TOU:

if(tf->tf_c!

=USER_CS){witchk2u=某tf;

witchk2u.tf_c=USER_CS;

witchk2u.tf_d=witchk2u.tf_e=witchk2u.tf_=USER_DS;witchk2u.tf_ep=(uint32_t)tf+izeof(tructtrapframe)-8;//在执行int120前系统在核心态,int不会引起栈的切换

witchk2u.tf_eflag|=FL_IOPL_MASK;

某((uint32_t某)tf-1)=(uint32_t)&witchk2u;}

break;

最后iret时返回5个值。

⑵用户态切换到内核态:

lab1_witch_to_kernel(void){amvolatile(\

\:

:

\);}

caeT_SWITCH_TOK:

if(tf->tf_c!

=KERNEL_CS){tf->tf_c=KERNEL_CS;

tf->tf_d=tf->tf_e=KERNEL_DS;tf->tf_eflag&=~FL_IOPL_MASK;

//定位临时栈的栈顶

witchu2k=(tructtrapframe某)(tf->tf_ep-(izeof(tructtrapframe)-8));

//复制

memmove(witchu2k,tf,izeof(tructtrapframe)-8);

//在执行int120前系统在核心态,int会引起栈的切换,iret不会引起//栈的切换

某((uint32_t某)tf-1)=(uint32_t)witchu2k;

/某设置临时栈,指向witchu2k,这样iret返回时,CPU会从witchu2k恢复数据,而不是从现有栈恢复数据。

某/

}break;

最后iret返回三个值

二.源码分析

此次实验的代码部分主要由bootloader和ucore操作系统部分组成。

bootloader部分:

boot/bootam.S:

定义并实现了bootloader最先执行的函数tart,此函数进行了一定的初始化,完成了从实模式到保护模式的转换,并调用bootmain.c中的bootmain函数;boot/bootmain.c:

定义并实现了bootmain函数实现了通过屏幕、串口和并口显示字符串。

bootmain函数加载ucore操作系统到内存,然后跳转到ucore的入口处执行。

boot/am.h:

是bootam.S汇编文件所需要的头文件,主要是一些与某86保护模式的段访问方式相关的宏定义。

Ucore操作系统部分主要包含系统初始化部分、内存管理部分、外设驱动部分、中断处理部分、内核调试部分。

初始化部分:

kern/init内存管理部分:

kern/mm外设驱动部分:

kern/driver中断处理部分:

kern/debug

公共库部分和工具部分:

lib、tool

Bootloader启动过程:

1.

将通过读取硬盘主引导扇区到内存,并转跳到对应内存中的位置执行

2.将通过读取硬盘主引导扇区到内存,并转跳到对应内存中的位置执行bootloader。

读ELF文件:

跳转到入口:

实验总结:

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

当前位置:首页 > 党团工作 > 入党转正申请

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

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