目录

Operating System Chapter9 操作系统的状态机模型


Operating System

$Nanjing\ University\rightarrow Yanyan\ Jiang\newline$

Overview

复习

  • 并发……就这么……讲完了……
    • 理解的方式:“玩一玩” 示例代码

本次课回答的问题

  • Q: 听说操作系统也是程序。那到底是鸡生蛋还是蛋生鸡?

本次课主要内容

  • 软件和硬件的桥梁
  • 操作系统的加载和初始化
  • AbstractMachine 代码导读

自己动手写操作系统

时事热评

小学生写了三个月的操作系统是什么样的?

  • 看到 i386 就知道了嘛

本学期的 OSLabs

热身实验

  • Lab0 (amgame): 熟悉代码框架

正经实验

  • 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 加载器

大学的真正意义

将已有的知识和方法重新消化,为大家建立好 “台阶”,在有限的时间里迅速赶上数十年来建立起的学科体系


  • 例子:破除 “写操作系统很难”、“写操作系统很牛” 的错误认识

    • 操作系统真的就是个 C 程序

    • 你只是需要 “被正确告知” 一些额外的知识

      • 然后写代码、吃苦头
      • 从而建立正确的 “专业世界观”

例子

  • “专业世界观” 的例子 (这些都没啥,paper 都发不了)

    • 写 x86 模拟器的时候,不知道哪条指令错了,怎么办?

    • 做操作系统实验的时候,如果遇到神秘 CPU Reset,怎么办?

    • 做实验做不下去的时候,该实现什么工具?


  • “专业世界观” 的学习方法

    • 经典研究论文 (OSDI, SOSP, ATC, EuroSys, …)

    • 久经考验的经典教学材料 (xv6, OSTEP, CSAPP, …)

    • 海量的开源工具 (GNU 系列, qemu, gdb, …)

    • 第三方资料,慎用 (tutorials, osdev wiki, …)

硬件和软件的桥梁

C 程序

  • 我们已经知道如何写一个 “最小” 的 C 程序了:

    • minimal.S

    • 不需要链接任何库,就能在操作系统上运行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#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:
  • “程序 = 状态机” 没问题

  • 带来更多的疑问

1
2
3
┌──(kali㉿kali)-[~/chapter9]
└─$ file minimal
minimal: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

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

      • 16-bit 模式 $\Longrightarrow$ 2010年前的老电脑会兼容老接口和老的操作系统
    • EFLAGS = 0x00000002

    • interrupt disabled(中断关闭)

  • TFM (5,000 页 by 2019)

    • 最需要的 Volume 3A 只有 468 页
/img/Operating System/chapter9-1.png
friendly manual
  • qemu模拟器停在第一条指令上,和上面的手册对照,会发现和手册一致

CPU Reset 之后:发生了什么?

  • 《计算机系统基础》:不仅是程序,整个计算机系统也是一个状态机

    • 从 PC (CS:IP) 指针处取指令、译码、执行……

    • 从 firmware 开始执行

      • ffff0(PC初始化的值) 通常是一条向 firmware 跳转的 jmp 指令
  • 厂商会在ROW里面放好写死的代码(Only-read)

  • Firmware: BIOS vs. UEFI

    • 都是主板/主板上外插设备的软件抽象

      • 支持系统管理程序运行
    • Legacy BIOS (Basic I/O System)

    • UEFI (Unified Extensible Firmware Interface)

/img/Operating System/chapter9-2.png
BIOS

Legacy BIOS: 约定

Firmware 必须提供机制,将用户数据载入内存

  • Legacy BIOS 把第一个可引导设备的第一个扇区加载到物理内存的 7c00 位置,这也是我们Fireware和OS之间的第一次,也是唯一一次握手

    • 此时处理器处于 16-bit 模式

    • 规定 CS:IP = 0x7c00, (R[CS] << 4) | R[IP] == 0x7c00

      • 可能性1:CS = 0x07c0, IP = 0

      • 可能性2:CS = 0, IP = 0x7c00

    • 其他没有任何约束

  • Windows启动模式:老的Windows的A,B盘都是软盘,BIOS先去软盘里面读前512个bytes,看最后两个byte是否为55aa(大端),如果是就加载这块磁盘,否则读下一块,如果都不是就启动失败。读完A/B盘之后引导到C盘,这也是为什么今天的Windows操作系统C盘是系统盘的原因

  • Firmware做的事情(以BIOS为例)

