Linux可执行文件如何装载进虚拟内存

发表于 4年以前  | 总阅读数:618 次

开篇先抛出几个问题,之后逐个击破:

  1. 什么是进程的虚拟地址空间?为什么进程要有自己的虚拟地址空间,这样做有什么好处?
  2. 我们都听说过页映射,什么是页映射,操作系统为什么要以页映射方式将程序映射到进程地址空间,这样做有什么好处?程序运行过程中发生页错误如何处理?
  3. 什么是进程?从操作系统的角度来看,进程是如何被建立的?
  4. 进程虚拟地址空间的分布是什么样的?
  5. Linux是如何装载并运行ELF程序的?

虚拟地址空间

what:虚拟地址空间就是我们常说的虚拟内存,虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存的使用也更有效率。当处理器读取或写入内存位置时,都会使用虚拟地址。在读取或写入操作过程中,处理器会将虚拟地址转换为物理地址。

why:使用虚拟内存有如下好处:

  • 程序员无需操心如何存储数据或者程序等内容。
  • 程序可以使用一系列连续的虚拟地址来访问物理内存中不连续的大内存区域,用户看到的是连续地址,而无需关心更底层物理地址的排布。
  • 通过使用虚拟内存,程序可以使用大于实际可用物理内存的空间,当物理内存不够用时,操作系统会将物理内存页保存在磁盘文件,数据页或者代码页会根据需要在物理内存和磁盘之间移动。
  • 不同进程使用的虚拟地址彼此隔离,用户无需担心会影响到其它程序内存地址中的数据,操作系统的内存管理模块会将虚拟地址映射到物理地址。

更多详解虚拟内存的内容可以看我之前的文章:

深入浅出虚拟内存

深入浅出虚拟内存(二)绘制虚拟内存排布图

深入浅出虚拟内存(三)堆内存分配及malloc实现原理

页映射

background:程序运行时所需要的指令和数据必须放在内存中才可以正常执行,最简单的办法就是将运行所需要的指令和数据全部装进内存,但是很多时候程序需要的内存可能大于实际可用的物理内存,为了解决这种不够用的问题引入了动态装入的概念,可以将程序最常用的部分驻留在内存中,而将一些不常用的数据存在磁盘中。

what:页映射不是一次性将所有的程序和数据装入内存,而是将内存和磁盘中的数据和指令按页为单位分成若干份,以后所有的装载和操作的单位就是页。页的大小不固定,但是一般都是4096字节。

how:如下图,举个例子,可执行程序所需要的指令和数据总和占8个页,编号为VP0-VP7,而实际的物理内存只有4个页,编号为PP0-PP3,4个页的物理内存无法同时将8个页的程序都装载进去,所以需要动态装入,假设程序入口地址在VP0,这时内核发现VP0不在内存中,所以将VP0分配给了PP0,将VP0的内容装入了PP0,运行一段后程序需要用到VP2,内核又将VP2分配给了PP1,之后又用到VP4和VP6,内核又分别分配给了PP2和PP3。这时候程序只需要VP0、VP2、VP4和VP6这四个页就可以一直运行下去,如果程序又需要VP5,那内核就必须会放弃正在使用的四个内存页中的一个才可以把VP5装载进去继续执行,至于选择哪个,操作系统内核会有多种换出算法来处理这种问题。

why:其实上面已经介绍了原因,如果一次性把所有指令和数据都加载到内存中,物理内存可能不够用,所以需要使用动态装入,所以引入了页映射的方法。

进程如何被建立

这里首先需要弄清楚程序和进程的区别?程序(可执行文件)是一个静态的概念,它就是预先编译好的指令和数据集合的一个文件,进程则是一个动态的概念,它是程序运行的一个过程。

从操作系统角度看,一个进程最关键的特征是它拥有独立的虚拟地址空间,很多时候一个程序被执行都伴随着一个新的进程被创建,之后装载相应的可执行文件并运行。上述经历了什么步骤?

  1. 创建一个独立的虚拟地址空间:这里的创建空间并不是真正的创建空间,而是创建映射函数所需要的数据结构,方便后面映射需要。
  2. 读取可执行文件头,建立虚拟空间和可执行文件的映射关系:上面的映射数据结构是为了建立虚拟空间到物理内存的映射关系,这一步是虚拟空间与可执行文件的映射关系。
  3. 将CPU的指令寄存器设置成可执行文件入口,启动运行:这里可以简单的理解为操作系统执行了一条跳转指令,跳转到可执行文件的入口地址。

页错误:当程序执行一个地址的指令时,发现是个空页面,所以就认为是个页错误,这时候控制权交由操作系统,操作系统有专门的错误处理程序处理这种情况,查询第二步骤建立的映射数据结构,找到空页面所在的虚拟内存区域,计算出相应的页面在可执行文件中的偏移,然后在物理内存中分配一个物理页面,将进程中的该虚拟页与物理页建立映射关系,控制权返还给进程,进程从页错误的位置继续执行。

进程虚拟空间分布

如果您读过我之前的文章应该就知道,一个正常的进程,可执行文件中不只包含数据段和代码段,还有好多个段,这里通过readelf可以查看:

