30天自制操作系统——第9天实验总结

实验日期实验项目
2020.11.26第9天 内存管理

一、实验主要内容

1、 内容1 整理源文件

(1).内容概要

  • 实验内容:对源文件进行整理,减少bootpack.c函数的大小,将各个功能函数分类,将来便于对代码的修改和管理。

新建一个mouse.c和keyboard.c文件来分别存储涉及到鼠标和键盘操作的功能函数。整理表如下:

在这里插入图片描述
对源文件进行整理后,需要注意两个问题,一是链接时,需要将编译生成的mouse.obj和keyboard.obj加上,二是在.h文件中加上用到函数的声明。

2、 内容2 内存容量的检查

(1).内容概要

  • 实验内容:理解高速缓存的概念,明白对内存容量检查的思路,并编写程序实现对内存容量的检查。
  • 实验重点:理解内存容量检查函数的实现;对编译器优化的问题进行解决。

高速缓存的引入:为了解决CPU高速运转和I/O设备速度不对等的问题,IBM的大叔在CPU中加入高速缓冲存储器。访问时内存都要将所访问的地址和内容存入高速缓存中,当下次CPU再次访问内存时,可以直接从高速缓存中读取数据。往内存中写入数据时,首先需要先更新高速缓存的信息,然后再写入内存。如果先写入内存的话,在等到写入完成的期间,CPU处于空闲状态,会影响处理速度。结构图如下:

在这里插入图片描述
如果CPU中不禁用缓存,就直接进行内存容量的检查,则写入和读出的不是内存,而是缓存,最后的处理结果将是所有内存都正常,检查处理不能完成。

CR0寄存器:有关缓存使用的设置和CR0寄存器有关,结构图如下。其中第29位表示写回策略,第30位表示缓存禁用,禁用缓存时,需要将这两位置1。

在这里插入图片描述

(2).关键代码分析

在这里插入图片描述
这部分代码是从地址start开始到地址end结束,检查连续可用内存的大小。

在检查内存容量之前,首先需要判断CPU是486以上还是386,如果是386,即使AC-bit是1,AC-bit的位依然会置0。根据类型,决定是否禁用缓存。AC标志位在eflag寄存器的第18位,第18位置1得到0x00040000, 即EFLAGS_AC_BIT。 首先我们将AC位置1(和EFLAGS_AC_BIT相或),存储到eflag寄存器后再取出,和EFLAGS_AC_BIT相与,结果为1,说明CPU是486以上的,将flg486这个标志位置1,后续用于对是否禁用缓存的进行判断。

根据硬件结构特点,禁用缓存需要对CR0寄存器的第29位和第30位置1,得到0x60000000,即CR0_CACHE_DISABLE,在软件处理上,使用汇编语言编写的load_cr0()取出CR0的值,和CR0_CACHE_DISABLE相或来禁用缓存,和~CR0_CACHE_DISABLE相与来允许缓存,最后使用store_cr0将修改后CR0的值存入寄存器CR0中。

在这里插入图片描述
这部分代码是对内存容量检查的具体函数实现。函数内定义两个值pat0和pat1,满足pat0=~pat1。循环从start地址开始,end地址结束,每次检查0x1000的内存,由于只是对内存容量进行一个检查,只需要检查最后的4个字节即可,p=(unsigned int )(i+0xffc)。使用old保存p原先的值,将pat0的值写入p中,对p反转,然后对p的值判断,如果不等于pat1,则说明检查到不可用地址,恢复p原值,并结束检查,否则接着对p进行反转,再判断。如果两次判断均有效,就对下一地址判断,直达检查到不可用地址。

在这里插入图片描述
这部分代码是在主函数中,调用memset函数对从0x00400000到0xbfffffff范围内的连续可用内存检查。

在这里插入图片描述
在这里插入图片描述
这部分汇编代码实现的是对内存容量进行检查。执行使用C语言编写的内存容量检查的代码后,发现输出是我们给的内存大小,如果改变这个大小,那么输出也会跟随改变。执行make –r bootpack.nas后,查看汇编代码,发现经过编译器优化后,我们对内存容量检查的代码已经因为优化被删除了,所以输出就是我们给出的内存大小。考虑到其他代码部分还需要编译器的优化,这里编写汇编代码实现对内存容量的检查,避免编译器优化带来的问题。

