本节内容
一个普通的程序编译完了以后,程序还没有运行就提供了虚拟内存地址空间,这个虚拟地址就已经有了。回过头来想一想这个事情,就会发觉虚拟地址的好处。
程序需要载入到内存中才能执行,既然每个程序都有一套完整的虚拟地址,那么在编译的时候就可以确定下来地址作用,比如某块地址用于映射程序可执行文件空间,运行期的堆、栈在别的地方分配。那么也就意味着编译时候可以确定执行时候放在哪,因为虚拟地址是我私有的,所以在编译时候就可以预先安排好虚拟地址。那么这就有很多好处,比如说.text段可以提前安排好了,起始地址加上偏移量就是函数在运行期的内存地址,编译汇编指令的时候就可以把地址写到汇编指令里面,编译时候就可以做这件事,所有的代码合并到.text段里面,既然这个地址已经确定下来了,里面有个函数指令,起始地址加上偏移量就是就等于这个函数的指针地址,这个地址就变成类似常量值。那调用这个函数时候直接call这个常量值就可以了,编译时候就能确定这个事情,这就是虚拟地址带来的好处。你如果是物理地址的话就麻烦了,因为物理地址你根本不知道你最终运行时候会挂在哪个地方,所以没有办法在编译时候把地址就确定下来但是虚拟地址可以因为虚拟地址是完整的一套独有的。所以我们在编译时地址都可以确定下来。这样在编译期通过虚拟地址就可以确定很多东西。
接下来搞清楚可执行文件究竟怎么样映射到内存里面去。
我们可以看到可执行文件内部有很多段,但是不是所有的段都会载入内存里面去,有些数据只是存下来在调试时候用,但是程序执行时候不需要这些数据,那我没必要把它放到内存里面去。
首先可执行文件内部有很多段,运行时候也有很多段,这两个地方的确有相似的地方,但也有不一样的地方。它可能把可执行文件某些东西映射到运行时候里面,比如说可执行文件中.text段映射到运行时某段中,当我们读取数据的时候,假如数据没有载入,引发缺页异常,然后就会把可执行文件.text段数据换入到内存里,这样的话我们就可以读到指令。也就是说可执行文件和进程之间是有映射关系的,这个映射关系并不是一一对应的。在可执行文件有些数据段在运行时是不需要的,还有些段需要合并。
进程内存模型也是把地址空间分为一个一个段,这些段在初始化状态下的时候,它会和可执行文件中某些段建立映射关系,当我们读这些信息时,如果没有载入物理内存,会引发缺页异常,然后从可执行文件里把这些数据交换到物理内存里面,接下来就可以读到。因为我们从这里面读取的数据基本上分为两种状态,一种是只读的,另外一种运行期写的,运行期写的不要求写回去,只读的比如像指令肯定不能换出的,因为没有必要换出,因为需要把它干掉重新换入就可以了。
- $ readelf - l test
上面是程序运行时进程初始化时分配好的段,这个才是程序执行时候需要创建的段,下面有个映射表是可执行文件和进程模型的映射关系。
上面所有的段除了内存地址结束地址大小以外还有运行期标记位,例如RW代表可读写。例如下面.data .bss段被映射到3段,3段LOAD的标记位是RW。.text段映射到2段,2段LOAD标记位是R E只读可执行。也就是说这两种存在一定的映射关系。操作系统载入器通过这个表来确定哪些东西怎么样去映射。读数据时候怎么去读。然后确定虚拟地址空间的起止地址在哪。这样就确定了映射关系当我们去读内存的时候如果这数据没有载入到物理内存,那么就引发缺页异常,把这些数据从可执行文件里面读出来。
这个系列的每篇文章有大半篇幅内容属于付费阅读。提供微信支付或支付宝支付打赏50元备注留言手动提供付费文章访问密码。
来源: http://www.cnblogs.com/lyj/p/foundation_07_processmemory_free.html