$ readelf -S test
There are 9 section headers, starting at offset 0x1208:

Section Headers:
[Nr] Name              Type             Address           Offset
      Size              EntSize          Flags  Link  Info  Align
[ 0]                   NULL             0000000000000000  00000000
      0000000000000000  0000000000000000           0     0     0
[ 1] .text             PROGBITS         00000000004000e8  000000e8
      0000000000000056  0000000000000000  AX       0     0     1
[ 2] .rodata           PROGBITS         000000000040013e  0000013e
      0000000000000006  0000000000000000   A       0     0     1
[ 3] .eh_frame         PROGBITS         0000000000400148  00000148
      0000000000000078  0000000000000000   A       0     0     8
[ 4] .data             PROGBITS         0000000000601000  00001000
      0000000000000008  0000000000000000  WA       0     0     8
[ 5] .comment          PROGBITS         0000000000000000  00001008
      0000000000000029  0000000000000001  MS       0     0     1
[ 6] .symtab           SYMTAB           0000000000000000  00001038
      0000000000000150  0000000000000018           7     7     8
[ 7] .strtab           STRTAB           0000000000000000  00001188
      000000000000003a  0000000000000000           0     0     1
[ 8] .shstrtab         STRTAB           0000000000000000  000011c2
      0000000000000042  0000000000000000           0     0     1
