片偏移怎么计算_计算机自制操作系统(十七):必须要插播一节Bochs的小广告...

216d097d0d7598df1b0af888655ee639.png

我们已经成功实现了键盘中断流程,可以说是完成了键盘驱动80%的工作。但是自从本专栏使用C语言开发之后,当时说的是再也不用抓狂的汇编语言了嘛,怎么又用起了汇编语言呢?而且还是纯汇编。哎,这也是没有办法,因为我是太热爱汇编语言了,凡是能用它实现的,我基本上就不想用C语言去绕一个圈了。而在上一章中,我通过调试汇编语言才发现了Bochs真正的用武之地。所以我必须要专门开启一章来隆重介绍一下。

一、中断服务程序异常问题

在上一章调试的时候,遇到中断程序始终无法正常返回的问题。当时我是键盘输入后每次都能正常进入中断服务程序,但是在执行完中断服务程序之后遇到iret指令就会报错死机,死活都无法再正常返回主程序。我们都知道中断服务程序执行的时候是先将CS和EIP入栈,IRET退出的时候,是将CS和EIP出栈,如此一来就能保证正常返回。现在我的程序返回异常,那一定是出栈CS和EIP的时候有问题。这种情况下,唯一的方法是跟踪IRET指令执行情况。

怎么跟踪呢?我突然想起了Bochs不是可以进行程序的单步调试吗?于是我就偶然间发现了这个操作系统开发的好东西。

二、Bochs调试解决

1.Bochs调试准备

首先,我们需要安装和配置Bochs,具体我本次调试的配置文件bochsrc.bxrc内容如下:

floppya:1_44=Kernela.bin,status=inserted

boot:floppy

log:bochsout.txt

mouse:enabled=0

比较简单,它的核心意思就是让计算机从软盘启动,软盘启动的二进制程序是Kernela.bin。此文件由上一章的汇编程序Kernela.asm编译得到。

2.Bochs调试启动

启动程序bochsdbg.exe(注意非bochs.exe),选择好配置文件bochsrc.bxrc,然后点击start启动。

d20eaf128a03167f8ec39e168723f1ba.png

这个时候就会弹出模拟计算机的启动窗口:Display,同时我们的调试窗口Console显示了这样一条指令:jmpf 0xf000:e05b。

21e5f8c1e092ac6c3bb384bf9d3c41cb.png

这条指令是什么意思?我贴一张前面章节的图便可知:

9d149fe5b60909dc8a90ea6ded160d59.png

没错,计算机就从BIOS开始启动了,这是一个完整计算机启动过程,所以Bochs模拟机厉害就在于此。同时从窗口还可以看到计算机启动到时候:CS=0XF000,IP=0XFFF0。而非部分书上所说的:CS=0XFFFF,IP=0X0000。

3.Bochs调试方法

这个时候,我们就可以用Bochs的各种调试方法进行跟踪程序执行了。

3.1 运行到用户程序入口

首先,我们需要程序运行到0x7c00的地方,也即我们自己写的MBR,方法如下:

b190fce283359dbb62f3997c8226e54f.png

可以看到,程序来到了我们写的第一条指令:jmp start。进入MBR之前,BIOS又将CS和IP的值做了设置:CS=0,IP=0X7C00。而非部分书上写的:CS=7C00,IP=0X0000。

3.2 运行到目标程序地址

我们的目标是观察中断服务程序intservice异常情况,所以现在我们要让程序运行到主程序的最后位置死循环处等待中断的发生,那怎么实现呢?我们来看一下汇编程序Kernela.asm中over的位置:

1ce6315878b866185741c3db1f04debc.png

可以看出位置偏移是0X000003F5,这个值是汇编器编译出来的相对文件头第一条指令:jmp start的相对偏移。我们现在需要找出它装进内存之后的真实地址,程序Kernela.asm中编译偏移地址和在内存中的存储位置逻辑如下图:

ea0e1a3e46f02e19be99ad5660971db9.png

因此程序运行到over之处的内存地址是:0x8000+X03F5-0x200=0x81F5。