能不能看一下代码?

Talk is cheap. Show me the code. ——Linus Torvalds

  • 有没有可能我们真的去看从 CPU Reset 以后每一条指令的执行?

  • 计算机系统公理:你想到的就一定有人做到

  • 模拟方案:QEMU

  • 真机方案:JTAG (Joint Test Action Group) debugger

    • 一系列 (物理) 调试寄存器,可以实现 gdb 接口 (!!!)
/img/Operating System/chapter9-3.png
ffmpeg

调试 QEMU: 确认 Firmware 的行为

亲眼确认 Firmware 到底是不是会加载启动盘第一个扇区到 0x7c00 内存位置!

调试 QEMU 模拟器

  • 查看 CPU Reset 后的寄存器

    • info registers
  • 查看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 通常是只读的 (当然……)

  • Intel 430TX (Pentium) 芯片组允许写入 Flash ROM

    • 只要向 Flash BIOS 写入特定序列,Flash ROM 就变为可写

      • 留给 firmware 更新的通道
    • 要得到这个序列其实并不困难

      • 似乎文档里就有 🤔 Boom……

CIH 的作者陈盈豪被逮捕,但并未被定罪

/img/Operating System/chapter9-4.gif
病毒代码结尾的CIH

今天的 Firmware: UEFI

  • IBM PC 所有设备/BIOS 中断是有 specification 的 (成就了 “兼容机”)

  • 今天的 boot loader 面临麻烦得多的硬件:

    • 指纹锁、不知名厂商生产网卡上的网络启动、USB 上的蓝牙转接器连接的蓝牙键盘、……
/img/Operating System/chapter9-5.png
UEFI

UEFI 上的操作系统加载

  • 标准化的加载流程

    • 盘必须按 GPT (GUID Partition Table) 方式格式化

    • 预留一个 FAT32 分区 (lsblk/fdisk 可以看到)

    • Firmware 加载任意大小的 PE 可执行文件.efi

      • 没有 legacy boot 512 字节限制

      • EFI 应用可以返回 firmware

  • 更好的程序支持

    • 设备驱动框架

    • 更多的功能,例如 Secure Boot,只能启动 “信任” 的操作系统

操作系统的状态机模型

“操作系统” 的状态机已经启动

  • Firmware 和 boot loader 共同完成 “操作系统的加载”
    • 初始化全局变量和栈;分配堆区 (heap)
    • main 函数传递参数
      • 谁给操作系统传递了参数?
      • 如何实现参数传递?

  • 进入 C 代码之后

    • 完全遵循 C 语言的形式语义

    • 但有一些行为 “补充” —— AbstractMachine API

操作系统:是个 C 程序

  • 一个迷你 “操作系统” thread-os.c
    • make 会得到一个 “磁盘镜像”,好像魔法一样
      • 就跟你们第一次用 IDE 的时候按一个键就可以编译运行一样
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int main() {
  cte_init(on_interrupt);

  for (int i = 0; i < LENGTH(tasks); i++) {
    Task *task    = &tasks[i];
    Area stack    = (Area) { &task->context + 1, task + 1 };
    task->context = kcontext(stack, task->entry, (void *)task->name);
    task->next    = &tasks[(i + 1) % LENGTH(tasks)];
  }
  mpe_init(mp_entry);
}

thread-os.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <am.h>
#include <klib.h>
#include <klib-macros.h>

#define MAX_CPU 8

typedef union task {
  struct {
    const char *name;
    union task *next;
    void      (*entry)(void *);
    Context    *context;
  };
  uint8_t stack[8192];
} Task;

Task *currents[MAX_CPU];
#define current currents[cpu_current()]

// user-defined tasks

int locked = 0;
void lock()   { while (atomic_xchg(&locked, 1)); }
void unlock() { atomic_xchg(&locked, 0); }

