参考
https://blog.51cto.com/13475106/category6.html及狄泰软件相关课程
我们知分页机制的关键是页请求和页置换,并且页置换发生在没有空闲页框的时候,但是现在出现了问题-是否可以知道还剩多少空闲页框?并且还有多少页框可用?--在这里就得获取物理内存的大小了
一.获取物理内存容量
在获取物理内存大小时,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 0x9000 jmp ENTRY_SEGMENT [section .gdt] ; GDT definition ; 段基址, 段界限, 段属性 GDT_ENTRY : Descriptor 0, 0, 0 CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32 VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32 DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DRW + DA_32 STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32 ; GDT end GdtLen equ $ - GDT_ENTRY GdtPtr: dw GdtLen - 1 dd 0 ; GDT Selector Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0 VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0 Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0 Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0 ; end of [section .gdt] TopOfStack16 equ 0x7c00 [section .dat] [bits 32] DATA32_SEGMENT: DTOS db "D.T.OS!", 0 DTOS_OFFSET equ DTOS - $$ HELLO_WORLD db "Hello World!", 0 HELLO_WORLD_OFFSET equ HELLO_WORLD - $$ Data32SegLen equ $ - DATA32_SEGMENT ; MEM_SIZE times 4 db 0 [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_DESC call InitDescItem mov esi, DATA32_SEGMENT mov edi, DATA32_DESC call InitDescItem mov esi, STACK32_SEGMENT mov edi, STACK32_DESC call 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 eax mov 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], ah pop eax ret ; ; GetMemSize: push eax push ebx push ecx push edx mov dword [MEM_SIZE], 0 xor eax, eax mov eax, 0xE801 int 0x15 jc geterr shl 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], ebx jmp getok geterr: mov dword [MEM_SIZE], 0 getok: pop edx pop ecx pop ebx pop eax ret [section .s32] [bits 32] CODE32_SEGMENT: mov ax, VideoSelector mov gs, ax mov ax, Stack32Selector mov ss, ax mov eax, TopOfStack32 mov esp, eax mov ax, Data32Selector mov ds, ax mov ebp, DTOS_OFFSET mov bx, 0x0C mov dh, 12 mov dl, 33 call PrintString mov ebp, HELLO_WORLD_OFFSET mov bx, 0x0C mov dh, 13 mov dl, 31 call PrintString jmp $ ; ds:ebp --> string address ; bx --> attribute ; dx --> dh : row, dl : col PrintString: push ebp push eax push edi push cx push dx print: 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 print end: pop dx pop cx pop edi pop eax pop ebp ret Code32SegLen equ $ - CODE32_SEGMENT [section .gs] [bits 32] STACK32_SEGMENT: times 1024 * 4 db 0 Stack32SegLen equ $ - STACK32_SEGMENT TopOfStack32 equ Stack32SegLen - 1
分析
从上图首先对获取内存函数进行设置,并且对其进行调用,首先运行bochs得到打印结果,发现程序没有出现错误,但是在这里无法对内存进行查看,所以需要反编译,在该段函数中反编译中,由于是在16位进行的,所以不需要打印32反编译结果,在反编译之后,找到函数所对应的代码,并进行断点设置,如下所示
设置完之后,在bochs进行断点设置进行调试,同时需要找到内存改变的点对应的内存值,分别两次查看对应的内存情况,如下图所示
将得到的内存值通过进制转换为10进制可以得到
但是该结果不表示位MB,通过相除1024*1024得到的结果位31MB,但是在配置文件中得到的文件大小位32MB
在这里获取的内存容量用两部分来表示,并且缺少1MB的原因是80286中的24根地址线最大寻址范围为16MB,之前在介绍两端的容量时,中间正好缺少1MB的内存
进行修改-添加1MB的内存
添加后代码如下:
%include "inc.asm" org 0x9000 jmp ENTRY_SEGMENT [section .gdt] ; GDT definition ; 段基址, 段界限, 段属性 GDT_ENTRY : Descriptor 0, 0, 0 CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32 VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32 DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DRW + DA_32 STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32 ; GDT end GdtLen equ $ - GDT_ENTRY GdtPtr: dw GdtLen - 1 dd 0 ; GDT Selector Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0 VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0 Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0 Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0 ; end of [section .gdt] TopOfStack16 equ 0x7c00 [section .dat] [bits 32] DATA32_SEGMENT: DTOS db "D.T.OS!", 0 DTOS_OFFSET equ DTOS - $$ HELLO_WORLD db "Hello World!", 0 HELLO_WORLD_OFFSET equ HELLO_WORLD - $$ Data32SegLen equ $ - DATA32_SEGMENT ; MEM_SIZE times 4 db 0 [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_DESC call InitDescItem mov esi, DATA32_SEGMENT mov edi, DATA32_DESC call InitDescItem mov esi, STACK32_SEGMENT mov edi, STACK32_DESC call 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 eax mov 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], ah pop eax ret ; ; GetMemSize: push eax push ebx push ecx push edx mov dword [MEM_SIZE], 0 xor eax, eax mov eax, 0xE801 int 0x15 jc geterr shl eax, 10 ; eax = eax * 1024; shl ebx, 6 ; ebx = ebx * 64; shl ebx, 10 ; ebx = ebx * 1024; mov ecx, 1 shl ecx, 20 ; ecx = 1MB add dword [MEM_SIZE], eax add dword [MEM_SIZE], ebx add dword [MEM_SIZE], ecx jmp getok geterr: mov dword [MEM_SIZE], 0 getok: pop edx pop ecx pop ebx pop eax ret [section .s32] [bits 32] CODE32_SEGMENT: mov ax, VideoSelector mov gs, ax mov ax, Stack32Selector mov ss, ax mov eax, TopOfStack32 mov esp, eax mov ax, Data32Selector mov ds, ax mov ebp, DTOS_OFFSET mov bx, 0x0C mov dh, 12 mov dl, 33 call PrintString mov ebp, HELLO_WORLD_OFFSET mov bx, 0x0C mov dh, 13 mov dl, 31 call PrintString jmp $ ; ds:ebp --> string address ; bx --> attribute ; dx --> dh : row, dl : col PrintString: push ebp push eax push edi push cx push dx print: 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 print end: pop dx pop cx pop edi pop eax pop ebp ret Code32SegLen equ $ - CODE32_SEGMENT [section .gs] [bits 32] STACK32_SEGMENT: times 1024 * 4 db 0 Stack32SegLen equ $ - STACK32_SEGMENT TopOfStack32 equ Stack32SegLen - 1
通过之前相同的设置以及断点设置,得到的值为0x02000000,通过进制转换为33554432,通过计算为32MB.