ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

《操作系统真象还原》分页

2020-01-06 21:03:58  阅读:330  来源: 互联网

标签:操作系统 物理地址 地址 分页 页表 线性 真象 内存


  本节是阅读第五章的收获。下面将阐述一些分页的相关内容。

分页

什么是分页

  分页,顾名思义,就是将内存分成大小相同的页。分页,通过映射的方式,将连续的线性地址转化为不连续的物理地址;这样,在处理器进入分页模式之后,用户直接访问的并不是物理地址,而是分页模式下的虚拟地址。

  上面有三个和地址相关的概念,分别为虚拟地址、线性地址和物理地址。

  在打开保护模式之前,仅有线性地址和物理地址的概念,物理地址就是CPU最终访问的真正地址,是指令或数据真正保存的数据的地方。而线性地址代表“段基址+段内偏移地址”,由于在实模式下,段基址+段内偏移地址等于物理地址,所以线性地址和物理地址数值上是一样的。

  而打开保护模式且打开分页模式之后,用户直接访问的是虚拟地址空间或是线性地址空间,线性地址仍然是段基址+段内偏移地址,虚拟地址数值上与线性地址相同。从概念上线性地址空间和虚拟地址空间有些不同,因为线性地址空间只有段的概念,没有页的概念;通过分页机制,将线性地址空间中大小不等的段转化为虚拟空间中大小相等的页。虚拟地址通过页表和页目录转化为最终的物理地址,分页机制如下图:

  总的来说,虚拟地址就是分页后程序或任务访问的地址,线性地址就是段基址+段内偏移地址,物理地址是CPU最终访问的地址。

 

为什么要分页

  那么为什么要分页呢?主要原因是内存分配的时候存在外部碎片。本来剩余的内存空间是足以分配给一个任务或进程的,但由于这些剩余的内存片并不连续,我们就不能连续地分配这些内存给任务或进程了。所以导致不能分配的关键原因是我们需要分配内存地址是连续的。

  有人可能会问为什么内存地址需要连续,不连续不行吗?我们先从指令说起,我们执行指令除非有跳转,都是默认下一条指令地址=当前指令的地址+当前指令的大小,换而言之,都是约定指令执行是连续的;数据也是,一般而言,我们请求一块数据缓冲区,我们都希望这个缓冲区是连续的,这样,我们就能够像数组一样对缓冲区进行连续读写。所以从用户的角度来说,我们都默认一个数据段或者代码段在逻辑上是连续的,这也符合我们的直觉。用户希望空间是连续的,但导致分配失败的原因又是“连续”,那么怎么才能解决这个问题呢?下面来看看分页是怎么解决这个问题的。

分页机制的原理

一级页表

  我们仍然希望访问的指令或数据是连续的,希望指令或数据的地址仍然保持着与分页前一样。所以我们像在分段模式下访问内存一样,采用段基址+段内偏移地址的模式访问内存,此时通过段部件输出线性地址,但我们肯定不能让线性地址代表物理地址,否则还是不能解决上述的内存分配的问题。我们这时候拿出了一张表,称为页表,将线性地址逐字节地映射到物理地址上,如下图,比如将0x0地址映射到物理内存0x0,将0x1映射到0x9,将0x2映射到0xfa,以此类推。这样子我们就能够保证用户访问的地址是连续的,但实际上,在物理内存是不连续的;以这种方式,我们能够充分的利用剩余的物理地址空间,不会因为“连续”的约束而导致分配内存失败。所以通过一级页表,我们就能很好地解决外部碎片产生的问题。

 

  那么,我们再细想一下,上面这种映射方式有什么问题。想一想,假设线性地址空间为4G,也就是有4G个地址,也就是页表里面有4G个页表项,假设一个页表项需要4个字节存储32位地址,那就是一个页表的大小是16G,比线性地址空间还大hhhh。因此,一一映射的方式太耗内存了,所以我们需要减少页表项的开销,页表项存储地址是4个字节变不了了,只能减少页表项的数量。我们将32位地址分为两部分,一部分代表内存块的数量,一部分代表内存块的尺寸,如下图。那么我们怎么找到合适的页尺寸呢,我们分配内存的单位是内存块的尺寸,尺寸太大,内存块数量太少,因而能分配的内存块也就少;尺寸太小,内存块数量太多,这样页表的开销就很大。所以我们只能折中选个值,而现在CPU采用的内存块大小恰好是4KB,这样内存块的数量就是1M个,我们就按这个值来吧。

  

   这样,我们不再将内存每个字节一一映射了。通过线性地址的高20位去索引页表的页表项找到对应的内存块基址,线性地址的低12位去索引这个内存块。既能够保证页表的大小不至于太大,也能确保分配的内存块不至于太少,也消除了外部碎片和“连续”导致的问题,其实已经差不多了,但一级页表并不是我们现在操作系统采用的页表模式,因为一级页表还是有一些问题,我们接下来继续来讨论。

