一丶查看 GDT 表.
我们通过 WinDbg + 虚拟机可以进行双机调试. 调试一下看下 GDT 表
我们知道, GDT 表中. 存储的是存储段信息. 保存了一系列的段和内存的属性.
但是微软并没有使用.
我们可以通过 ring3 的段寄存器. 当作 GDT 表的下标. 进行查表. 查询 GDT 表.
例如我们用 OD 随便打开一个 ring3 的 exe, 然后看下段和虚拟地址.:
虚拟地址: 0040256f 段选择子: cs:1B
那么此时段选择字当下表. 虚拟地址当作偏移. 去查询 GDT 表
下图为选择子结构
段选择子结构:
首先先拆分选择子.
1B = 0000000000011 011
查询出来下表为 3, 那么去 GDT 表的第三项进行查找.
然后我们从中取出段首地址 加上我们的偏移
00000000 + 40256F = 0040256F(线性地址)
那么通过查询 GDT 表. 那么可以找到线性地址. 而我们 GDT 表, 微软没有使用它来进行进程隔离.
PS: 微软因为不使用 GDT 表进行进程隔离, 所以段选择子都是一样的. 基地址都是 0, 我们的虚拟地址就是线性地址.
如果没有开分页保护. 那么就是物理地址了. 但是注意 FS 寄存器. FS 寄存器很特殊. 并不能说 GDT 表没用.
二丶控制寄存器 CR0
我们上面说过. 如果没有开启分页保护. 那么虚拟地址就是线性地址, 线性地址就是物理地址
而微软是通过分页进行进程内存的隔离的.
首先说一下什么分页的历史, 和 CR0 控制寄存器.
在保护模式下, 寄存器 CR0 的高位 1 表示开启分页. 0 表示不开启.
而这个在操作系统初始化的是否就已经完成了.
如果我们不开启, 那么访问虚拟内存, 就等价于访问物理内存了.
但是我们的三环是不可以操作的. 这个属于特权指令. 如果用汇编进行操作. 例如:
__asm mov cr0,8000000
程序会崩溃, 提示你权限不够, 并且报告错误码为 C096
但是我们 0 环可以进行操作的.
看一下 CR0 的结构:
但是我们 0 环可以进行操作的.
此图是从 inter 手册上截图下来的. 有兴趣的可以查询 CR0 查看.
刚才我们说的高位为 1 的是否就去掉分页保护, 此时访问虚拟内存 等价于访问物理内存, 其实我们修改的是 PG 位
关于位怎么说的, inter 手册也说. 这里我总结一下重要的, 如果不相信我可以查看 inter 手册.
PG: PG 位表示是否分页管理机制是否有效. PG = 1, 有效, PG = 0 无效.
WP: Wp 位 写保护位, WP 为 0 禁用写保护, 为 1 则启动
PS: 通过修改 WP 位可以进行过保护. 详情请看 https://www.cnblogs.com/hongfei/archive/2013/06/18/3142162.html 转载
三丶分页管理机制
讲解分页管理机制之前, 我们要明白以下几个关键词的意思.
页码:
在 80386 下, 一个页的固定大小是 4K 个字节, 也就是 4096, 一个页的辩解地址, 不许死 4K 的倍数.
所以 4G 大小的内存. 就可以划分为 1M 个节. 而我们的页的开始一般具有一个特点.
比如我们的虚拟地址:
004010123, 而页的首地址是 00401000
后 12 位都是 0.
所以我们把页的高 20 位称为页码.
进程内存的保护.
进程内存的保护就通过页的方式进行保护的.
当我们的线性地址转化为物理地址的是否, 会进行查表. 进而查询到物理地址是那个.
微软采用的是这种方式.
线性地址转物理地址需要注意的页问题.
这个问题则是页的映射.
我们知道, 页码是高 20 位, 那么低 12 为就是偏移了.
当 32 位的线性地址转为 32 为的物理地址的是否, 只需要记录页码就可以了. 低 12 位都是一样的. 所以不用记录.
比如线性地址:
004010123 映射到物理地址 002010123
那么我只需要记录前 20 位即可.
004010 -> 002010 , 然后物理地址加上我们的后 12 位即可. 002010+123 = 002010123
四丶线性地址到物理地址的转换.
我们说过, 操作系统为了隔离内存. 采用了分页管理. 而我们线性地址转化到物理地址的时候.
则需要查表.
那么我们觉着这个表应该怎么做
物理地址
Xxxx
内存保护属性
类似于这样, 我们只需要让虚拟地址当标进行查表即可.
我们每一个进程都提供这样的一张表. 但是在那个时代. 资源是匮乏的. 我们这样做. 开不了几个进程内存就会耗光了.
所以微软提供了自己的表. 而硬件上也提供了支持.
我们看下微软的表.
首先我们的 CR3 寄存器保存了表的首地址.
这里有一个页目录表, 还有页表的关键词.
页目录表: 也称为 PDE, 而页表称之为 PTE.
CPU 会通过虚拟地址, 当作下表. 去页目录表中查询. 然后查到的结果再去页表中查询. 这样就查到对应的物理地址了.
PDE 表的大小:
页目录表, 存储在一个 4K 字节的物理页中, 其中每一项是 4 个字节. 保存了页表的地址.
而最大是 1M 个页.
PTE 表的大小.
PTE 的大小也和 PDE 一样的.
微软为什么这样设计. 有人会问. 这样设计不就资源用的更多了吗. 其实不是的. 虽然我们设计怎么大.
但是通过两个表查询. 可以映射 4G 内存. 而上面的设计方法不行.
内核知识第八讲, PDE,PTE, 页目录表, 页表的内存管理
一丶查看 GDT 表.
我们通过 WinDbg + 虚拟机可以进行双机调试. 调试一下看下 GDT 表
我们知道, GDT 表中. 存储的是存储段信息. 保存了一系列的段和内存的属性.
但是微软并没有使用.
我们可以通过 ring3 的段寄存器. 当作 GDT 表的下标. 进行查表. 查询 GDT 表.
例如我们用 OD 随便打开一个 ring3 的 exe, 然后看下段和虚拟地址.:
虚拟地址: 0040256f 段选择子: cs:1B
那么此时段选择字当下表. 虚拟地址当作偏移. 去查询 GDT 表
下图为选择子结构
段选择子结构:
首先先拆分选择子.
1B = 0000000000011 011
查询出来下表为 3, 那么去 GDT 表的第三项进行查找.
然后我们从中取出段首地址 加上我们的偏移
00000000 + 40256F = 0040256F(线性地址)
那么通过查询 GDT 表. 那么可以找到线性地址. 而我们 GDT 表, 微软没有使用它来进行进程隔离.
PS: 微软因为不使用 GDT 表进行进程隔离, 所以段选择子都是一样的. 基地址都是 0, 我们的虚拟地址就是线性地址.
如果没有开分页保护. 那么就是物理地址了. 但是注意 FS 寄存器. FS 寄存器很特殊. 并不能说 GDT 表没用.
二丶控制寄存器 CR0
我们上面说过. 如果没有开启分页保护. 那么虚拟地址就是线性地址, 线性地址就是物理地址
而微软是通过分页进行进程内存的隔离的.
首先说一下什么分页的历史, 和 CR0 控制寄存器.
在保护模式下, 寄存器 CR0 的高位 1 表示开启分页. 0 表示不开启.
而这个在操作系统初始化的是否就已经完成了.
如果我们不开启, 那么访问虚拟内存, 就等价于访问物理内存了.
但是我们的三环是不可以操作的. 这个属于特权指令. 如果用汇编进行操作. 例如:
__asm mov cr0,8000000
程序会崩溃, 提示你权限不够, 并且报告错误码为 C096
但是我们 0 环可以进行操作的.
看一下 CR0 的结构:
但是我们 0 环可以进行操作的.
此图是从 inter 手册上截图下来的. 有兴趣的可以查询 CR0 查看.
刚才我们说的高位为 1 的是否就去掉分页保护, 此时访问虚拟内存 等价于访问物理内存, 其实我们修改的是 PG 位
关于位怎么说的, inter 手册也说. 这里我总结一下重要的, 如果不相信我可以查看 inter 手册.
PG: PG 位表示是否分页管理机制是否有效. PG = 1, 有效, PG = 0 无效.
WP: Wp 位 写保护位, WP 为 0 禁用写保护, 为 1 则启动
PS: 通过修改 WP 位可以进行过保护. 详情请看 https://www.cnblogs.com/hongfei/archive/2013/06/18/3142162.html 转载
三丶分页管理机制
讲解分页管理机制之前, 我们要明白以下几个关键词的意思.
页码:
在 80386 下, 一个页的固定大小是 4K 个字节, 也就是 4096, 一个页的辩解地址, 不许死 4K 的倍数.
所以 4G 大小的内存. 就可以划分为 1M 个节. 而我们的页的开始一般具有一个特点.
比如我们的虚拟地址:
004010123, 而页的首地址是 00401000
后 12 位都是 0.
所以我们把页的高 20 位称为页码.
进程内存的保护.
进程内存的保护就通过页的方式进行保护的.
当我们的线性地址转化为物理地址的是否, 会进行查表. 进而查询到物理地址是那个.
微软采用的是这种方式.
线性地址转物理地址需要注意的页问题.
这个问题则是页的映射.
我们知道, 页码是高 20 位, 那么低 12 为就是偏移了.
当 32 位的线性地址转为 32 为的物理地址的是否, 只需要记录页码就可以了. 低 12 位都是一样的. 所以不用记录.
比如线性地址:
004010123 映射到物理地址 002010123
那么我只需要记录前 20 位即可.
004010 -> 002010 , 然后物理地址加上我们的后 12 位即可. 002010+123 = 002010123
四丶线性地址到物理地址的转换.
我们说过, 操作系统为了隔离内存. 采用了分页管理. 而我们线性地址转化到物理地址的时候.
则需要查表.
那么我们觉着这个表应该怎么做
物理地址
Xxxx
内存保护属性
类似于这样, 我们只需要让虚拟地址当标进行查表即可.
我们每一个进程都提供这样的一张表. 但是在那个时代. 资源是匮乏的. 我们这样做. 开不了几个进程内存就会耗光了.
所以微软提供了自己的表. 而硬件上也提供了支持.
我们看下微软的表.
首先我们的 CR3 寄存器保存了表的首地址.
这里有一个页目录表, 还有页表的关键词.
页目录表: 也称为 PDE, 而页表称之为 PTE.
CPU 会通过虚拟地址, 当作下表. 去页目录表中查询. 然后查到的结果再去页表中查询. 这样就查到对应的物理地址了.
PDE 表的大小:
页目录表, 存储在一个 4K 字节的物理页中, 其中每一项是 4 个字节. 保存了页表的地址.
而最大是 1M 个页.
PTE 表的大小.
PTE 的大小也和 PDE 一样的.
微软为什么这样设计. 有人会问. 这样设计不就资源用的更多了吗. 其实不是的. 虽然我们设计怎么大.
但是通过两个表查询. 可以映射 4G 内存. 而上面的设计方法不行.
首先前边 20 位保存了页表或者物理地址的基地址.
比如我们的页目录表. 查到了第 5 项. 那么从中取出千 20 位来, 加上 000 就等于页表了.
然后从页表中查询千 20 位.+ 虚拟地址的偏移就等价于实际的物理地址了.
AVL 位: 可利用位.
D 位: 表示这个分页是否写过.
A 位: 表示这个分页是否读过
U/S 位: 表示这个分页是否用户可以访问还是 ring0 可以访问.
R/W 位: 内存保护位. 可读可写可执行. 还是可读可执行.
P 位: 内存是否有效.
R/W 位: 注意我这里说的有两种方式. 可读可执行, 和可读可写可执行. 有没有发现, 我们的 Ring3 程序. 不过是那个内存区域也好. 都是可以读的.
而我们 Ring3 下的修改内存分页保护属性, 其实就是将这个页表的这个 RW 位进行置位.
而我们的虚拟地址当作下表进行查表. 我们的虚拟地址不是分为了 20 位了吗. 前 10 位当作 页目录表的下表. 后 10 位当作页表的下表
设有物理地址为:
00401 000
那么下表则为:
00401 = 0000000001 0000000001
通过虚拟地址得知, 页目录表是第一项, 而页表也是第一项.
设页目录表第一项为
003f0111, 此时页表为 取前 20 位, 加上 3 个 0. 003f0 + 000 = 003f0000
设页表为
00201456, 此时取出前 20 位加上虚拟地址的后边 20 位偏移 物理地址 = 00201 + 000 = 00201000, 那么物理地址就是 201000
图示:
设虚拟地址为 00402567H, 取出前 20 位. 分为高 10 位, 和低 10 位做 PDE, 和 PTE 的索引.
然后进行查表.
最后的通过 PTE 查询的高 20 位加上原虚拟地址的低 12 位, 然后就找到了物理地址.
来源: https://www.cnblogs.com/iBinary/p/8307728.html