虚拟地址空间和可执行文件的映射方式
VMA(虚拟内存区域)方式
- 每个程序都有自己独立的虚拟地址空间(对于32位计算机而言)
使用范围:0x00000000 - 0xBFFFFFFF(用户进程空间,其余1G为操作系统内核空间) - 使用指令readelf -S 查看ELF文件所有的段(链接视图)
- 使用指令readelf -l 查看ELF文件如何被操作系统映射到进程的虚拟空间(执行视图)
装载的方式:覆盖载入、页映射
覆盖载入:
- 需要辅助代码实现覆盖管理,且需要手工将模块按照调用依赖关系组织成树状结构
页映射:
- 页映射是虚拟存储机制的一部分,页映射与覆盖载入原理相同,不是一次就将程序所有数据和指令都载入内存,而是按照页的大小进行装载
- 一般使用页的大小为4K,页可以是8K、2MB、4MB等。若一个程序大小为32K,则程序分为8个页,加入入口地址在P0,则装载管理器发现P0不再内存就会发起缺页终端,从磁盘中读入P0,然后重新执行失败指令,如果需要其它页,也是通过缺页中断进行页面调入(此处涉及操作系统的页面置换算法)
操作系统角度看可执行文件的装载
一个重要的硬件模块:MMU-负责地址转换的功能
进程的建立:
三个步骤:
-
1.创建一个独立的虚拟地址空间
虚拟空间是由一组映射函数将虚拟空间内各个页映射至相应的物理空间,那么创建一个虚拟空间实际是创建一个映射函数操作的数据结构(例如,key-value)
此时只是创建一个数据结构,并不对其进行初始化,初始化的过程在分配到物理内存时 -
2.读取可执行文件头,建立虚拟地址空间与可执行文件的映射,同样也是通过数据结构进行维护
当操作系统发生缺页错误时,它应当知道程序当前所需要的页在可执行文件的哪个位置,由于可执行文件在装载时实际是被映射的虚拟空间,所以可执行文件也被叫做映像文件
例如:
一个可执行文件只有一个代码段.text,其在可执行文件中的偏移为0,则经过映射后,其在虚拟内存空间中以一个.text的VMA表示,例如VMA的地址空间为0x08048000-0x08049000,此时,一个映射关系就建立了起来 -
3.将CPU的指令寄存器设置为可执行文件的入口地址,启动执行
经过上述步骤后,只是确定了可执行文件、虚拟地址空间以及物理地址空间的映射关系以及程序的入口地址。当CPU真正开始执行时,发现访问的入口地址是个空页面,此时OS会发出缺页中断,操作系统查询第二个步骤建立的数据结构,首先找到空页面所在的VMA,其次通过VMA计算出相应的页面在可执行文件中的偏移,然后在物理内存中分配一个物理页面,将进程中该虚拟页与分配的物理页建立映射关系,然后开始重新执行
Linux内核装载ELF文件过程
1.bash -> 2.fork -> 3.execve -> 4.sys_execve -> 5.do_execve ->
do_execve:
- 1.查找被执行文件
- 2.查看文件的前128字节判断文件格式,即读取可执行文件的魔数以确定文件解析方式
- 3.调用search_binary_handle搜索匹配合适的可执行文件装载处理过程
- 4.找到后调用,例如Linux则为load_elf_binary,其主要操作包括检查文件的有效性、寻找动态链接的.interp段,设置动态链接路径、根据文件头映射、初始化ELF进程环境、将系统调用的返回地址修改为ELF文件的入口点
- 5.返回系统调用