第六章 可执行文件的装载与进程
 
1     进程虚拟地址空间
32位的进程虚拟空间为4G,在linux系统中,操作系统占高位的1G,从0xC00000000xFFFFFFFF。用户进程虚拟空间从0x080480000开始映射。
 
2     装载方式
静态装载 and 动态装载
覆盖装入 and 页映射
覆盖装入需要程序员写一个常驻内存的覆盖管理器,需要保证两点:
           1)模块被调用时,整个调用路径上的模块必须都在内存中。
           2)任何一个模块不允许跨过树状结构进行调用。
页映射是虚拟存储的一部分,而是将内存和磁盘中的数据按页进行划分,当用到文件中的哪个页,则将其调入内存,这部分工作由操作系统的存储管理器负责。
 
3     从操作系统看可执行文件装载
进程的建立:一个进程的建立最关键的特征是拥有独立的虚拟地址空间。进程的建立分三步:
1 创建独立的虚拟地址空间:创建虚拟地址空间实际上是建立虚拟地址空间到物理空间的映射,在i386linux实际上就是创建一个页目录,或者称为页表。
2 读取可执行文件头,建立虚拟空间与可执行文件的映射关系。当程序发生页错误时,操作系统将从内存中分配一个物理页,并将该缺页从磁盘读入内存。然后设置页的映射关系。
显然,当缺页时,操作系统需要知道程序当前需要的页在磁盘中的哪个位置,此时指令的虚拟地址是知道的,这就需要建立一个虚拟地址到可执行文件之间的映射。
这样的一种数据结构称为VMA,它记录了各个段对应的虚拟空间,并记录该段在文件中的偏移。
3 CPU指令寄存器设置成可执行程序入口,启动。
页错误:CPU执行某条指令,如果该指令所在页是空页,则发生段错误。操作系统捕获,找到该指令所在的VMA,并计算出该页在文件中的偏移,分配一个物理页,将文件内容读入内存,设置虚拟地址也物理地址的映射关系,即页表。将控制权还给程序。
 
4     进程虚拟空间分布
ELF文件的链接视图和执行视图:
根据各个段的权限,相同权限段基本都在一起,可以将段分成不同的SegmentVMA实际上记录的是Segment信息。
Section是链接视图,而Segment是执行视图。可执行文件的程序头表就是记录各个Segment的一个数据结构。
Typedef struct{
          Elf32_Word               p_type;              //segment 类型
          Elf32_Word               p_offset;           //segment  在文件中的偏移
          Elf32_Addr                p_vaddr;           //segment 第一个字节在虚拟地址空间中的位置
          ------------                   p_paddr;           //物理装载位置
          Elf32_Word               p_filesz;            //ELF文件中占空间长度
          -------------                  p_memse;        //在虚拟地址空间中所占的长度
          ------------                   p_flags;             //读写执行属性
          ------------                   p_align;             //对齐属性
}Elf32_Phdr;
 
堆和栈
堆和栈也是通过VMA进行管理,不映射到文件,称为匿名虚拟内存区域。有时候数据Segment并没有VMA大小对应,因为BSS段的数据可能被操作系统放在了堆里面。
 
段地址对齐
最简单的情况是,各个Segment在物理内存中都分别映射,Segment起始地址都是4096整数倍。这样容易造成物理空间的浪费。
一种巧妙的方法是将各个段接壤的部分共享一个物理页,然后将该物理页映射两次。
为什么要映射两次?映射两次后的VMA地址怎么计算?
 
进程栈初始化
操作系统在进程启动前会会将一些信息保存在虚拟空间栈中(Stack VMA),最基本的是系统环境变量和进程运行参数。
栈顶指针指向初始化后栈顶位置。
进程启动后,程序的库部分会把堆栈里的初始化信息传递给main()函数,也就是argcargv,分别对应命令行参数数量和命令行参数字符串指针数组。
 
5     Linux内核装载
1         Fork()一个新进程
2         新进程调用execve系统调用执行指定ELF文件
a)         读取可执行文件前128个字节,进行分析,看是什么文件格式
b)         根据文件格式进行装载,ELF文件会进入load_elf_binary()进行处理。
                         i.              分析文件头
                       ii.              寻找动态链接的 .interp段(动态链接)
                      iii.              根据程序头表进行映射
                      iv.              初始化ELF进程环境
                       v.              将返回地址修改为程序入口
 
:链接完成后,所有的虚拟地址空间就已经确定了