5.2启动内存分页机制,畅游虚拟空间

本文涉及的产品
公网NAT网关,每月750个小时 15CU
简介: 5.2启动内存分页机制,畅游虚拟空间

5.2启动内存分页机制,畅游虚拟空间

即使机器上只有512MB的内存,每个进程自己的内存空间也是4GB,这4GB便是指的虚拟内存空间。下面就是讲解虚拟内存空间是怎么来的。


5.2.1内存为什么要分页

问题场景:由于多进程的发展,当内存固定时为了支持更多进程的运行,而传统的内存段基址:段内偏移是直接访问物理地址,而一段程序的地址是连续的这就导致了所需要的物理地址也是连续的。当内存中出现很多内存碎片的时候,总的空闲内存满足但是这些内存不满足连续,这就将导致进程无法加载。所有为了解决这个问题引出了内存分页机制。

下面是一个例子:

图54所示模拟了多个进程并行的情况。 在第1步中, 系统里有3个进程正在运行。 进程A、 B、 C各占了 10MB、 20MB、 30MB 的内存空间。 物理内存还是挺宽裕的,还剩下 15MB 可用。 到了第2步就悲催了。此时进程B 已经运行结束。 腾出了 20MB 的内存。 可是待加载运行的进程D需要20MB+3KB的内存空间, 即20483KB。 现在的运行环境未开启分页功能。 “段基址+段内偏移”产生的线性地址就是物理地址 ′程序中引用的线性地址是连续的 所以物理地址也连续。 虽然总共剩下 35MB 内存可用。可问题是明摆着的, 现在连续的内存块只有原来进程B 的20MB 和最下面可用内存 15MB。 哪一块都不够进程D用的这怎么办呢?


6a252a08586e404ca9462d624c92b66c.png

对于上面的解决方案有两个:

1.等待进程C运行完后腾出内存, 这样连续可用的内存就够运行进程D 了。

2.将进程A的段A3 或进程C的段 C1 换出到硬盘上。 腾出一部分空间。 加上邻接的 20MB. 足够容纳进程D。

针对第二种解决方案首先需要进行内存的换入换出:

内存是怎么换出的:

在保护模式下, 段描述符是内存段的身份i正。 CPU 在引用一个段时, 都要先查看段描述符。 很多时候,段描述符存在于描述符表中 (GDT或LDT), 但与此对应的段并不在内存中, 也就是说, CPU 允许在描述符表中已注册的段不在内存中存在,这就是它提供给软件使用的策略 我们利用它实现段式内存管理。 如果该描述符中的P位为1, 表示该段在内存中存在。 访问过该段后, CPU将段描述符中的A位置1, 表示近来刚访问过该段。相反, 如果P位为0, 说明内存中并不存在该段, 这时候CPU将会抛出个NP (段不存在) 异常, 转而去执行中断描述符表中NP异常对应的中断处理程序, 此中断处理程序是操作系统负责提供的, 该程序的工作是将相应的段从外存中载入到内存中, 并将段描述符的P位置1,中断处理函数结束后返回, CPU重复执行这个检查, 继续查看该段描述符的P位, 此时己经为1了, 在检查通过后,将段描述符的A位置1。

内存是怎么换出的:

段描述符的A位由 CPU置1, 但清0工作可是由操作系统来完成的。 此位干吗用的呢? 如果仅仅用来表示

该段被访问过, 这也意义不大啊。 其实这正是软件和硬件相互配合的体现,操作系统每发现该位为1后就将该位清0,这样一来, 在一个周期内统计该位为1 的次数就知道该段的使用频率了, 从而可以找出使用频率最低的段。当物理内存不足时, 可以将使用频率最低的段换出到硬盘以腾出内存空间给新的进程。 当段被换出至硬盘后,操作系统将该段描述符的P位置 0。 当下次这个进程上 CPU 运行后, 如果访问了这个段, 这样程序就回到了刚开始 CPU检查出P位为0、 紧接着抛出异常、 执行操作系统中断处理程序、 换入内存段的循环。

