Linux_Memory_Address_Mapping
主要根据www.ilinuxkernel.com站点上的同名pdf文档做实验分析
参考:https://jin-yang.github.io/post/kernel-memory-virtual-physical-map.html
https://stackoverflow.com/questions/8516021/proc-create-example-for-kernel-module
http://www.voidcn.com/article/p-zqylmcig-ty.html
http://www.cnblogs.com/hoys/archive/2011/04/10/2011261.html
体系架构
首先要了解操作系统的内存管理机制,一般都是知道是段页式管理,但是各个处理器架构提供的机制有所不同,主要由处理器中mmu部件负责翻译,操作系统负责管理.特别在intel架构中,完全做到了段页式的实现.但是Linux内核在实现的时候是用技巧越过了段式的地址特性,即逻辑地址=线性地址,但是处理器对于地址转换的有效性的检查不会改变,只是说Linux内核实现比较巧妙.下面就开始实验.
环境
原文给我的实验代码是Linux 2.6内核的代码,我特意从网上下载了Centos6.9,之后yum install kernel-devel.x86_64
安装发行版内核源代码,同时查看build
目录是否正确链接到内核源码路径,可修改实验代码的Makefile文件的路径使其正确.当然,为了能够使得我PC的Linux 4.x内核也能够做这个实验,我对比了内核代码的改动部分,其中主要是proc_create
接口变了,然后参考网上demo代码成功实现了迁移.可参考这里.
实验
mem_map
编译好内核模块后insmod两个ko文件,因为我的测试环境为64位,因此修改mem_map.c的部分代码,运行如下:
fileview
这个工具跟内核模块dram.ko挂钩,通过实现了lseek和read文件操作方法来对应用层开放查询进程物理地址信息.具体操作可见代码,其中主要可以按照显示方式为8421字节感觉很有用,若需要输入指定物理地址,按回车即可.
开始计算
查看gdt中ds描述符
按照pdf教程,tmp所在段在ds数据段,查看后发现确实段基址为0.
分解tmp地址为二进制
计算偏移的工具bc,我将其封装了一下,命名为calc.sh,代码在这里.tmp地址分解如下:1
2
3root@Rutk1t0r:Memory_Address_Mapping# calc.sh -i 16 -o 2 7FFD0A1330C8
11111111111110100001010000100110011000011001000
root@Rutk1t0r:Memory_Address_Mapping#
应该为分成9 9 9 9 12.
011111111 111110100 001010000 100110011 000011001000
CR3->一级
1 | root@Rutk1t0r:Memory_Address_Mapping# calc.sh -i 2 -o 16 8*11111111 |
CR3=36082000,一级=36082000+8*011111111b=360827F8
一级->二级
1 | root@Rutk1t0r:Memory_Address_Mapping# calc.sh -i 2 -o 16 8*111110100 |
三级->四级
1 | root@Rutk1t0r:Memory_Address_Mapping# calc.sh -i 2 -o 16 8*100110011 |
三级=3B04B000,四级=3B04B000+8*100110011=3B04B998
四级->真实物理地址(值)
四级=27D0F000,phy_addr=27D0F000+0c8=27D0F0C8.
而mem_map.c代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 [root@localhost mm_addr]
static char buf[BUFSIZE];
static unsigned long addr;
int main()
{
unsigned long tmp;
tmp = 0x12345678deadbeef;
static int cr_fd = - 1;
asm("movq %rbp, %rbx\n movq %rbx, addr");
printf("\n%%rbp:0x%08lX\n", addr);
printf("tmp address:0x%08lX\n", &tmp);
FILE_TO_BUF(REGISTERINFO, cr_fd);
printf("%s", buf );
while(1);
return 0;
}
[root@localhost mm_addr]#
成功通过段页式翻译找到tmp对应的值为0x12345678deadbeef.
总结
通过实验加深理解,如有时间可再深入研究物理地址映射的实现.