理解linux内存管理,前面预备知识讲到了i386虚实地址方面的东西,也涉及到了一些内存页式管理的知识。这里主要延伸一下,以一个基于X86_64平台的实例来做一个直观的理解。
代码:
//demo.c
#include <stdio.h>
greeting()
{
printf("Hello, world!\n");
}
main()
{
greeting();
}
编译及反汇编:
编译:
gcc -o test demo.c
反汇编:
objdump -d test
--- 部分反汇编log ---
0000000000400530 <greeting>:
400530: 55 push %rbp
400531: 48 89 e5 mov %rsp,%rbp
400534: bf e0 05 40 00 mov $0x4005e0,%edi
400539: e8 d2 fe ff ff callq 400410 <puts@plt>
40053e: 5d pop %rbp
40053f: c3 retq
0000000000400540 <main>:
400540: 55 push %rbp
400541: 48 89 e5 mov %rsp,%rbp
400544: b8 00 00 00 00 mov $0x0,%eax
400549: e8 e2 ff ff ff callq 400530 <greeting>
40054e: 5d pop %rbp
40054f: c3 retq
如上可知
400549: e8 e2 ff ff ff callq 400530 <greeting>
0x0000000000400530为greeting函数指令存放起始位置,也即需要根据该虚拟地址提取出实际的物理页地址及页内偏移信息。首先根据X86_64的分页管理:
页大小:4kB
寻址使用位数:48
分页级别:4
线性地址分级:9 + 9 + 9 + 9 + 12
则0x0000000000400530,对应的48bit:
000000000 000000000 000000010 000000000 010100110000
每个分页级别页表有2^9=512个表项,可知上示寻找过程:由第63bit为0得知该地址为虚拟地址后,据某全局寄存器得知L0页表基地址,在结合虚址提供的9bit作为偏移找到了对应的第0表项。根据L0页表中找到的表项得知L1页表的基地址,在据偏移找到L1第0表项。同理找到L2第2表项,L3第0表项,不过此时L3表项中存放的就不是任何页表的基地址了,而是实际对齐的物理页的地址,而低12位即0x530便是页偏移。到此,物理地址就找到了,函数的第一条指令就存放在该页偏移位置处,等待cpu的调度执行。
参考资料:
linux内核情景分析