第十五章 习题解析(以及完整源码)
准备文件 .lst文件
-
.lst文件根据同名.asm文件由配书工具nasmide.exe生成
c13_mbr.lst
ex15-1_core.lst
ex15-1_A.lst
ex15-1_B.lst
Bochs 调试命令(以及配置方法)
-
s: 单步执行 -
b: 断点设置 -
c: 继续执行 -
r: 查看寄存器 -
sreg: 查看段寄存器 -
xp: 查看内存
Bochs 完整调试过程
- 调试过程:加载程序mbr 到 内核程序core 到用户程序 user(任务Task-A Mss-1) 回 内核程序core;
-
<bochs:n> XXXX: XXXX 处输入 bochs指令 ;
<bochs:1> s
<bochs:2> b 0x7c00
<bochs:3> c
---- 进入 mbr -----
<bochs:4> b 0x7d38
<bochs:5> c
---- 跳转到 mbr 最后一条指令 ----------------------
| c13_mbr.lst
| 136 00000138 FF6F10 jmp far [edi+0x10]
| 0x7D38 = 0x7c00 + 0x138
--------------------------------------------------
<bochs:6> s
--------- 进入core -------------------------------------------------------------
| Bochs
| (0) [0x0000000415e2] 0038:00000000000003fa (unk. ctxt): mov ecx, 0x00000030
| 读出内核代码段选择子是 0x38
| ex15-1_core.lst
| 946 000003FA B930000000 mov ecx,core_data_seg_sel ;令DS指向核心数据段
| 0x03FA正是 .lst文件中 的指令前面的汇编地址
---------------------------------------------------------------------------------
<bochs:7> b 0x0038:0x053c
<bochs:8> c
----------- (core)来到习题 ex15-1 的第一条指令 ----------
| ex15-1_core.lst
| 1041 0000053C B946000000 mov ecx,0x46
---------------------------------------------------------
<bochs:9> b 0x0038:0x055d
<bochs:10> c
---------(core)即将切换去user Task-A Mss-1 -----------------------------------
| Bochs
| (0) [0x000000041745] 0038:000000000000055d (unk. ctxt): callf es:[ecx+20]
| ex15-1_core.lst
| 1049 0000055D 26FF5914 call far [es:ecx+0x14]
--------------------------------------------------------------------------------
<bochs:11> s
---------------- (user)切换到 任务 Task-A Mss-1 ----------------------
| Bochs
| (0) [0x0000001004c0] 000f:0000000000000000 (unk. ctxt): mov ax, ds
------------------------------------------------------------------------
<bochs:16> s
----------------- (user)一直单步执行 直到 执行完 赋值语句 ----------------------
| Bochs
| (0) [0x0000001004cb] 000f:000000000000000b (unk. ctxt): mov edx, dword ptr fs:0x00000328 ; 648b1528030000
| ex15-1_A.lst
| 80 0000000B 648B15[28030000] mov edx,[fs:mss_type]
------------------------------------------------------------------------------------------------
<bochs:18> r
rdx: 00000000_00000001
<bochs:19> sreg
fs:0x0007, dh=0x0040f310, dl=0x0150032b, valid=3
Data segment, base=0x00100150, limit=0x0000032b, Read/Write, Accessed
-----------------------------------------------------------------------
| fs 寄存器此时指向 用户程序 头部段
| 读出 其线性地址 base=0x00100150
| 读出 段内偏移量 见单步调试结果里的 fs:0x00000328
| [fs:mss_type] 内存真实物理地址 = 线性地址(0x100150) + 段内偏移量(0x328)= 0x100478
------------------------------------------------------------------------
<bochs:21> xp 0x100478
[bochs]:
0x0000000000100478 <bogus+ 0>: 0x00000001
------------------- 查看内存具体数值 --------
| rdx: 00000000_00000001
| 数值一致,正确√
--------------------------------------------
<bochs:23> b 0x000f:0x0037
<bochs:24> c
-----------------(user) 执行完 user,即将切换回core -------------------------------------------------
| Bochs
| (0) [0x0000001004f7] 000f:0000000000000037 (unk. ctxt): callf fs:0x00000128 ; 64ff1d28010000
------------------------------------------------------------------------------------------------------
<bochs:25> s
-----------------(user)调用门 TerminateProgram -----------------------------------------------
| ex15-1_core.lst
| (0) [0x000000040212] 0028:00000000000001fa (unk. ctxt): pushf ; 9c
-----------------------------------------------------------------------------------------------
<bochs:26> b 0x0028:0x0230
<bochs:27> c
--------------(user)调用门 TerminateProgram 的最后一条返回指令 ---------------------------------
| Bochs
| (0) [0x000000040248] 0028:0000000000000230 (unk. ctxt): iret ; cf
-----------------------------------------------------------------------------------------------
<bochs:28> s
----------------(core) 回到内核程序 ----------------------------------------------------------------
| Bochs
| (0) [0x000000041749] 0038:0000000000000561 (unk. ctxt): mov ebx, 0x00000f72 ; bb720f0000
| ex15-1_core.lst
| 1052 00000561 BB[720F0000] mov ebx,prgman_msg4
| 接下来就是 任务 Task-B Mss-1 相关
| 想要查看edx寄存器的内容 以及 内存的内容
| 重复 <bochs:9> 开始的指令,根据对应的.LST文件,将段内偏移进行替换即可
----------------------------------------------------------------------------------------------------
Bochs快速调试过程
- 任务 Task-B Mss-1
<bochs:1> s
<bochs:2> b 0x7c00
<bochs:3> c
---- 进入 mbr -----
<bochs:4> b 0x7d38
<bochs:5> c
<bochs:6> s
<bochs:7> b 0x0038:0x056D
<bochs:8> c
(0) [0x000000041755] 0038:000000000000056d (unk. ctxt): mov ecx, 0x00000046
<bochs:9> b 0x0038:0x058E
<bochs:10> c
(0) [0x000000041776] 0038:000000000000058e (unk. ctxt): callf es:[ecx+20]
-----------------------------------------------------------------
| 因为很清楚了,内核代码段选择子是 0x0038
| 段内偏移量只要查 .lst 文件,找汇编地址填写上去就可以了
-------------------------------------------------------------------
<bochs:11> s
(0) [0x000000104a10] 000f:0000000000000000 (unk. ctxt): mov ax, ds
---------- 进入了 user Task-B Mss-1 ---------------
<bochs:15> s
(0) [0x000000104a1b] 000f:000000000000000b (unk. ctxt): mov edx, dword ptr fs:0x00000328 ; 648b1528030000
<bochs:17> r
rdx: 00000000_00000001
<bochs:18> sreg
fs:0x0007, dh=0x0040f310, dl=0x46a0032b, valid=3
Data segment, base=0x001046a0, limit=0x0000032b, Read/Write, Accessed
------------ 计算真实内存地址 ---------
| fs:0x00000328
| base=0x001046a0
| 0x001046a0+ 0x328 = 1049C8
----------------------------------------------
<bochs:19> xp 0x1049c8
[bochs]:
0x00000000001049c8 <bogus+ 0>: 0x00000001
关于调试过程
设置断点

