可执行文件的装载与进程
前言:源文件经过编译,链接过程,生成了目标可执行文件,可执行文件只有装载到内存以后才能被CPU执行
程序 和 进程:前者理解为一些预先编译好的指令和数据集合的一个 文件,是一个 静态 的概念,后者理解为程序运行时的一个 过程,是一个 动态 的概念
每个进程都被操作系统分配以 虚拟的地址空间(Virtual Address Space) 来供运行时分配使用,如果被操作系统捕捉到进程非法访问了额外控件,将当做 非法操作强制结束进程,常见的 Windows:”进程因非法操作需要关闭” 和 Linux:Segmentation fault
限制: 对于32位系统而言,允许分配给进程最大为:4GB的地址空间使用,而64位系统,则17179869184GB,即使 4GB,操作系统也会分配占用之后,最终留给进程最大3GB的地址空间使用,再通俗一点:整个进程在执行的时候,所有代码、数据包括通过 C 语言 malloc() 等方法申请的虚拟空间之和不得超过 3GB
PAE(Physical Address Extension)和 AWE(Address Windowing Extension):Intel 扩展了地址线,由 对应的32根(32位)扩展到了 36根 地址线,为此 多出来了256MB的物理空间,使用 窗口映射 技术,变向的增大了内存空间,进而也打破了 3GB 的限制,作为一种补救地址空间不够大的非常规手段
装载的方式
原理:程序执行时所需要的指令和数据必须 在内存中 才能够正常运行
装载的方法:都是利用了 程序局部性 的原理,即某一时刻程序只用到了局部的一些执行和数据,并非全局
以时间换取空间(淘汰):覆盖装入(Overlay)
页映射(Paging),通常32位的Intel都是用4096字节的页作为页长,假设内存为16K,那么会被分为4个页,假设程序需要32K的内存,即需要8个页的空间,那么如何在4个页中不影响程序执行的情况下,自如装载8个页呢? 内存页的分配算法
先进先出算法(FIFO)
最少使用算法(LUR)
从 操作系统角度 看可执行文件的装载,干了三件事
创建 一个独立的虚拟地址空间(这也是进程最关键的特征,实质上是创建映射函数所需要的相应的数据结构)
读取 可执行文件头,并建立虚拟空间与可执行文件的映射关系
将CPU的指令寄存器设置成可执行文件的入口地址,启动运行
操作系统通过不断的 页错误 来不停地将程序通过虚拟地址管理器装载到物理内存,进程才得以正常运行
进程虚拟空间 分布
区分概念 段 和 页 的关系:ELF段在映射时长度应该都是系统页长度的 整数倍,如果不是,那么多余部分也会占用一个页,进而造成 浪费,实际上,操作系统装载可执行文件时,只会根据段的 权限 来区分,对于相同权限的段,把它们合并到一起当做一个段来映射
普及概念 Linux 中将进程虚拟空间中的一个段叫做 虚拟内存区域(VMA,Virtual Memory Area),Windows 叫做 虚拟段(Virtual Section)
两次合并:第一次是链接器把各个目标文件中的相同段(Section,链接视图)合并,统一存放于可执行文件中,第二次是操作系统按照权限把可执行文件中的各个段(Segement,执行视图)合并,映射到虚拟内存区域中
核心:操作系统 通过给进程空间划分出一个个 VMA 来 管理 进程的虚拟空间,基本原则是将相同 权限 属性的、有相同 映像文件 的映射成一个VMA,一个进程基本上可以分为如下几 种 VMA区域:
代码VMA,权限只读、可执行;有映像文件
数据VMA,权限可读写,可执行;有映像文件
堆VMA,权限可读写、可执行;无映像文件,匿名,可向上扩展
栈VMA,权限可读写、不可执行;无映像文件,匿名,可向下扩展
一些限制
堆的 最大 申请数量:受操作系统版本、程序本身大小、用到的动态/共享库数量、大小、程序栈数量、大小等因素影响(随机地址空间分布技术)
段地址 对齐:受页和段的长度 整数倍映射 问题,Unix采取了 段合并 映射的方法:各个段接壤部分共享一个物理页面
进程栈 初始化:进程初始化启动之前,一些系统环境变量和进程的运行 参数 会提前保存到 Stack VMA 中,程序的库部分会把堆栈里面的初始化信息中的参数信息传递给 main() 的 argc 和 argv 两个参数
大致了解:Linux 内核装载 ELF 过程简介
- bash 进程会调用 fork() 系统调用创建一个新的进程,然后新的进程调用 execve() 系统调用加载执行指定的 ELF 文件
- 检查 ELF 可执行文件格式的有效性,比如魔数、程序头表中段的数量
- 寻找动态链接的 .interp 段,设置动态链接器路径
- 根据 ELF 可执行文件的程序头表的描述,对 ELF 文件进行映射,比如代码、数据、只读数据
- 初始化 ELF 进程环境,动态链接准备
- 将系统调用的返回地址修改成 ELF 可执行文件的入口点
大致了解:Windows PE 的装载 过程简介
- 先读取文件的第一个页,包含了 DOS 头、PE文件头 和 段表
- 检查进程地址空间中,目标地址是否可用
- 使用段表中提供的信息,将 PE 文件中所有的段一一映射到地址空间中相应的位置
- 如果装载地址不是目标地址,则进行 Rebasing
- 装载所有 PE 文件所需要的 DLL 文件
- 对 PE 文件中的所有导入符号进行解析
- 根据 PE 头中制定的参数,建立初始化栈和堆
- 建立主线程并启动进程