3、 内容3 挑战内容管理

(1).内容概要

  • 实验内容: 内存管理的基础是内存分配和内存释放,使用内存分块法和列表管理法对内存进行管理,并能够利用列表管理法实现对内存的分配和释放。
  • 实验重点:实现内存的分配和释放。

下面对内存管理的两种方法进行说明

  • 方法1 内存分块

使用一个数组来进行管理,以4kb作为分块的大小,数组中值为1表示该块可用,否则该块不可用。以128MB内存的管理为例,说明这种方法。128MB有0x08000000个字节,块的大小为0x1000,则需要0x08000000/0x1000=32768个管理单元。

在这里插入图片描述
对100KB的空间进行内存分配

在这里插入图片描述
在这里插入图片描述
使用结束后,对从0x00123000开始的100KB 的空间释放

在这里插入图片描述
改进:管理表可用只使用1bit来存储,这样可用大大减少存储的空间。

优点:简单易行,容易实现
缺点:占用内存,对于3GB的内存,使用char管理时,占内存比0.02%,使用bit管理时,占内存0.003%
  • 方法2 列表管理

定义两个结构体,一个用来表示可用状况,包括地址addr和大小size,即从addr开始的size的内存可用,另外一个用来管理内存,free是空的可用内存条目。

在这里插入图片描述
对100KB的内存进行分配,并将找到的条目的可用状态更换为正在使用。
在这里插入图片描述
在这里插入图片描述
释放内存时,增加一条可用信息,free+1,另外还需要考虑释放的这个内存空间能否和前后可用内存合并。

优点:占用内存少;对大块内存的分配和释放十分迅速。
缺点:管理程序复杂,可用空间比较零散,并且部分被割舍的空间将再也不能使用。

(2).关键代码分析

实验中采用的是列表管理法,并使用”割舍掉的东西,只要以后还能找回来,就暂时不用去管它的方式”

在这里插入图片描述
这部分代码是对使用到的结构体和各个函数的声明。

在这里插入图片描述在这里插入图片描述
左图的代码是对MEMMAN结构体的初始化,右图是对剩余可用内存大小的计算。

在这里插入图片描述
这部分代码是对内存的分配。思路是从所有可用的内存中找到一个足够分配的内存,根据大小关系分配下去,在操作系统概念这本书中,这种方法称为首次适配法。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这部分代码是对内存的释放,实现思路是首先找到释放内存空间的起始地址在可用内存地址中的位置,将该释放条目插入管理表时,考虑到和相邻的可用内存空间合并,分为3种情况处理,一是和前面的可用内存合并,二是和后面的可用内存合并,三是和前后可用内存都不能合并,此时如果未超过最大管理表条目,就移出一个位置给释放的条目,否则就记录下该条目,不能为其释放内存。

在这里插入图片描述
这是程序的主函数内的代码,首先对内存容量进行检查,,初始化内存管理的结构体,注册0x00001000-0x0009efff的632KB和0x00400000-memtotal的28MB的内存。内存管理的空间的起始地址为0x003c0000。

二、遇到的问题及解决方法

1、 描述问题1

  • 问题描述

为什么不使用BIOS检查内存容量?

  • 解决方法

结合之前的内容,主要有这三个原因,第一,BIOS版本不同,其函数的调用方法也不尽相同,在不同的操作系统上需要有不同的实现;第二,asmhead.nas的代码量也会增加;第三,我们已经进入了32位模式,从32位模式切换到16位模式比较繁琐复杂。

2、 描述问题2

  • 问题描述

禁用缓存时,有关缓存禁用的是CR0寄存器的第30位,为什么要对29位也进行处理?

  • 解决方法

查阅资料,可以发现CR0寄存器的第29位表示的NOT WRITE THROUGH,WRITE THROUGH表示的是写通,也就是每当Cache收到写数据(store)指令时,若写命中,则CPU会同时将数据写到Cache和主存。由于我们不需要使用缓存,那和缓存有关的操作也必须禁止。