所以,我们输入如下命令,就可以让命令运行到OVER之处:

3b55f8ef34e57941b23ee1b2579bc088.png

同时可以看到,我们的操作系统也在显示窗口正常启动运行:

24e665929518c5c0a978cbf0b11bb08d.png

还可以看到,此时已经进入了保护模式:CS=8,EIP=0X01f5。为什么EIP=0X01F5呢?这是因为我们在MBR结束的时候用来一条跳转命令使IP=0,通过下图可以清楚明白:

4363d96d2586f6877af3866189180775.png

之所以特别强调这个IP值,是因为它是我们中断服务程序执行完成之后要返回的地址。在进入中断之前,我们来看一下堆栈区的数据,可以看到堆栈区是空的:

811dd19c6c87df4cd8bd0bbc172cd28b.png

这是因为我设置的栈底地址就是0x7c00。之所以Bochs列出了一堆数据,你可以理解为它是从栈顶开始罗列16个堆栈数据,现在栈顶就是栈底,所以它只有把一些无关的数据都搞出来了。如果你不放心栈顶地址SS:ESP是否是0x7c00,还可以用如下命令查看:

0251d8dd4ceb381aeaed6f1369fe5c42.png

7ebb8935a6dba7d9b5f4e55471ae181f.png

中断处理程序的时候,需要把EFLAGS、CS和EIP相继入栈,那我们最后再来看一下EFLAGS和EIP寄存器的值:

ea9ef0597b103ccd8fca81af060af853.png

现在,我们要调试进入中断服务程序了。我们在中断服务程序interservice中找一条指令,如下图,它的偏移地址是0x05ce:

f21d6f2ca41cb9511dbe33ed7f48cc62.png

那么根据之前同样的计算办法,我们需要把程序运行到内存物理地址为0x83ce的地方来观察。这里最好重新启动一次Bochs来运行,直接输入b 0x83ce命令来执行c,这个时候程序便会停止。原因很简单,因为我们没有触发中断,那么程序是到不了内存地址0x83ce的。这个时候,只需要按下任意按键按钮,屏幕显示如下:

dd56d7c3e0b134d1ba7a356d222a3b29.png

我们此时打开堆栈区,可以看到进入中断服务程序之后,堆栈区有了数据,对比中断发生前各个寄存器的值,可推断先后入栈的寄存器分别是:EFLAGS、CS和EIP!

最后,我们再来看从中断服务程序返回主程序的情况。我们把断点再次设置在中断发生之前: b 0x81f5。这个时候运行程序,可以看到,堆栈区已经完全清空了,也就是说EFLAGS、CS和EIP已经完全出栈,并且程序又回到了中断发生前:CS=8,EIP=0X01f5。

21856318db58461b166e26ae98898983.png

到这里就可以回答上一章中我们留下的疑问:为什么在我的中断服务程序里面有一条禁止中断的指令:cli,但是在退出中断服务程序之前却没有重新再打开中断的指令呢?这是因为中断服务程序在遇到iret指令的时候,CPU会自动把EFALGS从堆栈里pop出来,由于在入栈的时候EFALGS中的中断开关无疑是打开的,这样出栈之后中断开关也就相当于自动打开了。

三、Bochs神器

为了跟踪计算机的中断处理机制,我动用了Bochs调试工具,我才发现它是多么的强大,这简直就是一个汇编语言不可或缺的调试神器。不光如此,Bochs简直就是掌握了操作系统的任何数据。例如你可以通过命令查看GDT定义的各类寄存器和段详情:

54c2a03771b17890d1f553fb31140758.png

以及查看IDT中256个中断源的每一个中断描述符详情:

efaf4da8520e253e669640ea96e02eea.png

可惜的是,我知道它的这些用途实在太晚了,专栏走到今天才发现。我敢说要是早有它,我绝对不会在之前的汇编语言编写过程中那么的痛苦,因此谨以此文来祭奠那些被折磨的岁月。总之血泪推荐:对尝试写操作系统的人来说,Bochs请务必拥有

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值