二级页表

   一级页表已经很好了,为什么还不够呢。我们下面来讨论一下一级页表问题。

  ①一级页表有1M个页表项,也就是一个页表需要4M的内存空间,并且4M的内存空间也是需要连续分配的,这和采用页表的原因类似,如果内存的外部碎片过多,本来剩余的内存空间>4M的,但由于这些剩余内存并不连续,这样就分配不了内存给一个页表了。我们通过二级页表,将连续的4M内存划分为1K个4K大小的页,这样我们既可以用页为任务分配内存空间,也可以用页去为页表分配空间,给页表分配空间就不再具有特殊性了,不再需要连续了。

  ②一级页表,一定需要完整的4M的内存空间,这是肯定的,如果不分配完整的4M空间,某一些内存地址就访问不到了。每一个进程都会有一份页表的,如果进程数量很多,页表占用的内存还是挺可观的。采用二级页表就能节省空间,二级页表只需要为那些用到的内存地址分配页表;那些没有用到的内存地址,除非与用到的内存地址用到同一块页表,否则是不会分配页表的。所以,一级页表一般会比二级页表消耗更多的内存。

 

  下面来描述一下二级页表的结构,二级页表与一级相比多了一层,我们称这层为页目录。接着说,二级页表将一级页表的1M个页表项继续划分,将1M个页表项均分到1K个页表里,也就是1个页表有1K个页表项,这样1个页表的大小就是4K,正好一个页的大小。一个页表能索引的内存大小就是1K*4K=4M,1K个页表就能索引4M*1K=4G内存。页表已经划分好了,那么我们就需要考虑怎么索引到对应的页表。这时候页目录正式登场,页目录有1K个页目录项,一个页目录项指向一个页表,这样整个二级页表的结构大致就这样组成。

  那么如何将线性地址(虚拟地址)转换为最终的物理地址呢,和二级页表的关系是什么? 和一级页表类似,只不过32位的地址被划分为3个部分,高10位用来索引页目录找到页表的物理地址,中10位用来索引页表找到页的基址,低12位用于索引页。

  举个例子,如下图,指令是mov ax, [0x1234567],将0x1234567铺开到二进制0000_0001_00  1000_1101_00 010101100111后,可以得到页目录索引为4,页表的索引为0x234,页的索引为0x567。

  一个页目录项和页表项的大小为4B,设页目录的基址为DIR_BASE,页表的基址为PAGE_BASE,DIR_BASE+0x4 *(页目录项的大小4)=页目录索引为4所对应的物理地址,读取该物理地址存储的地址0x1000,即PAGE_BASE=0x1000。继续找页的基址,PAGE_BASE+0x234*(页表项的大小4)=页表索引为0x234时所对应的物理地址,同样读取该地址存储的地址0xfa000。继续找最终的物理地址,0xfa000+0x567=0xfa567得到最终的物理地址。

 

  从虚拟地址到物理地址的转换是由页部件去完成的,操作系统只需要准备好相应的结构,即可让处理器去计算最终的物理地址。

 

 

 

 

 

 

标签:操作系统,物理地址,地址,分页,页表,线性,真象,内存
来源: https://www.cnblogs.com/thougr/p/12158456.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有