30天自制操作系统——Day9实验报告

文章目录

  • 一、实验主要内容
    • 1、 内容1:整理源文件
    • 2、 内容2: 内存容量检查(1)
    • 3、内容3: 内存容量检查(2)
    • 4、 内容4: 挑战内存管理
  • 二、遇到的问题及解决方法
    • 1、 描述问题1:内存检查前为什么要使高速缓存的功能无效?
    • 2、 描述问题2:在内存检测中为什么每次遍历跳转的步长为0x1000?
    • 3、 描述问题3:C语言写的内存检测函数为什么在编译后反转不见了,只剩下for语句?
    • 4、 描述问题4:如何分配指定大小内存?
    • 5、 描述问题3:释放内存时空间归纳的几种情况?
  • 三、程序设计创新点
    • 1、 描述创新点1,采用最佳适应法进行连续内存分配
    • 2、 描述创新点2,最小替代损失法优化内存分配

一、实验主要内容

1、 内容1:整理源文件

重点总结:因为解决鼠标处理问题而导致程序变大了很多,所以准备整理程序
30天自制操作系统——Day9实验报告_第1张图片
在bootpack.h里加上了函数声明,并在Makefile中将keyborad.obj, mouse.obj也补进去
在这里插入图片描述

2、 内容2: 内存容量检查(1)

重点总结:进行内存检查首先要搞清楚“内存有多大”、“内存地址范围”。 虽然可以使用BIOS检查内存容量,但调用BOIS会使asmhead.nas变长,同时BIOS版本不同,BIOS的调用方式也不同。因此决定自己检查内存。
(1)内存检查步骤
Step1:让486以后的CPU的高速缓存功能无效
为了提高执行速度,在每次访问内存时,都要将所访问的地址和内容存入高速缓存里。在每次写入数据时,要首先更新高速缓存信息,然后再写入内存。
问题:为什么要使高速缓存的功能无效
我们通过循环:”for(i=0; i<100; i++){}这样检查内存信息。 因为缓存控制电路发现,i被频繁的引用和赋值。所以在写入值不断变化时,不写入内存,而是在缓存内处理。这样写入和读出的不是内存,内存检查的结果也就不正确了。
386的CPU没有缓存,486的CPU就有缓存了。因此我们需要先查看CPU是否在486以上,判断是否需要关闭高速缓存。
Step2:两次反转检查内存
通过设计两次数值反转,如果其中一次数据结果不正确,则终止调查,并报告终止时的地址(错误地址)

(2)bootpack.c——memtest, memtest_sub
Memtest:
30天自制操作系统——Day9实验报告_第2张图片
1、 内容说明
首先需要判断是否为CPU类型,若为486CPU则将EFLAGS寄存器的第18为AC标志位设置为0.(①②③)
另外还需要修改CR0寄存器中的标志位来禁止使用缓存。再获得内存大小后,重新恢复缓存设置。(④⑤)
2、 load_cr0, store_cr0
这两个函数不同使用C语言写,只能用汇编语言写,内容如下:

30天自制操作系统——Day9实验报告_第3张图片
memtest_sub
30天自制操作系统——Day9实验报告_第4张图片
1、内容说明
调查从start地址到end地址范围内,能够使用的内存的末尾地址。
(问题:为什么for循环中 I += 0x1000?)
由检测代码可知,每次反转是对4个字节的数据进行操作。也就是每次要检查4个字节,因此i+=0x1000(4)
2、函数优化
如果要检查全部的内存,速度就太慢了。因此改为”p = i+0xffc”,每次只检查末尾的4个字节(系统启动时内存已经被仔细检查过了)。
(3)结果展示
30天自制操作系统——Day9实验报告_第5张图片
(为什么不是32MB呢?)

3、内容3: 内存容量检查(2)

重点总结:在禁止缓存后检查内存容量,结果竟然超过了32MB!我们探究其中原因,发现竟是编译器做了最优化处理。为了解决这个问题我们用汇编重写了此函数。
(1) 编译器的最优化处理过程
我们用make -r bootpack.nas将bootpack.c编译成了机器语言。并发现了如下编译结果:
30天自制操作系统——Day9实验报告_第6张图片
理解此汇编语言发现,编译后只执行了for语句,并没有进行反转检查。
(问题:为什么编译后反转不见了,只剩下for语句?)
30天自制操作系统——Day9实验报告_第7张图片

  1. 省略if判断语句:
    p赋值pat0后取反值与pat1值肯定相同,后面取反同理。因此为了提高速度,编译器将这部分判断删掉了。 (如图)
    30天自制操作系统——Day9实验报告_第8张图片
  2. 删除两次反转
    两次反转后会回到原来状态,因此也可以删除(如图)30天自制操作系统——Day9实验报告_第9张图片
  3. 删除p赋值为pat0
    因为
    p之后会赋值为old,pat0赋值无效可以删除
    30天自制操作系统——Day9实验报告_第10张图片
  4. 产出old无效写入
    Old值为p原先内存值。因此再次赋值是无效操作,可以删除。
    最终通过编译器的优化处理,我们得到了如下程序:
    30天自制操作系统——Day9实验报告_第11张图片
    效果与之前所得的汇编代码相同,只剩下了for语句。

