x86-7-页式管理(Paging)

页式管理是重中之重!

在段式管理下操作系统的运作出现了很多问题,因为段的长度不定,在分配内存时,可能会发生内存中的空闲区域小于要加载的段,或者空闲区域远远大于要加载的段,这样一通分来分去最后会导致剩下一些内存碎片,也就是可以的内存还有但是都很小而且地址空间不连续,导致无法再继续利用了。为了解决这个问题,从80386 处理器开始,引入了分页机制。

概述:

分页功能从总体上说,是用长度固定的页来代替长度不一定的段, 来解决因段长度不同而带来的内存空间管理问题。

页式内存管理按照页的大小来将内存空间分成大小相同的页,页面的标准大小为4KB,也就是4096 字节,用十六进制数表示就是 0x1000。

因此,第1 个页的物理地址是0x00000000,第2 个页的物理地 址是0x00001000,第3 个页的物理地址是0x00002000,……,最后一个 页 的 物 理 地 址 是 0xFFFFF000 。 这 样 , 可 以 将 4GB 内 存 划 分 为 1048576(0x100000)个页。

段管理机制对于Intel 处理器来说是最基本的,任何时候都无法关闭。即使启用页管理功能,分段机制依然是起作用的,段部件也依然工作,所以当开启页式管理时,intel 实际上采用的是段页式管理。

段页式管理机制的主要功能:当一个程序要加载调用时操作系统先在虚拟内存中按照段分配空间,然后再根据页大小将虚拟内存地址中的段映射到物理地址空间中。

(注:通常在intel中开启页式管理后线性地址就和线性虚拟内存地址是一样的,这个是个叫法问题,为了方便,然后线性物理内存地址就通常叫物理地址)

段页式管理中的任务执行流程:

前面阐述过段式管理中的任务执行流程,其实就比他多了个按页的内存映射,然后最后实际内存安装页为单位而不是段为单位来执行。

首先将可执行文件的内容按照段映射到虚拟内存中,然后再按照页映射到物理内存中。最后CPU再通过物理内存进行实际性的操作。(如果要用到其他文件就将其加载到数据段中进行处理就行了)。

简单的段页式模型:

假设在某个任务里操作系统给该任务的某个段分配了一段虚拟内存空间,基地址为0x00200000,长度为8200 字节:

页的最小尺寸是4KB,也就是4096 字节。因此,8200 字节的段, 需要占用3 个页面,所以操作系统就将其映射到三个空闲的物理内存地址页面:

这里的任务以段的方式映射到虚拟内存中采用的是段式管理,然后将段按照页分割再映射到内存页上采用的是页式管理,两个合成段页式管理。

为了根据线性地址 找到页的物理地址,操作系统必须维护一张表,把线性地址转换成物理地址。

因为有1048576 个页,所以转换表也有1048576项。这是个一维表格,每个表项占4 字节,存储的内容为页的物理地址。

因为页的标准尺寸是4KB,所以将线性地址的低12用来作为页的偏移地址,将高20位用来作为在映射表中的索引,假设映射表如下:

然后这个时候我们执行指令:

mov edx,[0x2002]

这条指令首先会通过段部件将其转换为线性地址:

;访问内存 ds:0x2002的值然后赋值给edx
;这里的ds前面给出了基地址0x00200000
;所以就是访问虚拟地址为 0x00200000:0x2002的值
;也就是虚拟地址为:0x00202002的值

然后通过页部件转化为物理地址:

;       0x00202002可分为:0x00202 002
; 其中高20位内容为0x00202,低12位内容为0x002
; 将高20位的值0x00202 * 4 = 0x00808得到映射表中的索引
; 然后通过映射表得到页物理地址的起始地址0x00007000
; 然后再通过偏移0x002,得到物理地址:0x00007002

最后对0x00007002物理地址取四字节内容赋值给edx。

(注:映射表是操作系统自己来处理,自己来控制映射表中的内容,无需程序员操心)。

每个任务都有一个自己的映射表,所以不需要考虑有多对一产生冲突的情况:

但是就算这样处理了,当任务很多的时候4GB的物理内存页不够用,这个时候就和段式内存管理一样,操作系统会利用硬盘来作为缓冲区,进行页面置换,将暂时不用的页放到硬盘中,然后要用的时候再置换回来。

80386的分页机制(经典分页机制):

