首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

在 Linux x86-32模式下分析内存映射流程

在 Linux x86-32模式下分析内存映射流程

前言虚拟内存机制已经成为了现代操作系统所不可缺少的一部分, 不仅可以为每个程序提供独立的地址空间保证安全性,更可以通过和磁盘的内存交换来提高内存的使用效率。虚拟内存管理作为Linux 上的重要组成部分代码非常庞大。这次并不是探明 Linux 源码级的内存映射,而是通过实例来验证 x86-32 下的虚拟内存转换流程。
映射流程简述x86-32 模式下的内存映射分为2部分, 分段和分页。之所以使用 2 步映射更多的是历史兼容原因。
编译出的汇编代码里使用的是逻辑地址,表示形式为 [段标识符:段内偏移量], 在默认情形下可以省略段标识符,直接给出段内偏移即可。段标识符共有 6 个,分别是(CS, DS, SS, ES, FS, GS), 每个都有自己的含义。
逻辑地址经过 "分段" 会转换为 线性地址,从我之前的文章可以看出,分段机制现在已经不实际使用了,在linux 中使用的分段模式为 “扁平模式”, 即逻辑地址和线性地址是一样的。
从线性地址转换为实际物理地址的过程称为 "分页"。分页才是实际的虚拟地址转换。分页过程中使用了可迭代的页表机制,操作系统为每个程序维护独立的一组页表来保证程序之间的互不干涉。
因为本文是反向验证的流程,所以并不会仔细介绍整个映射流程,需要对虚拟内存机制有一定的了解。
验证方案本文整个流程参考了网上的另一篇文章,我会在文章末尾列出链接。
因为每个程序的相关资源都是独立的,必须要保证程序的运行不能终止,且要在程序中输出自己的相关寄存器状态。大部分寄存器的访问特权只支持在内核获取,不能在用户程序中获取,我们要编写相关模块运行于内核,通过linux 的 /proc 文件系统将参数传递到用户程序。
同时也要验证我们通过手动映射流程获得的物理地址是否就是程序内地址,要有工具能够直接查看指定物理地址的数据。我们编写一个字符设备来处理应用程序的请求,通过 kmap 函数将指定物理地址页临时映射获取其数据。
最终需要程序为 4 个,如下所示。
程序功能sys_reg.ko加载到内核,读取相关寄存器, 建立 /proc/sys_reg 文件running-prog测试程序,需要一直运行,读取 /proc/sys_reg 来打印本程序相关寄存器值phy_mem.ko加载到内核,读取指定物理地址数据,建立 /dev/phy_mem 文件read-phy-mem通过 /dev/phy_mem 来获取指定物理地址数据并打印验证过程编译加载编译文件,加载 sys_reg.ko, phy_mem.ko 模块
运行 running-prog运行后可以得到以下输出:

可以看到变量 a, 这就是我们要寻找物理地址的变量,其数据和地址都以及输出了。
分段机制通过 CR0.PG ,可以看出系统已经打开了分页机制。 变量 a 定在了数据段, 通过 ds 段寄存器的值可以看出使用的是 GDT ,entry 是 15. GDTR 的基址是 0xF7386000, 注意这里是线性地址, linux 内核的地址映射偏移量是 0xC0000000, 然后获得 使用的 GDT entry地址如下。
0xF7386000 - 0xC0000000 + 15 * 8 = 0x37386078


通过获得的 GDT entry值和 gdt entry 格式,可以知道该分段的参数:
继承事业,薪火相传
返回列表