1个人开发操作系统之内存容量检测

本文介绍了一种检测内存容量的方法,特别适用于486 CPU。通过屏蔽缓存并检查特定地址的有效性来确定可用内存的大小。文章还提供了具体的汇编语言实现,并展示了在不同内存设置下的检测结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内存管理的第一步是要知道内存的容量大小,也就是内存终了地址。检测内存容量的方法之一是从BIOS获取数据,但是使用BIOS的版本不同,获取的方式也大不相同。因此操作系统开发时采用另一种方法,就是检测内存地址是否有效。

首先,如果是486CPU,要先把缓存(Cache)屏蔽。为了提高CPU和内存之间数据交换的速度,CPU内的缓存会保留内存中刚被访问的数据。例如在CPU第一次访问0x18地址的数据54时,先会将54拷贝到缓存中,第二次访问0x18地址的数据时,就可以直接得到缓存中的54了。再例如for(i=0;i<100;i++)语句里的i的值被更新了100次,其实内存中的i值更新了1次,也就是第100次更新时,i才被写入内存,而前99i的值只是在缓存里更新。

386时代,CPU内没有缓存,486时代,有8-16KB的缓存,性能就提高了6倍以上。缓存虽然可以提高性能,但给内存容量的检测带来了不便,内存容量检测可以用一下的方式:先在一个地址中写入一个数值,然后再读取,如果读取的数值和写入的相等,则表示有个地址是内存中的有效地址。但是如果CPU里有缓存,这个方法就无效了,因此我们事先要把CPU的缓存屏蔽。

屏蔽CPU缓存的程序如下:

#define EFLAGS_AC_BIT 0x00040000

#define CR0_CACHE_DISABLE 0x60000000

unsigned int memtest(unsigned int start,unsigned int end)

{

char flg486=0;

unsigned int eflg,cr0,i;

/*386 or 486*/

eflg=io_load_eflags();

eflg |= EFLAGS_AC_BIT; /*AC-bit=1,区分386486*/

io_store_eflags(eflg);

eflg=io_load_eflags();

if((eflg & EFLAGS_AC_BIT)!=0){

flg486=1;

}

eflg&=~EFLAGS_AC_BIT; /*AC-bit=0*/

io_store_eflags(eflg);

if(flg486!=0){

cr0=load_cr0();/*486,需要屏蔽缓存*/

cr0!=CR0_CACHE_DISABLE; /*ban cache*/

store_cr0(cr0);

}

i=memtest_sub(start,end);

if(flg486!=0){

cr0=load_cr0();

cr0&=~CR0_CACHE_DISABLE; /*cache permit*/

store_cr0(cr0);

}

return i;

}

其中

_load_cr0: ;int load_cr0(void)

mov eax,cr0

ret

_store_cr0: ;void store_cr0(int cr0);

mov eax,[esp+4]

mov cr0,eax

ret

函数unsigned int memtest_sub(unsigned int start,unsigned int end)完成内存容量的检测。startend是要检测的初始地址和结束地址。循环检测该范围内的每个地址是否有效。检测的方式是:

1.把地址的值保存;

2.向该地址写入一个值;

3.取该地址值得异或(XOR);

4.如果取得的异或值与正确的不一致,则表示该地址无效,结束测试,返回循环次数;

如果一致,则去5

5.再次取该值得异或,结果如果和原值不等,则去4

如果值相等,则表示该地址有效,去6

6.恢复该地址的值,继续检测下一个地址,去1

注意,以上的测试方法如果用C语言实现,编译时,不能使用优化参数。因为GCC编译器会认为12345步都是冗余操作,自作主张的把它删掉。最后会变成只循环,不做任何处理。为了避免GCC的优化处理,本文使用汇编来实现。每次检测0x10004KB)个地址。

_memtest_sub: ;unsigned int memtest_sub(unsigned int start,unsigned int end)

push edi ;

push esi

push ebx

mov esi,0xaa55aa55 ;pat0=0xaa55aa55;

mov edi,0x55aa55aa ;pat1=0x55aa55aa;

mov eax,[esp+12+4] ;i=start;

mts_loop:

mov ebx,eax

add ebx,0xffc ;p=i+0xffc,检测末尾4Bytes的地址。

mov edx,[ebx] ;old=*p;

mov [ebx],esi ;*p=pat0;

xor dword [ebx],0xffffffff ;*p^=0xffffffff; 异或

cmp edi,[ebx] ;if(*p!=pat1) goto fin; 不等则是无效

jne mts_fin

xor dword [ebx],0xffffffff; *p^=0xffffffff; 异或

cmp esi,[ebx] ;if(*p!=pat0) goto fin; 不等则是无效

jne mts_fin

mov [ebx],edx ;*p=old; 有效,继续下4KB地址的检测

add eax,0x1000 ;i+=0x1000;

cmp eax,[esp+12+8]

jbe mts_loop

pop ebx

pop esi

pop edi

ret

mts_fin:

mov [ebx],edx ;*p=old;

pop ebx

pop esi

pop edi

ret

检测结果显示:

bootpack.c中加入如下语句:

i=memtest(0x00400000,0xbfffffff)/(1024*1024); /*检测范围为3GB,结果以兆为单位*/

sprintf(s,"memory %dmb",i);

drawstring(binfo->vram, binfo->scrnx,10, binfo->scrny-50, COL8_FFFFFF, s);

如图:

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 270pt; HEIGHT: 177.75pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5C%E8%B6%99%E7%A3%8A%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.png"></imagedata></shape>

为什么只有32MB

因为虚拟机QEMU设定了内存的大小

qemu.exe -L . -m 32 -localtime -std-vga -fda boot.bin

32改为64后,运行结果则为:

<shape id="_x0000_i1026" style="WIDTH: 270pt; HEIGHT: 177.75pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5C%E8%B6%99%E7%A3%8A%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image003.png"></imagedata></shape>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值