复习
本次课回答的问题
-
Q: 听说操作系统也是程序。那到底是鸡生蛋还是蛋生鸡?
本次课主要内容
- 软件和硬件的桥梁
- 操作系统的加载和初始化
- AbstractMachine 代码导读
一、自己动手写操作系统
时事热评
小学生写了三个月的操作系统是什么样的?
本学期的 OSLabs
热身实验
正经实验
- Lab1 (pmm): Physical memory management
- 多处理器 (bare-metal) 上的 kalloc/free
- Lab2 (kmt): Kernel multi-threading
- Lab3 (uproc): User processes
- Lab4 (vfs): Virtual file system
- devfs, procfs, 简单的文件系统;ELF 加载器
实现操作系统:到底有多难?
心路历程
韩寒:“被小学生支配的恐惧,而我也曾对那种力量,一无所知。”
- 「护球像梅西,射门像贝利」的金山区齐达内,满怀期待地去和儿童预备队比赛,结果被灌了 20 多球。
Bill Gates: 我选择退学。
大学的真正意义
将已有的知识和方法重新消化,为大家建立好 “台阶”,在有限的时间里迅速赶上数十年来建立起的学科体系。
今天的学术界可比 Bill Gates 的时代卷多了
例子:破除 “写操作系统很难”、“写操作系统很牛” 的错误认识
- 操作系统真的就是个 C 程序
- 你只是需要 “被正确告知” 一些额外的知识
- 然后写代码、吃苦头
- 从而建立正确的 “专业世界观”
例子
“专业世界观” 的例子 (这些都没啥,paper 都发不了)
“专业世界观” 的学习方法
- 经典研究论文 (OSDI, SOSP, ATC, EuroSys, …)
- 久经考验的经典教学材料 (xv6, OSTEP, CSAPP, …)
- 海量的开源工具 (GNU 系列, qemu, gdb, …)
- 第三方资料,慎用 (tutorials, osdev wiki, …)
二、硬件和软件的桥梁
C 程序
我们已经知道如何写一个 “最小” 的 C 程序了:
#include <sys/syscall.h>
.globl _start
_start:
movq $SYS_write, %rax // write(
movq $1, %rdi // fd=1,
movq $st, %rsi // buf=st,
movq $(ed - st), %rdx // count=ed-st
syscall // );
movq $SYS_exit, %rax // exit(
movq $1, %rdi // status=1
syscall // );
st:
.ascii "\033[01;31mHello, OS World\033[0m\n"
ed:
# 编译链接
gcc -c minimal.S && ld minimal.o && file a.out
# a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
# 查看汇编代码
objdump -d a.out | less
a.out: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000401000 <_start>:
401000: 48 c7 c0 01 00 00 00 mov $0x1,%rax
401007: 48 c7 c7 01 00 00 00 mov $0x1,%rdi
40100e: 48 c7 c6 2e 10 40 00 mov $0x40102e,%rsi
401015: 48 c7 c2 1c 00 00 00 mov $0x1c,%rdx
40101c: 0f 05 syscall
40101e: 48 c7 c0 3c 00 00 00 mov $0x3c,%rax
401025: 48 c7 c7 01 00 00 00 mov $0x1,%rdi
40102c: 0f 05 syscall
# gdb调试
gdb a.out
starti
layout src
layout asm
si
[Inferiprocess 28230 In: _startted with code 01] L?? PC: 0x40101c
0x0000000000401000 in _start ()
0x0000000000401007 in _start ()
0x000000000040100e in _start ()
0x0000000000401015 in _start ()
0x000000000040101c in _start ()
Hello, OS World
0x000000000040101e in _start ()
0x0000000000401025 in _start ()
0x000000000040102c in _start ()
[Inferior 1 (process 27863) exited with code 01]
“程序 = 状态机” 没问题
- 带来更多的疑问
- 但谁创建的这个状态机???
- 这个程序可以在没有操作系统的硬件上运行吗?
- “启动” 状态机是由 “加载器” 完成的
- 加载器也是一段程序 (状态机)
- 这个程序由是由谁加载的?
Bare-metal 与程序员的约定
为了让计算机能运行任何我们的程序,一定存在软件/硬件的约定
- CPU reset 后,处理器处于某个确定的状态
- PC 指针一般指向一段 memory-mapped ROM
- ROM 存储了厂商提供的 firmware (固件)
- 处理器的大部分特性处于关闭状态
- Firmware (固件,厂商提供的代码)
- 将用户数据加载到内存
- 例如存储介质上的第二级 loader (加载器)
- 或者直接加载操作系统 (嵌入式系统)
x86 Family: CPU Reset 行为
CPU Reset (Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A/3B)
-
寄存器会有初始状态
-
EIP = 0x0000fff0
-
CR0 = 0x60000010
-
EFLAGS = 0x00000002
-
TFM (5,000 页 by 2019)
CPU Reset 之后:发生了什么?
《计算机系统基础》:不仅是程序,整个计算机系统也是一个状态机
- 从 PC (
CS:IP
) 指针处取指令、译码、执行……
- 从 firmware 开始执行
-
ffff0
通常是一条向 firmware 跳转的 jmp 指令
Firmware: BIOS vs. UEFI
- 都是主板/主板上外插设备的软件抽象
- Legacy BIOS (Basic I/O System)
- UEFI (Unified Extensible Firmware Interface)
Legacy BIOS: 约定
Firmware 必须提供机制,将用户数据载入内存
- Legacy BIOS 把第一个可引导设备的第一个扇区加载到物理内存的
7c00
位置
- 此时处理器处于 16-bit 模式
- 规定
CS:IP = 0x7c00
, (R[CS] << 4) | R[IP] == 0x7c00
- 可能性1:
CS = 0x07c0, IP = 0
- 可能性2:
CS = 0, IP = 0x7c00
- 其他没有任何约束
能不能看一下代码?
Talk is cheap. Show me the code. ——Linus Torvalds
有没有可能我们真的去看从 CPU Reset 以后每一条指令的执行?
计算机系统公理:你想到的就一定有人做到。
- 模拟方案:QEMU
- 真机方案:JTAG (Joint Test Action Group) debugger
- 一系列 (物理) 调试寄存器,可以实现 gdb 接口 (!!!)
调试 QEMU: 确认 Firmware 的行为
亲眼确认 Firmware 到底是不是会加载启动盘第一个扇区到 0x7c00
内存位置!
调试 QEMU 模拟器
- 查看 CPU Reset 后的寄存器
- 查看
0x7c00
内存的加载
-
watch *0x7c00
- 《计算机系统基础》的良苦用心
- 查看当前指令
x/i ($cs * 16 + $rip)
- 打印内存
x/16xb 0x7c00
- 进入
0x7c00
代码的执行
-
b *0x7c00
, c
(撒花!我们一会再回来)
鸡和蛋的问题解决
有个原始的鸡:Firmware
- 代码直接存在于硬件里
- CPU Reset 后 Firmware 会执行
- 加载 512 字节到内存 (Legacy Boot)
- 然后功成身退
Firmware 的另一用处
- 放置一些 “绝对安全的代码”
-
BIOS 中断 (Hello World 是如何被打印的)
- ARM Trusted Firmware
- Boot-Level 1, 2, 3.1, 3.2, 3.3
-
U-Boot: the universal boot loader
小插曲:Firmware 的病毒 (1998)
Firmware 通常是只读的 (当然……)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)