我们知分页机制的关键是页请求和页置换,并且页置换发生在没有空闲页框的时候,但是现在出现了问题-是否可以知道还剩多少空闲页框?并且还有多少页框可用?--在这里就得获取物理内存的大小了
一.获取物理内存容量 在获取物理内存大小时,BIOS提供了一些操作方法
A.相关中断(int 0x15)
该中断的基础功能是(eax=0xE801)分别检测低15M和高16M-4G的内存,并且支持4GB内存检测,并且高级功能(eax=0xE820)可以遍历主机上所有的内存范围,获取各个内存范围的详细信息
详细介绍下int 0x15的基础功能-中断参数为eax=0xE801,有三个重要的返回值,cf->成功置0,出错置1;ax,cx是以1kb为单位,表示15MB以下的内存容量;bx,dx是以64KB为单位,表示16MB以上的内存容量
B.标志寄存器
文章图片
对int 0x15基础功能进行简单示例介绍
文章图片
15M以下内存容量左移10位(eax1024),16M以上内存容量先左移6位再左移10位(ebx64*1024)
代码实验
%include "inc.asm"org 0x9000jmp ENTRY_SEGMENT[section .gdt]
;
GDT definition
;
段基址,段界限,段属性
GDT_ENTRY:Descriptor0,0,0
CODE32_DESC:Descriptor0,Code32SegLen - 1,DA_C + DA_32
VIDEO_DESC:Descriptor0xB8000,0x07FFF,DA_DRWA + DA_32
DATA32_DESC:Descriptor0,Data32SegLen - 1,DA_DRW + DA_32
STACK32_DESC:Descriptor0,TopOfStack32,DA_DRW + DA_32
;
GDT endGdtLenequ$ - GDT_ENTRYGdtPtr:
dwGdtLen - 1
dd0;
GDT SelectorCode32Selectorequ (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelectorequ (0x0002 << 3) + SA_TIG + SA_RPL0
Data32Selectorequ (0x0003 << 3) + SA_TIG + SA_RPL0
Stack32Selectorequ (0x0004 << 3) + SA_TIG + SA_RPL0
;
end of [section .gdt]TopOfStack16equ 0x7c00[section .dat]
[bits 32]
DATA32_SEGMENT:
DTOSdb"D.T.OS!", 0
DTOS_OFFSETequ DTOS - $$
HELLO_WORLDdb"Hello World!", 0
HELLO_WORLD_OFFSET equ HELLO_WORLD - $$Data32SegLen equ $ - DATA32_SEGMENT;
MEM_SIZEtimes 4db0[section .s16]
[bits 16]
ENTRY_SEGMENT:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, TopOfStack16;
get hardware memory infomation
call GetMemSize;
initialize GDT for 32 bits code segment
mov esi, CODE32_SEGMENT
mov edi, CODE32_DESCcall InitDescItemmov esi, DATA32_SEGMENT
mov edi, DATA32_DESCcall InitDescItemmov esi, STACK32_SEGMENT
mov edi, STACK32_DESCcall InitDescItem;
initialize GDT pointer struct
mov eax, 0
mov ax, ds
shl eax, 4
add eax, GDT_ENTRY
mov dword [GdtPtr + 2], eax;
1. load GDT
lgdt [GdtPtr];
2. close interrupt
cli ;
3. open A20
in al, 0x92
or al, 00000010b
out 0x92, al;
4. enter protect mode
mov eax, cr0
or eax, 0x01
mov cr0, eax;
5. jump to 32 bits code
jmp dword Code32Selector : 0;
esi--> code segment label
;
edi--> descriptor label
InitDescItem:
push eaxmov eax, 0
mov ax, cs
shl eax, 4
add eax, esi
mov word [edi + 2], ax
shr eax, 16
mov byte [edi + 4], al
mov byte [edi + 7], ahpop eaxret;
;
GetMemSize:
push eax
push ebx
push ecx
push edxmov dword [MEM_SIZE], 0xor eax, eax
mov eax, 0xE801int 0x15jc geterrshl eax, 10;
eax = eax * 1024;
shl ebx, 6;
ebx = ebx * 64;
shl ebx, 10;
ebx = ebx * 1024;
add dword [MEaM_SIZE], eax
add dword [MEM_SIZE], ebxjmp getokgeterr:
mov dword [MEM_SIZE], 0getok:pop edx
pop ecx
pop ebx
pop eaxret[section .s32]
[bits 32]
CODE32_SEGMENT:
mov ax, VideoSelector
mov gs, axmov ax, Stack32Selector
mov ss, axmov eax, TopOfStack32
mov esp, eaxmov ax, Data32Selector
mov ds, axmov ebp, DTOS_OFFSET
mov bx, 0x0C
mov dh, 12
mov dl, 33call PrintStringmov ebp, HELLO_WORLD_OFFSET
mov bx, 0x0C
mov dh, 13
mov dl, 31call PrintStringjmp $;
ds:ebp--> string address
;
bx--> attribute
;
dx--> dh : row, dl : col
PrintString:
push ebp
push eax
push edi
push cx
push dxprint:
mov cl, [ds:ebp]
cmp cl, 0
je end
mov eax, 80
mul dh
add al, dl
shl eax, 1
mov edi, eax
mov ah, bl
mov al, cl
mov [gs:edi], ax
inc ebp
inc dl
jmp printend:
pop dx
pop cx
pop edi
pop eax
pop ebpretCode32SegLenequ$ - CODE32_SEGMENT[section .gs]
[bits 32]
STACK32_SEGMENT:
times 1024 * 4 db 0Stack32SegLen equ $ - STACK32_SEGMENT
TopOfStack32equ Stack32SegLen - 1
文章图片
文章图片
文章图片
分析
从上图首先对获取内存函数进行设置,并且对其进行调用,首先运行bochs得到打印结果,发现程序没有出现错误,但是在这里无法对内存进行查看,所以需要反编译,在该段函数中反编译中,由于是在16位进行的,所以不需要打印32反编译结果,在反编译之后,找到函数所对应的代码,并进行断点设置,如下所示
文章图片
文章图片
文章图片
设置完之后,在bochs进行断点设置进行调试,同时需要找到内存改变的点对应的内存值,分别两次查看对应的内存情况,如下图所示
文章图片
文章图片
文章图片
将得到的内存值通过进制转换为10进制可以得到
文章图片
但是该结果不表示位MB,通过相除1024*1024得到的结果位31MB,但是在配置文件中得到的文件大小位32MB
文章图片
文章图片
在这里获取的内存容量用两部分来表示,并且缺少1MB的原因是80286中的24根地址线最大寻址范围为16MB,之前在介绍两端的容量时,中间正好缺少1MB的内存
文章图片
进行修改-添加1MB的内存
文章图片
通过之前相同的设置以及断点设置,得到的值为0x02000000,通过进制转换为33554432,通过计算为32MB.
二.获取物理内存容量 上面介绍的是基础功能,这里介绍的是高级功能
文章图片
参数的设置
文章图片
edx是bios所需要的值,不做深究
地址范围描述结果(ARDS)
文章图片
文章图片
代码实验
%include "inc.asm"org 0x9000jmp ENTRY_SEGMENT[section .gdt];
GDT definition;
段基址,段界限,段属性GDT_ENTRY:Descriptor0,0,0CODE32_DESC:Descriptor0,Code32SegLen - 1,DA_C + DA_32VIDEO_DESC:Descriptor0xB8000,0x07FFF,DA_DRWA + DA_32DATA32_DESC:Descriptor0,Data32SegLen - 1,DA_DRW + DA_32STACK32_DESC:Descriptor0,TopOfStack32,DA_DRW + DA_32;
GDT endGdtLenequ$ - GDT_ENTRYGdtPtr:dwGdtLen - 1dd0;
GDT SelectorCode32Selectorequ (0x0001 << 3) + SA_TIG + SA_RPL0VideoSelectorequ (0x0002 << 3) + SA_TIG + SA_RPL0Data32Selectorequ (0x0003 << 3) + SA_TIG + SA_RPL0Stack32Selectorequ (0x0004 << 3) + SA_TIG + SA_RPL0;
end of [section .gdt]TopOfStack16equ 0x7c00[section .dat][bits 32]DATA32_SEGMENT:DTOSdb"D.T.OS!", 0DTOS_OFFSETequ DTOS - $$HELLO_WORLDdb"Hello World!", 0HELLO_WORLD_OFFSET equ HELLO_WORLD - $$Data32SegLen equ $ - DATA32_SEGMENT;
MEM_SIZEtimes4db0MEM_ARDS_NUMtimes4db0;
int mem_ards_num = 0;
MEM_ARDStimes 64 * 20db0 [section .s16][bits 16]ENTRY_SEGMENT:mov ax, csmov ds, axmov es, axmov ss, axmov sp, TopOfStack16;
get InitSysMemBufinfomationcall InitSysMemBuf;
initialize GDT for 32 bits code segmentmov esi, CODE32_SEGMENTmov edi, CODE32_DESCcall InitDescItemmov esi, DATA32_SEGMENTmov edi, DATA32_DESCcall InitDescItemmov esi, STACK32_SEGMENTmov edi, STACK32_DESCcall InitDescItem;
initialize GDT pointer structmov eax, 0mov ax, dsshl eax, 4add eax, GDT_ENTRYmov dword [GdtPtr + 2], eax;
1. load GDTlgdt [GdtPtr];
2. close interruptcli ;
3. open A20in al, 0x92or al, 00000010bout 0x92, al;
4. enter protect modemov eax, cr0or eax, 0x01mov cr0, eax;
5. jump to 32 bits codejmp dword Code32Selector : 0;
esi--> code segment label;
edi--> descriptor labelInitDescItem:push eaxmov eax, 0mov ax, csshl eax, 4add eax, esimov word [edi + 2], axshr eax, 16mov byte [edi + 4], almov byte [edi + 7], ahpop eaxret;
;
GetMemSize:push eaxpush ebxpush ecxpush edxmov dword [MEM_SIZE], 0xor eax, eaxmov eax, 0xE801int 0x15jc geterrshl eax, 10;
eax = eax * 1024;
shl ebx, 6;
ebx = ebx * 64;
shl ebx, 10;
ebx = ebx * 1024;
mov ecx, 1shl ecx, 20;
ecx = 1MBadd dword [MEM_SIZE], eaxadd dword [MEM_SIZE], ebxadd dword [MEM_SIZE], ecxjmp getokgeterr:mov dword [MEM_SIZE], 0getok:pop edxpop ecxpop ebxpop eaxret;
return ;
eax--> 0 : succeed1 : failedInitSysMemBuf: push edipush ebxpush ecxpush edxmov edi, MEM_ARDSmov ebx, 0memerr:mov dword [MEM_ARDS_NUM], 0mov eax, 1doloop:mov eax, 0xE820mov edx, 0x534D4150mov ecx, 20int 0x15jc memerradd edi, 20inc dword [MEM_ARDS_NUM]cmp ebx, 0jne doloopmov eax,0jmp memokmemok:pop edxpop ecxpop ebxpop ediret[section .s32][bits 32]CODE32_SEGMENT:mov ax, VideoSelectormov gs, axmov ax, Stack32Selectormov ss, axmov eax, TopOfStack32mov esp, eaxmov ax, Data32Selectormov ds, axmov ebp, DTOS_OFFSETmov bx, 0x0Cmov dh, 12mov dl, 33call PrintStringmov ebp, HELLO_WORLD_OFFSETmov bx, 0x0Cmov dh, 13mov dl, 31call PrintStringjmp $;
ds:ebp--> string address;
bx--> attribute;
dx--> dh : row, dl : colPrintString:push ebppush eaxpush edipush cxpush dxprint:mov cl, [ds:ebp]cmp cl, 0je endmov eax, 80mul dhadd al, dlshl eax, 1mov edi, eaxmov ah, blmov al, clmov [gs:edi], axinc ebpinc dljmp printend:pop dxpop cxpop edipop eaxpop ebpretCode32SegLenequ$ - CODE32_SEGMENT[section .gs][bits 32]STACK32_SEGMENT:times 1024 * 4 db 0Stack32SegLen equ $ - STACK32_SEGMENTTopOfStack32equ Stack32SegLen - 1
文章图片
结果分析
【x86|操作系统-获取物理内存容量】首先通反编译找到函数的调用地址,然后对该处设置地址,同时也对下个地址进行设置,同时打上断点,通过书写的函数,找到所需查看的地址,如下图所示
文章图片
文章图片
文章图片
文章图片
首先查看全局函数0x904d的内存,发下为0是我们所需的结果,查看寄存器eax寄存器为0,说明调用成功,再次查看0x904d发现其为6,这次需要查看数组前六个元素,通过数组地址0x9051,发现其有起始地址,长度,类型,通过打印六次之后,所打印的结果为0,说明得到内存信息
文章图片
文章图片
文章图片