第一个支持分页内存管理模式的Intel 处理器是80386,虽然该CPU比较古老了,但是后面的CPU都会兼容和参考该CPU的内容,比如80386的分页机制。

重点概念:页目录表(Page Directory Table,PDT)、页表(Page Table),页目录项(Page Directory Entry PDE)和页表项(Page Table Entry PTE)

前面那个段页式模型中的页式管理只能拿来参考,因为一个页映射表有 0x4*0xFFFFF = 4MB的大小,而且每个任务都有这么多个,这样来说对于内存的消耗太大了,并且在实践中并没有任务会真正的用到页映射表中的所有内容,所以采用了页目录表和页表来代替页映射表

80386CPU采用了一个4KB大小的页表来存放1024个页的地址和信息合成的结构体叫做页表项,每个页表项有4个字节,用来存放页的物理内存和属性,一个4KB大小的页表正好和页的大小相等,所以可以很好的保存到页里,由于一个任务最多对应0xFFFFF个页,所以最多可以有1024个页表。

然后采用了一个4KB大小的页目录表来存放1024个页表的地址和属性结合成的结构体叫做页目录项,每个页目录项的大小又是4字节,所以一个页目录表也有1024*4 = 4KB字节,也正好对应了一个标准页的大小。

(其实就是数组的概念)

页表和页目录表的最大大小为4KB+4KB*1024 = 4KB+4MB,虽然看起来比页映射表大,但是页表和页目录表也是用页来管理和正常的页一样,也会被回收和重新分配。

至于页目录和页表的内容是如何添加的,这就是操作系统来处理的了。

这样的分页结构体体系是每个任务都有的,所以每个任务都有自己的页表和页目录表,其中在CPU内部有一个CR3寄存器,来专门存放页目录表的基地址。

而每个任务都有自己的TSS段,TSS段中就有CR3字段,所以就可以通过任务的tss得到CR3再通过CR3来得到页表和页目录表再得到物理内存。

当任务切换时,处理器切换到新任务开 始执行,而CR3 寄存器的内容也被更新,以指向新任务的页目录位置。 相应地,页目录又指向一个个的页表,这就使得每个任务都只在自己的地址空间内运行。

页目录表(Page Directory Table,PDT)、页表(Page Table),页目录项(Page Directory Entry PDE)和页表项(Page Table Entry PTE) 总结

PDT和PT其实就是个数组用来存放PDE和PTE。

页目录项(Page Directory Entry,PDE)包含页表的地址和属性。

页表中存放的内容叫做页表项(Page Table Entry PTE)包含页的地址和属性。

在intel中的PDE和PTE结构:

从这里可以看出来通过页表项和页目录

PTE结构体:

PDE结构体:

![image-20220317023905088](C:\Users\onexia\AppData\Roaming\Typora\typora-user-images\image-20220317023905088.png)

80386 地址变化的具体过程:

前面我们理清楚了80386的页式管理,这里讲解如何通过页表页目录表,来进行页式管理的内存地址转换得到物理地址。

假如某个任务加载后,操作系统根据它的实际情况,在其4GB 虚拟 地址空间里创建了一个段,段的起始地址为0x00800000,段界限值为 0x5000,当该任务执行时,段寄存器DS 指向该段,并且执行了下面一条指令:

mov edx,[0x1050]

通过段部件得到线性地址为: 0x00801050

(段式管理这里就不继续详述了,如果没有页式管理这个地址就是物理地址了。)

页部件处理流程:

;1 页部件首先将段部件送来的32位线性地址截成高10,间的10位和低12位 
;高10 位是页目录的索引,中间10 位是页表索引,低12 位则作为页内偏移来用

;2 然后将通过高10位*4得到页目录表的索引和通过CR3寄存器中的页目录表首地址相加得到页目录项

;3 对页目录项的前12位清零得到页表基地址

;4 通过中10位*4得到页表的偏移地址,然后和页表基地址相加得到页表项PTE

;5 对页表项的前12位清零得到页的基地址


;6 通过低12位得到页的偏移地址,然后将偏移地址和页的基地址相加得到物理地址

在Windbg中实践:

Windows采用了很多中的分页机制,如果想要查看经典的80386分页类似模式需要在32位系统下进行以下设置:

bcdedit /set {current} nx AlwaysOff

bcdedit /set {current} PAE ForceDisable

(注: 重启后才生效)

首先我写了一个很简单的程序:

