一个更 “全面” 的操作系统模型

进程 + 线程 + 终端 + 存储 (崩溃一致性)

系统调用/Linux 对应 行为
sys_spawn(fn)/pthread_create 创建从 fn 开始执行的线程
sys_fork()/fork 创建当前状态机的完整复制
sys_sched()/定时被动调用 切换到随机的线程/进程执行
sys_choose(xs)/rand 返回一个 xs 中的随机的选择
sys_write(s)/printf 向调试终端输出字符串 s
sys_bread(k)/read 读取虚拟设磁盘块 $k$ 的数据
sys_bwrite(k, v)/write 向虚拟磁盘块 $k$ 写入数据 $v$
sys_sync()/sync 将所有向虚拟磁盘的数据写入落盘
sys_crash()/长按电源按键 模拟系统崩溃

模型做出的简化

被动进程/线程切换

  • 实际程序随时都可能被动调用 sys_sched() 切换

只有一个终端

  • 没有 read() (用 choose 替代 “允许读到任意值”)

磁盘是一个 dict

  • 把任意 key 映射到任意 value
  • 实际的磁盘
    • key 为整数
    • value 是固定大小 (例如 4KB) 的数据
    • 二者在某种程度上是可以 “互相转换” 的

模型实现

原理与刚才的 “最小操作系统玩具” 类似

  • mosaic.py - 500 行建模操作系统
  • 进程/线程都是 Generator Object
  • 共享内存用 heap 变量访问
    • 线程会得到共享 heap 的指针
    • 进程会得到一个独立的 heap clone

输出程序运行的 “状态图”

  • JSON Object
  • Vertices: 线程/进程、内存快照、设备历史输出
  • Edges: 系统调用
    • 操作系统就是 “状态机的管理者”

建模的意义

我们可以把状态机的执行画出来了!

  • 可以直观地理解程序执行的全流程
  • 可以对照程序在真实操作系统上的运行结果

这对于更复杂的程序来说是十分关键的

void Tsum() {
  for (int i = 0; i < n; i++) {
    int tmp = sum;
    tmp++;
    // 假设此时可能发生进程/线程切换
    sum = tmp;
  }
}