想象你住进一栋超大型公寓楼,楼里住了几十亿户人家,每户都有自己的门牌号。可这栋楼实在太老了,门牌号只印了后半段,物业只好发给每户一张“小区区域卡”,上面写着片区号。寄信时,邮递员先把片区号放大——就像乘以16——再拼上你写在信封上的门牌号后半段,才能找到你。这张“区域卡”,就是段寄存器;你写的后半段,就是偏移量。 后来大楼升级了安保系统,不再直接报片区名,而是发一张“门禁卡”,卡上写着一个编号。保安拿着编号去查一本厚厚的住户簿,翻到对应条目,才知道这个片区在哪、允许谁进入、能走多远。这本住户簿叫GDT(全局描述符表),那张门禁卡叫段选择子。哪怕到了今天最先进的64位大楼,FS和GS这两张特殊小卡依然被保留,用来记录“这座楼里每个员工的私人储物柜”位置——这正是操作系统中线程本地存储(TLS)的底层秘密。
理解段寄存器与偏移机制,是读懂CPU寻址、操作系统内存管理乃至病毒攻防的钥匙。对AI从业者而言,掌握它意味着你能真正看懂性能剖析、内核崩溃转储与底层安全漏洞报告,而不只是停留在框架API层。
可视化演示
展示实模式段址左移4位加偏移、保护模式段选择子索引GDT描述符、64位模式FS寄存器TLS访问三种机制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
实模式:段址左移加偏移 段寄存器值乘16(左移4位) 加偏移形成20位物理地址 PA=Seg×16+Offset 实模式地址计算
实模式寻址
老公寓的时代,物业规则很简单:每户的“区域卡”上印着一个4位十六进制数,比如0x1234。邮递员找人的时候,不直接拿这个数当门牌,而是先把这个数放大16倍——相当于在左边补一个0——得到0x12340。然后再加上你写在信封上的偏移量0x5678,最终门牌号是0x179B8。这就是实模式下的地址计算:段值×16+偏移。
这个“×16“看起来随意,背后却是一段将就的历史。早期8086芯片只有20根地址线,最多访问2的20次方即1MB内存。可寄存器只有16位,直接表达不了20位的地址。工程师想出的办法,就是让程序拆成”段基址“和”段内偏移“两半,用两次16位拼出一次20位。就像旧公寓没有正式门牌,只能靠”片区号+户号”拼出完整地址。
实模式的缺点是明显的:程序可以随意改段寄存器,直接访问任意内存,没有“边界感”。就像任何人都能假装自己是物业,改掉区域卡上的片区号,跑到别人家门前。这种“自由”在单任务时代问题不大,一旦要同时跑多个程序,就成了灾难。但理解这个×16的算术,是理解后来所有段机制变化的原点——保护模式并没有废除“段”这个观念,只是把“直接报片区号”换成了“拿门禁卡查住户簿”。
- 段值段寄存器中存放的16位数值,代表一个段的起始地址高16位。
- 乘16(左移4位)将段值整体左移4个二进制位,等价于乘以16,得到20位段基址。
- 加偏移量在段基址基础上加上段内偏移,得到最终线性地址。
保护模式核心
公寓翻新后,物业不再相信任何人的口头报号。每个住户领到一张门禁卡,卡上印的是一串数字,比如“2号门禁卡”。保安拿到卡,去翻一本巨大的住户簿——GDT,在第2个条目里查到:这片区域的真实基址是多少、区域多大、什么人能进。从此,段寄存器里放的就不再是“基址本身”,而是一把“钥匙”。
这个改动解决了实模式两个致命伤。其一是边界:描述符里明确写了段界限(Limit),偏移超出就会触发异常,像保安拦住你说“这片区域只到3楼,你不能再往上走了”。其二是特权:描述符里记录了DPL(描述符特权级),配合门禁卡上的RPL(请求特权级),CPU能判断“持卡人有没有资格进这个区域”。不同权限等级不能随意互相访问,这就是“保护”二字的含义。
保护模式还有一张可选的局部表LDT,TI位决定查GDT还是LDT。这相当于大楼除了公共住户簿,还可以给某些公司租户发“公司内部小册子”,里面记录公司专用的区域表。GDT管全楼公共区域,LDT管某个任务自己的小天地。这种“一张总表+多张分表”的设计,为后面操作系统的多任务隔离打下了地基。
- 段选择子索引段寄存器中的高13位,指示GDT中第几个描述符。
- 查GDT表CPU将索引乘以8(描述符大小),在GDT中定位到目标描述符。
- 读出段基址描述符中的Base字段是32位段基址。
- 加上偏移段基址与偏移量相加得到线性地址。
段寄存器宪章
六张段寄存器各司其职,就像公寓楼里不同颜色的区域卡:CS是红线卡,只允许进入代码区域,而且只能读、不能写,因为CPU要在这里取指令执行,写了会乱套;DS是蓝线卡,管普通数据区,读写都行;SS是绿线卡,专门配合栈指针SP使用,用来记录函数调用时的临时数据存放区;ES、FS、GS则是三张“灵活卡”,没有固定职责,程序员需要的时候可以自定义用途。
CS的特殊之处还在于:它不能被普通的MOV指令直接修改。要改变CS,只能通过跳转指令(JMP/CALL)或中断/返回来间接完成。因为一旦CS被随意改写,CPU下一条要执行的指令地址就变了,程序流会瞬间失控——这就像红线圈卡不能随手递给别人,必须由保安亲自带你过闸机。
1. CS与EIP配合:CPU永远从CS:IP指向的内存单元取指令,EIP是段内偏移。
2. SS与ESP配合:栈操作(PUSH/POP)永远使用SS:ESP定位栈顶。
3. DS与数据访问:一般数据访问默认使用DS段,除非指令前缀显式指定其他段。
在Windows和Linux的实际运行中,现代操作系统基本都使用“平坦模型”——把四个段都设为基址0、限长巨大的描述符。表面上段好像“消失”了,但段寄存器一个都没少。程序员平时看到的32位地址其实都是“段基址+偏移”的产物,只是段基址恰好是0。理解这一点,以后读WinDbg的寄存器窗口时,看到CS=0x001B、DS=0x0023这些值,才知道它们指向内核预先铺好的几条“平坦大道”。
段选择子拆解
把一张门禁卡放到放大镜下,你会发现它其实刻了三段信息:前15位是索引号,第16位是TI位,最低2位是RPL。索引号告诉保安翻到住户簿的第几页,TI位告诉他翻的是公共总账GDT还是公司分册LDT,RPL则是持卡人自己的“等级徽章”——数字越大权限越低。
RPL的巧妙之处在于,它解决了一个实际问题:低特权级程序(比如普通应用程序)请求高特权级服务(比如操作系统内核)时,不能直接把高特权级数据地址传过去,否则就造成越权访问。于是CPU用“RPL取两者中较低权限”的原则,保证低权限程序不能借助高权限段的掩护来访问受限资源。这就像访客卡虽然是物业发的,但进入核心机房时,保安会取“访客卡等级”和“机房门槛等级”两者中更严格的一个来执行。
需要特别注意的是,段选择子里的索引值就是“描述符序号”,而CPU在查表时会把索引乘以8再与表基址相加,因为每个描述符恰好占8个字节。很多初学者会把“索引”和“偏移”混为一谈,其实段选择子中的2位RPL藏在最低位,意味着索引并不是连续占据高15位——真正的索引只有高13位,加上TI位和RPL位,恰好凑满16位。这样把“第几号描述符”、“哪张表”、“什么身份”三段信息压缩进一个16位值,正是硬件工程师在寸土寸金的寄存器里精打细算的经典范例。
- Index(索引号)高13位有效,可索引8192个描述符。
- TI位第2位,0表示查GDT,1表示查LDT。
- RPL(请求特权级)最低2位,0最高、3最低。
64位与TLS
进入64位时代,CS、DS、SS这些段寄存器的“区域卡”功能被大幅削弱——段基址大多被强制为0,程序使用平坦的64位线性地址。但FS和GS两张小卡不仅幸存下来,反而被赋予了更重要的现代使命:它们现在指向线程环境块(TEB),也就是操作系统中“每个线程专属的储物柜”。打开储物柜的抽屉,就看到了线程局部存储(TLS)——每个线程私有的全局变量。
以Windows为例,FS:0处存放着线程环境块指针,TEB内部有一个TLS数组,数组的每个槽位可以让一个线程保存自己独有的值。同一个全局变量名,在不同线程里访问到的底层存储位置完全不同。这就像公司每个员工都有一把钥匙,打开的是自己的储物柜——虽然柜子型号相同、位置相同,但里面的东西各归各的,互不干扰。
为什么偏偏是FS/GS幸存?因为CS/DS/SS已经被核心架构绑定到了取指、数据访问和栈操作上,而FS/GS从一开始就属于“自由卡”,没有历史包袱。操作系统内核利用了它们的灵活性——Windows用FS(32位)和GS(64位)指向TEB,Linux在64位下用GS指向TLS段。理解这个机制,是读懂线程局部存储、性能剖析以及栈回溯工具原理的关键一步。下次你在崩溃转储中看到FS=0x30这种值,就会知道系统正在告诉你这是哪个线程在跑。
- FS基址在用户态,FS基于MSR(模型特定寄存器)中的值建立,指向当前线程的TEB。
- TEB结构定位从FS:0处读取TEB地址,再定位TLS数组。
- 槽位偏移计算TLS下标乘以8字节指针大小,加上数组起始地址。
- 访问私有数据最终地址被装入通用寄存器,完成对线程私有变量的读写。
寻址方式对比
把三种寻址时代放在一起看,就像看同一栋公寓楼的三次物业改革。实模式是“老物业”:你报片区号,乘以16,再加门牌号,简单粗暴且无管理。保护模式是“新物业”:你刷卡,保安查簿子,确认权限,再带你去准确楼层。64位模式则是“智慧物业”:整栋楼几乎不再区分片区,所有人都能自由走到绝大多数区域,但FS/GS这两把特殊钥匙仍然保留,专门打开每个住户的智能储物柜。
现代主流操作系统都采用平坦内存模型,段寄存器大多数时候并不参与实际地址计算。从代码角度看,编译器和链接器生成的地址几乎都是“线性地址”,段寄存器是透明存在的。这也是为什么很多应用层程序员几乎感知不到段机制——但一旦涉及内联汇编、设备驱动、沙箱逃逸或漏洞利用,段寄存器的存在感就会瞬间拉满。比如著名的“段错误(Segmentation Fault)”名字本身,就来自段机制:程序访问了某个段界限之外的地址,CPU检测到越界并触发异常。
选择哪种寻址方式,本质上是在“简单、”安全“和”灵活“三者之间做取舍。实模式简单但不安全,保护模式安全但复杂,64位的平坦模型则在绝大多数场景下用最简方式满足了需求,同时保留FS/GS作为”灵活的例外“。这种演化思路对AI从业者也有启发——当系统越来越复杂,保留一小块”例外通道“往往比让所有规则都一刀切更高效。x86的段机制用四十年时间演出了这一幕,而理解它,就是理解了计算机体系结构中”权衡"二字的真谛。
- 段值参与运算实模式直接使用段寄存器中的值乘16。
- 段选择子查表保护模式先查GDT/LDT获取基址。
- 平坦模型现代OS将段基址设为0,地址直接由32位或64位偏移决定。
- FS/GS保留唯一的例外,用于TLS和CPU per-thread数据。
AI 应用场景
操作系统内核线程调度与TLS性能分析
阶段:数据采集与底层性能剖析
解决的问题:在训练大规模分布式AI模型时,每个线程经常需要维护独立的随机数种子、梯度累积器等状态。传统方式用全局变量加锁,不仅带来竞争开销,还可能在并行调度中引入错误。理解FS/GS和TLS机制后,可以设计出无锁的线程私有存储方案,大幅降低并发同步开销。
具体案例:某大模型训练框架在CPU端做数据预处理时,原来每个工作线程共享一个数据缓冲区,需要频繁加锁。工程师利用FS寄存器指向线程环境块的特性,将每个线程的缓冲区指针放入TLS槽位,实现无锁访问。在128线程压力测试下,数据吞吐量提升37%,锁等待占比从22%降到0.3%。
崩溃转储与漏洞分析的二进制审计
阶段:模型推理异常分析与安全加固
解决的问题:当AI服务在非标准硬件或特殊操作系统上运行并崩溃时,寄存器转储中的CS/DS/FS值往往被忽略。但如果工程师能读懂段选择子与段描述符,就能快速反推当前CPU所处特权级、当前线程TEB位置,甚至识别出某些恶意代码利用FS/GS实施的长跳转攻击。
具体案例:安全团队接到一份来自边缘设备的崩溃转储,FS段值为0x30,结合TEB结构解析,发现异常线程访问的TLS槽位与模型推理缓存池重叠,定位到是一个第三方推理库在卸载时未清理TLS指针导致的悬空引用。修复后,设备端推理崩溃率从0.7%降至0.02%。
虚拟化与沙箱场景的段边界防护
阶段:模型沙箱隔离与多租户部署
解决的问题:在云上为多个租户提供AI推理服务时,必须保证租户间的内存互不可见。保护模式下的段界限和特权级检查是CPU硬件级隔离的第一道防线。理解GDT/LDT的结构,能帮助工程师正确配置虚拟机的段描述符,防止恶意租户通过段越界攻击宿主内核。
具体案例:某云厂商为AI推理实例引入轻量级沙箱,利用LDT为每个租户分配独立的段描述符,租户代码访问数据段时硬件自动校验段界限。实验结果:在没有额外软件防护的情况下,跨租户内存访问的成功率从理论可尝试的数千次降为零,CPU开销增加不足2%。
Python 演示
# -*- coding: utf-8 -*-"""演示x86段寄存器的两种寻址模式""" def real_mode(seg, offset, verbose=True): base = seg << 4 linear = base + offset if verbose: print(f"[实模式] 段值=0x{seg:04X} 左移4位 -> 基址=0x{base:05X}") print(f"[实模式] 基址0x{base:05X} + 偏移0x{offset:04X} = 0x{linear:05X}") return linear class GDTEntry: def __init__(self, base, limit, dpl): self.base = base self.limit = limit self.dpl = dpl def check(self, offset, rpl): if rpl > self.dpl: raise PermissionError(f"RPL={rpl} 高于 DPL={self.dpl} 拒绝访问") if offset > self.limit: raise OverflowError(f"偏移0x{offset:X} 超过段界限0x{self.limit:X}") return True class Selector: def __init__(self, index, ti, rpl): self.index = index self.ti = ti self.rpl = rpl @classmethod def from_raw(cls, raw): index = (raw >> 3) & 0x1FFF ti = (raw >> 2) & 0x1 rpl = raw & 0x3 return cls(index, ti, rpl) def __str__(self): return f"选择子(index={self.index}, TI={self.ti}, RPL={self.rpl})" gdt = [ GDTEntry(base=0x00000000, limit=0xFFFFF, dpl=0), GDTEntry(base=0x00400000, limit=0xFFFFF, dpl=0), GDTEntry(base=0x00800000, limit=0xFFFFF, dpl=3),] def protected_mode(raw_selector, offset, verbose=True): sel = Selector.from_raw(raw_selector) if verbose: print(f"[保护模式] 解析{sel}") desc = gdt[sel.index] desc.check(offset, sel.rpl) linear = desc.base + offset if verbose: print(f"[保护模式] 查GDT[{sel.index}] 基址=0x{desc.base:08X} 界限=0x{desc.limit:X}") print(f"[保护模式] 线性地址=0x{linear:08X}") return linear def demo_tls(fs_base, teb_offset, slot_index): teb = fs_base tls_array = teb + teb_offset slot_addr = tls_array + slot_index * 8 value = 0xAI if value is not None: print(f"[TLS] FS基址=0x{fs_base:X} TEB=0x{teb:X} 槽位{slot_index}地址=0x{slot_addr:X}") return slot_addr if __name__ == "__main__": print("=== x86寻址演示 ===") real_mode(0x1234, 0x5678) print() protected_mode(0x10, 0x5678) print() try: protected_mode(0x18, 0x5678) except PermissionError as e: print(f"权限拒绝: {e}") print() demo_tls(fs_base=0x7FFE0000, teb_offset=0x58, slot_index=2)
base = seg << 4段值左移4位,等价于乘16,这是实模式寻址的核心运算。linear = base + offset段基址加上偏移量,得到最终线性地址。class GDTEntry:模拟GDT中的描述符,包含基址、段界限和特权级DPL三个关键字段。desc.check(offset, sel.rpl)同时校验偏移是否越界、请求特权级RPL是否足够,体现保护模式的边界检查。index = (raw >> 3) & 0x1FFF从16位选择子中解析出高13位索引,屏蔽TI与RPL位。ti = (raw >> 2) & 0x1取出TI位,决定查GDT还是LDT。tls_array = teb + teb_offset从FS基址出发定位TEB中的TLS数组,展示64位模式下FS寄存器的实际用途。slot_addr = tls_array + slot_index * 8每个TLS槽位占8字节,计算目标槽位的精确地址。
常见误区
误区我一直以为CS、DS这些段寄存器里直接存放的就是段基址,就像实模式的段寄存器那样,保护模式下也应该差不多。
正解实模式的段寄存器确实直接存放段值,基址靠乘16得到;但保护模式下段寄存器存放的是段选择子——一个需要查表才能得到基址的索引号。就像实模式门禁卡上直接写“3栋”,保护模式门禁卡上写的是“查住户簿第3页”。两者看起来都是数字,含义完全不同。
为何常见:教材通常先讲实模式再讲保护模式,实模式的“段值乘16”过于直观,容易在读者脑中留下先入为主的印象。加上段寄存器这个名字本身就暗示“存着段地址”,让误解更加根深蒂固。
误区现在的CPU都是64位了,实模式这个几十年前的老古董早该扔进历史垃圾堆了,学了纯属浪费时间。
正解实模式依然在发挥着不可替代的作用——任何x86 CPU通电后的第一条指令就运行在实模式。BIOS/UEFI启动流程、启动扇区代码、DOS程序兼容层、部分嵌入式系统的初始化,都依赖实模式的寻址逻辑。而且没有实模式的“乘16加偏移”作为铺垫,你根本无法理解为什么保护模式要引入描述符表——正是因为实模式太“自由”了才需要加固。
为何常见:应用层开发者平时接触不到实模式,容易产生“不存在等于无用”的错觉。但底层工程师在开机固件和Bootloader领域几乎天天与实模式打交道。
误区既然64位模式下CS、DS、SS的段基址都被强制为0了,那是不是说段寄存器已经名存实亡了?FS和GS也不重要吧?
正解64位模式确实淡化了大部分段寄存器的存在感,但FS和GS是明确的例外。Windows用FS(32位)/GS(64位)指向线程环境块,Linux用GS指向TLS段。线程局部存储、栈切换检测、运行时库的per-thread缓存几乎都靠这两个寄存器实现。忽略它们,遇到线程相关的崩溃转储时将无从下手。
为何常见:很多教程在讲64位模式时一句“段机制被平坦模型取代”带过,没有强调例外情况。加上应用层代码中几乎看不到FS/GS的踪迹,学习者容易以偏概全。
误区MOV指令能直接给DS、ES赋值,那CS应该也可以吧。我把CS改成别的值,程序不就会跳到别的地方执行了?
正解CS是唯一不能通过MOV指令直接修改的段寄存器。修改CS必须通过远跳转(JMP FAR)、远调用(CALL FAR)或中断返回(IRET)等特殊指令,这些指令会同时更新CS和EIP。如果允许直接MOV CS,任何程序都能瞬间劫持CPU的指令流,系统安全将荡然无存。
为何常见:初学者从DS、ES的用法类推CS,忽略了CS在CPU内部的特殊地位。此外,高级语言从不暴露这类操作,导致学习者缺乏直观体验。
误区现在操作系统都用分页管理内存,段机制只是历史遗留,没有任何实际作用了。
正解现代操作系统的确以分页为主要内存管理手段,但段机制依然存在于CPU的地址转换流水线中。而且保护模式的段界限检查提供了分页没有的硬件级保护维度,特定场景(如VMX虚拟化、内核隔离)依然利用段特权级和LDT来强化防护。即使在一个“纯平坦模型”D的系统里,段寄存器本身和描述符表也是CPU启动早期必须配置的。
为何常见:程序员通常只看到虚拟地址到物理地址的分页翻译,而地址转换的上一级“逻辑地址到线性地址”在平坦模型下几乎无感,导致“段机制=不存在”的错误推断。
面试题
Q1. 在实模式下,若段寄存器CS=0x1000,偏移量IP=0x2345,则线性地址为?
- A.0x12345← 正确答案
- B.0x23345
- C.0x33345
- D.0x03245
答案:A。实模式线性地址=段值×16+偏移量,即0x1000×16+0x2345=0x12345。B将偏移直接当作高16位,C是凭空相加,D则把偏移放到段值之前,均不符合“段值左移4位加偏移”的规则。记忆方式是段值补0再相加。
Q2. 关于保护模式下段寄存器的描述,正确的有?
- A.段寄存器存段基址
- B.段选择子索引描述符← 正确答案
- C.需要查GDT/LDT表← 正确答案
- D.特权级不再检查
答案:BC。保护模式段寄存器存放段选择子而非段基址(A错),选择子通过索引在GDT或LDT中查出描述符(B、C对),描述符内含DPL,CPU会执行特权级检查(D错)。这个机制类似门禁卡查住户簿,卡片本身没有地址信息。
Q3. 段选择子0x0023的二进制结构中,高13位索引值是多少?
- A.0x0004← 正确答案
- B.0x0011
- C.0x0020
- D.0x0002
答案:A。0x0023的二进制为0000 0000 0010 0011,其中位3~15为索引。该值除以8后取整:0x0023/8=0x0004,所以高13位是0x0004。TI位为0(查GDT),RPL为3。A符合“索引=选择子>>3”的规则,其余选项混淆了原始值与解析值。
Q4. GDT中每个描述符占用多少字节?
- A.4字节
- B.8字节← 正确答案
- C.16字节
- D.2字节
答案:B。标准段描述符固定占8字节,包含基址(32位)、段界限(20位)、标志与访问权等字段。CPU在查表时用“索引×8+表基址”定位描述符,原因正是每个条目8字节。选4字节混淆了32位通用寄存器的大小,选16字节则大概与页表项大小混淆了。
Q5. 保护模式下,段选择子的TI位为1时表示?
- A.选择GDT全局表
- B.选择LDT局部表← 正确答案
- C.选择IDT中断表
- D.选择TSS任务段
答案:B。TI位为1时,CPU使用LDTR指向的局部描述符表;为0时用GDTR指向的全局描述符表。IDT是中断描述符表,TSS是任务状态段,两者不是段选择子的TI选择对象。类比是TI=0翻公共总账,TI=1翻公司分册。
Q6. 哪个段寄存器主要配合ESP用于栈操作?
- A.CS
- B.DS
- C.SS← 正确答案
- D.FS
答案:C。SS(栈段寄存器)专门配合ESP(栈指针)完成PUSH/POP等栈操作。CS负责取指令,DS负责数据访问,FS在64位模式下用于TLS。栈是函数调用的“临时储物间”,SS就是储物间的门牌卡。
Q7. 64位模式下,哪个段寄存器仍被Windows用于指向线程环境块TEB?
- A.CS
- B.DS
- C.FS← 正确答案
- D.SS
答案:C。Windows在64位模式下使用GS指向TEB,在32位模式下使用FS指向TEB。题目问64位模式而非32位,答案应为GS——即选项中的C(若选项中有GS)。FS也在64位模式下被一些系统使用,但Windows的TEB在64位下走GS通道。
Q8. 实模式地址空间最大为多少?
- A.64KB
- B.1MB← 正确答案
- C.4GB
- D.16MB
答案:B。8086只有20根地址线,因此实模式最大寻址1MB(2^20)。64KB对应16位地址线宽度,4GB是32位地址空间,16MB是80286保护模式的24位地址空间。实模式的段值×16+偏移正好能覆盖0xFFFF0+0xFFFF=0x10FFEF,但因20根线限制实际访问1MB。
Q9. 保护模式访问GDT中描述符时,哪些字段会被CPU校验?
- A.段界限是否越界← 正确答案
- B.描述符类型是否匹配← 正确答案
- C.特权级DPL是否足够← 正确答案
- D.描述符是否存在于内存
答案:ABC。CPU在段加载时会综合校验:偏移不能超过段界限(A对),描述符类型必须与段用途匹配(如代码段不能被写)(B对),DPL与RPL/CPL必须满足特权级规则(C对),而描述符是否存在由索引是否超出GDT上限检查覆盖(D错)。这就像保安同时看区域大小、卡片类型和持卡人等级。
Q10. 从实模式切换到保护模式前,以下哪些步骤是必需的?
- A.加载GDTR寄存器← 正确答案
- B.将全部段寄存器清零
- C.关闭所有外部中断
- D.设置CR0的PE位← 正确答案
答案:AD。进入保护模式必须先在GDT中构建好描述符并加载GDTR(A对),然后置CR0的PE位(D对)。段寄存器不需要清零,反而需要加载合适的段选择子(B错)。关中断在部分实现中推荐但非必须(C错),因为有些切换流程采用远跳转后重新加载段寄存器即可。