目录
在x86
系统中,为了能够更加充分、灵活的使用物理内存,把物理内存按照4KB
的单位进行分页。
然后通过中间的映射表,把连续的虚拟内存空间,映射到离散的物理内存空间。
映射表中的每一个表项,都指向一个物理页的开始地址。
但是这样的映射表有一个明显的缺点:映射表自身也是需保存在物理内存中的。
在 32 位系统中,它使用了多达4MB
的物理内存空间(每个表项4
个字节,一共有4G/4K
个表项)。
为了解决这个问题,x86
处理器使用了两级转换:页目录和页表。
这篇文章,我们就从最基础的底层计算过程入手,把这个最重要的内存管理机制搞定,以后再学习更深入的知识点时,就会更容易理解了。
在一个32
位的系统中,物理内存的最大可表示空间就是0xFFFF_FFFF
,也就是4GB
。
虽然实际安装的物理内存可能远远没有这么大,但是在设计内存管理机制的时候,还是需要按照最大的可寻址范围来进行设计的。
按照一个物理页4KB
的单位来划分,4GB
空间可以分割为1024 * 1024
个物理页:
在上一篇文章中,使用单一的映射表来指向这些物理页,导致了映射表自身占据了太多的物理内存空间。
一个用户程序中定义的几个段,可能实际上只使用了很小的空间,完全用不到 4 GB。
但是仍然需要为它分配多达 4GB 的物理内存空间来保存这个映射表,很浪费。
为了解决这个问题,可以把这个单一映射表拆分成1024
个体积更小的映射表:
- 每一个映射表中,只有 1024 个表项,每一个表项仍然指向一个物理页的起始地址;
- 一共使用 1024 个这样的映射表;
这样一来,1024(每个表中的表项个数) * 1024(表的个数),仍然可以覆盖4GB
的物理内存空间。
这里的每一个表,就称作页表,所以一共有1024
个页表。
一个页表中一共有1024
个表项,每一个页表项占用4
个字节,所以一个页表就占用4KB
的物理内存空间,正好是一个物理页的大小。
也许有的小伙伴就开始算账了:一个页表自身占用4KB
,那么1024
个页表一共就占用了4MB
的物理内存空间,仍然是很多啊?
是的,从总数上看是这样,但是:一个应用程序是不可能完全使用全部的 4GB 空间的,也许只要几十个页表就可以了。
例如:一个用户程序的代码段、数据段、栈段,一共就需要10 MB
的空间,那么使用3
个页表就足够了,加上页目录,一共需要16 KB
的空间。
计算过程:
每一个页表项指向一个 4KB 的物理页,那么一个页表中 1024 个页表项,一共能覆盖 4MB 的物理内存;
那么 10MB 的程序,向上对齐取整之后(4MB 的倍数,就是 12 MB),就需要 3 个页表就可以了。
记住上图中的一句话:一个页表,可以覆盖 4MB 的物理内存空间(1024 * 4 KB)。
页表中,每一个表项的格式如下:
注意下面的这几个属性:
P(Present): 存在位。1 - 物理页存在; 0 - 物理页不存在;
RW(Read/Write): 读/写位。1 - 这个物理页可读可写; 0 - 这个物理页只可读;
D(Dirty): 脏位。表示这个物理页中的数据是否被写过;
现在,每一个物理页,都被一个页表中的一个表项来指向了,那么这1024
个页表的地址,应该怎么来管理呢?
答案是:页目录表!
顾名思义:在页目录中,每一个表项指向一个页表的开始地址(物理地址)。
操作系统在加载用户程序的时候,不仅仅需要分配物理内存,来存放程序的内容;
而且还需要分配物理内存,用来保存程序的页目录和页表。
再来算算账:
刚才说过:每一个页表覆盖4MB
的内存空间,那么页目录中一共有1024
个表项,指向1024
个页表的物理地址。
那么页目录能覆盖的内存空间就是1024 * 4MB
,也就是4GB
,正好是32
位地址的最大寻址范围。
页目录中,每一个表项的格式如下:
其中的属性字段,与页表中的属性类似,只不过它的描述对象是页表。
还有一点:每一个用户程序都有自己的页目录和页表!下文有详细说明。
现在,所有页表的物理地址被页目录表项指向了,那么页目录的物理地址,处理器是怎么知道的呢?
答案就是:CR3 寄存器
,也叫做: PDBR(Page Table Base Register)
。
这个寄存器中,保存了当前正在执行的那个任务的页目录地址。
每个任务(程序)都有自己的页目录和页表,页目录表的地址被记录在任务的TSS
段中。
当操作系统调度任务的时候,处理器就会找到即将执行的新任务的 TSS
段信息,然后把新任务的页目录开始地址更新到CR3
寄存器中。
当新任务的指令开始被执行时,处理器在获取指令、操作数据时,操作的是线性地址。
页处理单元就会从CR3
寄存器中保存的页目录表开始,把这个线性地址最终转换成物理地址。
当然,处理器中还有一个快表,用来加快从线性地址到物理地址的转换过程。
CR3
寄存器的格式如下:
顺便把官网上的其他几个控制寄存器都贴出来:
其中,CR0
寄存器的最高位PG
,就是开启页处理单元的开关。
也即是说:
当系统上电之后,刚开始的地址寻址方式一直是 [段:偏移地址] 的方式。
当启动代码准备好页目录和页表之后,就可以设置 CR0.PG = 1。
此时,处理器中的页处理单元就开始工作了:面对任何一个线性地址,都要经过页处理单元之后,才得到一个物理地址。
在之前的文章中,介绍过一个用户程序被操作系统加载的全过程,简述如下:
- 读取程序 header 信息,解析出程序的总长度,从任务自己的虚拟内存中分配一块足够的连续空间;
- 分配一个空闲物理页,用作程序的页目录,页目录的地址会记录在稍后创建的 TSS 段中;
- 使用虚拟内存中的线性地址,分配一个物理页(4 KB),登记到页目录和页表中;
- 从硬盘上读取 8 个扇区的数据(每个扇区 512 字节),存放到刚才分配的物理页中;
- 检查程序内容是否读取完毕:是-进入第 6 步;否-返回到第 3 步;
- 为用户程序创建一些必要的内核数据结构,比如:TSS、TCB/PCB 等等;
- 为用户程序创建 LDT,并且在其中创建每一个段描述符;
- 把操作系统的页目录中高端地址部分的表项,复制给用户程序的页目录表。
这样的话,所有用户程序的页目录中,高端地址的表项都指向相同的页表地址,就达到了共享“操作系统空间”的目的。
这里主要聊一下第3
步,假设用户程序文件在硬盘上的长度是20 MB
,电脑中实际安装的物理内存是1 GB
。
可以先计算一下:页目录中,每一个表项覆盖的空间是 4 MB,那么 20 MB的数据,需要 5 个表项就可以了。
在初始状态,页目录中的所有表项都是空的,其中的P
位都是为0
,表示页表不存在。
操作系统首先从虚拟内存中,分配一块20 MB
的空间,假设从1 GB(0x4000_0000)
的地址处开始吧,这个地址是线性地址。
也就是说把应用程序的文件读取到内存中,从地址0x4000_0000
开始存放,向高地址方向增长。
注意:在“平坦”型分段模型下,线性地址等于虚拟地址。
0x4000_0000 = 0100_0000_0000_0000___0000_0000_0000_0000
前10
位表示该线性地址在页目录中的索引,中间10
位表示页表中的索引,最后12
位表示物理页中的偏移地址。
因此,前10
位就是 0100_0000_00
,表示这个线性地址位于页目录中的第256
个表项:
操作系统发现这个表项中为空,没有指向任何一个页表。
于是就从物理内存中,找一个空闲的物理页,用作页目录中第256
个表项指向的页表。
注意:这个物理页是用作页表,而不是用作存储用户程序文件。
假设在物理内存上128 MB (0x0800_0000)
的地址处,找到一个空闲的物理页,用作这个页表。
把页表中的1024
个表项全部清空,并且把页表的物理地址0x0800_0000
,登记在页目录中的第256
个表项中:0x08000
(上图黄色部分)。
为什么不是 0x0800_0000
?
因为一个物理页的地址一定是4KB
对齐的(最后的12
位全部为0
),所以页目录的表项中只需要记录页表地址的高 20 位即可。
现在,页表也有了,下面就是分配一个物理页来存储程序的内容。
假设在刚才那个物理页(用作页表的那个)的上面,又找到一个空闲的物理页,地址是:0x0800_1000
。
此时,这个用于存放程序内容的物理页的地址,就需要记录在页表的一个表项中了。
那么应该记录在页表中的什么位置呢?也就是应该登记在哪一个表项中呢?
需要根据线性地址的中间 10 位来确定:
0x4000_0000 = 0100_0000_0000_0000___0000_0000_0000_0000
中间10
位的全部是0
,说明索引值就是0
,也就是说页表中的第0
个表项,保存这个物理页的地址,如下图所示:
一个物理页的地址一定是
4KB
对齐的(最后的12
位全部为0
),所以只需要记录物理页地址的高 20 位即可。
用于存储程序文件内容的物理页分配好了,下面就开始从硬盘中读取程序文件的内容了。
一个物理页的大小是4 KB
,硬盘上一个扇区的大小是512 B
,那么从硬盘上连续读取8
个扇区的数据就可以把一个物理页写满。
刚才已经假设:用户程序文件在硬盘上的长度是
20 MB
。
当读取了一个物理页的内容后,通过计算发现用户程序内容还没有读取完,于是继续重复以上流程。
1 . 线性地址增加 4KB:0x4000_1000 = 0100_0000_0000_0000___0001_0000_0000_0000;
2 . 前 10 位没有变,仍然是页目录中的第 256 个表项,发现这个表项指向的页表已经存在了,于是就不用再分配物理页用作页表了;
3 . 分配一个空闲物理页,用于存放程序内容,假设在 0x0100_4000处找到一个,把这个地址登记在页表中;
此时,线性地址的中间 10 位的索引值是 1,所以登记在页表中的第 1 个表项。
4 . 从硬盘上读取 8 个扇区的数据,写入这个物理页;
因为页目录中一个表项所覆盖的范围是4 MB
(也就是一个页表中1024
个表项所指向的物理页空间的总和)。
所以当读取了4 MB
的程序内容之后,这个页表中的所有表项就被填满了。
此时,读取的程序内容所占用的【线性地址】空间是:0x4000_0000 ~ 0x403F_FFFF
。
下面再继续读取新内容时,就从 0x4040_0000
这个线性地址处开始存放,读取过程与上面都是一样的:
- 确定页目录表项:
0x4040_0000 = 0100_0000_0100_0000___0000_0000_0000_0000,前 10 位的索引值是 257;
2 . 发现 257 这个表项为空,于是分配一个空闲的物理页,用作页表;
3 . 分配一个物理页,用作存储程序文件的内容,并把这个物理页的地址记录在页表中;
线性地址 0x4040_0000 的中间 10 位的索引值是 0,所以登记在页表的第一个表项中;
后面的过程就不再唠叨了,一样一样的~~
最终的页目录和页表的布局,类似下面这张图:
如果理解了上一个主题的内容,那么部分应该就可以不用再看了,因为它俩是相反的过程,而且查找过程更简单一些。
仍然继续我们的假设:
- 用户程序的长度是 20 MB,存放在虚拟内存 0x4000_0000 ~ 0x4140_0000 (线性地址)这段空间内;
- 代码段的长度是 8 MB,从虚拟内存的 0x40C0_0000 处开始存放;
也就是如下图所示:
现在,用户程序的内容已经全部读取到内存中了,页目录、页表全部都安排妥当了。
在页目录表中,一共有 5 个表项,正好表示这20MB
的地址空间。
其中,8 MB 的代码所存储的物理页地址,登记在页目录表中的 259 和 260 这两个表项中(上图右侧的绿色表项)。
目标:处理器在执行代码时,遇到一个线性地址0x4100_8800
,页处理单元需要把它转换得到物理地址。
0x4100_8800 = 0100_0001_0000_0000___1000_1000_0000_0000
首先,根据线性地址的前 10 位(0100_0001_00
),得到它在页目录中的索引值为260
。
这个表项中记录的页表地址为 0x08040
,因为页表地址的低12
位一定是bit0
,因此这个页表的地址就是0x0804_0000
。
页目录表的开始地址,肯定是从 CR3 寄存器获取的;
然后,根据线性地址的中间 10 位(00_0000___1000
),得到页表中的索引值为8
。
这个表项中记录的物理页地址为 0x02004
,补上低位的12
个bit0
,就得到物理页的开始地址是0x0200_4000
。
最后,根据线性地址的最后 12 位(1000_0000_0000
),得到它在物理页的偏移量 2048
。
也就是说:从物理页的开始地址(0x0200_4000
),偏移2048
个字节的地方,就是这个线性地址(0x4100_8080
)对应的物理地址(0x0200_4800
)。
大功告成!
------ End ------
关于虚拟地址到物理地址的转换、页目录和页表的查找过程,基本就讨论结束了。
不知道客官您是否已经酒足饭饱?
下周再写一篇对页目录和页表自身的“元操作”,这个系列文章就基本结束了。
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/F2x6VDBBf9fe6Gzg7GMezA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为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 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。