这样通过A位P位,中断处理程序,硬件,操作系统实现了内存换入和换出的循环。

第二个方法虽然解决了内存不足的问题, 但也有缺陷。 比如物理内存特别小, 无法容纳任何一个进程的

段, 这就没法运行进程了。 更没法做段的换入换出。

想一想, 出现这种问题的原因是什么? 问题的本质是在目前只分段的情况下, CPU 认为线性地址等于物理地址。 而线性地址是由编译器编译出来的, 它本身是连续的, 所以物理地址也必须要连续才行。 但我们可用的物理地址不连续 换句话说,如果线性地址连续 而物理地址可以不连续不就解决了吗。

按照这种思路,我们首先要做的是解除线性地址与物理地址一一对应的关系 然后将它们的关系重新建立。 通过某种映射关系 可以将线性地址映射到任意物理地址。这种映射关系是通过一张表来实现的,该表就是我们所说的页表,查找也表的工作是由硬件完成的。


5.2.2一级页表

保护模式下的分段机制:

在保护模式中段寄存器中的内容已经是选择子。 但选择子最终就是为了要找到段基士止, 其内存访

问的核心机制依然是 “段基址= 段内偏移地士止″, 这两个地址在相加之后才是绝对地址。 也就是我们所说

的线性地址, 此线性地址在分段机制下被 CPU 认为是物理地址。 直接拿来就能用, 也就是说, 此线性地

士止可以直接送上地址总线。 将段基址和段内偏移地址相加求和的工作是由 CPU 的段部件自动完成的。 整

个访问内存的过程如图 5_6所示。

e743d613ab584266822a17df3dd7a43f.png


保护模式下的分页机制:

分页机制要建立在图 5-6 所示分段机制的基础上, 也就是说, 段部件的工作依然免不了, 所以分页只能是在分段之后进行的, 其过程如图 5-7 所示。

图 5-7 说明, CPU 在不打开分页机制的情况下,是按照默认的分段方式进行的段基址和段内偏移地址经过段部件处理后所输出的线性地址, CPU 就认为是物理地址。如果打开了分页机制, 段部件输出的线性地址就不再等同于物理地址了。 我们称之为虚拟地址, 它是逻辑上的, 是假的, 不应该被送上地址总线 (因为地址只是个数字, 任何数字都可以当作地址, 这里说的 “不应该″ 是指应该人为保证送上地址总线上的数字是正确的地址)CPU必须要拿到物理地址才行, 此虚拟地址对应的物理地址需要在页表中查找, 这项查找工作是由页部件自动完成的。 为了要搞清楚页部件的工作原理, 必须要搞清楚这两件事

1.分页机制的原理

2.页表的结构


73953f1ff65845f7a1eb3d15588ec7bb.png


分页机制的思想是: 通过映射, 可以使连续的线性地址与任意物理内存地址相关联, 逻辑上连续的线性地址其对应的物理地址可以不连续。

分页机制的作用有两方面。

1.将线性地址转换成物理地址。

2.用大小相等的页代替大小不等的段。这两方面的作用

339e5bd5fac94f728c3140f4304fcc47.png


由于有了线性地址到真实物理地址的这层映射经过段部件输出的线性地址便有了另外一个名字,虚拟地址。

下面根据上图介绍一下操作系统在分页机制下加载进程的过程。

图5-8 表示的是一个进程的地址转换过程, 从线性空间到虚拟空间再到物理地址空间, 每个空间大小都是 4GB。 图上的 4GB 物理地址空间属于所有进程包括操作系统在内的共享资源, 其中标注为已分配页的内存块被分配给了其他迸程, 当前进程只能使用未分配页。 此转换过程对任意一个进程都是一样的, 也就是说, 每个进程都有自己的 4GB 虚拟空间。

