前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux可执行文件与进程的虚拟地址空间

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

作者头像
刘盼
发布2020-05-07 23:08:24
4.1K0
发布2020-05-07 23:08:24
举报
文章被收录于专栏:人人都是极客人人都是极客

作者简介:

本文由西邮陈莉君教授研一学生贺东升编辑,梁金荣、张孝家校对

建议结合之前的《linux的内存寻址方式》看。

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

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

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

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

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

代码语言:javascript
复制
cat /proc/pid/maps

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

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

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

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

  1. 以代码段为代表的权限为可读可执行的段
  2. 以数据段和BSS段为代表的权限为可读可写的段
  3. 以只读数据段为代表的权限为只读的段

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

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

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

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

可以看到这个可执行文件中共有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可执行文件与进程虚拟地址空间的映射关系

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

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

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

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

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

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

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

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

视频讲解

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

视频内容

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 人人都是极客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 32位的虚拟地址空间
  • 64位的虚拟地址空间
  • Proc目录下的进程虚拟地址空间布局
  • 虚拟地址空间存储区的分布
  • ELF可执行文件与进程虚拟地址空间的映射关系
  • 视频讲解
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档