一个操作系统的设计与实现——第18章 欢迎回来
18.1 引言
我们已经实现了一个运行在保护模式下的操作系统,然而,读者朋友也许不会满足:如今早已是多核CPU,64位操作系统的时代,而我们的操作系统仅仅是单核CPU,32位的。因此,从本章开始,我们将在32位单核操作系统的基础上,将其升级为一个64位多核操作系统。
18.2 准备工作
我们仍然使用bochs
虚拟机作为操作系统的运行和调试环境。但其编译命令需要增加一些选项:
- 对于
bochs
:./configure --prefix=... --enable-x86-64 --enable-smp && make -jN && make install
- 对于
bochsdbg
:./configure --prefix=... --enable-debugger --enable-x86-64 --enable-smp && make -jN && make install
新增的--enable-x86-64
选项用于启用64位模式;--enable-smp
选项用于启用多处理器模式。
此外,bochsdbg
的-rc
参数是一个很实用的功能,其可以使bochsdbg
在启动时自动执行一串调试命令,建议读者根据需要自行配置这个参数。
18.3 64位寄存器
64位CPU的通用寄存器宽度是64位的,不仅如此,其还新增了8个通用寄存器r8 ~ r15
,并拓展了现有寄存器的用法:现在,所有的寄存器都有8、16、32、64位版本。这些寄存器如下表所示:
64位版本 | 32位版本 | 16位版本 | 8位版本 |
---|---|---|---|
rax |
eax |
ax |
al/ah |
rbx |
ebx |
bx |
bl/bh |
rcx |
ecx |
cx |
cl/ch |
rdx |
edx |
dx |
dl/dh |
rsi |
esi |
si |
sil |
rdi |
edi |
di |
dil |
rbp |
ebp |
bp |
bpl |
rsp |
esp |
sp |
spl |
r8 |
r8d |
r8w |
r8b |
r9 |
r9d |
r9w |
r9b |
r10 |
r10d |
r10w |
r10b |
r11 |
r11d |
r11w |
r11b |
r12 |
r12d |
r12w |
r12b |
r13 |
r13d |
r13w |
r13b |
r14 |
r14d |
r14w |
r14b |
r15 |
r15d |
r15w |
r15b |
64位CPU有一个特殊设定:当向一个32位寄存器传送时,其高32位将被自动清零。也就是说,不需要也不存在这样的指令:movzx 64位寄存器, 32位寄存器
;然而,movsx 64位寄存器, 32位寄存器
与此设定不矛盾,故仍然存在。
标志寄存器也拓展至64位,被称为rflags
。然而,由于其低32位都还没用完,因此拓展出的高32位均为保留位。
指令指针寄存器也拓展至64位,被称为rip
。
18.4 64位立即数
由于机器语言层面的限制,CPU对64位立即数的支持非常差,以至于只需要以下规则就能描述:
mov 64位寄存器, 64位立即数
是唯一支持64位立即数的指令- 其他指令如果强行使用64位立即数,也只会取其低32位
也就是说,很多非常依赖立即数的指令,如内存访问,加减法,位运算等,都不能使用64位立即数,而是需要先使用mov
指令进行周转。
18.5 RIP相对寻址
64位CPU新增了一种内存访问模式,被称为RIP相对寻址。具体来说:在nasm语法中,[标号]
和[abs 标号]
表示的是普通的内存寻址模式,此时,标号的值就是内存地址;[rel 标号]
表示的是新增的RIP相对寻址,此时,该指令会被编译器转换为[rip + 标号相对于rip的偏移量]
。这样做的好处在于:64位模式下,直接使用[标号]
是有风险的,因为标号很有可能是一个超过32位的立即数,不过,由于标号往往离rip
比较近,相对偏移量较小,故不会超过32位的限制。
在nasm中,可以通过声明[default rel]
将所有的[标号]
都视为[rel 标号]
。
需要注意的是,RIP相对寻址只适用于[标号]
这种形式,其他形式,如:[寄存器 + 标号]
,[64位立即数]
等,都是不适用于RIP相对寻址的,这是因为这些指令都无法确定地址与rip
的相对偏移量。
本章讨论了64位CPU的寄存器和内存寻址,64位CPU的其他特点将在后续章节中讨论。从下一章开始,我们将正式开始实现64位操作系统。