前面说过啦, 分页机制建立在分段机制之上, 与其脱离不了干系, 即使在分页机制下的进程也要先经过逻辑上的分段才行,每加载一个进程, 操作系统按照进程中各段的起始范围, 在进程自己的 4GB 虚拟地址空间中寻找可用空间分配内存段, 此虚拟地址空间可以是页表。也可以是操作系统维护的某种数掂结构总之此阶段的分配是逻辑上的,并没有真正写入物理内存。在分页机制下,分配情况如图 5-8 中所示的虚拟地址空间中的代码段和数据段。代码段和数据段在逻辑上被拆分成以页为单位的小内存块。 这时的虚拟地址虚如其名, 不能存放在任何数据。 接着操作系统开始为这些虚拟内存页分配真实的物理内存页, 它查找物理内存中可用的页, 然后在页表中

登记这些物理页地址。 这样就完成了虚拟页到物理页的映射, 每个进程都以为自己独享4GB地址空间。

对于从虚拟地址空间到物理地址空间的映射关系保存在页表中。页表就是个N行 1 列的表格页表中的每一行 (只有一个单元格) 称为页表项 (PageTableEntry,

PTE), 其大小是 4 字节, 页表项的作用是存储内存物理地址。 当访问一个线性地址时, 实际上就是在访问页表项中所记录的物理内存地址。

下图是映射方案原理:


a02a7da357f54a8b94fd9daef283cb54.png

下图是针对上面映射方案,为了使得页表不会占据太大的内存下图是选择合适的内存块大小:

1ef4e1007e044aa6b8bc75814cc687ec.png

官方中的内存块大小就是如图所示的情况,每个内存块大小是4KB,一种有1M的内存块,即也就是有1M的页表项。

上面所说的也就是一级页表:


f5c13696ae2240e8985b62af07fa1775.png

针对如何寻找到正确的地址,可以利用5-10理解。右边第11-0位用来表示页的大小,也就是这12位可以作为页内寻址。左边第31-12位用来表示页的数量,同样这20位也用来索引一个页,可以来找到第几页的情况。

任意一个地址最终会落到某一个物理页中。 32 位地址空间共有 1M (1048756)

个物理页, 首先要做的是定位到某个具体物理页, 然后给出物理页内的偏移量就可以访问到任意1字节的内存啦。 所以用20 位二进制就可以表示全部物理页啦。 标准页都是4KB, 12 位二进制便可以表达4KB之内的任意地址。

在32位保护模式下虚拟地址的高20 位可用来定位一个物理页, 低 12 位可用来在该物理页内寻址。 这是如何实现的呢? 物理地址写在页表的页表项中, 段部件输出的只是线性地址, 所以问题就变成了:怎样用线性地址找到页表中对应的页表项。

在转换之前需要提前知道这两件事情:

1:分页机制打开前要将页表地址加载到控制寄存器 CR3 中, 这是启用分页机制的先决条件之一, 在介绍二级页表时会细说。 所以, 在打开分页机制前加载到寄存器 cr3 中的是页表的物理地址,页表中页表项的地址自然也是物理地址了。

