30天自制操作系统日志第9天.docx
- 文档编号:9061992
- 上传时间:2023-02-03
- 格式:DOCX
- 页数:13
- 大小:574.04KB
30天自制操作系统日志第9天.docx
《30天自制操作系统日志第9天.docx》由会员分享,可在线阅读,更多相关《30天自制操作系统日志第9天.docx(13页珍藏版)》请在冰豆网上搜索。
30天自制操作系统日志第9天
操作系统实验日志
学号
20160810520
姓名
甘昆禄
专业年级班级
智能1601
实验日期
2018
实验项目
第1天:
……
一、实验主要内容
1、内存管理前的容量检查
管理内存先要知道容量有多大,这里作者介绍了自己的方法。
就是用指针去检查能否指向正确的地址,通过往指针对应内容写入再读出即可实现,这里向内容读入和写出就涉及到了一个概念,cache高速缓存,学过计组的都该知道。
CPU访问内存时寄存器对内存单元的访问速度并不快,相比寄存器与寄存器之间的访问而言,为此CPU的设计者使用缓存这一概念提高访问速度,读取内存单元数据时,CPU会先检查缓存中,如果存在则取出,这样无需通过访问内存单元。
当CPU向内存单元写入数据前,会先往缓存里写入。
值得一提的是机器语言乃至汇编语言,高级语言,程序运行的大部分时间都花费在循环上,为此。
比如for(inti=0;i<100;i++),CPU在缓存内会对i频繁赋值进行处理,在循环结束后才往内存单元中写入i最终的值,而不是每循环一次写入一次。
我们在检查内存时需要注意CPU是否有缓存,如果在忽略CPU缓存的情况下对内存进行检查是否被使用,而CPU缓存已经预约了内存中的某块地址,而仅仅是还没写入内存而已,这样我们的程序将会混乱。
486以上的CPU都有高速缓存这一机制,当我们检查到CPU位486以上时,我们需要先将CPU的高速缓存置为OFF,那么如何检查CPU是386还是486以上,我们检查EFLAGS的第18位即所谓的AC标志,如果CPU是386那么AC标志位则为0,如果是486以上则为1。
我们检查AC标志位是否为1即可。
unsignedintmemtest(unsignedintstart,unsignedintend)
{
charflg486=0;
unsignedinteflg,cr0,i;
/*确认CPU是386还是486以上的*/
eflg=io_load_eflags();
eflg|=EFLAGS_AC_BIT;/*AC-bit=1*/
io_store_eflags(eflg);
eflg=io_load_eflags();
if((eflg&EFLAGS_AC_BIT)!
=0){
/*如果是386,即使设定AC=1,AC的值还会自动回到0*/
flg486=1;
}
eflg&=~EFLAGS_AC_BIT;/*AC-bit=0*/
io_store_eflags(eflg);
if(flg486!
=0){
cr0=load_cr0();
cr0|=CR0_CACHE_DISABLE;/*禁止缓存*/
store_cr0(cr0);
}
i=memtest_sub(start,end);
if(flg486!
=0){
cr0=load_cr0();
cr0&=~CR0_CACHE_DISABLE;/*允许缓存*/
store_cr0(cr0);
}
returni;
}
这里对CR0的作用就是对CR0寄存器的第29,30号位进行操作,而相关函数是同汇编代码写的,存在naskfunc.nas里。
所以memtest_sub(start,end)函数是内存容量检查的重点。
如下:
unsignedintmemtest_sub(unsignedintstart,unsignedintend)
{
unsignedinti,*p,old,pat0=0xaa55aa55,pat1=0x55aa55aa;
for(i=start;i<=end;i+=0x1000){
p=(unsignedint*)(i+0xffc);
old=*p;/*先记住修改前的值*/
*p=pat0;/*试写*/
*p^=0xffffffff;/*反转*/
if(*p!
=pat1){/*检查反转结果*/
not_memory:
*p=old;
break;
}
*p^=0xffffffff;/*再次反转*/
if(*p!
=pat0){/*检查值是否恢复*/
gotonot_memory;
}
*p=old;/*恢复为修改之前的值*/
}
returni;
}
就像刚刚说的,作者通过for循环不断写入读出相应地址的值来判断是否存在合理内存地址,直到取到最后return的i即为内存大小,但是这里实际上是出了问题的,是编译器太优秀了哈哈哈。
相应的汇编如下:
(作者debug到这里还真是不容易啊,啊哈哈看大牛也出bug为什么这么开心)
_memset_sub:
PUSHEBP;C编译器的固定语句
MOVEBP,ESP;
MOVEDX,DWORD[12+EBP];EDX=end
MOVEAX,DWORD[8+EBP];EAX=start;/*EAX是i*/
CMPEAX,EDX;;if(EAX>EDX)gotoL30;
JAL30
L36:
L34:
ADDEAX,4096;EAX+=0x1000
CMPEAX,EDX;if(EAX<=EDX)gotoL36;
JBEL36
L30:
POPEBP;接受前文中push的EBP
RET;return;
然后经过作者分析,这里面for循环实际什么都没有做哈哈哈
这是因为啊我们作者的初衷是要找出无意义的地址,然后结束for循环,所以不断的进行数据的反转。
但是呢,这个C编译器很气人,它默认地址都存在,不会认为你会对没有内存的地方进行读写,所以把作者的不断数据反转看作没有意义的操作,全部优化至for循环“一丝不挂”。
换编译器是不可能换编译器的,这辈子都不会换的,只能先用汇编代码来实现内存容量检查了:
_memtest_sub:
;unsignedintmemtest_sub(unsignedintstart,unsignedintend)
PUSHEDI;由于还要使用EBX,ESI,EDI
PUSHESI
PUSHEBX
MOVESI,0xaa55aa55;pat0=0xaa55aa55;
MOVEDI,0x55aa55aa;pat1=0x55aa55aa;
MOVEAX,[ESP+12+4];i=start;
mts_loop:
MOVEBX,EAX
ADDEBX,0xffc;p=i+0xffc;
MOVEDX,[EBX];old=*p;
MOV[EBX],ESI;*p=pat0;
XORDWORD[EBX],0xffffffff;*p^=0xffffffff;
CMPEDI,[EBX];if(*p!
=pat1)gotofin;
JNEmts_fin
XORDWORD[EBX],0xffffffff;*p^=0xffffffff;
CMPESI,[EBX];if(*p!
=pat0)gotofin;
JNEmts_fin
MOV[EBX],EDX;*p=old;
ADDEAX,0x1000;i+=0x1000;
CMPEAX,[ESP+12+8];if(i<=end)gotomts_loop;
JBEmts_loop
POPEBX
POPESI
POPEDI
RET
mts_fin:
MOV[EBX],EDX;*p=old;
POPEBX
POPESI
POPEDI
RET
2、内存管理法1
那么管理内存又有什么用呢,假设我们内存任意使用,例如应用程序A需要120kb内存,画面控制需要100kb。
在内存被使用时应该标志这一段内存,避免其他应用占用而使系统应用错乱,在应用使用完所需的内存单元后,需要释放那部分不需要的内存单元,继而让其它应用使用。
方法一就是我们把所有内存单元以4KB为单位分割。
然后建立数组,那么就要有128*1024*1024/(4*1024)=32768个数组。
若已用,值为1;若未用,值为0。
这种方法很简单,但是比较占空间。
3、内存管理法2
采用列表管理的方法,把类似“从xx号地址开始的yy字节是空的”这种信息都列在表里。
这种方法不仅省内存,而且在分配和释放大块内存时都很迅速。
structFREEINFO{/*可用信息*/
unsignedintaddr,size;
};
structMEMMAN{/*内存管理*/
intfrees,maxfrees,lostsize,losts;//frees表示目前有空闲条目的数量
structFREEINFOfree[MEMMAN_FREES];
}
初始定义可用空闲条目frees为0条,通过释放让frees自加一。
需要使用内存时就看,每个free数组里的大小够不够,够就分配,就是按顺序哪里有空就放哪里,如果放的free[i]对应的size为0了,就要减少一个空闲列表,然后让i后面的数组排到前面来,占原来i的位置。
然后空闲的条目不能超过1000,作者定义的,如果你在有1000个空余列表了,又释放了一个跟存在的1000个都不能合并的,那就要舍弃了(就是说释放失败,我们仍然判断其为在使用),代码里有记录失败的次数和大小,以后再处理这些数据。
然后就是释放的时候对空闲内存的拼接了。
就有几种情况,代码写的很清楚了,画图又麻烦,我就举几个数字作为例子。
假设我们有10000个格子,一开始定义10000个格子都满了(都在使用),那么空闲条目为0。
然后我们知道程序刚开始,哪些格子是空的,就释放格子1000-9000,那么就有一个空闲条目,初始格子为1000,大小为8000;然后我们(假设经过了多次使用和释放后)使用了2000-3000的格子、5000-6000的格子、6500-8000的格子,那么我们现在的空闲条目应该有4个,就是1000-2000的条目、3000-5000的条目、6000-6500的条目,8000-9000的条目。
现在又想找连续的800个格子来存放东西,那么我们看空闲条目一有1000个空余格子,那么就可以使用来存,如果条目一像条目三一样只有500个格子,那就在检查条目二、三,直到找到有800个格子的条目为止。
那么这800个格子存在了条目一里,条目一就剩200个格子了,对应的信息就变为1800-2000(保存的时候信息为1800和200分别表示首地址和大小)。
释放的时候也就好理解了,释放过后能拼接在一起的条目就合并,不能就增加一个条目。
代码如下:
intmemman_free(structMEMMAN*man,unsignedintaddr,unsignedintsize)
/*释放*/
{
inti,j;
/*为便于归纳内存,将free[]按照addr的顺序排列*/
/*所以,先决定应该放在哪里*/
for(i=0;i
if(man->free[i].addr>addr){
break;
}
}
/*free[i-1].addr if(i>0){ /*前面有可用内存*/ if(man->free[i-1].addr+man->free[i-1].size==addr){ /*可以与前面的可用内存归纳到一起*/ man->free[i-1].size+=size; if(i /*后面也有*/ if(addr+size==man->free[i].addr){ /*也可以与后面的可用内存归纳到一起*/ man->free[i-1].size+=man->free[i].size; /*man->free[i]删除*/ /*free[i]变成0后归纳到前面去*/ man->frees--; for(;i man->free[i]=man->free[i+1];/*结构体赋值*/ } } } return0;/*成功完成*/ } } /*不能与前面的可用空间归纳到一起*/ if(i /*后面还有*/ if(addr+size==man->free[i].addr){ /*可以与后面的内容归纳到一起*/ man->free[i].addr=addr; man->free[i].size+=size; return0;/*成功完成*/ } } /*既不能与前面归纳到一起,也不能与后面归纳到一起*/ if(man->frees /*free[i]之后的,向后移动,腾出一点可用空间*/ for(j=man->frees;j>i;j--){ man->free[j]=man->free[j-1]; } man->frees++; if(man->maxfrees man->maxfrees=man->frees;/*更新最大值*/ } man->free[i].addr=addr; man->free[i].size=size; return0;/*成功完成*/ } /*不能往后移动*/ man->losts++; man->lostsize+=size; return-1;/*失败*/ } 二、遇到的问题及解决方法 1、首先这个内存容量32M是怎么得来的,作者一句话“根据模拟器的设定”,是不是默认32M就好了,为什么是32M而不是其他,有什么不同和好处吗? 2、还有一个疑问就是作者说当空余条目太多就会被舍弃(权当在使用,暂时不释放),当menman有空余时(frees小于1000吧),就将那部分内容再捡回来。 这个内容怎么实现呢? 如何对内存进行检查? 知道这块到底在不在使用呢? 三、程序设计创新点 1、我对内存的释放和分配进行了实验,然后还显示了当前空闲条目数,发现代码操作都是正确的,可以得到正确的空余内存大小和条目数都是正确的。 修改的那部分代码如下: 结果正确632kb+0x0001000+0x00010000+0x00afffff-0x00a80000=632+640=1272kb 四、实验心得体会 这次实验主要也是承接了上一次,因为我们上次也是讲的内存,而且刚好讲到0x00400000,这个部分,前面存的大概类似于内核,用户级别的是不能更改的,我们的启动区初始化等就在那里。 然后今天讲的的32M总内存中剩余的28M可用内存(当然还有前面那部分632KB)的管理方法,两种方法都不错,各有好处。 前面那部分检查内存容量的部分如此细讲,我觉得鸡肋了,跟管理没什么大的关系嘛,作者大可直接和我们说内存大小,然后讲屏蔽cache就可以了。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 30 自制 操作系统 日志