void func(void *arg) {
  while (1) {
    lock();
    printf("Thread-%s on CPU #%d\n", arg, cpu_current());
    unlock();
    for (int volatile i = 0; i < 100000; i++) ;
  }
}

Task tasks[] = {
  { .name = "A", .entry = func },
  { .name = "B", .entry = func },
  { .name = "C", .entry = func },
  { .name = "D", .entry = func },
  { .name = "E", .entry = func },
};

// ------------------

Context *on_interrupt(Event ev, Context *ctx) {
  extern Task tasks[];
  if (!current) current = &tasks[0];
  else          current->context = ctx;
  do {
    current = current->next;
  } while ((current - tasks) % cpu_count() != cpu_current());
  return current->context;
}

void mp_entry() {
  iset(true);
  yield();
}

int main() {
  cte_init(on_interrupt);

  for (int i = 0; i < LENGTH(tasks); i++) {
    Task *task    = &tasks[i];
    Area stack    = (Area) { &task->context + 1, task + 1 };
    task->context = kcontext(stack, task->entry, (void *)task->name);
    task->next    = &tasks[(i + 1) % LENGTH(tasks)];
  }
  mpe_init(mp_entry);
}

AbstractMachine 对 “C 程序语义” 做出的扩充

  • TRM + MPE

    • 完全等同于多线程 (处理器相当于线程) - L1/native

    • IOE API: 完全是普通的库函数

      • 同一设备的数据竞争 = undefined behavior

  • CTE

    • 允许创建多个执行流 (类比协程) - M2

    • yield 主动切换;会被中断被动打断

    • on_interrupt 会拦截到中断事件


  • VME

    • 允许创建一个 “经过地址翻译的执行模式”

    • 通过 CTE API 管理

  • 有关中断代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Context *on_interrupt(Event ev, Context *ctx) {
  extern Task tasks[];
  if (!current) current = &tasks[0];
  else          current->context = ctx;
  do {
    current = current->next;
  } while ((current - tasks) % cpu_count() != cpu_current());
  return current->context;
}

void mp_entry() {
  iset(true);
  yield();
}
1
2
3
4
5
6
void iset(bool enable) {
  extern sigset_t __am_intr_sigmask;
  // NOTE: sigprocmask does not supported in multithreading
  int ret = sigprocmask(enable ? SIG_UNBLOCK : SIG_BLOCK, &__am_intr_sigmask, NULL);
  assert(ret == 0);
}

RTFSC 时间

(0) 生成镜像和启动虚拟机

如果使用 “土办法”,你很可能被淹没在 Makefile 中

  • 读懂 Makefile 需要 STFW, RTFM,大量的精力
  • 虽然花点时间读是值得的,但很可能读了很久都没读到重要的地方
  • 花一点时间想 “应该怎么做”

    • 花几分钟创建一个小工具:“AbstractMachine 构建理解工具”

      • UNIX Philosophy: keep it simple, stupid
      • everything is a file; write things to work together using text interface
    • Get out of your comfort zone

Abstract-Machine 通过脚本+重定向可以转化为HTML代码进行阅读

1
2
3
4
┌──(jungle㉿jungle-virtual-machine)-[~/deom1/abstract-machine]
└─$ sudo apt install python3-markdown
┌──(jungle㉿jungle-virtual-machine)-[~/deom1/abstract-machine]
└─$ cat Makefile | sed 's/^\([^#]\)/    \1/g' | markdown_py > Makefile.html

注:使用了自动化工具python3-markdown

(0) 生成镜像和启动虚拟机 (cont’d)

  • 观察 AbstractMachine 程序编译过程的正确方法:
1
2
3
4
5
make -nB \
  | grep -ve '^\(\#\|echo\|mkdir\|make\)' \
  | sed "s#$AM_HOME#\$AM_HOME#g" \
  | sed "s#$PWD#.#g" \
  | vim -
  • Command line tricks

    • make -nB (RTFM)
    • grep: 文本过滤,省略了一些干扰项
      • echo (提示信息), mkdir (目录建立), make (sub-goals)
    • sed: 让输出更易读
      • 将绝对路径替换成相对路径
    • vim: 更舒适的编辑/查看体验
  • 部分make -nB的输出信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# Building hello-image [x86_64-qemu]
