1个人开发操作系统之内存容量检测
来源:互联网 发布:美发课程软件下载 编辑:程序博客网 时间:2024/04/30 09:37
内存管理的第一步是要知道内存的容量大小,也就是内存终了地址。检测内存容量的方法之一是从BIOS获取数据,但是使用BIOS的版本不同,获取的方式也大不相同。因此操作系统开发时采用另一种方法,就是检测内存地址是否有效。
首先,如果是486CPU,要先把缓存(Cache)屏蔽。为了提高CPU和内存之间数据交换的速度,CPU内的缓存会保留内存中刚被访问的数据。例如在CPU第一次访问0x18地址的数据54时,先会将54拷贝到缓存中,第二次访问0x18地址的数据时,就可以直接得到缓存中的54了。再例如for(i=0;i<100;i++)语句里的i的值被更新了100次,其实内存中的i值更新了1次,也就是第100次更新时,i才被写入内存,而前99次i的值只是在缓存里更新。
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,区分386和486*/
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)完成内存容量的检测。start,end是要检测的初始地址和结束地址。循环检测该范围内的每个地址是否有效。检测的方式是:
1.把地址的值保存;
2.向该地址写入一个值;
3.取该地址值得异或(XOR);
4.如果取得的异或值与正确的不一致,则表示该地址无效,结束测试,返回循环次数;
如果一致,则去5;
5.再次取该值得异或,结果如果和原值不等,则去4;
如果值相等,则表示该地址有效,去6;
6.恢复该地址的值,继续检测下一个地址,去1。
注意,以上的测试方法如果用C语言实现,编译时,不能使用优化参数。因为GCC编译器会认为1,2,3,4,5步都是冗余操作,自作主张的把它删掉。最后会变成只循环,不做任何处理。为了避免GCC的优化处理,本文使用汇编来实现。每次检测0x1000(4KB)个地址。
_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,检测末尾4个Bytes的地址。
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);
如图:
为什么只有32MB?
因为虚拟机QEMU设定了内存的大小
qemu.exe -L . -m 32 -localtime -std-vga -fda boot.bin
将32改为64后,运行结果则为:
- 1个人开发操作系统之内存容量检测
- 1个人开发操作系统之C语言的开始
- 1个人开发操作系统之C语言的开始
- 1个人开发操作系统之界面与字体
- 1个人开发操作系统之数组与字符串的使用
- 1个人开发操作系统之显示变量值
- 1个人开发操作系统之GDT和IDT的初始化
- 1个人开发操作系统-初篇
- 1个人开发操作系统-初篇
- 1个人开发操作系统-初篇
- 1个人开发操作系统-初篇
- 内存容量
- 破解内存容量的“缩水”之谜
- 个人开发之路
- 操作系统栈溢出检测之ucosII篇
- 检测操作系统
- 检测操作系统
- 1个人开发操作系统-初篇 http://blog.csdn.net/tyrone1979/archive/2006/08/03/1013734.aspx
- 我的真正blog地址
- 通宵结束
- 王文京的SOA新机会:用友中端ERP价格悄然回升
- 防盗链 request.servervariables 服务器变量
- JVM调优
- 1个人开发操作系统之内存容量检测
- [Postfix讨论] ExtMail Solution HOWTO for Linux
- 感动:如果滑倒,快松开我的手
- 在SDK程序中使用MFC中的辅助类库
- J2EE的概念
- 日本海军纲鉴
- 一个AJAX的缓存策略
- 本人有时间做了一个日程安排和名片管理系统,B/S结构,sql+ASP.NET(C#),赠送大家免费使用,需要的请留下EMAIL!
- 61条面向对象设计的经验原则