在 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 格式,可以知道该分段的参数:
| name | 值 |
|---|---|
| base | 0x00000000 |
| limit | 0xfffff |
| G | 1 |
可以看出段基址是 0x00000000, G和limit决定了该段是 4G 大小。所以从逻辑地址获得变量 a 的线性地址如下:
0x0804A044 + 0x00000000 = 0x0804A044;
分页机制
因为 CR4.PAE = 1,说明系统开启了 PAE(物理地址扩展). PAE 模式下的分页有如下 2 种。


寄存器及 entry 格式如下:

此时 CR3 中的基址就是 PDPTE 的基址 0x1EF49000, 变量 a 线性地址的bit 31-30 代表了 PDPTE 的序号。 我们可以算出 使用的 PDPTE 地址:
0x1EF49000 + 0 * 8 = 0x1EF49000

可以看到 page directory 的基址为 0x1ec9f000, 使用线性地址中的 bits 29~ 21 来确定偏移为 0x40, 所以使用的 PDE 地址为
0x1EC9F000 + 0x40 * 8 = 0x1EC9F200

PDE 为 0x0000000020A36067, bit7 = 0,说明指向的是 page table, page table 地址为0x20A36000, 使用线性地址的 bits 20~12 作为偏移为 0x4A, 使用的 PTE 地址为
0x20A36000 + 0x4A * 8 = 0x20A36250

PTE 为 0x000000000B628067, 得到了最终的 4K page frame 基址为 0x0B628000, 使用线性地址的 bits 11~0 作为偏移为 0x44, 我们计算出的 变量 a 的物理地址为
0x0B628000 + 0x44 = 0x0B628044

我们看到了数据 0x013579BB, 说明我们正确找到了 a 的物理地址,反向验证了 linux 在 x86-32 模式下开启了 PAE 后的线性地址映射。
结束
感谢 Linux内存地址映射 一文,我的整个流程参考了原作者的文档和代码, 谢谢原作者的分享。
下面是源代码链接.
study-linux-vm-32bit
在 linux x86-32 模式下分析内存映射流程的更多相关文章
- 在 linux x86-64 模式下分析内存映射流程
前言 在上一篇中我们分析了 linux 在 x86-32 模式下的虚拟内存映射流程,本章主要继续分析 linux 在 x86-64 模式下的虚拟内存映射流程. 讨论的平台是 x86-64, 也可以称为 ...
- ARC模式下的内存泄露问题
ARC模式下的内存泄露问题 iOS提供的ARC 功能很大程度上简化了编程,让内存管理变得越来越简单,但是ARC并不是说不会发生内存泄露,使用不当照样会发生. 以下列举两种内存泄露情况: 死循环造成的内 ...
- 鸿蒙内核源码分析(内存映射篇) | 虚拟内存虚在哪里 | 百篇博客分析OpenHarmony源码 | v15.03
百篇博客系列篇.本篇为: v15.xx 鸿蒙内核源码分析(内存映射篇) | 虚拟内存虚在哪里 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分 ...
- 【转载】linux内核笔记之高端内存映射
原文:linux内核笔记之高端内存映射 在32位的系统上,内核使用第3GB~第4GB的线性地址空间,共1GB大小.内核将其中的前896MB与物理内存的0~896MB进行直接映射,即线性映射,将剩余的1 ...
- windows游戏编程X86 32位保护模式下的内存管理概述(二)
本系列文章由jadeshu编写,转载请注明出处.http://blog.csdn.net/jadeshu/article/details/22448323 作者:jadeshu 邮箱: jades ...
- 【Linux】NAT模式下关于主机ping不通虚拟机的问题
今天打开虚拟机,然后用Xshell远程连接,发现连接不上.按照以下顺序检查了一遍. 1.虚拟机网络连接采用的是NAT模式 2.虚拟机IP采用的是自动获取. IP:192.168.191.130 子 ...
- Linux命令行模式下安装VMware Tools详细步骤
在Linux命令行模式安装VMware Tools 方法/步骤1: 首先启动CentOS 7,在VMware中点击上方"VM",点击"Install VMware Too ...
- linux命令行模式下对FTP服务器进行文件上传下载
参考源:点击这里查看 1. 连接ftp服务器 格式:ftp [hostname| ip-address]a)在linux命令行下输入: ftp 192.168.1.1 b)服务器询问你用户名和密码 ...
- Linux系统vi模式下显示行号
在命令模式下输入:set nu或者:set number都可以为vi设置行号,如果要取消的话,则输入:set nonu行号的设置是vi的环境设置,不会影响文本的内容.
随机推荐
- AlloyTouch实现下拉刷新
原文地址:https://github.com/AlloyTeam/AlloyTouch/wiki/Pull-to-refresh 效果展示 扫码体验 你也可以点击这里访问Demo 可以点击这里查看代 ...
- 分治法(一)(zt)
这篇文章将讨论: 1) 分治策略的思想和理论 2) 几个分治策略的例子:合并排序,快速排序,折半查找,二叉遍历树及其相关特性. 说明:这几个例子在前面都写过了,这里又拿出来,从算法设计的策略的角度把它 ...
- 小白日记48:kali渗透测试之Web渗透-XSS(二)-漏洞利用-键盘记录器,xsser
XSS 原则上:只要XSS漏洞存在,可以编写任何功能的js脚本 [反射型漏洞利用] 键盘记录器:被记录下的数据会发送到攻击者指定的URL地址上 服务器:kali 客户端 启动apache2服务:ser ...
- WPF 之 设置Dialog的父窗体
1.如果弹出窗体(如ChildWindow),调用Show方法,并且设置了其Owner属性: ClassRootWindow { void Foo() { ChildWindow cw = newCh ...
- CEP简介
CEP即Complex Event Processing缩写,翻译过来就是复杂事件处理(复合事件可能更加准确). 1.为什么我们需要CEP?CEP是具有实时分析以及快速响应等等功能.下面让我们通过 ...
- ShowModal在FireMonkey移动应用程序对话框
This is the only code that changes between the first and second code snippets: dlg.ShowModal(procedu ...
- 第02篇. Jetty 9 实战之安装/运行/部署
一直以来,想改变一些自己早已经习惯的事情. 一直都听说jetty跟Tomcat一样,是一个web容器. 一直都是在说等等,再等等,等有时间的时候! 一直都是给自己一些逃避的理由 1. 首先从Jetty ...
- FreeBSD 安裝 Tomcat JAVA JDK1.6 筆記
首先是安裝軟體 cd /usr/ports/java/jdk16/ make 在這一步,需要你手動到sun.com上下載幾個安裝包,按提示下載好後加入到 /usr/ports/distfiles/,再 ...
- poj 1821 动态规划
思路:每次枚举每个工人的右边界j,维护最优的左边界k.那么dp[j]=max(dp[j],dp[k]+(j-k)*w[i].p): 对于每个工人的初值k=w[i].s-1; 令x=j-w[i].l,如 ...
- BZOJ 3083: 遥远的国度 dfs序,树链剖分,倍增
今天再做一天树的题目,明天要开始专攻图论了.做图论十几天之后再把字符串搞搞,区域赛前再把计几看看. 3083: 遥远的国度 Time Limit: 10 Sec Memory Limit: 128 ...