我去,又又又被内存坑了( 三 )


接下来 , 我们来看一下这个神秘的GDT里面的内容到底是什么?很多人学了内存管理 , 可能还从来没看过真实的GDT里面到底是什么数据吧 。
GDT是位于操作系统内核地址空间中的 , 在Windows上有两种查看方式 , 一种是通过Windbg , 一种是通过一些ARK工具 , 我这里选择使用PChunter这个神器进行查看 。
前面提到过 , GDT中的表项是段描述符 , 这是一个比较复杂的数据格式 , 好在 , 这个神器对段描述符进行了解析 , 使用表格字段的方式进行了展示 , 让我们看起来轻松多了 。
废话不多说了 , 来看一下这个神秘的GDT吧:
我去,又又又被内存坑了
文章图片
注意看第3个表项和第4个表项哦 , 看看它们的基地址 , 都是0x00000000 。
再看它们的界限值 , 都是0x000FFFFF , 注意看这个界限的单位 , 不是字节 , 而是Page——页 , 把这个值乘以页面的大小4KB , 就是0xFFFFF000 。 也就说这个段的上限到了0xFFFFF000这个页面 , 再把这一个页面的大小加进去 , 就是0xFFFFFFFF了!
所以 , 重点来了!看到了吗 , GDT中的第3个和第4个表项所描述的这两个段 , 它们的基地址都是0x00000000 , 整个段的大小都是0xFFFFFFFF , 这意味着什么?这意味着整个进程的地址空间实际上就是一个段!
也就是说:进程的代码段、数据段、栈段、扩展段这四个段全部重合了 , 而且是整个进程地址空间共计4GB成为了一个段 。
说起来是分段 , 实际上等于没分了 , 再加上段的基地址全部是0 , 那进行地址翻译的时候 , 有没有段都没什么区别了 。
总结一句话:操作系统这样分段 , 实际上是相当于把段给架空了!
以上是Windows的情况 , 我们再来看一下Linux情况呢 。
使用GDB随意调试一个ELF32的可执行文件 , 使用infor命令查看一下寄存器情况:
我去,又又又被内存坑了
文章图片
段寄存器有0x23和0x2b两种情况:
十六进制:0023二进制:0000000000100011段序号:4表类型:GDT特权级:Ring3十六进制:002B二进制:0000000000101011段序号:5表类型:GDT特权级:Ring3Linux下我没有找到可以直接用什么命令或者工具查看GDT的方式(如果你知道记得一定告诉我哦) , 于是去源代码中寻找答案:
我去,又又又被内存坑了
文章图片
看到了吗 , 这两项所描述的段和Windows一样 , 基地址为0 , 大小为4GB 。
Windows和Linux都选择了通过这种方式架空了CPU的分段内存管理机制 。
但需要说明一下的时 , 虽然两个操作系统都是这种情况 , 但并不意味着段机制彻底没用到 , CPU的任务管理TSS还是需要用到 , 这一点大家知道就行了 。
64位情况
看到操作系统们都不待见这个分段式内存管理 , Intel似乎也感受到了这玩意确实很鸡肋 , 于是到了64位平台 , 彻底把段寄存器给打入了冷宫!
在Intel的指令手册中 , 关于64位下的段寄存器是这样描述的:
我去,又又又被内存坑了
文章图片
不管你的段寄存器中指向的段基址是什么内容 , 都会被当成0来对待 。
这一下 , 分段内存管理 , 彻底凉凉了···
总结
好了 , 最后来总结一下 。
无论是分段还是分页 , 这是CPU自身的机制 , 操作系统在管理内存时绕不过去 , 但通过巧妙的分段内存设计 , 相当于把分段的概念给屏蔽了 , 由此造成了我们平时在谈论虚拟地址翻译时 , 忘记了段的存在 , 但不代表它真的不存在 。
CPU硬件层面的工作必须是结合分段+分页的内存管理机制 , 操作系统是软件绕不过去 , 所以采取了上面的方式应付CPU了事 。