#include<iostream>
#include<Windows.h>
using namespace std;
int main()
{
char temp[] = "SnailGo";
__asm int 3


return NULL;
}

然后放到虚拟机中运行,并采用双机调试。

首先采用ollydbg来查看,并定位到缓存内容:

然后运行到这个int 3断点处,注意不要直接用ollydbg来运行,因为会先给od来捕获这个断点。

然后查看该字符串的线性地址:

线性地址为:009520F8

然后用Windbg断下来。接着就开始实践分析了:

先找到我们的进程的页目录表基地址:

!process 0 0

PROCESS 8809f860 SessionId: 1 Cid: 0d04   Peb: 7ffd5000 ParentCid: 0d3c
  DirBase: 1e0a1000 ObjectTable: a710a1e8 HandleCount:   8.
  Image: ApplicationTest1.exe

在操作系统中并没有使用TSS来进行任务切换,所以并不能采用查看tss然后得到cr寄存器这样的办法来处理,只能在进程中查看cr3寄存器的值或者支持采用Windbg中提供的DirBase,两个值是一样的。

然后开始通过虚拟地址查找到物理地址:

1 解析线性地址:

kd> .formats 009520F8
Evaluate expression:
Hex:     009520f8
Decimal: 9773304
Octal:   00045220370
Binary: 00000000 10010101 00100000 11111000
Chars:   .. .
Time:   Fri Apr 24 10:48:24 1970
Float:   low 1.36953e-038 high 0
Double: 4.82865e-317


得到二进制为:
Binary: 00000000 10010101 00100000 11111000

高10位为: 0000000010 ==0x2

中10位为: 0101010010 ==0x152
低12位为: 000011111000 ==0xF8

2 将高10位的值0x2*4和页目录表基地址1e0a1000相加得到页目录项:

kd> !dd 1e0a1000+0x2*4
#1e0a1008 2cc5b867 00000000 00000000 00000000
#1e0a1018 00000000 00000000 00000000 00000000
#1e0a1028 00000000 00000000 00000000 00000000

3 将页表项2cc5b867的前12位清零得到页表首地址:2cc5b000,

4 通过页表首地址+偏移值0x152*0x4得到页表项:

;查看页表中对应的页表项:
kd> !dd 2cc5b000+0x152*0x4
#2cc5b548 1fd95025 2426a867 3cd1e025 00000000

5 将页目录项1fd95025的前12位清零得到页表地址:1fd95000,

6 然后通过页表地址+偏移值查看物理内存

kd> !db 1fd95000+0xF8
#1fd950f8 53 6e 61 69 6c 47 6f 00-00 00 00 00 00 00 00 00 SnailGo.........
#1fd95108 dc fe 31 62 00 00 00 00-02 00 00 00 5a 00 00 00 ..1b........Z...
#1fd95118 5c 22 00 00 5c 14 00 00-00 00 00 00 dc fe 31 62 \"..\.........1b
#1fd95128 00 00 00 00 0c 00 00 00-14 00 00 00 b8 22 00 00 ............."..
#1fd95138 b8 14 00 00 00 00 00 00-dc fe 31 62 00 00 00 00 ..........1b....
#1fd95148 0d 00 00 00 6c 02 00 00-cc 22 00 00 cc 14 00 00 ....l...."......
#1fd95158 00 00 00 00 dc fe 31 62-00 00 00 00 0e 00 00 00 ......1b........
#1fd95168 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

