- 论坛徽章:
- 0
|
在论坛中经常看到有朋友问有关进程虚拟地址和物理地址的一些问题。这里随便写点东西,以期对这些朋友有丁点帮助。注意以下讨论中我们省略掉从定位和一些细节。
假如写了个叫test.c的小程序,用gcc把它编译成了test.elf,那么在这个elf文件中存在多个逻辑上的段,其中就有.text和.data段,分别是这个程序对应的代码和数据。我们假定:
1), .text段在文件中的偏移为100,大小为5000(0x1388) bytes,此段在链接时要求对应的虚拟地址(假设没有重定位,如果现在还知道虚拟地址是什么,不要紧,继续看)为 [0x08048000--0x08049388); 2), .data段在文件中的偏移为6000,大小为500(0x1f4) bytes,要求对应的虚拟地址为[0x08050000--0x080501f4)。
我们现在来运行这个程序 $ ./test.elf,操作系统内核在获知要运行新程序后,会在内核中建立起一个描述进程的数据结构,其中这个结构中有一个叫页表的成员,这个页表就是用 来记录虚拟地址到物理地址映射的。然后系统读取test.elf中的各个段放到物理内存中(我们忽略掉其它段的处理,在这里只讨论.text 和.data),系统在物理内存中找到了两个物理内存页面(为了满足x86cpu的规定,linux内核管理内存页面时也采用页式管理,每个个内存页面大 小为4096 bytes,内核管理分配这些物理内存时,一个页面是最小单位)来存放.text段5000 bytes的代码,假定其中一个物理内存页面的起始地址为0x7000,另一个为0x10000,注意虽然那5000 bytes的代码在逻辑上是连续的,也就是他们在一个连续的虚拟地址空间中,但我们看到他们被载入物理内存时却被分开成了两段.然后系统在那个页表中增加 两条记录,[0x08048000--0x08049000) --> [0x7000--0x8000); [0x08049000--x08050000) --> [0x10000--0x11000),这里我们可以看到虽然我们只剩下0x388 bytes了,但建立映射还是建立了一个页面的映射,也就是它消耗了一个页面的虚拟地址和一个页面的物理内存;之后系统读取.data段,因为这个段不到 一个页面的大小,所以系统在物理内存中只用找一个物理页面来存放来存放.data段中500bytes的数据,假定这个物理内存页面的起始地址是 0x3000,这时系统也会在页表中增加一条记录[0x08050000--0x08051000) --> [0x3000--0x4000),同理这个段也没有一个页面那么大,但却建立了一个页面大小的映射和占用掉一个物理内存页面。
一切准备工作做好后,系统就开始运行这个程序,假定这个程序中有一条指令为mov 0x08050100,%eax 大意就是把虚拟地址为0x8050100这个地方的连续4字节的内容搬移到eax寄存器中来,也许这个地方保存的就 是我们一个全局变量int Num,我们假定没有发生重定位,编译器在编译时就把Num的地址规定为了0x8050100。cpu在解析出这条指令后就知道要读取虚拟地址为 0x08050100这个地方的数据,因为这是一个虚拟地址,不能直接告诉内存控制器说我要读这个地址的内容,cpu只能把它转化为物理地址后才能告诉内 存控制器读取哪个物理内存地址,cpu是怎么把这个虚拟地址转化为物理地址的呢?对了,他就是去查看刚才在准备阶段建立的那个页表,cpu拿着 0x08050100这个地址发现它相对于0x08050000的偏移是0x100,且在表中找到了0x8050000映射到了0x3000这个地方,于 是他就知道0x08050100对应到物理地址应该是0x3000 + 0x100 = 0x3100,得到物理地址后,cpu告诉内存控制器说“麻烦你了,我要读0x3100这个地方的四个字节的值”。
我们写程序时老是出现segment fault,这是怎么回事呢,其实多数时候就是cpu在建立映射时没有找到某个虚拟地址到物理地址的映射,这时,cpu会进入一个操作系统初始化就设置好 了的某个地址上去执行一段处理程序,看是否能够建立起这个失败的映射,如果实在莫法,就打印出segment fault,发个段错误信号终止我们的程序。
水平有限,错误之处在所难免,请见谅哦!
上班无聊匆忙写的,不够详细和全面,如有啥子不清楚的,请踊跃跟贴提出来哈
再次编辑了一下,怎么有点不好弄
|
[ 本帖最后由 bobozhang 于 2008-8-22 20:05 编辑 ] |
|