2. 计算机体系结构基础回顾:CPU、内存、I/O设备的基本概念;x86与ARM架构的启动流程;实模式与保护模式

好,咱们正式开始动手之前,得先把地基打牢。很多同学一上来就急着写代码,结果连CPU怎么取指令、内存怎么编址都没搞明白,后面调试起来那叫一个痛苦。我个人习惯是,先花点时间把体系结构的关键脉络理清楚,后面写内核时心里才有底。

2.1 三大核心部件:CPU、内存、I/O

计算机系统说白了就三样东西:CPU、内存、I/O设备。你想想看,不管多复杂的系统,最终都逃不出这个框架。

CPU(中央处理器)

CPU是大脑,负责执行指令。它内部有几个关键部件:

  • 寄存器:CPU内部的高速存储单元,访问速度比内存快几个数量级。x86里有EAX、EBX、ECX等通用寄存器,还有EIP(指令指针)、EFLAGS(标志寄存器)等特殊寄存器。
  • 算术逻辑单元(ALU):负责加减乘除、与或非等运算。
  • 控制单元:负责取指令、译码、调度执行。
我的经验:写内核时,寄存器就是你最直接的“手”。比如切换页表时,你操作的是CR3寄存器;处理中断时,你读写的是IDTR寄存器。这些寄存器的手册,我建议你打印出来贴在工位上。

内存

内存就是存放指令和数据的地方。CPU通过地址总线访问内存,每个地址对应一个字节。这里有个关键概念——内存映射。在x86的实模式下,内存地址是20位的,最大寻址1MB。到了保护模式,地址变成32位,能访问4GB空间。

I/O设备

键盘、鼠标、硬盘、显示器……这些都是I/O设备。CPU怎么跟它们通信?两种方式:

  • 端口映射I/O:x86用IN/OUT指令,通过专门的I/O端口地址访问设备。比如键盘控制器通常挂在0x60端口。
  • 内存映射I/O:把设备寄存器映射到内存地址空间,直接用内存读写指令操作。ARM架构几乎全用这种方式。

我在项目中遇到过一个问题:某块网卡用内存映射I/O,但它的寄存器地址恰好和内存条的区域冲突了。折腾了两天才发现是BIOS里没正确配置MMIO区域。嗯,这种坑踩过一次就记住了。

2.2 x86与ARM的启动流程

启动流程,说白了就是CPU上电后,从哪开始执行第一行代码。x86和ARM的思路完全不同,咱们分别看看。

x86启动流程

  1. 上电复位:CPU的CS寄存器被初始化为0xF000,IP寄存器为0xFFF0。所以第一条指令的物理地址是0xFFFF0(即0xF000 << 4 + 0xFFF0)。
  2. 跳转到BIOS:这个地址通常是一条跳转指令,直接跳到BIOS的入口。BIOS开始执行POST(上电自检)。
  3. BIOS加载引导扇区:BIOS扫描启动设备(硬盘、U盘等),读取第一个扇区(512字节)到内存0x7C00处,然后跳过去执行。
  4. 引导程序接力:这个512字节的引导代码(比如GRUB的stage1)再加载更复杂的引导程序,最终加载内核。
关键点:x86启动时CPU默认处于实模式,地址空间只有1MB,而且没有内存保护。你写的引导代码必须在这个模式下运行。

ARM启动流程

ARM的启动方式灵活得多,但主流SoC(比如树莓派的BCM2837)通常是这样:

  1. 上电复位:CPU从固定的复位向量地址取指令。这个地址通常映射到ROM或片上SRAM。
  2. 一级引导(BootROM):芯片内部的BootROM代码开始执行,它初始化时钟、内存控制器等基础硬件。
  3. 二级引导:BootROM从SD卡、eMMC或SPI Flash中读取二级引导程序(比如树莓派的bootcode.bin),加载到片上SRAM。
  4. 加载内核:二级引导程序再加载真正的操作系统内核(比如Linux的kernel.img)到DRAM,然后跳转执行。

