精华内容
下载资源
问答
  • x86分页机制详解

    2020-07-13 23:10:18
    为什么会有分页机制?2. 从虚拟地址到物理地址3. 简单的分页模型4. 页表和页目录4.1 层次化的分页结构4.2 页表4.3 页目录5. 地址变换的具体过程 1. 为什么会有分页机制? 有些资料说是为了实现“虚拟内存”,真的是...

    1. 为什么会有分页机制?

    有些资料说是为了实现“虚拟内存”,真的是这样吗?如果没有分页机制,能否实现“虚拟内存”?答案是肯定的。

    当同时运行的任务很多时,内存可能就不够用。

    捕获

    如图所示,每个段描述符都有 AVL 位(简称 A 位),用于表示一个段最近是否被访问过(准确地说是表明从上次操作系统清零该位后一个段是否被访问过)。

    当创建描述符的时候,应该把 A 位清零。之后,每当该段被访问时,准确地说是处理器把这个段的段选择符加载进段寄存器时,CPU 就将该位置“1”;对该位的清零是由操作系统负责的,通过定期监视该位的状态,就可以统计出该段的使用频率(比如,每 1 秒钟查看一次,一旦置位就清零,统计 10 秒钟内被置位了多少次,次数越多说明使用越频繁)。当内存空间紧张时,可以把不经常使用的段退避到硬盘上,从而实现虚拟内存管理。

    当某个段被换出到磁盘时,操作系统应该将这个段的描述符的 P 位清零。过上一段时间,当再次访问这个段时,因为它的描述符的 P 位是 0,处理器就会引发段不存在异常(中断号 11)。这类中断通常是由操作系统处理的,它会用同样的方法腾出空间,然后把这个段从磁盘调入内存。当这类中断返回时,处理器会再次执行引发异常的那条指令,这时候段已经在内存中(P=1),于是程序又可以继续执行了。

    由此可见,即使没有分页机制,利用“分段”也可以实现“虚拟内存”。

    但是,因为段的长度不固定,在段的换入换出时会产生外部碎片,这样就浪费了很多内存。为了解决这个问题,从 80386 处理器开始,引入了分页机制。分页机制简单来说,是用长度固定的页来代替长度不定的段,以解决因段的长度不同带来的内存空间管理变得复杂的问题。

    尽管操作系统也可以利用纯软件来实施固定长度的内存分配,但是过于复杂。由处理器固件来做这件事情,可以省去很多麻烦,速度也可以提高。

    总结一下,引入分页机制并不是为了实现虚拟内存,而是为了解决内存碎片的问题。

    2. 从虚拟地址到物理地址

    分页机制是 80x86 内存管理机制的第二部分。分段机制把逻辑地址转换成线性地址,而分页机制则把线性地址转换成物理地址。
    在这里插入图片描述

    分页机制会把线性地址空间(段已映射到其中)划分成页面,然后这些线性地址空间的页面被映射到物理地址空间的页面上。如下图所示:

    在这里插入图片描述
    80x86 使用 4K(2 的 12 次方)字节固定大小的页面。每个页面均是 4KB,且对齐于 4K 地址边界处(地址的低 12 位全是 0)。

    3. 简单的分页模型

    4GB(2 的 32 次方)的线性地址空间可以划分为 1048576(= 2 的 20 次方,即 1M)个页面。为了根据线性地址找到对应的物理地址,操作系统必须维护一张表(如下图所示)。

    在这里插入图片描述
    这个表暂且叫做“页映射表”,它一共有 1048576 个表项,每个表项占 4 个字节,其内容是某个页的起始物理地址(共 32 比特,低 12 位全为 0)。

    页映射表是这样使用的:因为页的尺寸是 4KB,所以线性地址的低 12 位可以作为页内偏移,高 20 位可以用来索引一个表项,找到了这个表项,就找到了对应的物理页。

    具体可以参考我的博文:简单的分页模型

    4. 页表和页目录

    4.1 层次化的分页结构

    上文提到的页映射表,一共有 1048576(=1M)个表项,每个表项占 4 个字节,所以表的大小是 4MB,在当时看来要占用相当一部分内存。考虑到在实践中,没有哪个任务会真的用到所有表项,充其量只是很小一部分,所以内存中放一个 4MB 的表格确实很浪费。也许你会建议,能不能先划出一小片内存,只存表格用到的部分,然后根据需要动态扩展。的确,这个方法可行。但是因为特殊原因(任务的 4GB 地址空间包括两个部分:局部空间和全局空间。页目录的前半部分指向任务自己的页表,后半部分则指向内核的页表。整个映射表的前一半对应全局地址空间,后一半对应局部地址空间),这张表从一开始就必须完全定义,所以不可避免地要占用 4MB 的内存空间。为了解决这个问题,同时又不会浪费宝贵的内存空间,处理器设计了层次化的分页结构。

    4.2 页表

    4GB 的线性地址空间可以划分为 1048576(2 的 20 次方,即 1M,也可以看成是 1024*1024)个页面,所以,可以随机地抽取这些页面,每 1024 个页面是一组,可以分成 1024 组。对于每组中 1024 个页面的物理地址,按某种顺序排列可以构成一张表(每个表项都是一个页面的物理地址),这个表就是页表。页表的大小是 1024*4B=4KB,刚好是一个物理页的大小。

    4.3 页目录

    因为已经分成了 1024 组,每组都有一个页表(大小为 4KB),所以这 1024 个页表又可以用一张表来指向,这就是页目录。类似于页表,页目录共有 1024 个表项(称作页目录项),**每个页目录项的内容是某个页表的物理地址。**页表的大小是 1024*4B=4KB,刚好是一个物理页的大小。

    在这里插入图片描述

    有人说,这样不是更占内存吗?原来需要 4MB,现在存放 1024 个页表就要 4MB,再加上一个页目录,还要 4KB,何苦呢?

    1. 这样的层次化分页结构是每个任务都有的,或者说每个任务都有自己的页目录。在处理器内部,有一个控制寄存器叫 CR3,存放着当前任务的页目录的物理地址,故 CR3 又叫做页目录基址寄存器(Page Directory Base Register,PDBR).

    2. 每个任务都有自己的 TSS(Task-State Segment ,任务状态段),其中就包括了 CR3 寄存器域,存放着任务自己的页目录的物理地址。当任务切换时,CR3 寄存器的内容也会被更新,更新为新任务的页目录的物理地址。
      在这里插入图片描述

    3. 页目录和页表也是普通的页,混迹于全部的物理页中。它们和普通的页没有什么区别,无非就是功能不一样。当任务被操作系统撤销后,它们和任务所占用的普通的物理页一样会被回收。

    4. 页目录总是在物理内存中,页表可以在需要时再分配,这样就大大节省了物理内存。这就回答了前面的问题。

    5. 地址变换的具体过程

    具体怎么变换,还是用书上的例子来说明吧。
    假设段部件输出的线性地址是 0x00801050,如果没有开启分页,那么这个地址就是物理地址;但是现在开启了分页,所以要经过页部件的转换,才能得到物理地址。

    处理器的页部件专门负责线性地址到物理地址的转换工作。它首先将 32 位的线性地址分成 3 段,分别是高 10 位,中间 10 位和低 12 位。高 10 位用来索引页目录,中间 10 位用来索引页表,低 12 位作为页内偏移。

    在这里插入图片描述

    1. 当前任务的页目录的物理地址在 CR3 寄存器中,假设是 0x0000_5000;
    2. 段部件输出的线性地址是 0x0080_1050,按照高 10 位,中间 10 位和低 12 位分为三段,分别是0x002,0x001,0x050;
    3. 0x002 乘以 4(因为每个表项占 4 个字节)得到 0x008,作为偏移访问页目录,得到了0x0800_1000,这就是页表的物理地址,顺着它找到页表;
    4. 0x001 乘以 4(因为每个表项占 4 个字节)得到 0x004,作为偏移访问页表,得到了0x0000_c000,这就是我们要找的那个物理页的起始地址。
    5. 0x050 作为页内偏移,和物理页的起始地址 0x0000_c000相加,得到 0x0000_c050,这就是最终的物理地址。

    参考资料
    【1】《x86汇编语言:从实模式到保护模式》(李忠,电子工业出版社)
    【2】《Linux内核完全剖析》(赵炯,机械工业出版社,2006)

    展开全文
  • 分页机制

    万次阅读 多人点赞 2014-03-15 10:51:01
    本文为 第16章笔记 因为段的长度不定, 在分配内存时, 可能... 为了解决这个问题, 从80386处理器开始, 引入了分页机制. 分页功能从总体上来说, 是用长度固定的页来代替长度不一定的段, 藉此解决因段长度不同而带来的内

    本文为<x86汇编语言:从实模式到保护模式> 第16章笔记


    因为段的长度不定, 在分配内存时, 可能会发生内存中的空闲区域小于要加载的段, 或者空闲区域远远大于要加载的段. 在前一种情况下, 需要另外寻找合适的空闲区域; 在后一种情况下, 分配会成功, 但太过于浪费. 为了解决这个问题, 从80386处理器开始, 引入了分页机制. 分页功能从总体上来说, 是用长度固定的页来代替长度不一定的段, 藉此解决因段长度不同而带来的内存空间管理问题. 尽管操作系统也可以用软件来实施固定长度的内存分配, 但太过于复杂, 由处理器固件来做这件事, 可以使速度和效率最大化.

    分页机制概述

    简单的分页模型

    处理器中有负责分段管理的段部件, 每个程序或任务都有自己的段, 这些段都用段描述符定义. 随着程序的执行, 当要访问内存, 就用段地址加上偏移量, 段部件就会输出一个线性地址. 在单纯的分段模式下, 线性地址就是物理地址.

    一旦决定采用页式内存管理, 就应当把4GB内存分成大小相同的页. 页的最小单位是4KB, 也就是4096字节, 用十六进制表示就是0x1000. 因此, 第一个页的物理地址就是0x00000000, 第2个页的物理地址是0x00001000, 第3个页的物理地址是0x00002000....最后一个页的物理地址是0xfffff000. 这样,  4GB内存划分为1048576(0x100000)个页. 很显然, 页的物理地址, 其低12位始终为0.

    段管理机制对于Intel处理器来说是最基本的, 任何时候都无法关闭. 也就是说, 即使启用页管理功能, 分段机制依然是起作用的, 段部件依然工作.


    如上图所示, 内存的分配设计段空间的分配和页分配. 左边是虚幻的, 或者说虚拟的4GB内存空间, 称为虚拟内存; 右边是实实在在的内存, 被分成1048576个4KB的页面(每个方框4KB, 灰色代表已分配).

    在分页模式下, 操作系统可以创建一个为所有任务公用的4GB虚拟内存空间, 也可以为每一个任务创建独立的4GB虚拟内存空间, 这都是可行的. 当一个程序加载时, 操作系统既要在左边的虚拟内存中分配段空间, 又要在右边的物理内存中分配相应的页面. 因此, 第一步骤是寻找空闲的段空间, 该段空间既没有被其他程序使用, 也没有被同一程序内的其他段使用. 比如上图, 假设已经成功找到并分配了一个段空间, 基地址为0x00200000, 长度为8200字节.

    页的最小尺寸是4KB, 也就是4096字节, 因此, 8200字节的段, 需要占用3个页面, 其中最后一个页面只用了8个字节, 其余都是浪费着, 但这无关紧要, 如果允许页共享, 多个段或多个程序可以用同一个页来存放各自的数据. 在分段之后, 操作系统的任务就是把段拆开, 并分别映射到物理页. 注意, 段必须是连续的, 但不要求所分配的页都是连续的, 挨在一起的.

    就上图中的列子来说, 该段有8200字节, 需要分配3个页面. 操作系统在物理内存中搜索可用的空闲页, 接下来, 要建立线性地址和也之间的对应关系, 在图中, 0x200000~0x00200FFF对应着物理地址为0x00002000的页, 0x00201000~0x00201FFF对应着0x00004000, 0x00202000~0x00202007对应着0x00007000的页, 当然, 这里只是示例, 线性地址区间和页的对应关系可以随意.

    4GB虚拟内存空间不可能用来保存任何数据, 因为它是虚拟的, 它只是用来指示内存的使用情况. 当操作系统加载一个程序并创建为任务时, 操作系统在虚拟内存空间寻找空闲的段, 并映射到空闲的页, 然后, 到真正开始加载程序时, 再把原本属于段的数据按页的尺寸拆开, 分开写入对应的页中.

    从段部件输出的是线性地址, 或者叫做虚拟地址. 为了根据线性地址找到页的物理地址, 操作系统必须维护一张表, 把线性地址转换成物理地址, 这是一个反过程.

    如上图所示, 因为有1048576个页, 故转换表有1048576个表项. 这是个一维表格, 每个表项占4字节, 内容为页的物理地址. 这个表格的用法是这样的: 因为页的尺寸是4KB, 故, 线性地址的低12位可用于访问页内偏移, 高20位可用于指定一个物理页. 因此, 把线性地址的高20位当成索引, 乘以4, 作为表内偏移量, 从表中取出一个双字, 那就是该线性地址做对应的页的物理地址. 举个例子: mov edx, [0x0002]    执行这条指令, 段部件用段地址0x00200000加上指令中给出的偏移量0x2002, 得到线性地址0x00200002. 线性地址的高20位是表格索引, 即0x00200, 将索引乘以4, 得到0x00800, 这就是表内偏移, 看图, 从该单元可以取出一个双字0x00007000, 这就是页物理地址. 线性地址的低12位是页内偏移, 用页物理地址加上页内偏移量, 就是最终的物理内存地址. 0x00007000加上0x0002, 得到0x00007002, 这就是实际要访问的物理内存地址. 这里有个问题, 为什么表内表内偏移为0x000800的地方, 会恰好是物理地址0x00007000, 而不是其他页地址呢? 当程序加载时, 操作系统会首先在虚拟内存中分配段, 然后, 根据段需要分成多少页, 来搜索空闲页面. 当段较大时, 要按页的尺寸分成好几个地址区段, 操作系统用每个区段的首地址, 取高20位, 乘以4, 作为偏移量访问表格, 并将分配给区段的页的物理地址写入该表项. 最后, 把原本需要写入每个区段的程序数据, 写到对应的页中. 注意了, 在页式内存管理中, 页面的管理和分配是独立的, 和分段以及段地址没有关系.操作系统所要做的, 就是寻找空闲页面, 把它分配给需要的段, 并将页的物理地址填写到映射表内. 很显然, 也很重要的结论是, 线性地址, 包括线性地址空间, 和页面分配机制没有关系.

    基于以上特点, 同时为了充分挖掘分页内存管理的潜力, 一般来说, 每个任务都可以拥有4GB的虚拟内存空间; 同时, 每个任务都有自己的4GB虚拟内存空间, 但是, 很重要的是, 在整个系统中, 物理页面是统一调配的. 考虑这样一种情景: 任务A有一个段, 基地址为0x00050000, 长度为3000自己, 系统为它分配了物理地址0x08001000的页. 过了一会, 任务B加载了, 它也有一个段, 基地址也是0x00050000, 长度为4096字节, 此时, 操作系统为它分配了另外一个不同的, 物理地址为0x00700000的页. 在这种情况下, 在任务A内访问线性地址0x00050006, 访问的其实是物理地址0x08001006; 在任务B内访问同样的线性地址时, 访问的其实是物理地址0x00700006.

    另一个问题是, 每个任务都有4GB虚拟内存空间, 而物理内存只有一个, 最大也才4GB, 根本不够分的. 事实上, 确实不够分, 但是操作系统可以暂时将不用页退避到磁盘, 调入马上要使用的页, 通过这种手段来实现分页内存管理.

    以上, 就是基本的段页式内存管理机制. 基本的段页式内存管理示意图:

    页目录, 页表和页

    我们知道, 为了完成从虚拟地址(线性地址)到物理地址的转换, 操作系统应当为每个任务准备一张页映射表. 因为任务的虚拟地址空间为4GB, 可以分出1048576个页, 所以, 映射表需要1048756个表项, 又因为每个表项4字节, 故映射表总大小为4MB. 没错, 这张表很大, 要占用相当一部分空间, 考虑到在实践中, 没有哪个任务会真的用到所有表项, 充其量只是很小一部分, 这就很浪费了.  为了解决这个问题, 处理器设计了层次化的分页结构.

    分页结构层次化的主要手段是不采用单一的映射表, 取而代之的是页目录表和页表. 如下图所示:

    首先, 因为4GB的虚拟内存空间对应着1048576个4KB页, 可以随机的抽取这些页, 将它们组织在1024个页表内, 每个页表可以容纳1024个页. 页表内的每个项目叫做页表项, 占4字节, 存放的是页的物理地址, 故每个页表的大小是4KB, 正好是一个标准页的长度. 注意, 页在页表内的分布是随机的, 哪个页位于哪个页表中, 这是没有规律的.

    如图所示, 在将1048576个页归拢到1024个页表之后, 接着, 再用一个表来指向1024个页表, 这就是页目录表(Page Directory Table: PDT), 和页表一样, 页目录项的长度为4字节, 填写的是页表的物理地址, 共指向1024个页表, 所以页目录表的大小是4KB, 正好一个标准页的长度.

    这样的层次化分页结构是每个任务都拥有的, 或者说, 每个任务都有自己的页目录和页表. 如下图所示, 在处理器中有个控制寄存器CR3, 存放着当前任务页目录的物理地址, 故又叫做页目录基址寄存器(Page Directory Base Register: PDBR). 每个任务都有自己的TSS, 其中就包括了CR3寄存器域, 存放了任务自己的页目录物理地址. 当任务切换时, 处理器切换到新任务开始执行, 而CR3寄存器的内容也被更新, 以指向新任务的页目录位置. 相应的, 页目录又指向一个个的页表, 这就使得每个任务都只在自己的地址空间内运行. 从下图可以看出, 页目录和页表也是普通的页, 混迹于全部的物理页中. 它们和普通页的不同支持仅仅在于功能不一样. 当任务撤销之后, 它们和任务所占用的普通页一样会被回收, 并分配给其他任务.

    地址变换的具体过程

    对于Intel处理器来说, 有关分页, 最简单和最基本的机制就是这些; CR3寄存器给出了页目录的物理地址; 页目录给出了所有页表的物理地址, 而每个页表给出了它所包含的页的物理地址. 好了, 该清楚的都清楚了, 唯一还不明白的, 应该是如何用这种层次性的分页结构把线性地址转换成物理地址? 这里举个例子, 某任务加载后, 在4GB虚拟地址空间创建了一个段, 起始地址为0x00800000, 段界限为0x5000, 字节粒度. 当前任务执行时, 段寄存器DS指向该段. 又假设执行了下面一条指令

    mov edx, [0x1050]
    此时, 段部件会输出线性地址0x00801050. 在没有开启分页机制时, 这就是要访问的物理地址. 但现在开启了分页机制, 所以这是一个下虚拟地址, 要经过页部件转换, 才能得到物理地址.

    如下图所示, 处理器的页部件专门负责线性地址到物理地址的转换工作. 它首先将段部件送来的32位线性地址分为3段, 分别是高10位, 中间10位, 低12位. 高10位是页目录的索引, 中间10位是页表的索引, 低12位则作为页内偏移量来用.


    当前任务页目录的物理地址在处理器的CR3寄存器中, 假设它的内容为0x00005000. 段管理部件输出的线性地址是0x00801050, 其二进制的形式如图中给出. 高10位是十六进制的0x002, 它是页目录表内的索引, 处理器将它乘以4(因为每个目录项4字节), 作为偏移量访问页目录. 最终处理器从物理地址00005008处取得页表的物理地址0x08001000.

    线性地址的中间10位为0x001, 处理器用它作为页表索引取得页的物理地址. 将该值乘以4, 作为偏移量访问页表. 最终, 处理器又从物理地址08001004处取得页的物理地址, 这就是我们一直努力寻找的那个页.

    页的物理地址是0x0000c000, 而线性地址的低12位是数据所在的业内偏移量. 故处理器将它们相加, 得到物理地址0x0000C050, 这就是线性地址0x00801050所对应的物理地址, 要访问的数据就在这里.

    注意, 这种变换不是无缘无故的, 而是事先安排好的. 当任务加载时, 操作系统先创建虚拟的段, 并根据段地址的高20位决定它要用到哪些页目录项和页表项. 然后, 寻找空闲的也, 将原本应该写入段中的数据写到一个或者多个页中, 并将页的物理地址填写到相对应的页表项中. 只有这样做了, 当程序运行的时候, 才能以相反的顺序进行地址变换, 并找到正确的数据.

    页目录项, 页表项, CR3和打开分页

    页目录项和页表项

    页目录和页表中分别存放为页目录项和页表项, 它们的格式如下:

    可以看出, 在页目录和页表中, 只保存了页表或者页物理地址的高20位. 原因很简单, 页表或者页的物理地址, 都要求必须是4KB对齐的, 以便于放在一个页内, 故其低12位全是0. 在这种情况下, 可以只关心其高20位, 低12位安排其他用途.

    • P 是存在位, 为1时, 表示页表或者页位于内存中. 否则, 表示页表或者页不在内存中, 必须先予以创建, 或者从磁盘调入内存后方可使用.
    • RW 是读/写位. 为0时表示这样的页只能读取, 为1时可读可写
    • US 是用户/管理位. 为1时, 允许所有特权级别的程序访问; 为0时, 只允许特权级别为0, 1和2的程序访问.
    • PWT(Page-level Write-Through) 是页级通写位, 和高速缓存有关. "通写"是处理器高速缓存的一种工作方式, 这一位用来间接决定是否采用此种方式来改善页面的访问效率.
    • PCD(Page-level Cache Disable)是页级高速缓存禁止位, 用来间接决定该表项所指向的那个页是否使用高速缓存策略.
    • A 是访问位. 该位由处理器固件设置, 用来指示此表项所指向的页是否被访问过.
    • D(Dirty) 是脏位. 该位由处理器固件设置, 用来指示此表项所指向的页是否写过数据
    • PAT(Page Attribute Table) 页属性表支持位. 此位涉及更复杂的分页系统, 和页高速缓存有关, 可以不予理会, 在普通的4KB分页机制中, 处理器建议将其置0.
    • G 是全局位. 用来指示该表项所指向的页是否为全局性质的. 如果页是全局的, 那么, 它将在高速缓存中一直保存(也就意味着地址转换速度会很快). 因为页高速缓存容量有限, 只能存放频繁使用的那些表项. 而且, 当因任务切换等原因改变CR3寄存器的内容时, 整个页高速缓存的内容都会被刷新.
    • AVL位卑处理器忽略, 软件可以使用.

    CR3(PDBR)和开分页机制

    控制寄存器CR3, 也就是页目录表基地址寄存器PDBR, 该寄存器如上图所示.

    由于页目录表必须位于一个自然页内(4KB对齐), 故其物理地址的低12位是全0. 低12位除了PCD和PWT外, 都没有使用. 这两位用于控制页目录的高速缓存特性, 参见上面解释.

    控制寄存器CR0的最高位PG位, 用于开启分页或者关闭页功能. 当该位清0时, 页功能关闭, 从段部件来的线性地址就是物理地址. 当它置位时, 页功能开启. 只能在保护模式下才能开启分页功能, 当PE位清0时(实模式), 设置PG位将导致处理器产生一个异常中断.

    展开全文
  • 内存分段分页机制理解

    千次阅读 2019-08-29 08:14:30
    现代操作系统,计算机内存是按照先分段再分页的方式管理的。 注意: 以下描述都是基于32位计算机进行描述的。 1. 分段 1) 程序直接使用物理地址的问题 考虑最原始,最直接的情况,程序中访问的地址都直接对应于物理...

    现代操作系统,计算机内存是按照先分段再分页的方式管理的。
    注意:
    以下描述都是基于32位计算机进行描述的。

    1. 分段

    1) 程序直接使用物理地址的问题

    考虑最原始,最直接的情况,程序中访问的地址都直接对应于物理地址。
    这种方式有以下几个问题:
    1)两个使用的地址有交集的程序没法同时动行。
    2)写代码时,必须考虑到要运行的计算机内存大小。比如说,程序要在两台机器上运行,但是一台内存为4G,一台内存为8G,那么程序最大能够使用的物理地址也就只有4G。
    3)各个程序使用的内存无法进行控制。系统程序无法得到保护,各个程序之间无法隔离。

    所以引入了分段机制。那么什么是分段机制呢?
    将内存分成一段一段的(段大小不固定),为程序被分配某个段之后,程序便只能访问固定的段,无法访问其他地址。
    而且代码中不再使用绝对地址,而是使用相对地址。当程序加载到内存中运行的时候,操作系统为程序分配合适的段。并记录段的起始地址,及界限。当程序需要访问内存中某个变量时,用段起始地址+相对地址(段内偏移地址)得到真实的物理地址。
    通过这么一个机制,上面提到的直接使用物理内存的几个问题就都得到了解决。

    2) 分段机制详述

    内存的分段信息由操作系统进行维护,整个系统独一份(网上找到的文章大多都说一个处理器一份,不太理解)。维护分段信息的表叫做段描述符表,英文叫做GDT(global describetor table),里面存储的是段描述符,每个段描述符占8个字节。包含段起始地址,段界限(大小),属性。计算机有一个专门的寄存器来存储段描述符表的起始地址:GDTR。这个寄存器长48个bit,32位表示基地址,16位表示长度。
    知道了段描述符表,还得知道对应表中的哪个表项,才能找到段起始地址及界面。这就需要另一个寄存器了:段选择符寄存器。段选择符寄存器长16位,13位用来存储在段描述符表中的偏移,另外3位用来存储属性。所以可以看出,GDT表长度最多只能到2^13=8192个字节。8192/8=1024b也就是1024个表项。

    可以想象,对于庞大的操作系统而言,仅仅分1024个段是远远不够的。所以又设计了LDT的机制:
    段选择符寄存器的3个属性位中,有一个属性位标识要访问的段描述符表项一个普通的表项,还是一个LDT。LDT和GDT一样,就是一个段描述符表,不过不是全局唯一的了。那么在LDT中,到底对应哪一个表项呢?答案是LDTR,LDTR中存储了在LDT表中的偏移。

    可以看出,GDTR寄存器中的值是不需要变的。
    但是段选择符寄存器LDTR都是随着运行的程序的变化而变化的。所以保存对应程序的上下文时,需要保存对应寄存器的值。

    附以上涉及的各个寄存器各个bit详细说明:
    段描述符(8个字节)
    在这里插入图片描述
    GDTR(48bit,存储段描述符表的起始地址及长度)
    在这里插入图片描述
    段选择符
    在这里插入图片描述
    LDTR
    长度也是16位。没找到关于详细结构的说明。略。。

    参考
    内存寻址原理(详细透彻)

    3) 实模式下的分段机制

    上面讲的是保护模式下的分段机制,在保护模式下利用GDT存储段信息来实现相对地址到线性地址的转换。
    在实模式下也有分段机制,不过原因有所不同。
    段寄存器的产生
    段寄存器的产生源于Intel 8086 CPU体系结构中数据总线与地址总线的宽度不一致。数据总线的宽度,也即是ALU(算数逻辑单元)的宽度,平常说一个CPU是“16位”或者“32位”指的就是这个。8086CPU的数据总线是16位。
    地址总线的宽度不一定要与ALU的宽度相同。因为ALU的宽度是固定的,它受限于当时的工艺水平,当时只能制造出16位的ALU;但地址总线不一样,它可以设计得更宽。地址总线的宽度如果与ALU相同当然是不错的办法,这样CPU的结构比较均衡,寻址可以在单个指令周期内完成,效率最高;而且从软件的解决来看,一个变量地址的长度可以用整型或者长整型来表示会比较方便。但是,地址总线的宽度还要受制于需求,因为地址总线的宽度决定了系统可寻址的范围,即可以支持多少内存。如果地址总线太窄的话,可寻址范围会很小。如果地址总线设计为16位的话,可寻址空间是2^16=64KB,这在当时被认为是不够的;Intel最终决定要让8086的地址空间为1M,也就是20位地址总线。地址总线宽度大于数据总线会带来一些麻烦,ALU无法在单个指令周期里完成对地址数据的运算。有一些容易想到的可行的办法,比如定义一个新的寄存器专门用于存放地址的高4位,但这样增加了计算的复杂性,程序员要增加成倍的汇编代码来操作地址数据而且无法保持兼容性。
    Intel想到了一个折中的办法:把内存分段,并设计了4个段寄存器,CS,DS,ES和SS,分别用于指令、数据、其它和堆栈。把内存分为很多段,每一段有一个段基址,当然段基址也是一个20位的内存地址。不过段寄存器仍然是16位的,它的内容代表了段基址的高16位,这个16位的地址后面再加上4个0就构成20位的段基址。而原来的16位地址只是段内的偏移量。这样,一个完整的物理内存地址就由两部分组成,高16位的段基址和低16位的段内偏移量,当然它们有12位是重叠的,它们两部分相加在一起,才构成完整的物理地址。

    参考:
    段寄存器
    (搜到搜去,百度百科居然是讲得最清楚的)

    2.分页

    1) 为什么需要分页?

    线性地址是连续的,如果直接使用线性地址作为物理地址,那么为每个段分配的物理内存就必须是连续的物理内存。这不利用碎片化内存的利用,为内存管理增大了难度。所以引入了分页机制,将地址分为大小固定的页(一般为4096字节),按页为单位进行映射。连续的线性地址可以映射到不连续的的物理内存上。
    分页的另一个优点是,当物理内存不足时,可以按页为单位将内存的内容转换到磁盘上保存起来。
    如果不使用分页,则只能整个段整个段的进行转换。

    2) 分页机制详述

    对于4GB线性地址而言,每个页4KB,则总共可以分成1024*1024=1048576个页面。
    要表示每个页面的物理地址,需要4个字节。那么总共需要4MB的空间来存储分页相关的信息。
    按我的理解,基于上面讲的分段机制,所有程序是共享4GB的线性地址的,所以整个系统保存一份分页相关的信息即可。
    可能是哪里理解得有点不对,实际情况貌似是程序可以独享4GB的线性地址。所以每个程序都需要维护一个分页信息。那么每个程序都要4M的空间用来存储分页信息,这是不可接受的。

    所以设计了两级分页机制:
    在这里插入图片描述
    第一级叫做页目录,包含1024个表项。每个表项指向二级页表的地址。
    第二级叫做页表,包含1024个页。每个表项对应一个物理地址。
    页目录和页表项以及内存都是4KB对齐的。也就是说,地址的低12位必须全部为0,只需要使用地址的高20位即可。低12位可以用来存储属性。

    那么怎么确定页目录存储的位置呢?答案是CR3寄存器。需要为每个程序维护一个CR3寄存器的值。

    CPU内置了段部件和页部件。所以说CPU支持从相对地址(逻辑地址)到线性地址的转换,也支持线性地址到物理地址的转换。

    线性地址转换为物理地址时,线性地址被分为3个部分:高10位,中间10位,低12位。
    高10位4(每个表项4字节)作为索引在页目录上查找。
    中间10拉
    4,在页表中查找。
    低12位作为页内偏移,得到最终的物理地址。

    参考
    内存分页机制

    疑问

    1)每个程序的线性地址是独立的吗?为什么要为每个程序维护一份分页信息?

    根据我的理解来看,各个程序的线性地址是独立的。
    正是因为各个程序的线性地址是独立的,所以需要为各个程序维护分页信息。
    貌似操作系统都未真正使用分段机制,分段起始地址都是0。
    但是在linux上查看各个进程,却发现他们的堆栈地址空间是无重叠的部分。所以说,还是没有弄清楚。

    2)以上所讲分段和分页机制,还无法支持32位计算机使用超过4GB的内存,其机制是怎么样的?

    在linux上使用的机制叫PAE机制:
    简述:
    使用PAE扩展之后(设置CR4寄存器的第5位),地址变为8个字节。页目录和页表的大小没变,所以表示的项变少为一半。为了解决这个问题,增加了一级:cr3不再指向页目录表,而是指向一个大小为4的页目录指针表。(32字节对齐,所以只需要27位从便足够表示)
    为了寻址超过4GB的空间,就需要对cr3设置不同的值。
    通过设置cr3不同的值,就可以访问总共超过4GB大小的物理空间。
    只有内核能够修改进程的页表,所以用户态下运行的进程不能使用大于4GB的物理空间。

    https://blog.csdn.net/qq_31567335/article/details/84420554

    其实还是理解得比较模糊。

    3)有了分页机制,还要分段机制有什么用呢?

    展开全文
  • 内存寻址之分页机制

    2019-01-01 13:32:35
    分页机制完成线性地址到物理地址的转换 80x86 规定分页机制是可选的。分段和分页没有什么必然联系,分段可以说是 Intel 的 CPU 一直保持着的一种机制,而分页只是保护模式下的一种内存管理策略。想开启分页机制,CPU...

    写在前面:
    分页机制完成线性地址到物理地址的转换 80x86 规定分页机制是可选的。分段和分页没有什么必然联系,分段可以说是 Intel 的 CPU 一直保持着的一种机制,而分页只是保护模式下的一种内存管理策略。想开启分页机制,CPU必须工作在保护模式,而工作在保护模式可以不开启分页。

    分页机制由控制寄存器 CR0 中的 PG 位启用,如PG=1则启用分页机制,把线性地址转换为物理地址;如果PG=0则直接把段机制产生的线性地址当作物理地址使用。

    为什么要分页?
    问题的本质是在目前只分段的情况, CPU 认为线性地址等于物理地址,而线性地址是由编译器编译出来的,它本身是连续的,所以物理地址也必须要连续才行,但我们可用的物理地址不连续。换句话说,如果线性地址连续,而物理地址可以不连续,不就解决了吗。所以要解除线性地址和物理地址一一对应的关系,然后将他们的关系重新建立,通过某种映射关系,可以将线性地址映射到任意物理地址。

    页与页表
    为了效率起见,将线性地址空间分成若干大小相等的片,称为页( Page )。相应的地,逻辑上把内存划分为与页大小相等的若干存储块,称为( 物理 )块或页面( Page Frame )。常见的页面大小为 4KB,每一页都有 4K 字节长,每一页的起始地址都能被 4K 整除。

    分页机制通过把线性地址空间中的页,重新定位到物理地址空间来进行管理,因为每个页面的整个 4K 字节作为一个单位来映射,并且每个页面都对齐 4K 字节的边界,因此,线性地址的低 12 位经过分页机制直接地作为物理地址的低 12 位使用。所以,线性地址的高 20位可用来定位一个物理页,低 12 位可用来在该物理页内寻址。

    页表项结构
    页表就是个 N 行 l 列的表格,页表中的每一行(只有一个单元格)称为页表项( Page Table Entry,PTE ),其大小是 4 字节,页表项的作用是存储内存物理地址。当访问一个线性地址时,实际上就是在访问页表项中所记录的物理内存地址。

    不管是页目录还是页表,每个表项占4个字节,其表项结构基本相同,如上图。

    页面地址对页目录而言,指的是页表所在的物理页面在内存的起始地址;对页表而言,指的是页所对应的物理页面在内存的起始物理地址。页面的起始地址是 4K 的整数倍,所以页面项的低 12 位另做它用,内核用这 12 位来存放页的属性。

    属性包括以下内容:

    第0位是存在位,如果P=1,表示该页存在于物理内存中,如果P=0,表示不在物理内存中。
    第1位是读/写位,若为1表示可读可写,若为0表示可读不可写。
    第2位是普通用户/超级用户位,这两位为页目录项提供硬件保护。当特权级为3的进程要想访问页面时,需要通过页保护检查,而特权级为0的进程就可以绕过页保护。
    第3位是PWT(Page Write-Through)位,表示是否采用写透方式,写透方式就是既写内存(RAM)也写高速缓存,该位为1表示采用写透方式
    第4位是PCD(Page Cache Disable)位,表示是否启用高速缓存,该位为1表示启用高速缓存。
    第5位是访问位,若为 1 表示该页被 CPU 访问过了。
    第6位是脏页位,当 CPU 对一个页面执行写操作时,就会设置对应页表项的D位为1。仅针对页表项有效,并不会修改目录项中的 D 位。
    第7位是Page Size标志,只适用于页目录项。如果置为1,页目录项指的是4MB的页面,请看后面的扩展分页。
    第8位为全局位,用来指定该页是否为全局页,为 1 表示是全局页,为 0 表示不是全局页。若为全局页,该页在高速缓存TLB中一直保存,给出虚拟地址直接给物理地址
    第9~11位由操作系统专用,Linux也没有做特殊之用。
    怎样用线性地址找到页表中对应的页表项?
    首先要明确:分页机制打开前,要将页表地址加载到控制寄存器 CR3 中,这个地址是页表的物理地址。虽然分页机制的作用是将线性地址转换成物理地址,但其转换过程相当于在关闭分页机制下进行,过程中所涉及到的页表及页表项的寻址,它们的地址都被 CPU 当作最终的物理地址(本来也是物理地址)直接送上地址总线,不会被分页机制再次转换(否则会无限递归下去)。

    一个页表项对应一个页,所以,用线性地址的高 20 位作为页表项的索引,每个页表项要占用 4 字节大小,所以这高 20 位的索引乘以 4 后才是该页表项相对于页表物理地址的字节偏移量。用 CR3 寄存器中的页表物理地址加上此偏移量便是该页表项的物理地址,从该页表项中得到映射的物理地址,然后用线性地址的低 12 位与该物理页地址相加,所得的地址就是最终要访问的物理地址。

    为什么使用两级页表?
    32 位环境下,4GB 线性空间分成 4KB 一个页,那就是 1M 个页,每个页表项需要 4 个字节来存储,那么整个 4GB 空间的映射就需要 4MB 的内存来存储管理信息。这只是一个线性地址空间的映射,每个进程都有自己的映射,假设有100个进程在运行,就需要 400MB 的内存来存储管理信息,太浪费了 。

    一级页表中所有页表项必须提前建好,且要求是连续的。为什么需要连续的呢? 如果不连续,就没有办法通过页表寄存器+偏移量找到对应的页表项了。

    所以要解决的是:不要一次性地将页表项全建好,需要时动态创建页表项。二级页表很好的解决了这个问题。

    两级页表结构
    所谓两级页表就是对页表再进行分页,一个页表内的所有页表项是连续存放的,页表本质上是一堆数据,也是以页为单位存放在主存中。

    第一级称为页目录表。每个页表的物理地址在页目录表中都以页目录项( Page Directory Entey,PDE ) 的形式来存储,4MB 的页表再次分页可以分为 1K ( 4MB / 4KB )个页,对每个页的描述需要 4 个字节,所以页目录表占用 4K 大小,正好是一个标准页的大小,其指向第二级表。线性地址的高 10 位产生第一级的索引,由索引得到的表项中,指定并选择了 1K 个二级表中的一个页表。

    第二级称为页表,存放在一个 4K 大小的页面中,包含 1K 个表项,每个表项包含一个页的物理基地址。线性地址的中间 10 位产生第二级索引,可以获得包含页的物理地址的页表项。这个物理地址的高 20 位与线性地址的低 12 位形成了最终的物理地址。

    有人觉着这样做的话映射 4GB 地址空间需要 4MB+4KB 的内存,不是更大了吗? 当然不是,在一个进程中,实际使用到的内存远没有4GB这么大,一级页表需要一次分配所有页表空间,且要求是连续的,两级页表则可以在需要的时候再分配页表空间,这样节省内存。

    线性地址到物理地址的转换


    CR3包含着页目录的起始地址,用32位线性地址的最高10位A31~A22作为页目录的页目录项的索引,将它乘以4,与CR3中的页目录的起始地址相加,形成相应页表的地址。
    从指定的地址中取出32位页目录项,它的低12位为0,这32位是页表的起始地址。用32位线性地址中的A21~A12位作为页表中的页面的索引,将它乘以4,与页表的起始地址相加,形成32位页面地址。
    将A11~A0作为相对于页面地址的偏移量,与32位页面地址相加,形成32位物理地址。
    整个过程是比较机械的,每次转换先获取物理页基地址,再从线性地址中获取索引,合成物理地址后再访问内存。不管是页表还是要访问的数据都是以页为单位存放在主存中的,因此每次访问内存时都要先获得基址,再通过索引(或偏移)在页内访问数据,因此可以将线性地址看作是若干个索引的集合。

    快表 TLB( Translation Lookaside Buffer )简介
    分页机制虽然很灵活,但是从线性地址转换为物理地址的过程还是比较麻烦,过程不再赘述。每一个线性地址到物理地址的转换都要重复转换的过程,涉及到多级页表就更显得麻烦。而且处理器的速度和内存的速度完全是两个数量级,页表在内存中,转换过程中频繁的访问内存,使得地址转换的速度慢上加慢。

    那能不能通过线性地址直接得到对应的物理地址,免去查表的过程? 答案是可以的,就是应用到缓存,根据程序局部性原理,可以将近来常访问的地址和指令加载到速度更快的设备中。处理器准备了一个高速缓存,专门用来存放虚拟地址页框与物理地址页框的映射关系,这个缓存就是 TLB,俗称快表。

    有了 TLB,处理器在寻址之前会用虚拟地址的高 20 位作为索引来查找 TLB 中的相关条目,如果命中则返回虚拟地址所映射的物理页框地址,否则会查询内存中的页表,获得物理地址后再更新 TLB。

    Linux中的分页机制


    页全局目录
    页顶级目录
    页中间目录
    页表
    页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录的地址,而页中间目录又包含若干页表的地址。每一个页表项指向一个页框。线性地址因此被分成五个部分。图中没有显示位数,因为每一部分的大小与具体的计算机体系结构有关。

    对于没有启用物理地址扩展的32位系统,两级页表已经足够了。从本质上说Linux通过使“页上级目录”位和“页中间目录”位全为0,彻底取消了页上级目录和页中间目录字段。不过,页上级目录和页中间目录在指针序列中的位置被保留,以便同样的代码在32位系统和64位系统下都能使用。内核为页上级目录和页中间目录保留了一个位置,这是通过把它们的页目录项数设置为1,并把这两个目录项映射到页全局目录的一个合适的目录项而实现的。

    启用了物理地址扩展的32 位系统使用了三级页表。Linux的页全局目录对应80×86 的页目录指针表(PDPT),取消了页上级目录,页中间目录对应80×86的页目录,Linux的页表对应80×86的页表。

    最后,64位系统使用三级还是四级分页取决于硬件对线性地址的位的划分。

    我们虽然讨论的是Linux的分页机制,实际上我们用了大部分篇幅来讨论Intel CPU的分页机制实现。因为Linux的分页机制是建立在硬件基础之上的,不同的平台需要有不同的实现。Linux在软件层面构造的虚拟地址,最终还是要通过MMU转换为物理地址,也就是说,不管Linux的分页机制是怎样实现的,CPU只按照它的分页实现来解读线性地址,所以Linux传给CPU的线性地址必然是满足硬件实现的。例如说:Linux在32位CPU上,它的四级页表结构就会兼容到硬件的两级页表结构。可见,Linux在软件层面上做了一层抽象,用四级页表的方式兼容32位和64位CPU内存寻址的不同硬件实现。
    --------------------- 
    作者:Tanswer_ 
    来源:CSDN 
    原文:https://blog.csdn.net/Tanswer_/article/details/78695370 
    版权声明:本文为博主原创文章,转载请附上博文链接!

     

     

     

    展开全文
  • 内存分页机制

    万次阅读 多人点赞 2018-09-27 18:08:33
    为什么要分页 在保护模式中,内存访问使用分段机制——即&amp;quot;段基址:段内偏移地址&amp;quot;的方式,为加强段内存的安全性和可管理性还引入了段描述符的概念对段内存加强管理。但仅仅这样还是不够的...
  • Intel通过在它的处理器上把管脚数从32增加到36满足这样的需要, 从Pentinum Pro开始, Intel所有处理器的寻址能力可达到2^36=64GB, 但是只有引入一种新的分页机制才能把32位现行地址转换为36位物理地址才能使用所增加...
  • 分页机制在段机制之后进行,以完成线性—物理地址的转换过程。段机制把逻辑地址转换为线性地址,分页机制进一步把该线性地址再转换为物理地址。分页机制由CR0中的PG位启用。如PG=1,启用分页机制,并使用本节要描述...
  • 恢复进程的运行 在分页机制下,因为内存空间都是提前划分好的,并且在分页机制下,内存释放都是以页为单位进行释放的, 所以不会像分段机制那样产生非常小的内存空闲空间,即外部内存碎片 在分页机制下,如果内存...
  • x64 分页机制

    2021-06-12 14:16:05
    x64 分页
  • 内存管理-分页机制

    2014-10-10 21:17:10
    对与内存管理保护模式中的分页机制讲得停详细的,个人觉得很不错。共享给大家学习下。
  • 80386分页机制

    2012-10-12 14:00:33
    对80386 的分页进行了详细的介绍,并加以学习的注释
  • 分段机制和分页机制

    2017-11-13 00:47:00
    一分段机制  1、什么是分段机制  分段机制就是把虚拟地址空间中的虚拟内存组织成一些长度可变的称为段的内存块单元。  2、什么是段  每个段由三个参数定义:段基地址、段限长和段属性。  段的基地址、段限长...
  • 前面我们提到Linux内核仅使用了较少的分段机制,但是却对分页机制的依赖性很强,其使用一种适合32位和64位结构的通用分页模型,该模型使用 四级分页机制 ,即 页全局目录(Page Global Directory) 页上级...
  • x86处理器的分页机制

    2020-11-06 11:22:28
    为了解决这个问题, 从80386处理器开始, 引入了分页机制. 分页功能从总体上来说, 是用长度固定的页来代替长度不一定的段, 藉此解决因段长度不同而带来的内存空间管理问题. 尽管操作系统也可以用软件来实施固定长度的...
  • 首先:在未开启分页机制的情况下,逻辑地址在分段管理机制作用下,直接转换成线性地址(物理地址);在开启分页机制的情况下,逻辑地址在分段管理机制下,转换成线性地址,然后在分页机制管理下,再从线性地址转换成...
  • 段机制(虚拟地址转换成线性地址),“段”是虚拟地址空间的基本单位,一般情况下虚拟地址的表示方法为段基址:偏移量,但是有些平台不支持段机制,只支持分页机制,为了增加linux的可移植性,linux创造性的提出了段...
  • 内存分段机制与分页机制

    千次阅读 2017-01-12 14:29:11
    (一):逻辑地址(Logical Address)   指由程式产生的和段相关的偏移...只有在Intel实模式下,逻辑地址才和物理地址相等(因为实模式没有分段或分页机制,Cpu不进行自动地址转换);逻辑也就是在Intel保护模式
  • ---其中: (*)分段机制(物理地址/线性地址 = 段基址 + 偏移量) 保护模式下未开启分页(段基址+偏移量)直接对应物理地址空间;...(*)分页机制(物理地址=页表项内的地址+偏移量) 一级页表 ...
  • 分段和分页机制

    万次阅读 多人点赞 2018-03-29 17:05:12
    一 分段机制  1、什么是分段机制  分段机制就是把虚拟地址空间中的虚拟内存组织成一些长度可变的称为段的内存块单元。  2、什么是段  每个段由三个参数定义:段基地址、段限长和段属性。  段的基地址、段...
  • Linux的分段和分页机制

    千次阅读 2015-11-07 21:23:58
    1 基于80x86的Linux分段机制80386的两种工作模式:80386的工作模式包括实地址模式和虚地址模式(保护模式)。Linux主要工作在保护模式下。在保护模式下,80386虚地址空间可达16K个段,每段大小可变,最大达4GB。逻辑...
  • 虚拟内存(分页机制)

    2020-09-30 17:20:05
    以前的一片文章讲了为什么会出现分页, 是为了解决什么问题? 首先澄清一个概念.其实虚拟内存和分页是两件事, 分页是指将把地址空间人为地等分成固定大小的页, 这个页的粒度比段更小, 以方便在外存与内存换入换出, 而...
  • 1.概述 一个进程的虚拟地址空间是4GB,它的物理...将虚拟地址转换为物理地址依赖的规则就是分页机制。 物理地址也并不是内存条地址,物理地址需要再进行一层转换才能找到真正的内存条地址。 我们只要会转换到物理地...
  • 分页机制让我们在程序开发的时候不需要管理内存的,也不需要操心内存够不够用它是实现虚拟存储的关键,位于线性地址与物理地址之间。 整个主内存区域划分成 4096 字节(4k)为一页的内存页面 程序申请使用内存时,...
  • 1、逻辑地址,线性地址,虚拟地址,物理地址傻傻分不清? (1)逻辑地址 在IA-32架构下,分段是必须的,而分页并不是必须的。不论分页与否,访问内存都必须采用“段基址:...如果不启用分页机制,线性地址就是物理地址,.
  • 现代操作系统都使用分页机制来管理内存,这使得每个程序都拥有自己的地址空间。每当程序使用虚拟地址进行读写时,都必须转换为实际的物理地址,才能真正在内存条上定位数据。如下图所示: 内存地址的转换是通过一种...
  • Linux内存寻址之分段机制及分页机制

    千次阅读 2016-03-04 17:32:34
    原本以为自己对分段分页机制已经理解了,结果发现其实是一知半解。于是,查找了很多资料,最终理顺了内存寻址的知识。现在把我的理解记录下来,希望对内核学习者有一定帮助,也希望大家指出错误之处。 分段到底是...
  •  终于要开始讲分段机制了,前边为什么啰嗦了那么多,就是为了给linux分段和分页机制做铺垫。以前我也看了很多关于linux分段和分页的文章,就是不明白其中的奥秘,后来才发现,原来内存寻址是你学习中无法绕过的东西...
  • 计算机内存的分页机制 ​ 当计算机运行时,某个时间段内,它只是使用了一小部分的数据。其他数据在一个时间段内并不会被使用。 ​ 如果以整个程序为单位进行映射,会把不需要的数据读取到内存中,也会把过多的数据...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 94,032
精华内容 37,612
关键字:

分页机制