x86|操作系统-获取物理内存容量

我们知分页机制的关键是页请求和页置换,并且页置换发生在没有空闲页框的时候,但是现在出现了问题-是否可以知道还剩多少空闲页框?并且还有多少页框可用?--在这里就得获取物理内存的大小了
一.获取物理内存容量 在获取物理内存大小时,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.标志寄存器
x86|操作系统-获取物理内存容量
文章图片

对int 0x15基础功能进行简单示例介绍
x86|操作系统-获取物理内存容量
文章图片

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

x86|操作系统-获取物理内存容量
文章图片
x86|操作系统-获取物理内存容量
文章图片
x86|操作系统-获取物理内存容量
文章图片

分析
从上图首先对获取内存函数进行设置,并且对其进行调用,首先运行bochs得到打印结果,发现程序没有出现错误,但是在这里无法对内存进行查看,所以需要反编译,在该段函数中反编译中,由于是在16位进行的,所以不需要打印32反编译结果,在反编译之后,找到函数所对应的代码,并进行断点设置,如下所示
x86|操作系统-获取物理内存容量
文章图片
x86|操作系统-获取物理内存容量
文章图片
x86|操作系统-获取物理内存容量
文章图片

设置完之后,在bochs进行断点设置进行调试,同时需要找到内存改变的点对应的内存值,分别两次查看对应的内存情况,如下图所示
x86|操作系统-获取物理内存容量
文章图片

x86|操作系统-获取物理内存容量
文章图片

x86|操作系统-获取物理内存容量
文章图片

将得到的内存值通过进制转换为10进制可以得到
x86|操作系统-获取物理内存容量
文章图片

但是该结果不表示位MB,通过相除1024*1024得到的结果位31MB,但是在配置文件中得到的文件大小位32MB
x86|操作系统-获取物理内存容量
文章图片
x86|操作系统-获取物理内存容量
文章图片

在这里获取的内存容量用两部分来表示,并且缺少1MB的原因是80286中的24根地址线最大寻址范围为16MB,之前在介绍两端的容量时,中间正好缺少1MB的内存
x86|操作系统-获取物理内存容量
文章图片

进行修改-添加1MB的内存
x86|操作系统-获取物理内存容量
文章图片

通过之前相同的设置以及断点设置,得到的值为0x02000000,通过进制转换为33554432,通过计算为32MB.
二.获取物理内存容量 上面介绍的是基础功能,这里介绍的是高级功能x86|操作系统-获取物理内存容量
文章图片

参数的设置
x86|操作系统-获取物理内存容量
文章图片

edx是bios所需要的值,不做深究
地址范围描述结果(ARDS)
x86|操作系统-获取物理内存容量
文章图片
x86|操作系统-获取物理内存容量
文章图片

代码实验
%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|操作系统-获取物理内存容量
文章图片

结果分析
【x86|操作系统-获取物理内存容量】首先通反编译找到函数的调用地址,然后对该处设置地址,同时也对下个地址进行设置,同时打上断点,通过书写的函数,找到所需查看的地址,如下图所示
x86|操作系统-获取物理内存容量
文章图片
x86|操作系统-获取物理内存容量
文章图片
x86|操作系统-获取物理内存容量
文章图片
x86|操作系统-获取物理内存容量
文章图片

首先查看全局函数0x904d的内存,发下为0是我们所需的结果,查看寄存器eax寄存器为0,说明调用成功,再次查看0x904d发现其为6,这次需要查看数组前六个元素,通过数组地址0x9051,发现其有起始地址,长度,类型,通过打印六次之后,所打印的结果为0,说明得到内存信息
x86|操作系统-获取物理内存容量
文章图片
x86|操作系统-获取物理内存容量
文章图片
x86|操作系统-获取物理内存容量
文章图片

    推荐阅读