可执行文件的装载与进程


注:本文为阅读《程序员的自我修养》第六章后所作总结

内存

操作系统会给每个进程分配独立的虚拟内存空间。根据处理器的位数,每个进程能够访问的最大内存 有所不同,但总物理内存的大小不受处理器位数的限制,而取决于地址线数量。

装载方式

程序运行需要把数据和代码装入内存。为了提高内存利用效率,操作系统使用覆盖装入页映射 的方法实现动态装入,以确保读写最频繁的数据总是在内存中。覆盖装入允许同一程序不同模块共用 一块内存空间,页映射则把数据以“页”为单位选择性装载到内存中,减少读写频率小的数据占用的内存。

虚拟内存的结构

程序被编译后,分为许多“段(Section)”。以下代码展示了一个C语言程序中包含的部分Section。

ubuntu@myserver:~$ readelf -S test.elf
[Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .note.gnu.pr[...] NOTE             0000000000400270  00000270
       0000000000000030  0000000000000000   A       0     0     8
  [ 2] .note.gnu.bu[...] NOTE             00000000004002a0  000002a0
       0000000000000024  0000000000000000   A       0     0     4
  [ 3] .note.ABI-tag     NOTE             00000000004002c4  000002c4
       0000000000000020  0000000000000000   A       0     0     4
  [ 4] .rela.plt         RELA             00000000004002e8  000002e8
       0000000000000240  0000000000000018  AI      29    20     8
  [ 5] .init             PROGBITS         0000000000401000  00001000
       000000000000001b  0000000000000000  AX       0     0     4
  [ 6] .plt              PROGBITS         0000000000401020  00001020
  ......

一般而言,操作系统按页来映射内存,因此每个Section对应一个页。然而这样显然会造成内存的浪费,因为 Section的大小不一定是页大小的整数倍。因此实际上,操作系统会把多个Section合成一个segment进行映射。 合成的依据是这些Section的“权限”,读、写、执行等。Segment的信息保存在程序头中。

除了可执行文件本身包含的数据外,操作系统还会分配栈和堆空间给进程使用。

装载过程

Linux可执行文件

  • 检测可执行文件类型
  • 检测可执行文件是否有效
  • 配置动态链接
  • 对ELF进行映射
  • 初始化并启动进程

Windows 可执行文件

Windows中可执行文件称作PE。和Linux类似,也是由Section和Segment组成的。 不同点在于:

  • 无需考虑地址对其
  • 使用相对虚拟地址(所谓偏移地址)

文章作者: LouisZ
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 LouisZ !
  目录