美文网首页程序员
4、可执行文件的装载与进程

4、可执行文件的装载与进程

作者: eesly_yuan | 来源:发表于2017-02-26 01:03 被阅读170次

首先简单说明程序和进程的区别

程序就如同菜谱,人就如同cpu,其他厨具就如同计算机的其他硬件,整个炒菜的过程就是一个进程

进程创建过程

从操作系统的角度看,一个进程最关键的特征是拥有独立的虚拟地址空间。创建进程通常需要执行以下三个步骤:

  • 1、创建独立的虚拟地址空间
    虚拟地址空间是一组页映射函数将虚拟空间映射至物理内存,因此创建虚拟地址空间则是创建映射函数所需的数据结构,真正映射关系通常在后续的程序发送页错误再进行设置。
  • 2、读取可执行文件头,建立虚拟空间和可执行文件的映射
    需注意这与上一步是有所区别的,此处是虚拟空间与可执行文件的映射,关系入下图所示。
  • 3、将指令寄存器设置为可执行文件入口,启动运行。
    虚拟地址空间-可执行文件-物理内存映射
    在上述步骤后,执行文件的指令和数据并为加载入内存,只是通过elf文件头部信息建立起可执行文件和虚拟地址空间的映射关系而已,真正加载过程将在发生页错误时执行(访问或者执行一个地址的指令或者数据时,但发现对应的内存区域为空),装载执行过程如下:
  • 1、内核根据上面第二步建立的映射关系,找到所需的内容在可执行文件位置
  • 2、分配一个物理内存页面,并将可执行文件内容装载到该内存页中
    针对这里的装载由装载管理器或者存储管理器执行,并不是把所有的数据都加载到内存中,而是按照程序运行局部性原理将目前在用的代码或者数据装载并驻留在内存中,当前尚未用到的数据或者代码不进行装载,简单介绍两种方式,一种是覆盖装入(已基本被淘汰,原理不多说看下面图应该就能理解),另一种是页映射,将内存和磁盘的中的数据和代码按照页进行划分,装载则以页为单位进行。
  • 3、建立该物理页面和虚拟地址空间的映射关系,然后把控制权交换给进程


    覆盖装载
segment和section

section是elf文件的链接视图;而segment是elf文件的执行视图,用于程序加载时的映射。具体含义下面进行详细介绍。

readelf -S file.elf //查看elf文件的section段
readelf -l file.elf //查看elf文件的segment段,目标文件无需加载,所以没有segment段

在进程创建的第二步2、读取可执行文件头,建立虚拟空间和可执行文件的映射,读取的是可执行文件的程序表头,程序表头中保存的是segment信息,一个segment对应链接过程中的多个权限相似的段section,之所以引入segment是因为elf中可能存在很多的section,如果对每个section在加载的时候都分别进行映射,那将产生很多浪费空间的问题。因此对于权限相同的段section,将其合并成一个段segment进行映射一个VMA。虚拟地址空间可以划分为以下几种VMA

  • 1、代码VMA,只读可执行,映射执行文件
  • 2、数据VMA,可读写可执行,映射执行文件
  • 3、堆VMA,可读写可执行,无映射文件向高地址扩展
  • 4、栈VMA,可读写不可执行,无映射文件向低地址扩展(命令行为$prog 123的进程的栈初始化示意图如下)


    进程栈初始化示意图
ELF装载过程

linux中在bash执行命令bash -c "cat 11 > 12"时,linux装载这个cat文件过程如下:

  • 1、首先看看用户层执行了哪些动作,用strace抓到如下信息,bash执行后调用的clone创建了一个新的进程43237,然后这个新的进程里调用了execve装载执行cat命令
execve("/bin/bash", ["bash", "-c", "cat 11 > 12"], [/* 21 vars */]) = 0
...
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7ff6cb473a10) = 43237
...
[pid 43237] execve("/bin/cat", ["cat", "11"], [/* 20 vars */]) = 0
...
  • 2、查看内核代码将发现,sys_execve系统调用,最终将调用do_execve(),do_execve()的执行流程如下

2.1查找cat文件是否存在,如果存在读取其前128个字节,用于获取魔数,辨别文件类型。
2.2识别执行文件类型后,调用search_binary_handler()函数,寻找能够加载此类型文件的装载处理函数,对应elf来说,将找到load_elf_binary()这个函数,其内部主要执行过程如下

  • 2.2.1检查elf文件格式有消息
  • 2.2.2寻找动态链接段.interp,设置动态链接器路径
  • 2.2.3根据elf文件程序表头对elf文件进行映射,文件到虚拟地址空间的映射
  • 2.2.4初始化elf进程环境
  • 2.2.5将系统调用的返回地址修改为elf执行入口点,对于静态链接的elf,入口点即elf文件头的e_entry指定地址,对于动态链接的elf,入口点将设置成为动态链接器。
  • 3、上述执行完毕后,将从系统调用execve返回,由于返回前已经将返回地址修改为elf的执行入口点,因此返回到用户态后将开始执行elf文件,新的程序就开始执行了。装载结束~
    对应的内核代码分析可参考这篇文件 linux进程管理之可执行文件的加载和运行(一)

相关文章

网友评论

    本文标题:4、可执行文件的装载与进程

    本文链接:https://www.haomeiwen.com/subject/awciwttx.html