操作系统上的程序

操作系统上的程序

2025 南京大学《操作系统原理》
操作系统上的程序

Hello, OS World

Operating System: A body of software, in fact, that is responsible for making it easy to run programs (even allowing you to seemingly run many at the same time), allowing programs to share memory, enabling programs to interact with devices, and other fun stuff like that. (OSTEP)

要想理解 “操作系统”,就要理解什么是 “程序”

  • (这门课也一直从应用的视角讲操作系统)
2025 南京大学《操作系统原理》
操作系统上的程序

计算机程序

#include <stdio.h>

int main() {
    int a = 1;
    int b = 1;
    int c = a + b;

    printf("%d + %d = %d\n", a, b, c);
}

计算机:无情的执行指令的机器

  • 机器永远是对的
  • 如果编译器没有优化,“我们写什么,机器就执行什么
    • (编译器会对这个代码做什么优化呢?)
2025 南京大学《操作系统原理》
操作系统上的程序

理解 “计算机程序”

Everything is a state machine.

  • 任何程序都在计算机上运行
  • 计算机是状态机
  • 程序的执行就是状态的变化

C 语言的状态机模型

  • PicoC: a very small C interpreter for scripting; 和你的 NEMU 一样 “单步执行”:
while (1) {
    stmt = next_statement();
    execute(stmt);
}
2025 南京大学《操作系统原理》
操作系统上的程序

AI 老师登场

能不能把刚才的想法真的实现出来?

  • AI 时代:只要你想到,就能做到
    • 曾经为 gdb 编程是非常繁琐的
    • 但现在繁琐的事已经不需要人类来干了
      • 未来任何事都不需要人类了 😭

给我一个 Python 脚本,使得它能单步执行 gdb,并且输出一个 plot.md,嵌入 main 执行的状态迁移图,状态是局部变量 (只保留我程序中定义的变量),每一次 step 是一次迁移,执行的语句去掉行首空格画在 step 上。

2025 南京大学《操作系统原理》
操作系统上的程序

理解程序:递归 v.s. 非递归

实现递归到非递归的转换

center

C++ 课的噩梦?

  • 第一次领教了计算机里的 “函数” 不是数学里的 “函数”
  • 后悔没有选修 SICP 了 (里面讲了函数式编程呢)
2025 南京大学《操作系统原理》
操作系统上的程序

一年一度大语言模型挑战:递归转非递归

int f(int n) { return (n<=1) ? 1 : f(n-1) + g(n-2); }
int g(int n) { return (n<=1) ? 1 : f(n+1) + g(n-1); }
模型 汉诺塔 f 和 g 输出调用序列
GPT-3.5 (22.1) ✅ N/A N/A
GPT-4 (24.3) ✅ ❌ N/A
deepseek-v3 ✅ ❌ ❌
deepseek-r1 ✅ ✅ ❌ 错的 Python
o3-mini ✅ ✅ 逃课 ✅ 工整的标准答案

(deepseek-r1 有非常长的思考过程,思路正确,未来有戏)

2025 南京大学《操作系统原理》
操作系统上的程序

把任意程序改写成非递归

C 代码总是可以改写成等价的 “SimpleC”

  • 每条语句只做 “一次运算” (函数调用也是运算)
  • 条件语句中不包含运算

Everything (C 程序) = 状态机

  • 状态 = 变量数值 + 栈
  • 初始状态 = main 的第一条语句
  • 状态迁移 = 执行一条语句中的一小步
    • 为什么你觉得还写不出汉诺塔?
2025 南京大学《操作系统原理》
操作系统上的程序

非递归:实现 (Notes 里有加强版)

typedef struct {
  int pc, n;
  char from, to, via;
} Frame;

#define call(...) ({ *(++top) = (Frame) { .pc = 0, __VA_ARGS__ }; })
#define ret()     ({ top--; })
#define goto(loc) ({ f->pc = (loc) - 1; })

void hanoi(int n, char from, char to, char via) {
  Frame stk[64], *top = stk - 1;
  call(n, from, to, via);
  for (Frame *f; (f = top) >= stk; f->pc++) {
    switch (f->pc) {
      case 0: if (f->n == 1) {
      printf("%c -> %c\n", f->from, f->to); goto(4); }  break;
      case 1: call(f->n - 1, f->from, f->via, f->to);   break;
      case 2: call(       1, f->from, f->to,  f->via);  break;
      case 3: call(f->n - 1, f->via,  f->to,  f->from); break;
      case 4: ret();                                    break;
      default: assert(0);
    }
  }
}
2025 南京大学《操作系统原理》
操作系统上的程序

程序 = 状态机

“状态机” 是拥有严格数学定义的对象。这意味着你可以把定义写出来,并且用数学严格的方法理解它 —— 形式化方法

状态

  • [StackFrame, StackFrame, ...] + 全局变量

初始状态

  • 仅有一个 StackFrame(main, argc, argv, PC=0)
  • 全局变量全部为初始值

状态迁移

  • 执行 frames[-1].PC 处的简单语句
2025 南京大学《操作系统原理》
操作系统上的程序

这就是 C 语言的一切 (“形式语义”)

可以用这个语义实现任何纯粹的计算

  • 从简单到复杂: strlen, strstr, memcpy, sprintf, ...

但还有些东西实现不了

  • 有些标准库的行为超出了 “纯粹的计算”
  • 例子: putchar, exit
    • 观察:“纯粹的计算” 只能改变程序内的状态
    • 但这些 API 涉及到 “程序外的状态”
    • 这就是《操作系统》课的内容了
2025 南京大学《操作系统原理》