3、 描述问题3

  • 问题描述

一般来说,当释放内存时,可用信息条目需要增加1,如果能合并再对可用信息条目减少1。分配内存时,可用信息条目需要减少1。但是在释放内存的代码中,第1个if语句中,释放内存后,并没有对free加1,合并后只有减1的操作?

  • 解决方法

这是因为如果释放的内存可以和前后的可用内存均合并的话,对应可用信息的条目是减少1的,如果只能和前面的可用内存合并,对应的可用信息条目不变。

三、程序设计创新点

1、 描述创新点1,关键代码及结果截图

  • 创新点1

使用最佳适配法实现内存分配。最佳适配法是每次分配最小的足够大的内存空间,而不是笔者实现第一次找到足够大的空间。

  • 关键代码

在这里插入图片描述
在这里插入图片描述
最佳适配法程序的代码实现思路是很简单的,首先去找到可用内存中最小的一个,然后将其分配出去,分配后该可用条目起始地址增加size,可用大小减少size,当可用大小变为0时,需要删除该可用条目。
在这里插入图片描述
主函数中编写了一个简单的程序去验证最佳适配法的正确性。

  • 结果截图
    在这里插入图片描述

结果分析
内存的起始地址是0x00400000。代码首先分配了0x01400000(20MB)的内存,接下来分别在0x00400000,0x00900000,0x00e00000处释放4MB,3MB和2MB的内存,释放后的内存分配图如下所示,黄色部分为可用内存区域。根据最佳适配法,1MB的内存应该分配在0x00e00000处,剩余内存为16MB=16384KB,结果和输出一致,说明算法实现基本上是正确的。
在这里插入图片描述

2、 描述创新点2,关键代码及结果截图

  • 创新点2

笔者在教材P179页中提到,当可用信息表满了时,直接舍去刚刚进来的可用信息,并没有实现具体的损失最小的替换算法。这里以可用内存大小损失最小为原则,实现程序。

  • 关键代码

在这里插入图片描述
在这里插入图片描述

这部分代码就是当可用信息条目不够用时,找到一个最小的信息条目,将其替换。实现思路:首先找到要替换条目的位置,使用pos记录下来,接着根据之前找到的插入条目的位置i和pos的大小关系更换信息条目,如果在插入位置之前,则从pos开始到i依次往前移动。如果在插入位置之后,则从i到pos依次往前移动。最后将对应替换的一项更新为要释放的可用信息条目的起始地址和大小。

在这里插入图片描述
在这里插入图片描述
为了实现测试,将可用信息数量最大修改为4。先预先分配20MB内存,接下来分别释放4MB,3MB,2MB的内存,此时可用信息表已满,这时候接着释放2MB的内存,然后将可用信息条目输出查看结果。

  • 结果截图
    在这里插入图片描述

结果分析
按照主函数中的分配释放后,可用信息条目刚刚存放满后的内存分配图如下。此时根据算法,应该将0x00e00000处的可用信息条目替换。查看上图输出结果,没有了在0x00e00000处大小为2MB的可用内存,替换后的是在0x01400000处大小为2MB的可用内存。说明算法实现基本上是正确的。
在这里插入图片描述

四、实验心得体会

本次实验是自制操作系统的第9天,这一天开始对内存进行了管理了。首先是对内存容量进行检查,检测出连续可用的内存空间段,接下来介绍了2种常见的内存管理的方法,并以列表管理法对内存分配和内存释放进行了代码实现。看内存分配的代码的时候,想到了操作系统原理理论课上的动态内存分配的内容,笔者采用的分配方法就是首次适配法,对第1次找到的足够用的内存分配出去。在理论课上还学习了最佳适配法,在实验的创新点中对最佳适配法进行了实现,并设计测试样例对其进行简单验证。学了快半学期操作系统,今天终于将理论课上学到的东西和实践操作结合到一起了,并且还是一个日趋完善的操作系统,自然是很高兴的。越来越期待后面的内容了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值