天天看点

《30天自制操作系统》笔记(07)——内存管理进度回顾什么是内存管理如何获取内存容量内存管理算法总结

《30天自制操作系统》笔记(07)——内存管理

中处理掉了绝大部分与CPU配置相关的东西。本篇介绍内存管理的思路和算法。

现在想想,从软件工程师的角度看,CPU也只是一个软件而已:它的功能就是加载指令、执行指令和响应中断,而响应中断也是在加载指令、执行指令。就像火车沿着一条环形铁轨前进;当中断发生时,就好像铁轨岔口处变轨了,火车就顺着另一条轨迹走了;走完之后又绕回来重新开始。决定CPU是否变轨的,就是CPU里的特定寄存器。

《30天自制操作系统》笔记(07)——内存管理进度回顾什么是内存管理如何获取内存容量内存管理算法总结

这是题外话,就此为止。

假设内存大小是128MB,应用程序A暂时需要100KB,画面控制需要1.2MB……。

像这样,操作系统有时要分配一定大小的内存,用完后又要收回。因此,必须管理好哪些内存空闲可用,哪些正在被占用。这就是内存管理。

内存管理的两个任务,一是内存分配,一是内存释放。

要管理内存,首先得知道操作系统所在的这个计算机内存有多大。检查内存容量的方法很简单,就是从要检查的起始位置到最后位置依次写入一个数值(例如0xaa55aa55),然后按位反转,检查是否正确地完成了反转(变成0x55aa55aa);然后再次反转,检查是否正确地完成了反转。如果反转结果都是正确的,说明这个地址的内存是存在的;否则就说明内存的最大地址就到此为止了。其代码如下。

但直接用C语言来写这个函数的话,C编译器会把它优化成这个样子。

C编译器不会理睬"内存到头了"这种事情,它只在应用程序的层面看问题。所以它认为for循环里的if判定都是必然为真(或为假)的,认为没有被其它代码使用的变量都是没用的。所以它就干脆把这些"没用的"代码删掉了。

为了解决这个问题,还是用汇编语言来写这个memtest_sub函数好了。代码如下。

《30天自制操作系统》笔记(07)——内存管理进度回顾什么是内存管理如何获取内存容量内存管理算法总结
《30天自制操作系统》笔记(07)——内存管理进度回顾什么是内存管理如何获取内存容量内存管理算法总结

汇编版本的memtest_sub

知道了内存容量,就可以进行管理了。

486以上的CPU是有高速缓存(cache)的。CPU每次访问内存,都要将所访问的地址和内容存入cache,也就是存放成这样"18号地址的值是54"。如果下次要用18号地址的内容,CPU就不需要再读内存了(速度慢),而是直接从cache中取得18号地址的内容(速度快)。

如果开启着CPU高速缓存(cache),上述的检测代码就不会正常工作。因为写入一个内存地址,然后立即读出,这样的操作符合cache到的情况,CPU不会从内存读,而是直接读cache到的东西。结果,所有的内存都"正常",检测代码就起不到作用了。

假设内存有128MB(0x08000000字节),以4KB(0x1000字节)为单位进行管理。

128MB/4KB=32768。所以我们创建32768字节的区域,写入0表示对应的内存是空闲的,写入1表示对应的内存是正在使用的。这个方法的名字我没有查到。

比如需要100KB的内存,那么只要从a中找出连续25个标记为0的地方就可以了。

需要收回这100KB的时候,用地址值/0x1000,计算出j就可以了。

当然,我们可以用1bit来代替1个char,这样所需的管理空间就可以省下7/8,使用方法则是一样的。

把类似于"从xxx号地址开始的yyy字节的空间是空闲的"这种信息都存储到列表里。

比如,如果需要100KB的内存,只要查看memman中free的状况,从中找到100MB以上的可用空间就行了。

释放内存时,增加1条可用信息,frees加1。而且还要看看新释放出的内存与相邻的内存能不能连到一起,如果可以,就要归为1条。

与上文的最简单的方法相比,这种列表管理的方法,占用内存少,且内存的申请和释放更迅速。

缺点是释放内存的代码比较复杂。另外,如果内存变成零零碎碎的,那么需要的MEMMAN里的数组就会超过1000,这是个问题。如果真发生这种情况,只能将一部分零碎的空闲内存都视作被占用的,然后合并。

内存管理要结合GDT的设定进行。按段(Segment)设计的GDT,内存就得按段申请和回收。按页设计的GDT,额我不知道,以后再说。

内存管理需要的预备知识还包括"获取内存容量"、"禁止/启用高速缓存"、"数据结构-链表"。

内存管理的算法还有很多,本篇只给出了两种最基本最简单的,够做个简易的OS就行了,现在不是深究算法的时候。