想象一座老城:最早的房子只有几十间,门牌号两三位数就够用了;后来城市扩建,门牌号变成八位数,地址规则跟着重写了一遍。如今城市彻底翻新,规划师决定推行一套全新的「现代都市门牌系统」——门牌号最长可以有16位数字。为了让这座千万人的大都市不陷入混乱,规划师立了一条铁规矩:不是所有号码都能当门牌,只有符合「门牌规范」的地址才合法。这座现代都市,就是x86_64处理器全力奔跑的「长模式」;那套门牌规范,就是64位寻址里的地址合法性规则。本文将带你逛一逛这座新城市:认识开启新秩序的总控开关、看懂覆盖整条银河的地址地图、学习门牌挂牌套路、了解城市挂号的四级系统,以及让居民「跟着感觉走」的导航技巧。 对AI从业者而言,理解长模式与64位寻址是读懂性能瓶颈、内存故障、云原生部署与编译优化日志的底层基石,也是写出高效AI训练框架代码的必备底层直觉。
需要学习长模式与64位寻址,长模式是x86_64的64位运行模式,通过设置IA32_EFER寄存器的LME位激活。在指令集架构(ISA) > x86/x86_64指令集的学习路径中,长模式与64位寻址直接影响对深层原理的理解。
可视化演示
展示IA32_EFER激活长模式流程、规范化地址空间划分及4级页表翻译过程,支持步骤切换与播放控制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
长模式激活流程 设置CR0.PG=1开启分页,置IA32_EFER.LME=1激活长模式 EFER[8]=LME, CR0[31]=PG LME与PG同时为1时进入IA-32e模式
长模式之门
老城升级现代都市,不能把整座城市推倒重建,城市规划师的做法是:在大厦总控室里装一个「模式总控箱」,拨动里面的开关,整座城市的运行规则就悄悄切换了。x86_64处理器的设计者也是这样想的——他们不希望32位的旧程序一夜之间全作废,于是留了一个兼容的过渡方案,再在某个特殊寄存器里藏一个总开关。这个总开关叫IA32_EFER寄存器(Extended Feature Enable Register,扩展特性使能寄存器),它里面的LME位就是切换到长模式的大闸。
具体来说,长模式的激活不是一拨开关就完成的。它需要两个条件同时成立:LME位被置1,同时CR0控制寄存器的分页使能位PG也被置1。你可以把LME想象成「新城市规则批准书」,把PG想象成「分页供电闸」。只有批准书签了字、电闸也合上了,整座城市才会切换到长模式;如果只有批准书而没有合闸,处理器会稳稳地停在兼容模式,让旧程序继续在小区里生活。
IA32_EFER是一个模型专属寄存器(MSR),需要通过rdmsr和wrmsr两条特权指令来读写。进入长模式后,CPU并不会完全抛弃过去的记忆——它提供了一种「兼容模式」,32位的老程序在长模式城市里依然可以运行,只是它们看到的门牌号仍然按老规则编号。这也是为什么操作系统在启动时能从容地一步步把CPU从实模式带到保护模式,再带到长模式,而途中所有旧代码都不必推倒重写。
回到「总控箱」的比喻:你先把批准书放进抽屉(LME=1),再合上整栋大楼的电闸(PG=1),城市才真正进入新时代。缺了任何一步,旧世界都不会改变。
- LME总闸将IA32_EFER寄存器的LME位写为1,代表CPU「允许」切换长模式,但尚未真正切换。
- PG电闸将CR0寄存器的PG位置1,正式启用分页机制,为虚拟地址到物理地址的翻译供电。
- 双条件成立两个位都为1时,CPU切换到长模式;若仅LME为1而PG为0,CPU停留在兼容模式继续执行32位代码。
地址空间总览
新城市的地图有多大?规划师打开地图软件,拉到底,发现地址编号最长可以有48位二进制数字。这意味着理论上可以编出2的48次方个门牌号——256太字节(TB)的虚拟地址空间。相比之下,32位时代的4GB地址空间就像一张社区小地图,而256TB相当于一张能覆盖整条银河的巨幅星图。
这256TB空间被一条人为的界线劈成两半。低半区从0x0000000000000000到0x00007FFFFFFFFFFF,是「百姓生活区」,普通应用程序的代码、堆、栈都住在这里;高半区从0xFFFF800000000000到0xFFFFFFFFFFFFFFFF,是「行政特区」,操作系统内核、驱动、页表结构等高权限代码驻扎在这里。用户程序与内核各占半壁江山,谁也不干扰谁,这正是现代操作系统稳定性的根基。
为什么偏偏用48位而不是64位全用?因为48位地址配合四级页表结构,已经足以支撑当前所有真实物理内存的映射需求,同时还能让页表规模保持可控。多出来的高16位并不是「废弃」,而是留给未来扩展的预留位。记住这256TB的感觉:你在AI训练时经常看到的OOM(内存不足)报错,本质上就是进程想在这张星图上圈地,却发现行政特区不批地、百姓生活区也不够用了。
这座「银河星图」虽然浩瀚,但并非处处可住人。它被划成百姓生活区与行政特区两片大陆,中间隔着一道深不见底的鸿沟,等待下一节的门牌规范来揭示。
- 指数来源虚拟地址使用48位,所以总数为2的48次方。
- 换算单位2的40次方等于1TB,故2的48次方等于256个TB。
- 对比全空间64位全部使用本应是2的64次方,48位只用了它的65536分之一。
门牌规范canonical
地图上的门牌号虽然可以长达64位,城市规划师却立了一条铁规矩:地址的第48位到第63位(高16位)必须完全等于第47位的值。换句话说,第47位像一个「分水岭」,它右边的16位数字必须忠实复刻它的值——第47位是0,高16位就全是0;第47位是1,高16位就全是1。这种规则在计算机里叫符号扩展(sign extension),符合这个规则的地址叫canonical地址。
满足canonical规则的地址自然分成两段:低半区从0x0000000000000000到0x00007FFFFFFFFFFF,高半区从0xFFFF800000000000到0xFFFFFFFFFFFFFFFF。中间那段从0x0000800000000000到0xFFFF7FFFFFFFFFFF的巨型空间,是永远不能挂牌的「无人区」。如果程序胆敢访问这个区间里的地址,处理器会当场翻脸——抛出通用保护异常(#GP),把进程一脚踢出城市。
为什么要这么麻烦?因为48位地址通过符号扩展补齐到64位后,CPU在做地址比较、大小判断时可以直接当作有符号数处理,无需额外判断;同时也能把无效地址留作天然的「陷阱区域」,让野指针、空指针越界等问题更早暴露。你可以把高16位想象成门牌上的「城市前缀码」:要么全零代表市民区,要么全一代表行政区,出现「3721」这样混合的乱码前缀,警察(CPU异常机制)立刻拦下。
「城市前缀码」规则让整张地图只剩两片可住人的大陆。任何试图把门牌号编到中间无人区的程序,都会在第一时间被警察拦下——这正是64位寻址世界的第一道安全护栏。
- 取最高位取出48位地址的最高位,即第47位,作为整个地址的「符号」。
- 扩展高16位把第47位的值复制到第48到63位。第47位为0时高16位为全0,为1时高16位为全1。
- 判断合法性如果高16位恰好等于符号扩展的结果,地址合法;否则视为非canonical地址,访问时触发#GP异常。
四级页表寻址
新城市的地址翻译系统不再像32位那样用平坦的段式翻译,而是采用一套「四级挂号制度」。想象你要找一个市民的住址:先查他属于哪个大区(PML4),再查大区下哪个街道(PDPT),然后查街道里哪栋楼(PD),接着查楼里哪个房间(PT),最后确认房间里的具体床位(页内偏移)。这一层层查下来,任何虚拟地址都能被精确翻译成物理内存里的真实位置。
一个48位的线性地址被平均切成五段:前四段各9位,最后一段12位。9位二进制能表示512个编号,所以每一级目录都有512个表项;12位能表示4096种偏移,正好对应一个4KB页框内的所有字节。这种「512×512×512×512×4096」的嵌套结构,就是x86_64长模式下的四级页表(4-level paging)。它的好处是:页表按需建立,绝大多数进程根本用不到全部表项,只有用到的地址段才会创建对应的目录条目。
CPU拿这些页表项做地址翻译时,会配合TLB(转址旁路缓存)来加速——TLB就像城市交通部门的「热门地址速查册」,最近常查的门牌直接翻册子秒回,不必每次都层层挂号。这也是为什么操作系统在切换进程、刷新CR3时总伴随着TLB flush,因为每个进程的挂号册内容不同,必须清空旧缓存。
四级挂号制度让虚拟地址的翻译像查户籍一样有条不紊。每一级的目录项都像档案盒上的标签,逐层剥开,最终落到物理内存的真实房间——这一整套链条,是长模式一切地址稳定的地基。
- PML4索引最高9位(第39到47位)用来索引顶级页目录PML4,找到对应大区表项。
- PDPT索引接下来9位(第30到38位)索引页目录指针表,找到街道级表项。
- PD索引再下9位(第21到29位)索引页目录,定位楼栋级表项。
- PT索引随后9位(第12到20位)索引页表,定位房间级表项。
- 页内偏移最低12位(第0到11位)直接作为物理页框内的字节偏移,找到床位。
RIP相对寻址
新城市里有一种很聪明的「导航指令」:它不告诉你目的地的绝对地址,而是告诉你「从你当前位置往前走多少步就到」。比如一条指令说「从下一条指令的位置再往前走300个字节」,CPU随时都能算出目标。这种寻址方式叫RIP相对寻址,这里的RIP(Instruction Pointer,指令指针)就是CPU当前执行流的位置指针,而它指向的是下一条指令的地址。
为什么设计者这么喜欢「相对」而不是「绝对」?因为绝对地址就像写死一个门牌号——如果整栋代码被搬到城市另一个街区,门牌号就全错了,必须重新雕刻。而RIP相对寻址天然免疫「搬家」:无论代码被加载到内存的任何位置,只要指令与目标数据之间的距离不变,计算结果就自动正确。这种特性叫位置无关代码(Position-Independent Code,PIC),动态共享库(.so文件)、可执行程序都因此能轻松地在内存中任意摆放。
实际编码时,RIP相对寻址的位移量是一个32位有符号立即数,CPU将它符号扩展后与RIP相加。这意味着它可以在当前位置前后约正负2GB的范围内跳转——绝大多数函数调用、全局变量访问都绰绰有余。编译器在生成位置无关代码时,会把对全局变量、函数的访问统统改写为「RIP + 偏移」的形式,彻底摆脱对固定加载地址的依赖。
「导航式」寻址让代码像候鸟一样,无论飞到哪片内存都能精准找到食物源。位置无关代码正是靠这一招,让共享库在不同进程地址空间里「一次编译、处处运行」。
- 取出RIPCPU读取当前指令的RIP值,它指向下一条待执行指令。
- 符号扩展偏移将指令中的32位立即数按符号扩展成64位,保证正负方向正确。
- 执行加法两者相加即得到目标地址,CPU据此完成跳转或内存访问。
模式切换流程
从老城切换到现代都市,可不是按一个按钮就完事,而要遵循一套严格的「搬迁流程」。规划师的步骤表上写着四步:先在总控箱里签下LME批准书(设置EFER.LME为1);再开启物理地址扩展的PAE开关(CR4.PAE置1),因为长模式的分页表项是8字节的大表项,没有PAE打底,页表结构就无法工作;接着把新城市的「户籍总册」地址装进CR3寄存器,也就是PML4页表的基地址;最后合上PG电闸,长模式正式运转。
这个顺序不能乱。如果先合PG电闸再设置LME,CPU会在老模式下直接启用分页,反而导致混乱;而如果LME设了、PAE却没开,长模式无法被激活,因为长模式强制依赖PAE来支持8字节页表项。CR3的装载也必须发生在开PG之前,就像搬家前必须先把新户籍总册放进控制室,否则警察一上岗就找不到户籍本。
搬迁完成后,CPU中的段寄存器角色被大大削弱:在64位模式下,段基址被强制视为0,分段几乎退出了历史舞台,所有地址翻译都交给分页。32位的旧程序如果想继续运行,CPU会为它们切到兼容模式,用老一套规则处理——一城两制,互不干扰。
这套「先签批、再亮灯、后入库、终合闸」的搬迁流程,像极了城市改造的验收清单。操作系统每次启动,都是严格按这张清单把CPU从尘土飞扬的工地领进光鲜亮丽的现代都市。
- 签批准书设置EFER.LME为1,告知CPU准备切换到长模式。
- 开PAE灯设置CR4.PAE为1,确保页表项使用8字节宽格式,这是长模式的硬性前提。
- 装户籍册将PML4页表的内存基地址写入CR3,让CPU知道去哪里查地址映射。
- 合PG电闸设置CR0.PG为1,正式启用分页,CPU跃入长模式。
长模式新规则
入住新城市后,居民们发现交通规则也变了。32位时代,一辆货车一次最多拉32位数据;64位时代,货车理论上可以拉64位,但城市规定默认还是用32位小车跑——除非你给车挂上一个「加宽通行证」REX前缀。REX是长模式新增的指令前缀字节,其中最高位的W字段(REX.W)如果为1,指令的操作数宽度就从默认的32位升格为64位。
除了REX前缀,长模式还带来了八个全新的通用寄存器:R8到R15,配合原有的RAX、RBX等寄存器,通用寄存器总数从8个翻到16个,很多函数调用不再需要频繁访问内存栈,而可以直接把参数塞进寄存器,性能大幅提升。与此同时,指令指针也升级成RIP,能够进行64位的寻址运算。
这些新规则让x86_64焕然一新:更多的寄存器意味着编译器可以更好地安排变量,REX前缀让指令既能兼容旧代码的32位宽度,又能在需要时优雅地扩展到64位。你在反汇编AI推理内核时看到的那些mov、add指令,一旦前面缀上「48」这个前缀字节,就是在告诉CPU:这次请用64位宽度的货车来拉数据。
「加宽通行证」让同一辆车既能跑小区窄路,也能上高速宽道。正是这种灵活设计,让x86_64在不抛弃亿万存量软件的前提下,悄然驶入了64位的新纪元。
- 默认宽度绝大多数指令不带REX.W时,操作数按32位处理,与旧代码兼容。
- 加宽触发指令前出现REX前缀且W位为1时,操作数宽度切换到64位。
- REX前缀编码REX前缀占一个字节,位于指令最前面,其中第3位即W位。
AI 应用场景
内核地址布局与KASLR
阶段:模型训练与推理环境搭建时的系统调优
解决的问题:AI框架(如PyTorch/TensorFlow)运行时需要分配大块CPU内存。在高负载场景下,内存分配失败往往源于进程虚拟地址空间碎片化。理解低半区(用户空间)与高半区(内核空间)的划分后,运维人员能快速从地址范围判断OOM发生在用户态还是内核态,还能理解KASLR(内核地址空间布局随机化)为何要把内核随机摆在高半区——即便攻击者猜到用户态地址,也难以定位内核基址。
具体案例:某AI训练集群频繁出现「cannot allocate memory」报错,工程师通过查看/proc/PID/maps发现进程堆与共享库都挤在低半区0x00007f区域,而大量线程栈也堆积在该区间,导致低半区碎片化。通过调整线程栈大小和共享库加载顺序,OOM问题显著缓解——这背后全靠对48位虚拟地址空间布局的理解。
编译器生成位置无关代码
阶段:AI框架的C++扩展模块编译与部署
解决的问题:AI推理框架(如ONNX Runtime)发布时通常以共享库(.so)形式分发。如果代码使用绝对寻址,每次加载到不同虚拟地址都要重定位,启动慢且无法实现代码页共享。借助RIP相对寻址生成位置无关代码,同一份.so可以被十多个进程安全映射到各自不同的地址,极大节省物理内存,加速模型容器冷启动。
具体案例:某AI推理服务在Kubernetes集群上每小时启动数百个Pod,每个Pod加载约500MB的推理库。改用PIC编译并启用-fPIC优化后,同一节点上多Pod的相同代码页可以共享同一物理页帧,内存占用下降40%,启动时间缩短近一半。
虚拟化与容器地址隔离
阶段:云上GPU训练集群的资源隔离与调度
解决的问题:每个虚拟机或容器都需要独立的虚拟地址空间。64位寻址的256TB地址空间让云平台可以轻松为每个租户划分大片互不重叠的地址区间,配合四级页表按需建立映射,内存超卖变得安全可控。
具体案例:某云厂商的AI训练平台为每个训练容器分配独立的虚拟地址空间,进程访问高半区内核地址会被硬件以#GP异常拦下,有效防止容器间越权访问。同时借助canonical地址规则,平台能快速校验指针合法性,拦截了大量因模型代码越界写入导致的崩溃。
Python 演示
class CanonicalAddrChecker: def __init__(self): self.canonic_ok_count = 0 self.reject_count = 0 def is_canonical(self, addr): """检查64位地址是否符合canonical规范。""" sign_bit_47 = (addr >> 47) & 1 high_bits = (addr >> 48) & 0xFFFF if sign_bit_47 == 0 and high_bits == 0: self.canonic_ok_count += 1 return True if sign_bit_47 == 1 and high_bits == 0xFFFF: self.canonic_ok_count += 1 return True self.reject_count += 1 return False def page_split(self, linear): """把一个48位线性地址拆成四级页表索引加偏移。""" l = linear & ((1 << 48) - 1) return { 'PML4': (l >> 39) & 0x1FF, 'PDPT': (l >> 30) & 0x1FF, 'PD': (l >> 21) & 0x1FF, 'PT': (l >> 12) & 0x1FF, 'offset': l & 0xFFF, } def report(self): print("合法地址数量:", self.canonic_ok_count) print("非法地址数量:", self.reject_count) def demo(): checker = CanonicalAddrChecker() samples = [ 0x0000000000000000, 0x00007FFFFFFFFFFF, 0x0000800000000000, 0xFFFF800000000000, 0xFFFFFFFFFFFFFFFF, ] for addr in samples: ok = checker.is_canonical(addr) print(hex(addr), "->", "canonical" if ok else "非canonical") print("--- 拆分一个线性地址 0x123456789ABC ---") parts = checker.page_split(0x123456789ABC) for name, val in parts.items(): print(name, "=", val) checker.report() if __name__ == "__main__": demo()
sign_bit_47 = (addr >> 47) & 1取出第47位作为符号位,这是canonical判断的核心依据。high_bits = (addr >> 48) & 0xFFFF取出高16位,与第47位的符号扩展结果比对。if sign_bit_47 == 0 and high_bits == 0第47位为0时,高16位必须全0才合法。if sign_bit_47 == 1 and high_bits == 0xFFFF第47位为1时,高16位必须全1才合法。l = linear & ((1 << 48) - 1)先掩掉48位以上部分,确保只处理有效的线性地址。'PML4': (l >> 39) & 0x1FF取第39到47位,恰好9位,作为PML4级索引。'offset': l & 0xFFF取低12位作为页内偏移,对应一个4KB页内的字节位置。checker.report()汇总合法与非法地址统计,验证校验逻辑的执行情况。if __name__ == "__main__":确保脚本被直接运行时才执行demo,便于作为模块导入。
常见误区
误区我一直以为往IA32_EFER寄存器的LME位写1,CPU下一秒就在跑64位代码了。资料上说「设置LME开启长模式」,那不就是一瞬间的事吗?
正解LME只是「批准书」,真正的切换还需要CR4.PAE置1、CR3装载页表基址、CR0.PG置1这三步配合。只有PG位也置1,处理器才真正切换到长模式。硬件的模式切换像一场接力赛,LME是第一棒,但最后一棒由PG完成。
为何常见:很多入门教程为了简化,只说「置LME位」,省略了后面三个步骤,导致初学者误以为一个开关搞定一切。
误区48位地址空间那么大,高16位随便用应该也没事吧?canonical规则听起来像是系统给我提的建议,不遵守最多浪费一些地址,不会出大问题。
正解这是硬性规则而非建议。访问非canonical地址时,CPU直接抛出#GP通用保护异常,进程当场被终止。这就像一个城市的门牌规则:你编一个「ABCD-1234」的乱码门牌,快递员不仅不送,还会报警。硬件用异常强制保证地址合法,绝不是可选项。
为何常见:人们容易把「高16位不用」理解成「高16位可自由发挥」,忽略了符号扩展是硬件级校验的刚性约束。
误区反汇编时看到`lea rax, [rip+0x1234]`,我以为是直接把0x1234当作最终地址用。既然目标地址写在指令里,那不就是绝对地址吗?
正解指令里存的是相对偏移,目标地址必须用RIP加上偏移量才算出来。rip+0x1234的含义是「以当前指令位置为参照,向前走0x1234字节」。这正是位置无关代码能实现的关键——如果把固定地址写死在指令里,共享库换个地址加载就全崩了。
为何常见:反汇编工具常直接显示计算后的结果地址,让初学者误以为偏移量就是绝对地址。
误区买了64位CPU,进入了64位模式,一条`mov eax, ebx`以外,我写`mov rax, rbx`就自动是64位了——所有指令的默认宽度应该都是64位吧?
正解长模式下指令默认操作数宽度仍然是32位。想要64位操作,必须依赖REX前缀的W位来「加宽」。mov rax, rbx之所以能工作,是因为编译器自动在机器码前面加了REX前缀。这也保证了32位时代的海量二进制代码在长模式下依然能正确运行。
为何常见:「64位CPU」的宣传让大众以为指令宽度自动翻倍,忽视了x86为了向后兼容而保留的32位默认宽度设计。
误区PAE物理地址扩展不是十几年前为了让32位系统用上大内存才搞的吗?现在都64位长模式了,PAE应该没用了,提都不用提。
正解恰恰相反,长模式强制要求CR4.PAE置1。长模式的分页表项必须使用8字节宽格式,这正是PAE引入的结构。没有PAE,长模式根本无法激活。PAE不是过时产物,而是长模式分页体系的基石。
为何常见:PAE在32位时代的名声太「接地气」,让很多人只记得它是内存补丁,忽略了它在x86_64中的关键角色。
面试题
Q1. 长模式的「总开关」位于哪个寄存器?
- A.CR0 控制寄存器
- B.IA32_EFER MSR← 正确答案
- C.CR3 页表基址寄存器
- D.GDTR 全局描述符
答案:B。IA32_EFER寄存器(MSR)是长模式的「总控箱」,它的LME位就是总开关。CR0负责PG分页使能,CR3负责装载页表基址,GDTR负责全局描述符表,它们都不是长模式开关的所在位置。
Q2. 长模式下虚拟地址实际使用多少位?
- A.使用32位地址
- B.使用48位地址← 正确答案
- C.使用64位地址
- D.使用52位地址
答案:B。x86_64长模式使用48位虚拟地址,总空间为256TB。64位是寄存器和指针的宽度,但地址线有效位只有48位。52位是某些物理地址的可用位数,不是虚拟地址位数。
Q3. canonical地址要求第63到48位满足什么条件?
- A.全部固定为零值
- B.全部固定为一值
- C.等于第47位的值← 正确答案
- D.等于第46位的值
答案:C。canonical规则要求高16位等于第47位的符号扩展——第47位是0则全0,是1则全1。这与「全部为零」「全部为一」都不同,因为第47位本身可变化。第46位与规则无关。
Q4. 访问非canonical地址会引发什么异常?
- A.缺页异常PF
- B.通用保护异常GP← 正确答案
- C.对齐检查异常AC
- D.设备不可用异常NM
答案:B。直击非canonical地址会触发#GP(通用保护异常)。缺页异常发生在地址合法但页表未映射时;对齐检查与设备不可用异常各有各的触发场景,都与canonical校验无关。
Q5. RIP相对寻址的目标地址如何计算?
- A.当前RIP加位移量← 正确答案
- B.基址寄存器加位移
- C.立即数本身作地址
- D.栈顶保存的目标地址
答案:A。RIP相对寻址公式是「目标地址 = RIP + 符号扩展偏移量」。RIP指向下一条指令,偏移量由指令携带。基址寄存器寻址依靠其他寄存器,立即数作地址是绝对寻址,栈顶保存的是返回地址,均不符合RIP相对寻址的定义。
Q6. 进入长模式前必须使能哪个扩展特性?
- A.PAE地址扩展← 正确答案
- B.SEP系统调用
- C.SSE流式扩展
- D.AVX向量扩展
答案:A。长模式强制要求CR4.PAE=1,因为长模式使用8字节页表项,这正是PAE引入的结构。SEP、SSE、AVX都是处理器特性扩展,与长模式分页的基础要求无关。
Q7. 高半区有效地址的起始值是多少?
- A.0xFFFF800000000000← 正确答案
- B.0xFFFFFFFFFFFFFFFF
- C.0x00007FFFFFFFFFFF
- D.0x0000800000000000
答案:A。高半区从0xFFFF800000000000开始到0xFFFFFFFFFFFFFFFF结束,第47位为1且高16位全1。0xFFFFFFFFFFFFFFFF是高半区终点;0x00007FFFFFFFFFFF是低半区终点;0x0000800000000000是非canonical无效区间的起点。
Q8. 要使长模式真正激活,需要满足哪些条件?
- A.EFER.LME位写1← 正确答案
- B.CR0.PG分页使能← 正确答案
- C.CR3指向PML4基址← 正确答案
- D.CR4.PAE置1← 正确答案
答案:ABCD。长模式激活是四步接力:LME批准书、PAE前置灯、CR3户籍册、PG合闸。四个条件缺一不可。LME只表示「允许」,PG才真正「切换」,PAE支撑8字节页表项,CR3提供页表根。这正是模式切换流程一节的核心内容。
Q9. 关于canonical地址,下列说法正确的是?
- A.高16位等于第47位← 正确答案
- B.地址空间分为两个区间← 正确答案
- C.48位地址空间共256TB← 正确答案
- D.非canonical可正常访问
答案:ABC。canonical规则要求高16位等于第47位的符号扩展,合法地址因此分成低半区和高半区两段,总容量为2^48即256TB。非canonical地址一旦访问就触发#GP异常,绝不可能「正常访问」,所以D错误。
Q10. RIP相对寻址的优势有哪些?
- A.支持位置无关代码← 正确答案
- B.避免绝对重定位← 正确答案
- C.天然支持共享库← 正确答案
- D.显著扩大寻址空间
答案:ABC。RIP相对寻址用「当前位置+偏移」,所以代码搬到任何地址都能正确工作,即位置无关代码,共享库因此无需重定位就能加载。扩大寻址空间是长模式48位地址的功劳,与RIP相对寻址无关。