开篇先抛出几个问题,之后逐个击破:
what:虚拟地址空间就是我们常说的虚拟内存,虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存的使用也更有效率。当处理器读取或写入内存位置时,都会使用虚拟地址。在读取或写入操作过程中,处理器会将虚拟地址转换为物理地址。
why:使用虚拟内存有如下好处:
更多详解虚拟内存的内容可以看我之前的文章:
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:其实上面已经介绍了原因,如果一次性把所有指令和数据都加载到内存中,物理内存可能不够用,所以需要使用动态装入,所以引入了页映射的方法。
这里首先需要弄清楚程序和进程的区别?程序(可执行文件)是一个静态的概念,它就是预先编译好的指令和数据集合的一个文件,进程则是一个动态的概念,它是程序运行的一个过程。
从操作系统角度看,一个进程最关键的特征是它拥有独立的虚拟地址空间,很多时候一个程序被执行都伴随着一个新的进程被创建,之后装载相应的可执行文件并运行。上述经历了什么步骤?
页错误:当程序执行一个地址的指令时,发现是个空页面,所以就认为是个页错误,这时候控制权交由操作系统,操作系统有专门的错误处理程序处理这种情况,查询第二步骤建立的映射数据结构,找到空页面所在的虚拟内存区域,计算出相应的页面在可执行文件中的偏移,然后在物理内存中分配一个物理页面,将进程中的该虚拟页与物理页建立映射关系,控制权返还给进程,进程从页错误的位置继续执行。
如果您读过我之前的文章应该就知道,一个正常的进程,可执行文件中不只包含数据段和代码段,还有好多个段,这里通过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文件中段的权限主要就有几种组合:
对于相同权限的段,可以把它们(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区域:
Linux如何装载并运行ELF程序
Linux内核装载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
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。