(2)汇编语言重写内存检查处理函数:
30天自制操作系统——Day9实验报告_第12张图片
30天自制操作系统——Day9实验报告_第13张图片
30天自制操作系统——Day9实验报告_第14张图片
删除bootpack.c中的memtest_sub函数。
(2)结果展示
30天自制操作系统——Day9实验报告_第15张图片
成功执行内存检查,获得内存容量32MB

4、 内容4: 挑战内存管理

重点总结:为了应付操作系统工作中的内存需求,需要进行内存管理。其中包括“内存分配”、“内存释放”。我们学习了两种方式:“内存区域划分”与“可用内存管理表”实现内存管理。
(1)bootpack.c
Mdec.x, mdec.y表示鼠标向横向、纵向移动的大小。可以用此实现鼠标移动

(2)“可用内存空间管理表”
1)可用内存空间管理表结构
30天自制操作系统——Day9实验报告_第16张图片
2)可用空间管理初始化
30天自制操作系统——Day9实验报告_第17张图片
将frees初始化为0,可以理解为初始状态下,内存无可用空间。

3)可用内存空间实际大小
30天自制操作系统——Day9实验报告_第18张图片
遍历可用空间列表,返回可用内存大小之和。
4)分配指定大小内存
30天自制操作系统——Day9实验报告_第19张图片

(问题:如何分配指定大小内存?)
Step1:找到可用空间
遍历空间列表,找到第一个符合条件的可用空间。

Step2:分配空间并动态改变原可用空间信息
取出指定大小内存空间,并更新取出部分空间的地址和剩余可用空间大小

Step3:处理可用空间为0时特殊情况
若更新后可用空间剩余容量大小为0,则舍去该空间。列表后续元素前移。并使frees–
因为先进行了frees—操作,所以后续移动时是令free[i] = free[i+1]的。

5)释放内存
(问题: 释放内存时空间归纳的几种情况?)
Step1:找到释放内存地址相邻的可用空间下标:
遍历空间列表,找到第一个地址大于addr的下标i。

Step2:判断所释放的内存空间能否与前一个可用内存空间融合:
若所释放内存的起始地址是前一个可用内存空间的末尾地址([i-1]addr+[i-1].size),则将此内存空间归入前一可用内存空间[i-1],否则跳入Step3。 然后,需要判断后一个内存空间[i]的起始地址是否与此空间的末尾地址(addr+size)是否重合,若重合,则将后一可用内存空间[i]也加入新合并的内存空间[i-1]。此时空间列表中元素数量减少1,故需要使frees–。
30天自制操作系统——Day9实验报告_第20张图片

Step3:判断所释放的内存空间能否与后一个可用内存空间融合:
需要判断后一个内存空间[i]的起始地址是否与此空间的末尾地址(addr+size)是否重合。若重合,则将后一内存空间起始地址前一位addr,并增加对应可用内存空间大小。否则跳入Step4。

Step4:若不与相邻可用空间地址重合,新开辟可用空间:
若释放的内存空间不能与相邻空间[i], [i-1]重合,则新开辟可用空间。新的可用内存空间在空间列表的下标为i,将原来下标为i->frees的元素一次后移一位,并增大空间列表的当前最大可用值frees。
30天自制操作系统——Day9实验报告_第21张图片

6)应用
30天自制操作系统——Day9实验报告_第22张图片
(3)结果展示
30天自制操作系统——Day9实验报告_第23张图片
成功的建立了内存模式

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

填写说明:分条目列出本次的实验过程中遇到的问题和解决方法(可注明是哪位同学帮忙解决问题),描述问题时应配上相关的截图和标记,描述解决方法时应先分析出现该问题的原因再讲解决方法,尽量详细。

1、 描述问题1:内存检查前为什么要使高速缓存的功能无效?

我们通过循环:”for(i=0; i<100; i++){}这样检查内存信息。 因为缓存控制电路发现,i被频繁的引用和赋值。所以在写入值不断变化时,不写入内存,而是在缓存内处理。这样写入和读出的不是内存,内存检查的结果也就不正确了。
386的CPU没有缓存,486的CPU就有缓存了。因此我们需要先查看CPU是否在486以上,判断是否需要关闭高速缓存。