你可能会问:为什么ARM搞这么复杂?因为ARM芯片的启动介质太多了,SD卡、NAND Flash、USB……BootROM得先判断从哪启动,再一步步把代码“搬”到内存里。

2.3 实模式与保护模式

这两个模式是x86体系结构的精髓。我刚开始学的时候也觉得绕,但搞明白后,你会发现它其实很优雅。

实模式(Real Mode)

这是x86 CPU的“出厂设置”。特点如下:

  • 地址空间:20位地址线,最大1MB。
  • 寻址方式:段地址 << 4 + 偏移地址。比如段地址0x1000,偏移0x1234,物理地址就是0x1000 << 4 + 0x1234 = 0x11234。
  • 无保护:任何程序都能访问任何内存地址,包括硬件中断向量表。写错了直接死机。
  • 中断向量表:固定在内存0x00000处,每个中断向量占4字节(段地址+偏移地址)。
注意:实模式下没有“权限级别”的概念。你写的引导代码如果不小心覆盖了中断向量表,键盘一按系统就崩了。我曾经在调试时犯过这个错,查了整整一个下午。

保护模式(Protected Mode)

从80386开始,x86引入了保护模式。它带来了三个核心能力:

  • 32位地址空间:直接寻址4GB,不再需要段寄存器左移4位。
  • 内存保护:通过段描述符和页表,实现权限控制(内核态/用户态)、地址隔离。
  • 分页机制:虚拟地址到物理地址的映射,每个进程可以拥有独立的地址空间。

从实模式切换到保护模式,需要做这几步:

  1. 关闭中断(CLI指令)。
  2. 加载全局描述符表(GDT),定义代码段、数据段等描述符。
  3. 设置CR0寄存器的PE位(保护模式使能位)为1。
  4. 执行远跳转(JMP指令),刷新CPU流水线。

下面是一个简化的切换代码示例(NASM语法):

; 切换到保护模式
cli                     ; 关闭中断
lgdt [gdt_ptr]          ; 加载GDT
mov eax, cr0
or eax, 0x1             ; 设置PE位
mov cr0, eax
jmp 0x08:protected_mode ; 远跳转刷新流水线

[bits 32]
protected_mode:
    ; 现在已经在保护模式下了
    mov ax, 0x10        ; 数据段选择子
    mov ds, ax
    mov es, ax
    mov ss, ax
    ; ... 继续初始化
我的建议:第一次写切换代码时,先用QEMU的调试功能单步执行。观察CR0寄存器的变化,以及跳转后CS寄存器的值。亲眼看到模式切换的过程,比看十遍书都管用。

2.4 知识体系总览

为了让你更直观地理解本章的知识结构,我画了一张图。它把CPU、内存、I/O的关系,以及x86/ARM的启动路径、实模式与保护模式的切换,都串在了一起。

计算机体系结构基础 - 知识体系 CPU 寄存器(EAX, EIP, CR3...) ALU + 控制单元 实模式 / 保护模式 内存 地址空间(1MB / 4GB) 内存映射 I/O 中断向量表 / GDT I/O 设备 端口映射 I/O(IN/OUT) 内存映射 I/O(MMIO) 中断控制器(PIC/APIC) 地址/数据总线 I/O 指令 / MMIO 访问 启动流程对比 x86 复位 → 0xFFFF0 → BIOS → 引导扇区(0x7C00) → 引导程序 → 内核 实模式启动,逐步加载 ARM 复位 → BootROM → 二级引导(SD/eMMC) → 内核加载到DRAM 多级引导,启动介质灵活 实模式 → 保护模式切换 CLI → 加载GDT → 设置CR0.PE位 → 远跳转刷新流水线 关键:段描述符、权限级别、分页机制

这张图把本章的核心内容都囊括了。你可以把它当作一个“导航图”,后面写内核代码时,随时回来对照,看看当前代码运行在哪个阶段、操作了哪些硬件。


公众号:蓝海资料掘金营,微信deep3321