内存分段与分页

内存分段与分页机制

因为实际物理内存与应用程序的大小之间的差别往往非常大,应用程序本身的大小甚至经常超过物理内存的大小,因此引入了虚拟内存的概念,使用分页机制把二者结合起来。分页各个页大小固定,也有操作系统使用几种不同大小的分页策略

而分段,例如编译器在编译程序的时候,要分符号表,堆栈区,指令区,常量区等,各个区域大小不固定,如果要直接在分页机制上做(因为分页大小固定哇!),就有点太困难了,所以使用分段的抽象,为程序员提供方便。

也就是说,分段是面向程序员的,分页是面向机器的。


分页

每一页4KB,随使用换入换出,因为程序在使用中肯定是要访问整个程序的某些不在内存里的部分。

物理内存也按照4KB来分,每一个4KB大小的存储区称之为页框(没错,页面就是放在页框中的,超级形象!发明这个词的人真是天才),由于物理内存只有那么大,显然是存不下整个程序的,访问到内存里面不存在的页面称之为缺页 ,这会产生一个中断,然后系统会陷入内核态,执行中断处理,将物理内存中最近不太用的某一页换出,然后把新的需要用的那一页换入。

有这么复杂的机制,肯定要有与之对应的数据结构来控制,所以就采用了页表来控制,进程有一个指向页表的指针。页表里面存着这一页对应的物理页框号,以及几个标记位:

  • 修改位:指示该页是否被修改过,如果修改过的话,在换出的时候需要写回到磁盘,如果没有修改过,那么直接将该页丢弃。
  • 保护位:指示该页读写权限
  • 。。。

    在使用了虚拟存储器的机器上,每次要读取一条指令的时候,CPU需要发出一个虚拟地址给MMU,经过MMU来将其转换成实际的物理地址,也就是说MMU先访问页表(页表也在内存中),获取到物理页框号,然后再把地址转换成实际的物理地址,再去获取指令执行后面的操作。换句话说,执行一条指令需要访问两次内存。

    而在这里,分页机制最明显的两个问题就是:
  • 把虚拟页号转换成对应的页框号太耗时
  • 如果程序的虚拟地址空间非常大,那么页表本身也会非常大。想想吧,现在的程序很多都是64位的,2的64次方的寻址空间,这个数字大到无法想象……更可怕的是,每一个进程还都有这么一套虚拟地址空间

    所以为了解决第一个问题,就有了TLB,which is Translation Lookaside Buffer,即转换检测缓冲区。它一般是一个硬件寄存器,里面放着少数的几个页表项,也就是说,把最常用的那几个页表项放在这个寄存器里面,毕竟根据计算机科学家们的统计,实际上一个程序在一段时间里,最常访问的总是相同的或者临近的几个页面,所以我们就把这几个页面的页表项放在硬件里面,这样子每次CPU向MMU发虚拟地址,MMU直接找TLB要页表项,然后就能够直接定位到物理地址了。省去了一次访问内存的IO操作,显然速度快了许多。
    当然,TLB里面因为只有少数的几条页表项,所以会发生访问的页面对应的页表项不在TLB里面的情况,这时候TLB就要换出最少用的那个页面的页表项,从内存里面把那个需要的页面的页表项读进来了。

    然后,为了解决第二个问题,也就是页表太大这个问题,就有了多级页表的概念,顶级页表,或者叫一级页表,里面的每一个页表项是一个指向着二级页表的指针,考虑一个32位也就是4GB的地址空间,如果不使用多级页表,那么因为每一页是4KB,我们需要一百万个页表项来存储,把这么大一个页表放内存里,只有土豪敢这么干。而如果使用多级页表,我们在内存里面存放页表的时候,就不用整个页表都堆在内存里了,相反的,只需要在内存里面放置顶级页表,以及你需要访问的那几个页面对应的二级页表就好了,当访问不到的时候,再从磁盘里面把需要的那部分二级页表通过IO送到内存里面来就好了,完美解决问题。

分页存在的问题

对于机器来说,这样当然是很美滋滋的,一个内存页才4KB,就算有一些页面没存满,浪费掉的空间也不到4KB。但是对于程序员来说呢?大型项目光代码都不只4KB,一页就4KB哪里够用呢?如果写个代码还要兼顾对内存页的管理,不如改行回家种地好了。
所以,这时候就引入一种面向程序员的抽象了,也就是分段

分段

写过汇编的程序员都知道,在写代码的时候,需要为代码分配存储空间,包括数据段,堆栈段,代码段等……把这些东西分开放,便于我们对程序进行调试、维护等工作,没有人能够在杂乱无章的代码里面长久地干活的,这也是代码规范,设计模式,编程框架等东西诞生的初衷。

分段存在的问题

如果只是使用分段来管理内存是什么场景呢?我们来考虑一下,你的内存里面现在运行着好几个程序,这几个程序是紧紧相连放在内存里面的。突然一个程序结束了,它占用的内存被回收,新的一个进程开始,比结束的那个占用的空间小一点,操作系统干脆直接把上一个被回收的进程的内存分配给它了,这样,在这个进程和下一个进程的存储空间之间,就有一些空隙。上面这种回收分配的操作多来几次,这样的空隙就会越来越多,也就是说,会造成大量的内存碎片,浪费了内存……


分段与分页的结合

所以,我们只需要将内存的分段和分页机制结合起来就好了,进程维持一个段表,在段表里面存着各个段,各个段里面存着这个段对应于哪些页。这样子在访问某个段的时候,就知道要去找哪些页,然后再把这个页的虚拟地址转成物理地址,就完成了分段到分页的整合了……


以上仅对普通PC机进行讨论,另外,针对每一个最好找一个案例来简单看一下,毕竟我没上图,只是干讲。
……谁让我其实就是想体验一把用Markdown写文章的快感呢。

转载于:https://www.cnblogs.com/Lighters-c/p/f5098da54abe0acac2950806a601ab1a.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值