mkdir -p /home/jungle/deom1/deom/build/x86_64-qemu/ && echo + CC main.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/deom/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/deom/build/x86_64-qemu/main.o /home/jungle/deom1/deom/main.c
mkdir -p /home/jungle/deom1/deom/build/x86_64-qemu/ && echo + CC say.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/deom/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/deom/build/x86_64-qemu/say.o /home/jungle/deom1/deom/say.c
make -s -C /home/jungle/deom1/deom/../abstract-machine/am archive
# Building am-archive [x86_64-qemu]
mkdir -p /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/ && echo + AS src/x86/qemu/start64.S
x86_64-linux-gnu-gcc -MMD -I/home/jungle/deom1/deom/../abstract-machine/am/src -I/home/jungle/deom1/abstract-machine/am/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -m64 -fPIC -c -o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/start64.o /home/jungle/deom1/abstract-machine/am/src/x86/qemu/start64.S
mkdir -p /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/ && echo + AS src/x86/qemu/trap64.S
x86_64-linux-gnu-gcc -MMD -I/home/jungle/deom1/deom/../abstract-machine/am/src -I/home/jungle/deom1/abstract-machine/am/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -m64 -fPIC -c -o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/trap64.o /home/jungle/deom1/abstract-machine/am/src/x86/qemu/trap64.S
mkdir -p /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/ && echo + CC src/x86/qemu/trm.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/deom/../abstract-machine/am/src -I/home/jungle/deom1/abstract-machine/am/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/trm.o /home/jungle/deom1/abstract-machine/am/src/x86/qemu/trm.c
mkdir -p /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/ && echo + CC src/x86/qemu/cte.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/deom/../abstract-machine/am/src -I/home/jungle/deom1/abstract-machine/am/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/cte.o /home/jungle/deom1/abstract-machine/am/src/x86/qemu/cte.c
mkdir -p /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/ && echo + CC src/x86/qemu/ioe.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/deom/../abstract-machine/am/src -I/home/jungle/deom1/abstract-machine/am/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/ioe.o /home/jungle/deom1/abstract-machine/am/src/x86/qemu/ioe.c
mkdir -p /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/ && echo + CC src/x86/qemu/vme.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/deom/../abstract-machine/am/src -I/home/jungle/deom1/abstract-machine/am/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/vme.o /home/jungle/deom1/abstract-machine/am/src/x86/qemu/vme.c
mkdir -p /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/ && echo + CC src/x86/qemu/mpe.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/deom/../abstract-machine/am/src -I/home/jungle/deom1/abstract-machine/am/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/mpe.o /home/jungle/deom1/abstract-machine/am/src/x86/qemu/mpe.c
echo + AR "->" build/am-x86_64-qemu.a
ar rcs /home/jungle/deom1/abstract-machine/am/build/am-x86_64-qemu.a /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/start64.o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/trap64.o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/trm.o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/cte.o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/ioe.o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/vme.o /home/jungle/deom1/abstract-machine/am/build/x86_64-qemu/src/x86/qemu/mpe.o
make -s -C /home/jungle/deom1/deom/../abstract-machine/klib archive
# Building klib-archive [x86_64-qemu]
mkdir -p /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/ && echo + CC src/stdio.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/abstract-machine/klib/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/stdio.o /home/jungle/deom1/abstract-machine/klib/src/stdio.c
mkdir -p /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/ && echo + CC src/int64.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/abstract-machine/klib/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/int64.o /home/jungle/deom1/abstract-machine/klib/src/int64.c
mkdir -p /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/ && echo + CC src/string.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/abstract-machine/klib/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/string.o /home/jungle/deom1/abstract-machine/klib/src/string.c
mkdir -p /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/ && echo + CC src/cpp.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/abstract-machine/klib/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/cpp.o /home/jungle/deom1/abstract-machine/klib/src/cpp.c
mkdir -p /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/ && echo + CC src/stdlib.c
x86_64-linux-gnu-gcc -std=gnu11 -O2 -MMD -Wall -Werror -I/home/jungle/deom1/abstract-machine/klib/include -I/home/jungle/deom1/deom/../abstract-machine/am/include/ -I/home/jungle/deom1/deom/../abstract-machine/klib/include/ -D__ISA__=\"x86_64\" -D__ISA_X86_64__ -D__ARCH__=x86_64-qemu -D__ARCH_X86_64_QEMU -D__PLATFORM__=qemu -D__PLATFORM_QEMU -DARCH_H=\"arch/x86_64-qemu.h\" -fno-asynchronous-unwind-tables -fno-builtin -fno-stack-protector -Wno-main -U_FORTIFY_SOURCE -m64 -fPIC -mno-sse -c -o /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/stdlib.o /home/jungle/deom1/abstract-machine/klib/src/stdlib.c
echo + AR "->" build/klib-x86_64-qemu.a
ar rcs /home/jungle/deom1/abstract-machine/klib/build/klib-x86_64-qemu.a /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/stdio.o /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/int64.o /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/string.o /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/cpp.o /home/jungle/deom1/abstract-machine/klib/build/x86_64-qemu/src/stdlib.o
echo + LD "->" build/hello-x86_64-qemu.elf
x86_64-linux-gnu-ld -melf_x86_64 -N -Ttext-segment=0x00100000 -o /home/jungle/deom1/deom/build/hello-x86_64-qemu.elf --start-group /home/jungle/deom1/deom/build/x86_64-qemu/main.o /home/jungle/deom1/deom/build/x86_64-qemu/say.o /home/jungle/deom1/deom/../abstract-machine/am/build/am-x86_64-qemu.a /home/jungle/deom1/deom/../abstract-machine/klib/build/klib-x86_64-qemu.a --end-group
echo \# Creating image [x86_64-qemu]
make -s -C /home/jungle/deom1/deom/../abstract-machine/am/src/x86/qemu/boot
echo + CC start.S main.c
x86_64-linux-gnu-gcc -static -m32 -fno-pic -Os -nostdlib -Ttext 0x7c00 -I/home/jungle/deom1/deom/../abstract-machine/am/src -o bootblock.o start.S main.c
python3 genboot.py bootblock.o
echo + CREATE "->" build/hello-x86_64-qemu
( cat /home/jungle/deom1/deom/../abstract-machine/am/src/x86/qemu/boot/bootblock.o; head -c 1024 /dev/zero; cat /home/jungle/deom1/deom/build/hello-x86_64-qemu.elf ) > /home/jungle/deom1/deom/build/hello-x86_64-qemu

