linux可执行程序elf文件分析及进程的内存分布情况

马谦马谦马谦 操作系统评论950字数 1997阅读6分39秒阅读模式

一、ELF文件

ELF(Executable and Linkable Format)文件是linux下的二进制可执行文件,它同时兼容可执行文件和可链接文件。

一个ELF文件包含两个部分:一个固定长度的文件头和多个可扩展的数据块。其中,文件头是整个可执行文件的总地图,描述了整个文件的组织结构。可扩展数据块分为两类,对应着不同的视图——在链接视图下,数据块的单位是节(Section),用多个节区头索引所有内容;而在执行视图下,数据块的单位是段(Segment),用程序头(Program Header)索引所有的段。如下图所示:

linux可执行程序elf文件分析及进程的内存分布情况-图片1

通过readelf -S命令可以打印出文件的节区头部分,这里使用ln命令作为源程序分析:

节区头包含了我们经常见到的字段:.data/.text/.init等,这里只描述一下这几个常见节区的作用。

1.1 .text 节区

该节区存储了源文件编译生成的机器指令。对开发者来说,这可能是最重要的一个节区,所有的程序逻辑都放在这里。但开发者对该节区能做的控制却很少,能影响它的因素只有开发者写的程序逻辑,以及编译时使用的选项。比如,使用 -O1 优化选项编译程序可以生成尽量紧凑的 .text 节区,而用 -O2 优化选项会使编译器倾向于生成执行速度更快的指令组合,但有可能让 .text 节区的体积轻微地增大。

1.2 .rodata 节区

从字面上就能看出,ro表示read only,即不可写,因此该节区存储了程序中的常量数据,例如:

1.3 .data 节区

所有的全局和静态的已初始化变量会存放在这个节区中,这个节区是可读可写的。

1.4 .bss 节区

该节区存储了所有未初始化或初始化为 0 的全局和静态变量,该节区的设计初衷就是为了节省目标文件的存储空间。变量未被初始化,或者虽被初始化了,但值为 0,就没必要浪费空间,再在目标文件中存储大量的 0 值。

1.5 .got 和 .plt 节区

这两个节区存储了动态链接用到的全局入口表和跳转表。当程序中用到动态链接库中的某个函数时,会在该节区内记录相应的数据。

二、进程内存分布

2.1 虚拟地址空间

linux为每个进程都分配了4G(32位平台)的虚拟地址空间,虚拟空间可以认为是操作系统给每个进程准备的沙盒。就像电影《黑客帝国》中 Matrix 给每个人准备的充满营养液的容器一样。实际上,每个进程只存活在自己的虚拟世界里,却感觉自己独占了所有的系统资源(内存)。

当一个进程要使用某块内存时,它会将自己世界里的一个内存地址告诉操作系统,剩下的事情就由操作系统接管了。操作系统中的内存管理策略将决定映射哪块真实的物理内存,供应用使用。操作系统会竭尽全力满足所有进程合法的内存访问请求。一旦发现应用试图访问非法内存,它将会把进程杀死,防止它做“坏事”影响到系统或其他进程。

这样做,一方面为了安全,防止进程操作其他进程或者系统内核的数据;另一方面为了保证系统可同时运行多个进程,且单个进程使用的内存空间可以超过实际的物理内存容量。

该做法的另一结果则是降低了每个进程内存管理的复杂度,进程只需关心如何使用自己线性排列的虚拟地址,而不需关心物理内存的实际容量,以及如何使用真实的物理内存。

2.2 虚拟地址空间分布

虚拟内存的排布规则如下所示:

linux可执行程序elf文件分析及进程的内存分布情况-图片2

从下往上依次是0-4G的内存空间,分别分给了不同的区段。可以用一段程序来验证这一个观点:

代码分别创建了静态变量、全局变量以及临时变量等多种不同场景下的变量,打印出它们的地址。使用gcc编译运行:

可以看到已经初始化的静态变量s_init_var和已经初始化的全局变量g_init_var两者地址紧紧相邻,间距刚好隔了4个字节,因为他们被初始化了,都放在了.data区域。而未初始化的两个虽然也是相连在一起的,但是和初始化了的变量还是隔了一段距离,但距离不是很大,因为.bss.data地址差别不大。

而被const修饰的两个变量地址也是一样,处在同一个地址空间,和上面的静态变量以及全局变量相比,地址比它们小,这也就说明了.rodata是在.data.bss下面的。

最后的a/bpa/pb则验证了栈区地址和堆区地址的位置,堆是处在靠下的地址,而栈从地址位数上来说就已经很高了。

这里还能看出一点:局部变量b在a后面申请,但是它的地址比a要小;而pb同样也是在pa后面申请,地址却比pa大。

这说明了栈的地址空间是从上往下申请的,而堆则正常由下往上。

三、参考文档

文章部分摘抄于:攻克 Linux 系统编程

原文写得很不错,有兴趣可自行参考。

 最后更新:2020-3-17
马谦马谦马谦
  • 本文由 马谦马谦马谦 发表于 2019年4月6日11:14:10
  • 转载请务必保留本文链接:https://www.dyxmq.cn/program/operating-system/linux_elf-and-process-virtual-memory-html.html
进程和线程的区别 操作系统

进程和线程的区别

一、两者对比 进程是最小的资源分配单位,线程是最小的执行单位: 每个进程至少有一个线程,任务的执行都是由线程来完成,也就是说,线程时进程运行时的实体。 线程运行时依赖进程中分配的资源,一个进程可以有多...
进程的状态变迁 操作系统

进程的状态变迁

一、进程状态 通常情况下,我们描述进程的状态主要有三种: 就绪态:进程已经拥有了执行条件,但是没有获取到CPU,无法执行。 运行态:进程已经占有了CPU,此时正在CPU上运行。 阻塞态:进程在等待某项...
匿名

发表评论

匿名网友
:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:
确定

拖动滑块以完成验证