前言
对于一个新事物的掌握 本质上是给了一个能够说服自己的理由 能够自圆其说 这就够了
在这一部分,书中首先介绍了两个问题,第一个是为什么要把程序载入内存,第二个是什么是载入内存。第一个问题的答案是CPU的硬件电路被设计成只能运行处于内存中的程序,这是因为内存在速度、容量和兼容性上都有很好的平衡。第二个问题的答案是:第一步程序被加载器(硬件或软件)加载到内存某个区域;第二步CPU的cs:ip寄存器被指向这个程序的起始地址。加载器实际上是由一堆函数组成的模块。
当你按下主机的电源键时,第一个运行的软件是BIOS(基本输入输出系统,BIOS的作用主要是检测、初始化硬件,其通过建立中断向量表,使用int中断号来实现相关的硬件调用,因为BIOS的这些功能就是对硬件的IO操作,精彩的世界是在进入保护模式以后才开始的,所以才叫基本输入输出系统),它是由硬件加载的,这个硬件就是只读存储器ROM,正如其名字一样,这个存储器中的内容是不可擦除的,它不像动态(要不断通电,因为其电容小,所以漏电很快,所以叫动态)随机访问存储器DRAM那样,这也表明了BIOS的内容是一成不变的,直接写死的,ROM也属于一种内存,内存需要被访问,所以其被映射到了低端1MB内存的顶部,即0xF0000~0xFFFFF处,只要访问此处的地址就是访问了BIOS,这个映射是由硬件完成的,这个地址也是其程序的入口地址,CPU访问内存是用段地址+偏移地址实现的,由于这是第一个软件,所以我们只能使用硬件来替代软件完成访问内存的过程,即当通电的一瞬间cs:ip的寄存器被强制初始化为0xF000:0xFFF0,由于实模式下段基址要乘以16,也就是左移4位,于是其等效地址为0xFFFF0,也就是前面说的BIOS的入口地址。
这里我们要介绍一下硬件层面的内存,在CPU眼里,插在主板上的物理内存不是它眼里全部的内存,这是因为地址总线宽度决定了可以访问的内存空间大小,但是地址总线还有别的外设需要访问,因此会有一些地址空间给这些外设使用,剩下的地址空间才会指向内存条,这也就构成了CPU眼中的内存。
我们可以看出,BIOS是在实模式下运行的,而实模式只能访问1MB空间(20位地址线,2的20次方是1MB),而0xFFFF0距离1MB的上限只剩下了16字节的空间,这也说明BIOS真正的代码并不在这里,从下表我们知道,这16个字节的指令是跳转指令,也就是跳转到BIOS代码真正的开始处。
下面是实模式下的内存布局:
实模式和保护模式
在开始之前我们先更加详细的介绍一下实模式和保护模式的概念:
实模式(Real Mode)的全面解析
一、实模式的定义与历史背景
实模式是x86架构处理器(如Intel 8086及后续芯片)的一种工作模式,也是处理器加电启动后的默认模式。它起源于20世纪70年代末,设计初衷是为了兼容早期8位处理器(如8080)的编程模型,同时支持16位运算。在实模式下,处理器对内存的访问方式和指令集特性较为基础,是理解现代操作系统启动流程和底层机制的关键概念。
二、实模式的核心特性
-
内存寻址方式
- 16位地址总线限制:早期8086处理器仅有20条地址线(后续处理器在实模式下仍兼容此限制),理论最大寻址空间为 (2^{20} = 1MB)。
- 分段寻址机制:通过“段寄存器(如CS、DS)+ 偏移量”的方式组合物理地址,公式为:
[
\text{物理地址} = \text{段地址} \times 16 + \text{偏移量}
]
例如,段地址为0x1000
,偏移量为0x0010
,则物理地址为0x1000 × 16 + 0x0010 = 0x10010
(即65552字节)。 - 地址空间映射:内存地址从
0x00000
到0xFFFFF
(1MB),其中低地址(如0x00000~0x003FF
)为BIOS中断向量表,高地址(如0xF0000~0xFFFFF
)为ROM BIOS区域,中间部分为可用内存。
-
指令集与特权级
- 支持16位指令:如
MOV
、ADD
、JMP
等基础指令,不支持32位或64位扩展指令。 - 单一特权级别:实模式下处理器不区分用户态和内核态(所有程序均拥有最高特权级,可直接访问硬件),这也是早期操作系统(如DOS)安全性较低的原因之一。
- 支持16位指令:如
-
硬件兼容性
- 实模式是处理器启动时的“兼容模式”,确保旧程序(如DOS应用)能直接运行,无需修改底层代码。
三、实模式的工作流程(以计算机启动为例)
-
加电初始化
- 处理器复位后自动进入实模式,CS寄存器设为
0xF000
,IP寄存器设为0xFFF0
,指向ROM BIOS的入口地址(物理地址0xFFFF0
,通常为BIOS启动程序的第一条指令)。
- 处理器复位后自动进入实模式,CS寄存器设为
-
BIOS执行
- 在实模式下,BIOS完成硬件检测(如内存、硬盘)、初始化设备,并加载引导程序(如MBR,主引导记录)到内存
0x7C00
位置。
- 在实模式下,BIOS完成硬件检测(如内存、硬盘)、初始化设备,并加载引导程序(如MBR,主引导记录)到内存
-
引导程序加载
- MBR程序(512字节)在实模式下运行,负责定位操作系统内核(如Linux的
boot.img
),并将其加载到内存。由于实模式下内存限制(1MB),若内核超过此大小,需通过“分段加载”或切换到保护模式解决。
- MBR程序(512字节)在实模式下运行,负责定位操作系统内核(如Linux的
-
切换到保护模式
- 当需要突破1MB内存限制或启用更安全的内存管理时,引导程序会通过设置CR0寄存器的PE位(Protection Enable)切换到保护模式,这是现代操作系统(如Windows、Linux)的主要工作模式。
四、实模式与保护模式的对比
特性 | 实模式 | 保护模式 |
---|---|---|
内存寻址 | 1MB限制,分段寻址无分页支持 | 最大支持4GB(32位)或更大空间,支持分页 |
特权级别 | 单一特权级(所有程序可访问硬件) | 4级特权级(Ring 0~3,区分内核与用户程序) |
安全性 | 无内存保护,程序可任意修改系统数据 | 内存保护机制,禁止用户程序非法访问内核空间 |
指令支持 | 仅16位基础指令 | 支持32位/64位扩展指令(如MMX、SSE) |
五、实模式的应用场景
- 系统启动阶段:所有x86架构计算机启动时必须经过实模式,完成BIOS初始化和引导程序加载。
- 底层开发与调试:如引导程序编写、BIOS扩展(UEFI之前的传统BIOS)、硬件驱动开发(需直接操作端口)。
- 兼容旧程序:部分工业控制软件或嵌入式系统仍依赖实模式运行。
六、实模式的局限性
- 内存限制:1MB空间无法满足现代程序需求,需通过保护模式或扩展内存技术(如XMS、EMS)突破。
- 安全性不足:无特权级保护,程序错误或恶意代码可能导致系统崩溃。
- 功能有限:不支持虚拟内存、多任务调度等现代操作系统特性。
七、总结
实模式是x86架构的“历史遗产”,虽因功能限制逐渐被保护模式取代,但仍是理解计算机启动流程和底层硬件交互的基础。现代操作系统(如Linux)的启动过程中,仍需通过实模式完成初始引导,再切换到保护模式以支持更大内存和更安全的运行环境。对于系统开发者或逆向工程师而言,掌握实模式原理是深入理解计算机底层机制的关键一步。
保护模式全面解析
保护模式(Protected Mode)是现代x86架构处理器的一种工作模式,与早期的实模式(Real Mode)相对,是为解决实模式在内存寻址、多任务处理和系统安全性等方面的局限性而设计的。以下从多个维度详细解析保护模式的核心特性和工作原理:
一、保护模式的诞生背景与核心目标
- 实模式的局限:
实模式是x86处理器(如8086)的初始启动模式,仅支持1MB内存寻址(20位地址线),且所有程序共享同一内存空间,缺乏内存保护和多任务机制,一旦程序出错易导致系统崩溃。 - 保护模式的目标:
- 支持更大的内存寻址空间(突破1MB限制)。
- 实现内存保护和任务隔离,防止程序越权访问或破坏系统资源。
- 支持多任务操作系统,确保不同任务高效、安全地运行。
二、保护模式的关键特性
1. 内存寻址与地址空间扩展
- 32位/64位地址线支持:
- 从80286开始,保护模式启用32位地址线(80286支持16MB,80386及以后支持4GB物理内存寻址),64位处理器(如x86-64)更可支持TB级内存。
- 通过分段(Segmentation) 和分页(Paging) 机制管理内存:
- 分段:将逻辑地址(程序使用的地址)转换为线性地址,每个段可独立设置访问权限(如可读、可写、可执行)。
- 分页:将线性地址转换为物理地址,支持虚拟内存(将部分内存数据交换到硬盘),并通过页表实现内存保护(如禁止用户程序访问内核地址空间)。
2. 特权级与内存保护机制
- 4级特权级(Ring 0-Ring 3):
- Ring 0:内核态,运行操作系统内核和设备驱动,拥有最高权限,可访问所有内存和硬件资源。
- Ring 3:用户态,运行应用程序,权限最低,禁止直接访问关键系统资源,防止程序恶意操作或崩溃影响系统。
- 例如,当应用程序尝试访问内核地址空间时,处理器会触发异常(如“一般保护错误”),确保系统安全。
3. 多任务支持与任务切换
- 任务控制块(TCB)与任务寄存器:
保护模式通过TCB存储任务状态(如内存上下文、寄存器值),切换任务时只需加载对应TCB,快速实现任务切换(毫秒级),且不同任务的内存空间相互隔离,避免数据冲突。
4. 虚拟内存与内存映射
- 通过分页机制,保护模式支持将物理内存与硬盘虚拟内存(页面文件)结合,允许程序使用超过物理内存大小的虚拟地址空间,提升内存利用率。
三、保护模式与实模式的对比
特性 | 实模式 | 保护模式 |
---|---|---|
地址线位数 | 20位(1MB寻址) | 32位(4GB)或64位(TB级) |
内存保护 | 无,程序可任意访问内存 | 有,通过特权级和分页控制 |
多任务支持 | 不支持(或仅通过切换模拟) | 原生支持,任务隔离安全 |
特权级 | 无(所有程序权限相同) | 4级(Ring 0-Ring 3) |
典型应用 | BIOS启动阶段、老DOS程序 | Windows、Linux等现代系统 |
四、保护模式的启动流程(以x86为例)
- 处理器加电:默认进入实模式,初始化寄存器(如CS=0xFFFF,IP=0x0000),执行BIOS代码。
- BIOS引导:加载操作系统引导程序(如Windows的bootmgr),此时仍在实模式。
- 切换保护模式:
- 操作系统引导程序设置处理器的CR0寄存器(开启保护模式标志位PE=1)。
- 加载全局描述符表(GDT),定义内存段的访问权限和基地址。
- 刷新流水线,处理器正式进入保护模式,开始使用32位寻址。
- 后续初始化:加载分页机制,启用虚拟内存,最终运行操作系统内核。
五、保护模式在现代系统中的应用
- 操作系统核心基础:Windows、Linux、macOS等现代系统均依赖保护模式实现内存管理、多任务和安全性(如防止应用程序篡改系统文件)。
- 硬件虚拟化支持:保护模式的扩展(如Intel VT-x、AMD-V)为虚拟机(VMware、VirtualBox)提供底层支持,允许在一台物理机上运行多个隔离的操作系统。
- 安全机制强化:结合特权级和内存保护,防止恶意软件(如病毒)直接访问内核,提升系统安全性。
总结
保护模式是现代x86处理器的核心工作模式,通过扩展内存寻址、引入特权级保护和多任务机制,为复杂操作系统的运行奠定了基础。从Windows到Linux,几乎所有主流系统都在保护模式下运行,而实模式仅保留在系统启动初期(如BIOS阶段)用于兼容性。理解保护模式,有助于深入掌握操作系统的内存管理、安全机制和多任务原理。
正式开始
BIOS的最后一步是校验启动盘中位于0盘0道1扇区的内容(相当于磁盘的最开始的扇区,因为CHS中扇区的编号是从1开始的)如果此扇区末尾的两个字节分别是魔数(魔数是计算机领域中用于 “身份识别” 的关键机制,魔数是一段固定的、具有唯一标识性的二进制或文本数据,通常位于文件头部、数据块开头或协议报文的特定位置,用于表明数据的类型、格式或所属系统。操作系统和专业软件优先通过魔数识别文件格式,而非扩展名(如修改.jpg为.txt,魔数仍存在,软件仍按 JPEG 解析)0x55和0xaa,BIOS便认为此扇区中确实存在可执行的程序,这个程序便是我们今天的主角,MBR主引导记录。MBR的任务是加载某个程序,这个程序一般都是内核加载器,很少有直接加载内核的,并将控制权交给它,所谓的移交控制权就是jump过去而已,之后MBR就没用了,被覆盖也没关系,但是不能过早覆盖,也就是不能让mbr自己破坏自己。
MBR的大小必须是512字节,这是为了保证0x55和0xaa这两个魔数正好位于该扇区的最后两个字节处,即第510字节和第511字节,由于bochs模拟的是x86平台,使用的是小端字节序,所以最后两个字节内容是0xaa55。这里简单介绍一下,对于多字节数据(如 2 字节的short、4 字节的int、8 字节的long long等),小端序将数据的最低有效字节(LSB,Least Significant Byte)放在内存的起始地址(低地址),而最高有效字节(MSB,Most Significant Byte)放在后续的高地址。
了解上面这些,我们下面就可以正式开始编写MBR了,我们需要用到两个工具,一个是nasm(一种被广泛使用的开源汇编语言编译器,尤其在 x86 和 x86-64 架构下表现出色。它被设计用于生成高效、可移植的汇编代码。可以生成多种目标文件格式,例如Linux下的ELF,Windows下的COFF,以及macOS下的Mach-O,这些文件格式是由操作系统的程序加载器执行的,而Linux下的bin文件以及Windwos下的exe文件是纯二进制文件,可以被CPU直接执行),另一个工具是dd,这个工具非常强大,可以深入操作磁盘的任何一个扇区。
下面是我们编写的第一代MBR文件,主要功能就是清屏和打印字符串:
;主引导程序
;------------------------------------------------
SECTION MBR vstart=0x7c00 ;起始地址编译在0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
;这个时候 ds = es = ss = ss = 0 栈指针指向MBR开始位置
;清屏利用0x06号功能,该功能由BIOS提供,上卷全部行就能达到清屏的效果
;INT 0x10 功能号:0x06 功能描述:上卷窗口
;输入:AH 功能号=0x06
;BH=上卷行属性
;(CL,CH)=窗口左上角的(X,Y)位置
;(DL,DH)=窗口右下角的(X,Y)位置
;无返回值
mov ax,0x600
mov bx,0x700
mov cx,0 ;左上角(0,0)
mov dx,0x184f ;右下角(80,25)
;在VGA文本模式中,一行只能容纳80个字符,共25行
;下标从0开始,所以0x18=24,0x4f=79
;调用BIOS中断
int 0x10
;;;;下面三行代码获取光标位置;;;;;
mov ah,3 ;输入:3号子功能是获取光标位置,需要存入ah寄存器
mov bh,0;bh寄存器存储的是待获取光标的页号
;获取光标位置 需要打印信息
int 0x10 ;输出:ch=光标开始行,cl=光标结束行
;dh=光标所在行号,dl=光标所在列号
;;;;;;;;打印字符串;;;;;;;;;
;还是用10h中断,不过这次调用13号子功能打印字符串
mov ax,message
mov bp,ax ;es:bp为串首地址,es此时同cs一致,
;开头时已经为sreg初始化
;光标位置要用到dx寄存器中内容,cx中的光标位置可忽略
mov cx,5 ;cx为串长度,不包括结束符0的字符格式
mov ax,0x1301 ;子功能号13显示字符及属性,要存入ah寄存器
;al设置写字符方式,ah=01:显示字符串,光标跟随移动
;bh存储要显示的页号,此处是第0页,
;bl中是字符属性,属性黑底绿字(bl=02h)
;执行BIOS 0x10号中断
;;;;;;;打字字符串结束;;;;;;;;
mov bx,0x71;白底蓝字好cooi
int 0x10 ;写字符串
jmp $ ;无限循环 一直跳转到当前命令位置,使程序悬停在次
;字符串声明 db == define byte dw == define word ascii一个字符占一个字节
message db "1 MBR"
;预留两个字节 其余空余的全部用0填满 为使检测当前扇区最后两字节为0x55 0xaa 检测是否为有效扇区
;510 = 512字节-2预留字节 再减去(当前位置偏移量-段开始位置偏移量)求出来的是剩余空间
times 510 - ($ - $$) db 0
db 0x55,0xaa
我们将上面的内容保存为mbr.s(汇编语言源代码文件),然后执行nasm -o mbr.bin mbr.s,我们就能在当前目录得到mbr.bin文件。使用ls -l我们可以得到具体的文件属性,可以看到其文件大小正好为512字节:
然后我们使用dd将文件插入到磁盘中:
sudo dd if=/home/cooiboi/bochs/mbr.bin of=/home/cooiboi/bochs/hd60M.img bs=512 count=1 conv=notrunc
然后我们重启bochs:
可以看到已经执行成功。
总结
下一篇我们将完善MBR。