(0) 生成镜像和启动虚拟机 (cont’d)

想要看得更清楚一些?

  • :%s/ /\r /g
    • 每一个命令就像 “一句话”

编译

  • -std=gnu11, m64, -mno-sse, -I, -D, … (这对你配置 vscode 很重要)

链接

  • -melf_x86_64, -N, -Ttext-segment=0x00100000
  • 链接了需要的库 (am-x86_64-qemu.a, klib-x86_64-qemu.a)

彩蛋

  • make html

(1) 启动加载器 (Boot Loader)

512 字节中的代码,假设了镜像格式 (真正的的加载器有很多 stages)

  • 16-bit → 32-bit
  • ELF32/64 的加载器
    • 按照约定的磁盘镜像格式加载

代码讲解:

  • am/src/x86/qemu/boot/start.Smain.c
    • 它们都可以调试!
1
2
3
4
5
if (elf32->e_machine == EM_X86_64) {
  ((void(*)())(uint32_t)elf64->e_entry)();
} else {
  ((void(*)())(uint32_t)elf32->e_entry)();
}

总结

本次课回答的问题

  • Q: 操作系统也是程序,它如何用状态机如何定义?

Take-away message

  • 一切皆可调试 (包括 firmware)
    • 理解操作系统是如何被启动的
    • 学会使用 gdb (必备生存技能)
  • 操作系统也是程序
    • AbstractMachine 扩展了程序的语义,仅此而已

声明:本文章引用资料与图像均已做标注,如有侵权本人会马上删除