x86-7-页式管理(Paging)的更多相关文章

  1. c模拟 页式管理页面置换算法之FIFO

    写的操作系统作业.... 放上来给需要的小伙伴 需要注意的地方: 1.该算法只涉及单进程 2.只是用c模拟FIFO的思想 FIFO思想:选择在内存中存活时间最久的页面淘汰 关于该算法我的理解: 一个进 ...

  2. Linux段式管理与页式管理

    内存管理有2种机制:1.段式管理:2.页式管理 在80386CPU中增加了2个寄存器:1.全局性的段描述表寄存器GDTR 2.局部性的段描述表寄存器LDTR 段寄存器的高13位用于在全局或局部描述表项 ...

  3. 【转帖】linux内存管理原理深入理解段式页式

    linux内存管理原理深入理解段式页式 https://blog.csdn.net/h674174380/article/details/75453750 其实一直没弄明白 linux 到底是 段页式 ...

  4. linux内核源码——内存管理:段页式内存及swap

    os的内存管理大概可以分成两块:1.段页式管理(虚存)2.swap in 和 swap out 段页式管理 段式管理的图像:运行时重定位 多级页表的管理图像  块表加速 用户(程序员)希望用段,物理内 ...

  5. x86-3-段式管理(segmentation)

    x86-3-段式管理(segmentation) 3.1 段式管理概述: 从8086CPU开始,为了让程序在内存中能自由浮动而又不影响它的正常执行,CPU将内存划分成逻辑上的段来给程序使用. x86继 ...

  6. 操作系统之cache、伙伴系统、内存碎片、段式页式存储管理

    存储管理是操作系统非常重要的功能之一,本文主要介绍操作系统存储管理的基础知识,包括缓存相关知识.连续内存分配.伙伴系统.非连续内存分配.内存碎片等,并结合linux系统对这些知识进行简单的验证.文章内 ...

  7. 寄存器,cache、伙伴系统、内存碎片、段式页式存储管理

    cache.伙伴系统.内存碎片.段式页式存储管理 目录 分层的存储管理 cache 局部性原理 置换算法 写回策略 linux环境下的cache 连续内存分配与内存碎片 内部碎片与外部碎片 动态分区分 ...

  8. Thermostat:双层存储结构的透明巨页内存管理机制

    这是一篇由密歇根大学的Neha Agarwal 和 Thomas F. Wenisch,发表在计算机系统顶会ASLOS的论文,Thermostat: Application-transparent P ...

  9. 【av68676164(p54)】段式和段页式虚拟存储

    段式存储管理 进程分段 把进程按逻辑意义划分为多个段,每段有段名,长度不定,进程由多段组成 例:一个具有代码段.数据段和堆栈段的进程 段式内存管理系统的内存分配 以段为的单位装入,每段分配连续的内存 ...

随机推荐

  1. nginx启动失败:Redirecting to /bin/systemctl start nginx.service Failed to start nginx.service: Unit not found.

    解决方法: 是因为nginx没有有添加到系统服务,手动手动添加一个即可. 在 /etc/init.d/下创建名为nginx的启动脚本即可,内容如下: #!/bin/bash # # chkconfig ...

  2. sql注入,xss攻击,csrf(模拟请求),防盗链

    如何防止别人模拟请求? 使用令牌token解决模拟请求  好处是 唯一性只能有一次请求 已经拿到生成的token  如何防止呢?  怎样防止非人工? 使用验证码 xss攻击? xss攻击也叫脚本注入 ...

  3. python 使用pip安装包的总结

    multiprocessing.logging模块安装 如果使用在cmd中使用 pip install multiprocessing 会报错, 将命令改为 pip3 install multipro ...

  4. CSS解决父级边框坍塌的问题

    1. 浮动元素后面增加空的div 首先在父级标签内添加如下<div>标签 <div id="clear"></div> 然后在CSS中对该标签进 ...

  5. Linux性能优化实战(二)

    一.CPU使用率过高 1,CPU使用率 a>节拍率 为了维护CPU时间,Linux通过事先定义的节拍率(内核中表示为HZ),触发时间中断,并使用全局变量Jiffies记录开机以来的节拍数.每发生 ...

  6. Note -「计算几何」模板

      尚未完整测试,务必留意模板 bug! /* Clearink */ #include <cmath> #include <queue> #include <cstdi ...

  7. Linux CentOS 搭建SVN并用钩子自动实现同步到Web目录

    linux安装配置SVN并设置钩子   安装说明 系统环境:CentOS-6.3安装方式:yum install (源码安装容易产生版本兼容的问题)安装软件:系统自动下载SVN软件 检查已安装版本 # ...

  8. JDK中线程中实现同步等待闭环的一种方式

    实际Thread类自带的join方法就实现了线程同步等待,具体可以通过案例实践,如下: 本文的重点不是join,而是另一种设计的同步等待实现,涉及的关键类有:Thread.Runable.Callab ...

  9. 请求XSS攻击原理

    起因 巨人的肩膀 一个神秘URL酿大祸,差点让我背锅! (qq.com)

  10. 利用iptables做网络转发

    常见的网络拓扑图结构如下: 但是内网服务器偶尔有上网需求,比如yum工具,wget文件.而我们又不能让重要业务直接暴露在公网上. 好用的安全策略有:三层交换机.路由器做nat映射,防火墙做安全策略. ...