Key to Flags:
 W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
 L (link order), O (extra OS processing required), G (group), T (TLS),
 C (compressed), x (unknown), o (OS specific), E (exclude),
 l (large), p (processor specific

通过上面的结果可以看出这里的段叫Section,拿这个举例,ELF文件映射是以系统页为单位,每个段在映射时的长度都是系统页的整数倍,假设程序有8个段,每个段都占512字节,占用了8个页,但是一个页却有4K的大小,空间利用率只有1/8,造成极大的空间浪费。实际上,从操作系统装载可执行文件的角度看,可以发现它实际上并不关心可执行文件各个段所包含的实际内容,它主要就是关心段的权限(可读、可写、可执行),ELF文件中段的权限主要就有几种组合:

  • 以代码段为代表的权限为可读可执行的段
  • 以数据段和BSS段位代表的权限为可读可写的段
  • 以只读数据段位代表的权限为只读的段

对于相同权限的段,可以把它们(Section)合并到一起当作一个段(Segment)进行映射。拿前面的例子,之前8个section需要8个页,而这种方式8个Section可能会被合并成2个Segment,占用2个页。

Segment的概念实际上是从装载的角度重新划分了ELF的各个段,在将目标文件链接成可执行文件的时候,链接器尽量把相同权限属性的段分配在同一空间,多个Section变成一个Segment,而系统就是按这种Segment来映射可执行文件的。

Segment和Section是从不同的角度划分一个ELF文件,称为不同的视图,从Section的角度来看ELF文件就是链接视图,从Segment的角度来看ELF文件就是执行视图,当我们在谈到ELF装载时,段专门指Segment,其它情况下,段指的是Section。

通过readelf命令可以查看可执行文件的Segment。

$ readelf -l test

Elf file type is EXEC (Executable file)
Entry point 0x400123
There are 3 program headers, starting at offset 64

Program Headers:
 Type           Offset             VirtAddr           PhysAddr
                FileSiz            MemSiz              Flags  Align
 LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                0x00000000000001c0 0x00000000000001c0  R E    0x200000
 LOAD           0x0000000000001000 0x0000000000601000 0x0000000000601000
                0x0000000000000008 0x0000000000000008  RW     0x200000
 GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                0x0000000000000000 0x0000000000000000  RW     0x10

Section to Segment mapping:
 Segment Sections...
  00     .text .rodata .eh_frame
  01     .data
  02

这里有很少的Segment,描述Segment属性的结构叫程序头,这里只需要知道ELF可执行文件中有一个专门的数据结构叫程序头表,它用来保存Segment的信息,因为目标文件不需要被装载,所以没有程序头表,而可执行文件和共享库文件都有头表,他们会被用于装载,这里的各个Segment都是通过匿名虚拟内存区域(VMA)来映射。

可以通过cat来查看VMA:

cat /proc/72/maps
7f445f4b0000-7f445f4c7000 r-xp 00000000 00:00 516559             /lib/x86_64-linux-gnu/libgcc_s.so.1
7f445f4c7000-7f445f4c8000 ---p 00017000 00:00 516559             /lib/x86_64-linux-gnu/libgcc_s.so.1
7f445f4c8000-7f445f6c6000 ---p 00000018 00:00 516559             /lib/x86_64-linux-gnu/libgcc_s.so.1
7f445f6c6000-7f445f6c7000 r--p 00016000 00:00 516559             /lib/x86_64-linux-gnu/libgcc_s.so.1
7f445f6c7000-7f445f6c8000 rw-p 00017000 00:00 516559             /lib/x86_64-linux-gnu/libgcc_s.so.1
7f445f6d0000-7f445f86d000 r-xp 00000000 00:00 516611             /lib/x86_64-linux-gnu/libm-2.27.so
7f445f86d000-7f445f870000 ---p 0019d000 00:00 516611             /lib/x86_64-linux-gnu/libm-2.27.so
7f445f870000-7f445fa6c000 ---p 000001a0 00:00 516611             /lib/x86_64-linux-gnu/libm-2.27.so
7f445fa6c000-7f445fa6d000 r--p 0019c000 00:00 516611             /lib/x86_64-linux-gnu/libm-2.27.so
7f445fa6d000-7f445fa6e000 rw-p 0019d000 00:00 516611             /lib/x86_64-linux-gnu/libm-2.27.so
7f445fa70000-7f445fc57000 r-xp 00000000 00:00 516391             /lib/x86_64-linux-gnu/libc-2.27.so
7f445fc57000-7f445fc60000 ---p 001e7000 00:00 516391             /lib/x86_64-linux-gnu/libc-2.27.so
7f445fc60000-7f445fe57000 ---p 000001f0 00:00 516391             /lib/x86_64-linux-gnu/libc-2.27.so
7f445fe57000-7f445fe5b000 r--p 001e7000 00:00 516391             /lib/x86_64-linux-gnu/libc-2.27.so
7f445fe5b000-7f445fe5d000 rw-p 001eb000 00:00 516391             /lib/x86_64-linux-gnu/libc-2.27.so
7f445fe5d000-7f445fe61000 rw-p 00000000 00:00 0
7f445fe70000-7f445ffe9000 r-xp 00000000 00:00 540399             /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f445ffe9000-7f445fff6000 ---p 00179000 00:00 540399             /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f445fff6000-7f44601e9000 ---p 00000186 00:00 540399             /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f44601e9000-7f44601f3000 r--p 00179000 00:00 540399             /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f44601f3000-7f44601f5000 rw-p 00183000 00:00 540399             /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f44601f5000-7f44601f9000 rw-p 00000000 00:00 0
7f4460200000-7f4460226000 r-xp 00000000 00:00 516353             /lib/x86_64-linux-gnu/ld-2.27.so
7f4460226000-7f4460227000 r-xp 00026000 00:00 516353             /lib/x86_64-linux-gnu/ld-2.27.so
7f4460427000-7f4460428000 r--p 00027000 00:00 516353             /lib/x86_64-linux-gnu/ld-2.27.so
7f4460428000-7f4460429000 rw-p 00028000 00:00 516353             /lib/x86_64-linux-gnu/ld-2.27.so
7f4460429000-7f446042a000 rw-p 00000000 00:00 0
7f4460440000-7f4460442000 rw-p 00000000 00:00 0
7f4460450000-7f4460452000 rw-p 00000000 00:00 0
7f4460460000-7f4460462000 rw-p 00000000 00:00 0
7f4460600000-7f4460601000 r-xp 00000000 00:00 205513             /mnt/d/wzq/wzq/util/test/a.out
7f4460800000-7f4460801000 r--p 00000000 00:00 205513             /mnt/d/wzq/wzq/util/test/a.out
7f4460801000-7f4460802000 rw-p 00001000 00:00 205513             /mnt/d/wzq/wzq/util/test/a.out
7fffc7576000-7fffc7597000 rw-p 00000000 00:00 0                 [heap]
7fffcf186000-7fffcf986000 rw-p 00000000 00:00 0                 [stack]
7fffcfe84000-7fffcfe85000 r-xp 00000000 00:00 0                 [vdso]

上面的我们可以先忽略,看最下面,主要有堆和栈,这两个VMA几乎在所有的进程中都存在,而[vdso]是一个内核模块,程序通过这个模块和内核进行通信。

小总结:

图片来自网络,侵权删

操作系统通过给进程空间划分出一个个VMA来管理进程的虚拟空间,基本原则是将相同权限属性的、有相同映射文件的映射成一个VMA,一个进程主要可以分成以下几种VMA区域:

  • 代码VMA:权限只读可执行,有映射文件
  • 数据VMA:权限可读写可执行,有映射文件
  • 堆VMA:权限可读写可执行,无映射文件,匿名,向上扩展
  • 栈VMA:权限可读写不可执行,无映射文件,匿名,向下扩展

Linux如何装载并运行ELF程序

Linux内核装载ELF文件主要有两步:

  1. 通过fork系统调用创建一个新的进程
  2. 通过execve系统调用执行指定的ELF文件,附带环境变量和参数 a. 检查ELF可执行文件的有效性,比如魔数(通过魔数可以确定文件格式)、Segment的数量等 b. 寻找动态链接的段,设置动态链接器路径 c. 根据ELF可执行文件的程序头表描述,对ELF文件进行映射,比如代码、数据、只读数据 d. 初始化ELF进程环境 e. 将系统调用的返回地址修改为ELF可执行文件的入口地址

参考资料

《程序员的自我修养:链接装载与库》

https://docs.microsoft.com/zh-cn/windows-hardware/drivers/gettingstarted/virtual-address-spaces

https://blog.csdn.net/chenlycly/article/details/53367336

https://www.zhihu.com/question/290504400

https://zh.wikipedia.org/wiki/%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98

https://www.cnblogs.com/Tan-sir/p/7488796.html

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237231次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8065次阅读
 目录