2:虽然内存分页机制的作用是将虚拟地址转换成物理地址, 但其转换过程相当于在关闭分页机制下进行, 过程中所涉及到的页表及页表项的寻址, 它们的地址都被 CPU 当作最终的物理地址 (本来也是物理地址) 直接送上地址总线, 不会被分页机制再次转换 (否则会递归转换下去〉。

地址转换过程原理如下:

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

上面从虚拟地址到物理地址的过程通过页部件来计算:

页部件的工作:用线性地址的高20位在页表中索引页表项,用线性地址的低12位与页表项中的物理地址相加,所求的和便是最终线性地址对应的物理地址。


20eebc3aceb5466f81e64d4af1bdce8e.png

假设咱们是在平坦模型下工作, 不管段选择子值是多少, 其所指向的段基址都是 0, 指令 mov ax,[0xl234]中的 0x1234 称为有效地址, 它作为 “段基址= 段内偏移地址″ 中的段内偏移地址。 这样段基址为 0, 段内偏移地址为 0x1234. 经过段部件处理后, 输出的线性地址是 0x1234。由于咱们是演示分页机制, 必须假定系统己经打开了分页机制, 所以线性地址0x1234被送入了页部件。 页部件分析 0x1234的高20 位, 用十六进制表示高 20 位是 0x00001。 将此项作为页表项索引, 再将该索引乘以4后加上 cr3 寄存器中页表的物理地址, 这样便得到索引所指代的页表项的物理地址, 从该物理地址处 (页表项中〉 读取所映射的物理页地址= 0x9000。 线性地址的低 12 位是 0x234, 它作为物理页的页内偏移地址与物理页地址0x9000 相加, 和为 0x9234这就是线性地址 0X1234 最终转换成的物理地址。


5.2.3二级页表

问题引出:为什么有了一级页表为什么还要搞二级页表?

(1) 一级页表中最多可容纳 1M (1048576) 个页表项每个页表项是4字节, 如果页表项全满的话便是4MB大小

(2) 一级页表中所有页表项必须要提前建好, 原因是操作系统要占用 4GB 虚拟地址空间的高1GB,挪用户进程要占用低 3GB。

(3) 每个进程都有自己的页表, 进程一多, 光是页表占用的空间就很可观了。

归根结底,我们要解决的是不要一次性地将全部页表项建好需要时动态创建页表项。如何解决呢?

什么是二级页表?

无论是几级页表, 标准页的尺寸都是4KB, 这一点是不变的。 所以 4GB 线性地址空间最多有1M个标准页。 一级页表是将这 1M个标准页放置到一张页表中, 二级页表是将这 1M 个标准页平均放置lK个 页表中。 每个页表中包含有 1K个页表项。 页表项是4字节大小, 页表包含1K个页表项, 故页表大小为 4KB, 这恰恰是一个标准页的大小。

拆分出了这么多个页表, 如何使用它们昵? 为此,专门有个页目录表来存储这些页表。 每个页表的物理地址在页目录表中都以页目录项 (PageDirectoryEntry,PDE) 的形式存储, 页目录项大小同页表项一 样, 都用来描述一个物理页的物理地址, 其大小都是4字节, 而且最多有 1024 个页表所以页目录表也是4KB大小,同样也是标准页的大小。页表是用于管理内存的数据结构, 其也要占用内存, 所以页目录表和页表所占用的物理页, 同样混迹 于物理内存之中, 如图5-13所示

b26b1d6a829c4a8d8c13c485e208e7a2.png

二级页表从虚拟地址到物理地址的转换方法:

在二级页表转换中, 依然用 32 位虚拟地址的不同部分来定位物理页。每个页表中可容纳1024个物理页, 故每个页表可表示的内存容量是10244KB=4MB。 页目录中共有1024 个页表, 故所有页表可表示的内存容量是 10244MB=4GB, 这已经达到了 32 位地址空间的最大容量。所以说, 任意一个 32 位物理地址, 它必然在某个页表之内的某个物理页中。 我们定位某一个物理页, 必然要先找到其所属的页表。页目录中 1024 个页表只需要 10 位二进制就能够表示了, 所以, 虚拟地址的高10位 (第 31~22 位) 用来在页目录中定位一个页表,也就是这高 10 位用于定位页目录中的页目录项 PDE,PDE 中有页表物理页地址。找到页表后,〉到底是页表中哪一个物理页呢? 由于页表中可容纳 1024 个物理页,故只需要 10 位二进制就能够表示了。所以虚拟地址的中间 10 位 (第 21-12 位) 用来在页表中定位具体的物理页, 也就是在页表中定位一个页表项 PTE。 PTE 中有分配的物理页地址。 由于标准页都是4KB, 12 位

二进制便可以表达4KB 之内的任意地址, 故线性地址中余下的 12 位 (第11~0 位) 用于页内偏移量。

总结一下:对于32位情况下可分为三部分:31-22位用于定位页目录表中的页目录项(PDE)页目录项中有指向对应页表的地址,21-12位用于定位页表中的页表项(PTE)页表项中有指向对应物理页的地址,0-11位便表示一个物理页中的偏移地址。

一级页表地址转换停理是将 32 位虚拟地址拆分成高 10位 中间 10位 低 12位三部分, 它们的作用是 高10位作为页表的索引用于在页目录表中定位一个页目录项 PDE. 页目录项中有页表物理地址,也就是定位到了某个页表。 中间 10 位作为物理页的索引 用于在页表内定位到某个页表项 PTE, 页表项中有分配的物理页地址也就是定位至到某个物理页低12位作为页内偏移量用于在己经定位到的物理页内寻址。

具体转换方法如下:

同一级页表一样,访问任何页表内的数据都要通过物理地址由于页目录项PDE 和页表项 PTE 都是4字节大小, 给出了 PDE和 PTE 索引后, 还需要在背后悄悄乘以4, 再加上页表物理地址, 这才是最终要访问的绝对物理地址。转换过程背后的具体步骤如下:

(1) 用虚拟地址的高10位乘以4,作为页目录表内的偏移地址, 加上页目录表的物理地址, 所得的和, 便是页目录项的物理地址。 读取该页目录项,从中获取到页表的物理地址。

(2) 用虚拟地址的中间 10 位乘以4, 作为页表内的偏移地址, 加上在第1步中得到的页表物理地址,所得的和, 便是页表项的物理地址。 读取该页表项, 从中获取到分配的物理页地址

(3) 虚拟地址的高 10 位和中间 10 位分别是PDE 和 PTE 的索引值二 所以它们需要乘以4。 但低 12 位就不是索引值啦, 其表示的范围是 0~0xffff, 作为页内偏移最合适, 所以虚拟地址的低 12 位加上第 2步中得到的物理页地址 所得的和便是最终转换的物理地址。


a3bb4084347a423d93c0acbc87b0fddc.png

页目录项和页表项结构:

b00568f73fc14eb4850bfef2fc3a1dbe.png


页目录项和页表项低十二位中的属性:

P, Present, 意为存在位。 若为 1表示该页存在于物理内存中, 若为0表示该表不在物理内存中。操作系统的页式虚拟内存管理便是通过P位和相应的 pagefault异常来实现的。

RW, Read/Write. 意为读写位。 若为1表示可读可写, 若为0表示可读不可写。

US。 User/Supervisor, 意为普通用户/超级用户位。 若为1时。 表示处于User级。 任意级别 (0、 l、 2、3) 特权的程序都可以访问该页。 若为 0, 表示处于 Supervisor级特权级别为3的程序不允许访问该页,该页只允许特权级别为 0、 l、 2的程序可以访问。

PWT, Page_level Write-Through, 意为页级通写位, 也称页级写透位。 若为1表示此项采用通写方式。表示该页不仅是普通内存还是高速缓存。 此项和高速缓存有关。 “通写” 是高速缓存的一种工作方式,本位用来间接决定是否用此方式改善该页的访问效率。 这里咱们直接置为0就可以啦。

PCD, Page-level Cache Disable, 意为页级高速缓存禁止位。 若为1表示该页启用高速缓存, 为 0表示禁止将该页缓存。 这里咱们将其置为0。

A, Accessed, 意为访问位。 若为1表示该页被 CPU访问过啦。 所以该位是由 CPU设置的。 还记得段描述符中的A位和 P位吗? 这两位在一起可以实现段式虚拟内存管理。 和它们一样。 这里页目录项和页表项中的A位也可以用来记录某一内存页的使用频率 (操作系统定期将该位清 0, 统计一段时间内变成1的次数〉, 从而当内存不足时, 可以将使用频率较低的页面换出到外存 (如硬盘〉, 同时将页目录项或页表项的P

位置 0, 下次访问该页引起pagefault异常时。 中断处理程序将硬盘上的页再次换入, 同时将P位置为1。

D, Dirty。 意为脏页位。 当 CPU 对一个页面执行写操作时, 就会设量对应页表项的 D位为 1。 此项仅针对页表项有效。 并不会修改页目录项中的D位。

PAT, Page AmibuteTable, 意为页属性表位。 能够在页面一级的粒度上设置内存属性。 比较复杂将此位置0即可。

G Global, 意为全局位。 由于内存地址转换也是颇费周折。 先得拆分虚拟地址, 然后又要查页目录, 又要查页表的。 所以为了提高获取物理地址的速度。 将虚拟地址与物理地址转换结果存储在 TLB (Translation Lookaside Buffer) 中, TLB 以后咱们会细说。 在此先知道TLB 是用用来缓存地址转换结果的高速缓存就OK啦。此G位用来指定该页是否为全局页。 为 1 表示是全局页, 为0表示不是全局页。 若为全局页, 该页将在高速缓存TLB 中一直保存, 给出虚拟地址直接就出物理地址啦, 无需那三步骤转换。 由于 TLB容比较小 (一般速度较快的存储设备容量都比较小)。 所以这里面就存放使用频率较高的页面。 顺便说一句, 清空TLB有两种方式, 一是用 invlpg指令针对单独虚拟地址条目清理, 或者是重新加载CR3寄存器, 这将直接清空TLB。

AVL,意为Available 位, 表示可用, 谁可以用? 当然是软件。 操作系统可用该位,CPU 不理会该位的值。那咱们也不理会吧。

上面介绍了分页,一级页表,二级页表,页目录项,页表项等那么怎么开启分页呢?

1.准备好页目录表及页表。

2.将页表地址写入控制寄存器中cr3

3.寄存器cr0的PG位置为1

页表同描述符表一样, 是个内存中的数据结构, 处理器要使用它们,必须要知道它们的物理地址所以页表也有个专门的寄存器来存储其地址。 其实这就是前面咱们多次提到的众多控制寄存器(目前处理器中的控制寄存器有cr0~cr7)中的一个:cr3寄存器。控制寄存器 cr3 用于存储页表物理地址, 所以 cr3寄存器又称为页目录基址寄存器 (Page Directory Base Register, PDBR)。 其结构如图 5-17 所示。

acd05a6ab89e4b569dd58b4c29c7e800.png


由于页目录表所在的地址要求在一个自然页内, 即页

目录的起始地址是4KB的整数倍, 低 12 位地址全是0。 所以, 只要在cr3寄存器的第 31~12位中写入物理地址的高20 位就行了。 另外,cr3 寄存器的低 12 位中, 除第3位的PWT位和第4位的PCD 位外, 其余位都没用。 PWT誓立和PCD位在介绍页表项时说过了, 它们用于高速缓存相关的特性, 在此将其置为0即可。 这样一来低12位全部为0, 故只需要把页目录表物理地址的高20位写入cr3 寄存器即可。因为控制寄存器是可以与通用寄存器互相传递数据的, 所以为cr3 寄存器赋值则没有那么复杂, 可以用现成的mov 命令, mov 指令中控制寄存器与通用寄存器互传数据的格式是= mov er[0-7], r32或mov r32, cr[0-7]。

前面执行了

第一步:准备好页目录表及页表。

第二步:将页表地址写入控制寄存器中cr3。

前两步是打开分页机制的铺垫, 现在看第3步,启动分页机制的开关是将控制寄存器cr0的PG 位置1,PG位是cr0 寄存器的最后一位: 第31位。如果大家忘记了 cr0 寄存器结构,请参见图 4-10 控制寄存器CR0。PG 位为1后便进入了内存分页运行机制, 段部件输出的线性地址成为虚拟地址 (顺便说一下, 第0位是PE位, 用来进入保护模式的开关)。 在将 PG 位置1之前, 系统都是在内存分段机制下工作, 段部件输出的线

性地址便直接是物理地址也就意味着在第2步中, cr3寄存器中的页表地址是真实的物理地址。


5.2.4规划页表之操作系统与用户进程的关系

分页的第一步要准备好一个页表,我们的页表是什么样子呢?现在我们要设计一个页表啦。

设计页表其实就是设计内存布局,不过在规划内存布局之前,我们需耍了解用户进程与操作系统之间的关系。

前面讲保护模式时, 我们知道, 为了计算机安全, 用户进程必须运行在低特权级,当用户进程需要访问。硬件相关的资源时, 需要向操作系统申请, 由操作系统去做, 之后将结果返回给用户进程。 进程可以有无限多个, 而操作系统只有一个, 所以操作系统必须 “共享″ 给所有用户进程。 它们的关系如图 5-18所示。

图 5-18 不仅展示了用户进程共享操作系统的逻辑依赖关系, 还用插槽展示了它们的配合关系, 用户进程要想完成某件工作, 需要与操作系统结合在一起才行,那用户进程和操作系统它们是什么关系呢?

要完成一件事, 用户进程做的事情只能算个半成品, 您可以理解成:用户的代码加上所需要的操作系统中的部分代码才算完整的程序, 为什么说是操作系统中的部分代码呢? 原因很简单因为操作系统严格来说是一套功能的集合用户进程所需要的部分可能仅仅是其中的小部分, 并不是所有功能都会用到。用户进程能用哪些功能, 是由操作系统决定的, 不是用户想用什么就用什么, 而是操作系统提供什么它就用什么。 完整的程序概念如图 5-19 所示。

0ff3f69badea4194b2348ad4425a9607.png

a26fcc48ab564a4fbb3011d92ea770cd.png

上述所说的用户进程和操作系统的关系,都是基于用户进程共享操作系统的。我们设计的页表也要满足这个基本要求:共享。

如何在页表中实现共享呢? 这个简单, 只要操作系统属于用户进程的虚拟地址空间就好了。

说起来简单, 这该怎么做呢? 我们可以把4GB 虚拟地址空间分成两部分, 一部分专门划给操作系统,另一部分就归用户进程使用。比如我们之前都听说过, 操作系统在4GB内存的高地址, 用户进程在4GB内存的低地址。 比如 Linux, 它就运行在虚拟地址的3GB 以上, 其他用户迸程都-运行在 3GB 以下。

页表的设计是要根据内存分布情况来决定的, 我们也学习 Linux 的做法, 在用户进程4GB 虚拟地址空间的高 3GB 以上的部分划分给操作系统, 0-3GB 是用户进程 自己的虚拟空间。 为了实现共享操作系统让所有用户进程3GB-4GB 的虚拟地址空间都指向同一个操作系统 也就是所有进程的虚拟地址3GB~4GB 本质上都是

指向的同一片物理页地址, 这片物理页上是操作系统的实体代码。 实现起来也比较容易,只要保证所有用户进程虚拟地址空间 3GB~4GB 对应的页表项中所记录的物理页地址是相同的就行啦。这句话确实有点长 ,我自己也反复断句了几次,不过这个在加载用户 进程时咋们再细说,在此我们只需要完成内存空间划分就行了。

所有实现的结果就是:虚拟地址空间的0-3GB是用户进程,3GB-4GB是操作系统。


5.2.5启用分页机制

首先先建立一个页表印象:

分页机制得有页目录表, 页目录表中的是页目录项,其中记录的是页表的物理地址及相关属性, 所以还得有页表。 我们实际的页目录表及页表也将按照此空间位置部署地址的最下面是页目录表, 往上依次是页表。

页目录表和页表都存在于物理内存之中,它总该有个 “安身” 的地址。 我们把它们安装在哪里呢?

页目录表的位置, 我们就放在物理地址 0x100000 处。 为了让页表和页目录表紧凑一些 〈这不是必须的〉, 咱们让页表紧挨着页目录表。 页目录本身占4KB, 所以第一个页表的物理地址是 0x101000。 它们的物理布局如图5-21所示。

6f329638ba2c403494b330c3e1cedaf3.png

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
目录
相关文章
|
6月前
|
存储 缓存 算法
内存分页机制
内存分页机制
100 0
|
4月前
|
存储 分布式计算 Hadoop
HadoopCPU、内存、存储限制
【7月更文挑战第13天】
272 14
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
330 0
|
12天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
25 1
|
17天前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
21天前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
26天前
|
存储 编译器
数据在内存中的存储
数据在内存中的存储
37 4
|
24天前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
46 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
1月前
|
存储 机器学习/深度学习 人工智能
数据在内存中的存储
数据在内存中的存储
|
27天前
|
存储 C语言
深入C语言内存:数据在内存中的存储
深入C语言内存:数据在内存中的存储