设置断点.png
- 知道段选择子(
Bochs读出),知道汇编地址(查看.lst文件),就可以精确地设置断点,比如在内核代码段执行的时候,段选择子是0x0038,那么断点肯定是0x0038:NNNN,由于任务切换返回的时候要通过调用门,走到内核公用例程段,公用例程段的选择子是0x0028,处理器就是根据这些段选择子是去GDT里查找段描述符的。
执行流程的基本判断
---------------------------- ex15-1_A.lst ----------------------------
77 ;-------------------------------------------------------------
78 ; ex15-1
79 ;-------------------------------------------------------------
80 0000000B 648B15[28030000] mov edx,[fs:mss_type]
81
82 00000012 81FA01000000 cmp edx,1
83 00000018 7511 jne .mss2
84 0000001A BB[00000000] mov ebx,mss_1
85 0000001F 64FF1D[28000000] call far [fs:PrintString]
86 00000026 E90C000000 jmp .mssend
87 .mss2:
88 0000002B BB[21000000] mov ebx,mss_2
89 00000030 64FF1D[28000000] call far [fs:PrintString]
90 .mssend:
91 ;---------------------------------------------------------------
92
93
94 00000037 64FF1D[28010000]
举例说明:当执行 Task-A Mss-1(任务A 显示消息1)的切换任务时,根据代码的条件判断,可以知道代码的正确流程走向就是执行这条跳转
86 00000026 E90C000000 jmp .mssend,因此,调试设置断点的时候,不能设置断点到.mss2 后面的 88 0000002B BB[21000000] mov ebx,mss_2,任务A的消息1的切换过程走不到这里的,然而,此时,在bochs里面,会看到无论哪次切换到用户程序,显示的段选择子都是0x000F,因此如果贸然用了b 0x 000F:0000002B(企图进入.mss2标号后面),那么bochs就会走到这一步(它会走到这一步的),因为在全部的代码流程里,的确是有一次可以走到这里,那就是再次切换到任务A显示消息2的时候;全部的代码流程是A1 B1 A2 B2,这样就是直接跳到了A2,在bochs的虚拟机窗口可以看到A1 B1的输出验证这一点。虽然
fs看上去都是0x000F,但是本质上不同,通过sreg查看段寄存器,可以看到base的数据都是不同的!这恰恰说明了,A1 B1 A2 B2 其实就是4个完全不同的任务,它们在内存的不同位置存放着,它们彼此毫无关系。
详细图解(根据文件名对应具体步骤)

[core]1.进入内核程序 使用断点 跳转到显示完CPU信息处.png

[core]2、跳转到习题1 解答代码开始处.png

[user]1、切换到任务A 显示message1.png

[user]2、读出用户程序代码段选择子.png

[user]3、查看控制消息显示参数的值是否正确传递.png

[user]4、调用门 执行内核程序 公用例程段的子程序.png

[user]5、第一次切换到任务A 显示消息1 并返回.png

真实内存地址.png

如何计算 mss_type 所在的真实内存物理地址.png

任务A 消息2.png

任务B 消息2.png