以前嫌麻烦留着没看,今天补了一下,发现其实只是一些api的调用罢了,这里做个笔记。
对于我们用户来来说,获取物理内存的容量一般用内核调用即可,比如Linux 2.6中使用的detect_memory函数调用。其本质就是使用BIOS的中断0x15检测的。
这里稍微解释一下BIOS(可跳过),BIOS,基本输入输出系统,PC启动加载的第一个软件,介于操作系统与硬件的抽象层,可用于检测,加载引导程序或者操作系统。显然,PC的启动流程应该是 BIOS -> MBR -> loader -> kernel。
简要说明
开始正文。中断0x15有三个子功能,子功能号放到EAX或者AX中,其简要说明:
- EAX = 0xE820 遍历所有内存
- AX = 0xE801 分别检测低 15 MB 和 16 MB ~ 4 GB的内存,最大支持 4 GB。
- AX = 0x88 最简单的子功能,只能检测64MB的内存,若超过也只返回 64 MB。
三分之二的功能对于我们当今的64位系统基本是废的,可见BIOS被淘汰也是历史必然的趋势,虽然现在还活着。
0xE820
0xE820的功能最强,当然也最复杂,它需要多次调用,每次调用都返回一种类型的内存,直到检测完毕。其结果主要通过ARDS结构存储在内存中。ARDS全称地址范围描述符
ARDS结构如下
字节偏移量 | 描述 |
---|---|
0 | 基地址低32位 |
4 | 基地址高32位 |
8 | 内存长度的低32位,单位字节 |
12 | 内存长度的低32位,单位字节 |
16 | 类型 |
下面是使用前使用说明
寄存器 | 用途说明 |
---|---|
EAX | 子功能号,这里位 0xE820 |
EBX | ARDS后续值,必须初始化0,表示下一个待返回的ADRS |
ES:DI | ARDS写入的内存 |
ECX | ARDS 的大小,这里是20字节 |
EDX | 固定为0x534d4150 ,”SMAP”的ASCII码,意义不明 |
寄存器 | 用途说明 |
---|---|
CF位 | CF为1,调用出错,否则正常 |
EAX | 固定为 SMAP的ASCII码 |
ES:DI | 同输入 |
ECX | 同输入 |
EBX | 更新后的ARDS后续值 |
0xE801
0x801是稍微适中一点的方法,但是最多只能识别4GB,对于32位系统一般是足够的。
调用结果放在两个寄存器中,低于15KB的内存以1KB位单位,存储在AX和CX中,高于16KB的内存以64KB位单位,存储在BX和DX中。
稍微计算一下便可得出,AX和CX的最大值应为 0x3c00 ,BX和DX的最大值应为 0x10000。
提一下“消失”的 15MB ~ 16MB 去哪了,仍然是历史遗留问题,80286的寻址空间是16MB,当时有些ISA设备需要用到 这1MB空间作为缓存,然后就空出来了。为了兼容,就变成现在这样了。
以下是 0xE801 的使用步骤:
- AX寄存器写入 0xE801
- 调用中断0x15
- CF为0的情况下,输出 $ AX \times 1024 + BX \times 64 \times 1024 $,最后单位是byte。
0x88
最简单的检测法,不详说,只能检测到 物理内存 1MB 之上的内存,故最多先是63MB。
其使用步骤为
- 在 ax 中写入0x88
- 调用中断0x15
- CF位为0的情况下,单位大小为1KB,输出对应结果 $ ax \times 1024 + 1 MB $ 字节
下面是一段三个方法的汇编程序
total_mem_bytes dd 0
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
ards_buf times 244 db 0
ards_nr dw 0
loader_start:
xor ebx, ebx
mov edx, 0x534d4150
mov di, ards_buf
.e820_mem_get_loop:
mov eax, 0x0000e820
mov ecx, 20
int 0x15
jc .e820_failed_so_try_e801
add di, cx
inc word [ards_nr]
cmp ebx, 0
jnz .e820_mem_get_loop
mov cx, [ards_nr]
mov ebx, ards_buf
xor edx, edx
.find_max_mem_area:
mov eax, [ebx]
add eax, [ebx+8]
add ebx, 20
cmp edx, eax
jge .next_ards
mov edx, eax
.next_ards:
loop .find_max_mem_area
jmp .mem_get_ok
.e820_failed_so_try_e801:
mov ax,0xe801
int 0x15
jc .e801_failed_so_try88
mov cx,0x400
mul cx
shl edx,16
and eax,0x0000FFFF
or edx,eax
add edx, 0x100000
mov esi,edx
xor eax,eax
mov ax,bx
mov ecx, 0x10000
mul ecx
add esi,eax
mov edx,esi
jmp .mem_get_ok
.e801_failed_so_try88:
mov ah, 0x88
int 0x15
jc .error_hlt
and eax,0x0000FFFF
mov cx, 0x400
mul cx
shl edx, 16
or edx, eax
add edx,0x100000
.mem_get_ok:
mov [total_mem_bytes], edx ;存入total_mem_bytes处。
....
.error_hlt
hlt