虚拟内存详解之三 内存的分区和分页

前面讲了内存的分段。在分段的时候我们需要找到一块空闲的内存区域。这段内存区域怎么找呢?那就是通过分区的方式。
有两种分区方式,固定分区和可变分区。
固定分区:每个分区一样大,不管很大的程序还是很小的程序都有可能放在同一个分区。利用率非常的低。
可变分区:分区是可变的,通过分区表和未分区表来实现。
如下图所示:

在这里插入图片描述
用两张表来描述内存的分配情况——已分配分区表和未分配分区表。这个在算法上是比较容易实现的。这里模拟了简单的情况,假设0K-100K被操作系统所占用了。Seg1占用了100K到200K的位置。空闲区域为250K到500K。可以通过这两张表非常清楚的描述清楚内存的分配情况。
假设有这样一个请求,请求分配一个100K大小的段Seg3。
可以像下图这样分配:
在这里插入图片描述

Seg3从250K开始,350K结束,长度为100K。已分配分区表也多出了相应的一行。未分配分区表没有变化。
还有一种可能的情况是一个进程结束了,那么对应的段也就销毁了。假如销毁的段是Seg2。那么销毁后,内存和两张表的情况如下图所示。

在这里插入图片描述

200K到250K这段内存变成了空闲状态,已分配分区表的Seg2行也删除了,已分配分区表多出对应的一行。
如果这时候又有一个请求,请求分配一个40K大小的段。那么应该怎么分配呢?
现在有两个空闲的位置,一个是从350K开始,长度为150K的区域。一个是从200K开始,长度为50K的区域。两个区域都是足够分配的,那么应该选择哪一个呢?
容易想到的有三种算法:
1.第一适应算法:
按顺序扫描,直到找到第一个符合的区块,这个方法是非常不错的,因为他的时间复杂度是O(1)。
2.最佳适应算法:
变量整个内存,并找出符合要求里面最小的那个区块。会产生非常细小的碎片,而且时间复杂度是0(n)。虽然叫最佳,但并不是很好。
3.最差适应算法:
变量整个内存,并找出符合要求里面最大的那个区块。会产生比较多块比较大的区块。原理和最佳适应算法是类似的,只是找的是最大的空闲块。时间复杂度也是0(n)。最差这个名字不太好。

内存碎片问题

不管使用上面的哪种算法,都会产生内存碎片。假如有一个请求200K大小的段要分配空间。而所以的内存碎片都比200K小,这时候就放不下了,需要进行内存紧缩。也就是移动位置,这个过程是非常慢的,会造成用户在几秒甚至几分钟内无法进行任何操作。这对用户来说是无法容忍。
因为存在这个问题,所以在实际上,内存并没有使用分区这种做法,而是使用分页的方式,使用分页的方式能够很好的解决内存碎片的问题。

内存的分页

分页就是把内存全部切割成均匀的小块,没一块叫做一页,通常每页的大小为4k是比较合适的。页和段是相关联的如下图所示,内存被分割成一个个页框,页框编号是固定,从内存0位置为页框0,后面每4k类推为页框1,2…n。
在这里插入图片描述
段的内容被分散到多个页中,而且不一定是有顺序的,同一个段中的页通过页表来描述。页表记录每个页的位置,这样就能管理分页了。页表的寄存器是CR3。PCB保存了页表的指针。
在这里插入图片描述

只有在一个段的最后一个页可能存在浪费的情况,最多浪费4k,这个是非常少的,一个程序可能有5个段,不管多大的或者多小的程序,最多浪费20k,这对今天的内存条来说是非常小的。
因为页是不连续的,所以每个页都能够被利用上,也就不存在碎片的问题。即使是一个段的最后一个页会产生内存碎片,但是数量都是非常少的。
页的移位也是非常方便的,4k=2^12,通过移位运算就可以快速的定位页。
例如:

mov eax,[0x2240]

2240是16进制,2240H除以4k等于将2240对应的二进制数右移12位,相对于把2240的右边3位抹去。结果为2,也就是对应页表的页号2,这个还是逻辑地址,查上面的页表发现页号2对应页框号是3,所以对应的物理地址就是0x3240。这个操作是通过硬件MMU来完成的。

那么这个页表需要占用多少内存呢?可以看下图
在这里插入图片描述
在左边的连续的页表图中,页号2是没有被使用的,但是是占用内存的,这样做的好处是页号是连续的,可以直接查找,缺点就是浪费内存。而右边的非连续页表中,页号2也是没有被使用的,但并没有占用内存,这样的好处就是节省内存,缺点就是需要遍历页号才能找到想要的页号。
那么连续的页表这种方式需要多少内存呢?
因为要保证可以通过偏移的方式找到对应位置页号的位置,所以所有的内存单元都要写到这个表里面。以32位系统为例,一个页的大小为4K,32位内存最大寻址大小为4G,4G/4K= 2 32 / 2 12 = 2 20 2^{32} / 2^{12}=2^{20} 232/212=220,所以有1M个页表项,对应的页表项只需要20bit就可以找到所有的内存了。但寻址方式是以字节为单位的,使用用24个bit页就是3字节就足够了。但实际上,真正的页表并不是上图的结构,前面只是纸上谈兵,真实的页表结构如下。
在这里插入图片描述
并不需要保存页框的信息,因为页号就对应了页框,而且20位就足够表示所有的4G内存空间了。后面的12位称为控制位,主要放如页是否空闲,权限等信息。先不用管具体内容,后面再解释。所以20+12=32也就是4字节。有1M个页表项,所以一个页表的占用内存4字节x1M=4MB。一个进程对应一个页表,一般一个操作系统都有几百个进程,假设是100个的话,这些进程页表就占用了100x4MB=400MB。光页表就占用了400MB,如果是500个进程就是2GB,这显然是不行的。
所以连续的页表的实现方式是行不通的。空间占用太多。
那么不连续的方式怎么样哪?
因为不连续,那么查找的时候就不能直接移位查找,而需要遍历,虽然是不连续的,但页号是有序的,所以可以使用二分法,查找的时间复杂度是O(log(n)),这里是以2为底的。差不多需要ln(2^20)=14次。也就是说没找一个地址CPU就要多算10多次,计算机性能直接下降10倍。这是不能接受的。所以这种方式也是不可行。
实际上这两者方式是分别从时间和空间的角度来考虑的,有没有什么办法能够做到时间和空间的平衡呢?或者即满足时间要求,又能满足空间要求呢?
那就需要用到我们的多级页表。
下一篇见。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值