Linux可执行文件与进程的虚拟地址空间

栏目: IT技术 · 发布时间: 5年前

内容简介:作者简介:本文由西邮陈莉君教授研一学生贺东升编辑,梁金荣、张孝家校对Linux可执行文件与进程的虚拟地址空间

作者简介:

本文由西邮陈莉君教授研一学生贺东升编辑,梁金荣、张孝家校对 Linux可执行文件与进程的虚拟地址空间

Linux可执行文件与进程的虚拟地址空间

一个可执行文件被执行的同时也伴随着一个新的进程的创建。Linux会为这个进程创建一个新的虚拟地址空间,然后会读取可执行文件的文件头,建立虚拟地址空间与可执行文件的映射关系,然后将CPU的指令指针寄存器设置成可执行文件的入口地址,然后CPU就会从这里取指令执行。

一个可执行文件包含可被CPU执行的指令和待处理的数据,上CPU之前,指令和数据全部被翻译成成二进制的形式。在可执行的文件的内部,划分出了一些专门的段,如代码段,数据段,BSS段等。代码段中存放的是可执行的二进制指令,数据段存放初始化过的变量,BSS段存放未初始化的变量,从装载的角度,把这些段称为segment。

32位的虚拟地址空间

Linux可执行文件与进程的虚拟地址空间

64位的虚拟地址空间

Linux可执行文件与进程的虚拟地址空间

Proc目录下的进程虚拟地址空间布局

Linux在装载可执行文件的时候,会将这些segment映射到进程的地址空间中。映射的时候,这里面的segment会对应一个VMA。Linux将进程虚拟地址空间中的一个段叫做虚拟内存区域(VMA)。在/proc目录下,可以查看一个进程的虚拟地址空间,通过命令

cat /proc/pid/maps

Linux可执行文件与进程的虚拟地址空间

这里面的每一行都对应一个VMA,每一个VMA都通过 vm_area_struct 结构体来描述。结构体中的 vm_start vm_end 是VMA的起始地址和结束地址,还有其他的一些域来描述VMA的权限等。我们需要关注的是前三个VMA,这是ELF可执行文件的segment映射过来的。可以看到,这里面并没有标明哪个是TEXT段,哪个是DATA段和BSS段。但是可以发现,前三个VMA的权限都不一样。

虚拟地址空间存储区的分布

Linux可执行文件与进程的虚拟地址空间

所以,操作系统实际上并不关心可执行文件各个段所包含的的实际内容,OS只关心一些跟装载相关的问题,最主要的是段的权限(可读,可写,可执行)。

ELF文件中,段的权限往往只有为数不多的几种组合,基本上就3种:

  1. 以代码段为代表的权限为可读可执行的段

  2. 以数据段和BSS段为代表的权限为可读可写的段

  3. 以只读数据段为代表的权限为只读的段

ELF可执行文件中有两个概念,分别是段(segment)和节(section)。通过readelf -S name.elf可以查看ELF可执行文件的节头表,这里面有所有节的信息

Linux可执行文件与进程的虚拟地址空间

在将目标文件链接成可执行文件的时候,链接器会尽量把相同权限属性的段分配在同一空间。比如可读可执行的段都放在一起,这种段的典型是代码段;可读可写的段都放在一起,这种段的典型是数据段。在ELF中,把这些属性相似的,又连在一起的段叫做一个“segment”,而系统正是按照“segment”而不是“section”来映射可执行文件的。

可以使用命令  readelf -l name.elf 来查看ELF的段。在ELF的程序头表,保存着segment的信息

Linux可执行文件与进程的虚拟地址空间

最下面是是段与节的归属关系:

Linux可执行文件与进程的虚拟地址空间

可以看到这个可执行文件中共有9个segment。从装载的角度看,我们只关心两个“LOAD”型的segment,因为只有它是需要被映射的,其他诸如“NOTE”,"GNU_STACK"都是在装载时起辅助作用的。下面的0到8分别对应着上面的一个segment,两个LOAD类型的segment分别对应着02和03,可以看到每个LOAD类型的segment里面都包含了许多的section。

ELF将相同或者相似属性的section合并为一个segment并映射到一个VMA中,是为了减少页面内部碎片,以节省内存空间的使用。因为在有了虚拟存储机制以后,装载的时候采用页映射的方式。Intel系列的处理器,页尺寸最小是4096个字节,也就是4KB。当写的程序很小的时候,每个section可能只有几十或者几百个字节,如果每个section都占用一个页的话,对内存的浪费是海量的。所以在将目标文件链接成可执行文件的时候,链接器会尽量把相同或相似权限属性的section分配在同一空间,在程序头表中,将一个或多个属性类似的section合并为一个segment,然后在装载的时候,将这个segment映射到进程虚拟地址空间中的一个VMA中。

ELF可执行文件与进程虚拟地址空间的映射关系

Linux可执行文件与进程的虚拟地址空间

很明显,属性相同或相似的section会被归类到一个segment,并且被映射到同一个VMA。

Linux可执行文件与进程的虚拟地址空间

总的来说,“segment”和“section”是从不同的角度来划分同一个ELF文件。这个在ELF文件中被称为不同的视图(view),从section的角度来看ELF文件就是链接视图(Linking View),从segment的角度来看就是执行视图(Execution View)。当我们在谈到ELF装载时,段专门指segment,而在其他的情况下,段指的是section。

在实际的映射过程中,只发现有代码段映射的VMA,有数据段映射的VMA,却没有BSS段映射的VMA。

Linux可执行文件与进程的虚拟地址空间

如果仔细观察程序头表,查看两个LOAD型的segment,会发现一些映射的细节。

Linux可执行文件与进程的虚拟地址空间

FileSiz表示segment在ELF文件中所占的大小,MemSiz表示segment在进程虚拟地址空间中所占的大小。可以发现,MemSiz比FileSiz多出了0x20个字节,十六进制的20对应的十进制是32。再来看一下这个ELF可执行文件中BSS段的大小。

Linux可执行文件与进程的虚拟地址空间

可以看到,BSS段的大小正好是十进制的32,。这说明在实际映射的时候,数据段在内存中所分配的空间大小超过实际的大小,超出去的这部分空间就是BSS段,并没有为BSS段进行专门的映射,这就是为什么在查看程序头表时,只看到了两个LOAD类型的段,而不是三个,BSS段已经被合并到了数据类型的段里面。

这样做的好处就是在构造ELF可执行文件时,不需要再额外设立BSS的segment了,只需把数据segment的内存扩大,那些额外的部分就是BSS。而这部分多出的BSS空间,会被全部填充为0 。在 C语言 中,没有初始化的全局变量和一些静态变量会被默认初始化为0 ,这就是原因,因为它们会被分配到BSS段上,被一次性初始化为0。

最后我们通过一个打印变量地址的小程序进行验证,仔细观察没有初始化的全局变量和一些静态变量的线性地址。

Linux可执行文件与进程的虚拟地址空间

视频讲解

这部分内容录了一个视频,因为这是我第一次录讲解视频,没有什么经验,如果视频内容有任何问题还希望各路大神指出,不胜感激。

  • 腾讯视频:


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ethnography and Virtual Worlds

Ethnography and Virtual Worlds

Tom Boellstorff、Bonnie Nardi、Celia Pearce、T. L. Taylor / Princeton University Press / 2012-9-16 / GBP 21.00

"Ethnography and Virtual Worlds" is the only book of its kind - a concise, comprehensive, and practical guide for students, teachers, designers, and scholars interested in using ethnographic methods t......一起来看看 《Ethnography and Virtual Worlds》 这本书的介绍吧!

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具