【HUST】论于渊《Orange‘s:一个操作系统的实现》第三章中PagingDemoProc的必要性,是否可以直接调用LinearAddrDemo?

相关代码如下(第三章pmtest9a.asm改写):

LinearAddrDemo	equ	00401000h
ProcHust		equ	00401000h
ProcIS19		equ	00501000h
ProcPagingDemo	equ	00301000h

; 启动分页机制 --------------------------------------------------------------
PagingStart:
	mov	ax, cs
	mov	ds, ax
	mov	ax, SelectorFlatRW
	mov	es, ax

	push	LenHust
	push	OffsetHust
	push	ProcHust
	call	MemCpy
	add	esp, 12

	push	LenIS19
	push	OffsetIS19
	push	ProcIS19
	call	MemCpy
	add	esp, 12

	push	LenPagingDemoAll
	push	OffsetPagingDemoProc
	push	ProcPagingDemo
	call	MemCpy
	add	esp, 12

	mov	ax, SelectorData
	mov	ds, ax			; 数据段选择子
	mov	es, ax

	call	SetupPaging		; 启动分页
	
	call	SelectorFlatC:ProcPagingDemo	; 调用函数
	call	PSwitch	; 切页(切换LinearAddrDemo对应的函数过程)
	call	SelectorFlatC:ProcPagingDemo  ; 调用函数

	retf
; ---------------------------------------------------------------------------

PagingDemoProc:
OffsetPagingDemoProc	equ	PagingDemoProc - $$
	mov	eax, LinearAddrDemo
	call	eax
	retf
LenPagingDemoAll	equ	$ - PagingDemoProc

hust:
OffsetHust		equ	hust - $$
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	al, 'H'
	mov	[gs:((80 * 17 + 0) * 2)], ax	; 屏幕第 17 行, 第 0 列。
	mov	al, 'U'
	mov	[gs:((80 * 17 + 1) * 2)], ax	; 屏幕第 17 行, 第 1 列。
    mov	al, 'S'
	mov	[gs:((80 * 17 + 2) * 2)], ax	; 屏幕第 17 行, 第 2 列。
    mov	al, 'T'
	mov	[gs:((80 * 17 + 3) * 2)], ax	; 屏幕第 17 行, 第 3 列。
	ret
LenHust			equ	$ - hust

is19:
OffsetIS19		equ	is19 - $$
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	al, 'I'
	mov	[gs:((80 * 17 + 0) * 2)], ax	; 屏幕第 17 行, 第 0 列。
	mov	al, 'S'
	mov	[gs:((80 * 17 + 1) * 2)], ax	; 屏幕第 17 行, 第 1 列。
	mov	al, '1'
	mov	[gs:((80 * 17 + 2) * 2)], ax	; 屏幕第 17 行, 第 2 列。
	mov	al, '9'
	mov	[gs:((80 * 17 + 3) * 2)], ax	; 屏幕第 17 行, 第 3 列。
	ret
LenIS19			equ	$ - is19

在代码中,调用LinearAddrDemo地址对应的函数时,往往通过调用call SelectorFlatC:ProcPagingDemo来调用。
目的是确保物理地址=LinearAddrDemo

在保护模式中,线性地址=段基址+段偏移地址,其中段基址从GDT(或别的什么段)的代码段描述符中找出。
在没有开启分页机制时,线性地址就是物理地址。开启后,经过页部件转换,才能得到物理地址。
其中,页部件转换由硬件自动完成,不需要特殊考虑。
因此,只要确保线性地址=LinearAddrDemo,即可保证物理地址=LinearAddrDemo

直接call LinearAddrDemo时,相当于call SelectorCode32:LinearAddrDemo
此时线性地址=SelectorCode32选择子对应的描述符中代码段的段基址 + LinearAddrDemo
由于SelectorCode32选择子对应的描述符中代码段的段基址是在实模式跳转保护模式的过程中计算出来的,不为0。所以线性地址≠LinearAddrDemo,得到的线性地址是一个illegal access。
使用SelectorFlatC,它的段基址是0,计算就没有问题。

因此,使用call SelectorFlatC:ProcPagingDemo的方式,去调用LinearAddrDemo对应的函数,是正确的、没有问题的。

但是,从上述代码里,我们显然可以发现,ProcPagingDemoLinearAddrDemo的定义方式完全一致,LinearAddrDemo也是一个常量。

那么,为什么不直接call SelectorFlatC:LinearAddrDemo

试试就试试。

第一步:

直接把第一个call SelectorFlatC:ProcPagingDemo改成call SelectorFlatC:LinearAddrDemo
运行结果如下图所示。
在这里插入图片描述
如上图所示,call调用是成功的,但是返回时出现异常中断,bochs显示错误write_virtual_checks(): write beyond limit, r/w

因此可能是返回异常。hustis19两个函数过程的返回使用的是ret,实际上,当远跳转时,压栈cs,ip,如果使用ret返回,只pop了ip,会导致出现异常的返回值。

第二步:

hustis19两个函数过程的返回方式改成retf
运行结果如下图所示。
在这里插入图片描述

上图bochs显示错误fetch_raw_descriptor: GDT: index (20f) 41 > limit (77)。这是由于代码中既有call SelectorFlatC:ProcPagingDemo,又有call SelectorFlatC:LinearAddrDemo

ProcPagingDemo,它调用LinearAddrDemo时采取的是近跳转,应该与ret配套使用。但此时我们已经将hustis19两个函数过程的返回方式改成retf
此时会出现怎样的情况呢?栈如下图所示:
在这里插入图片描述

第三步:

不要混用。
只要将所有的call SelectorFlatC:ProcPagingDemo改成call SelectorFlatC:LinearAddrDemo,就不会再报错。

总结:

论于渊《Orange‘s:一个操作系统的实现》第三章中PagingDemoProc的必要性,是否可以直接调用LinearAddrDemo?
答:可以直接调用LinearAddrDemo,只要将函数过程的返回均写成retf即可,PagingDemoProc不是必要的。

本次探索过程让我充分理解了近跳转和远跳转、近返回和远返回的概念以及作用,不再乱用retretf。同时对实模式和保护模式的段、地址有了更清晰的了解。

posted @   shandianchengzi  阅读(127)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示