2、 描述问题2:在内存检测中为什么每次遍历跳转的步长为0x1000?

由检测代码可知,每次反转是对4个字节的数据进行操作。也就是每次要检查4个字节,因此i+=0x1000(4)

3、 描述问题3:C语言写的内存检测函数为什么在编译后反转不见了,只剩下for语句?

  1. 省略if判断语句:
    p赋值pat0后取反值与pat1值肯定相同,后面取反同理。因此为了提高速度,编译器将这部分判断删掉了。 (如图)
  2. 删除两次反转
    两次反转后会回到原来状态,因此也可以删除
  3. 删除p赋值为pat0
    因为
    p之后会赋值为old,pat0赋值无效可以删除
  4. 产出old无效写入
    Old值为p原先内存值。因此再次赋值是无效操作,可以删除。
    效果与之前所得的汇编代码相同,只剩下了for语句。

4、 描述问题4:如何分配指定大小内存?

Step1:找到可用空间
遍历空间列表,找到第一个符合条件的可用空间。
Step2:分配空间并动态改变原可用空间信息
取出指定大小内存空间,并更新取出部分空间的地址和剩余可用空间大小
Step3:处理可用空间为0时特殊情况
若更新后可用空间剩余容量大小为0,则舍去该空间。列表后续元素前移。并使frees–
因为先进行了frees—操作,所以后续移动时是令free[i] = free[i+1]的。

5、 描述问题3:释放内存时空间归纳的几种情况?

Step1:找到释放内存地址相邻的可用空间下标:
遍历空间列表,找到第一个地址大于addr的下标i。
Step2:判断所释放的内存空间能否与前一个可用内存空间融合:
若所释放内存的起始地址是前一个可用内存空间的末尾地址([i-1]addr+[i-1].size),则将此内存空间归入前一可用内存空间[i-1],否则跳入Step3。 然后,需要判断后一个内存空间[i]的起始地址是否与此空间的末尾地址(addr+size)是否重合,若重合,则将后一可用内存空间[i]也加入新合并的内存空间[i-1]。此时空间列表中元素数量减少1,故需要使frees–。
Step3:判断所释放的内存空间能否与后一个可用内存空间融合:
需要判断后一个内存空间[i]的起始地址是否与此空间的末尾地址(addr+size)是否重合。若重合,则将后一内存空间起始地址前一位addr,并增加对应可用内存空间大小。否则跳入Step4。
Step4:若不与相邻可用空间地址重合,新开辟可用空间:
若释放的内存空间不能与相邻空间[i], [i-1]重合,则新开辟可用空间。新的可用内存空间在空间列表的下标为i,将原来下标为i->frees的元素一次后移一位,并增大空间列表的当前最大可用值frees。

三、程序设计创新点

1、 描述创新点1,采用最佳适应法进行连续内存分配

(1) 问题描述
作者在书中采用的是“首次适应”的连续分配算法。即从表头开始,找到足够大的孔时就停止查找。同时我们还可以采用“最佳适应法”进行连续内存分配。虽然首次适应法和最佳适应法在利用空间方面难分伯仲,但是还可以进行如此尝试。

(2) 算法分析
30天自制操作系统——Day9实验报告_第24张图片
在书中给出的内存分配基础上,使在找到可行内存空间时继续遍历空间表,进而找到最优空间地址best_pos。接着按照一般的内存分配方案在best_pos位置分配内存。

(3) 测试分析
我们可以设计如下内存分配情况:
初始化时将分配全部内存,然后分别释放2MB, 4MB, 3MB的内存空间。接着,向内存请求3MB大小的内存空间。两种方式都会跳过2MB的可用内存空间,而常规方法会选择4MB,最佳选择法会选择3MB空间。
测试代码如图:
30天自制操作系统——Day9实验报告_第25张图片
下面为内存分配情况简视图:
30天自制操作系统——Day9实验报告_第26张图片

(3)结果示意
30天自制操作系统——Day9实验报告_第27张图片
正如我们所料,(左图)为首次适应内存分配算法,它会选择4MB空间进行分配。(有图)为我们设计的最佳适应连续内存分配算法,它会选择3MB空间进行分配

2、 描述创新点2,最小替代损失法优化内存分配

(1)问题描述:
在本书的内存分配算法中,当可用内存空间已满时会割舍掉需要分配内存的那段信息。这样可能会导致一些信息丢失。
(2)算法分析:
先找到最小的空余内存段,然后与要释放的内存大小进行比较。如果最小的空余内存段较大,那么就将其扔掉,空出一个内存数量序列,然后将要释放的内存序列存入其中。
增加如下代码:
30天自制操作系统——Day9实验报告_第28张图片

你可能感兴趣的:(30天自制操作系统,操作系统)