操作系统玩具:API

四个 “系统调用” API

  • choose(xs): 返回 xs 中的一个随机选项
  • write(s): 输出字符串 s
  • spawn(fn): 创建一个可运行的状态机 fn
  • sched(): 随机切换到任意状态机执行

除此之外,所有的代码都是确定 (deterministic) 的纯粹计算

  • 允许使用 list, dict 等数据结构

操作系统玩具:应用程序

操作系统玩具:我们可以动手把状态机画出来

count = 0

def Tprint(name):
    global count
    for i in range(3):
        count += 1
        sys_write(f'#{count:02} Hello from {name}{i+1}\n')
        sys_sched()

def main():
    n = sys_choose([3, 4, 5])
    sys_write(f'#Thread = {n}\n')
    for name in 'ABCDE'[:n]:
        sys_spawn(Tprint, name)
    sys_sched()

实现系统调用

有些 “系统调用” 的实现是显而易见的

def sys_write(s): print(s)
def sys_choose(xs): return random.choice(xs)
def sys_spawn(t): runnables.append(t)

有些就困难了

def sys_sched():
    raise NotImplementedError('No idea how')

我们需要

  • 封存当前状态机的状态
  • 恢复另一个 “被封存” 状态机的执行
    • 没错,我们离真正的 “分时操作系统” 就只差这一步

️🌶️ 借用 Python 的语言机制

Generator objects (无栈协程/轻量级线程/...)

def numbers():
    i = 0
    while True:
        ret = yield f'{i:b}'  # “封存” 状态机状态
        i += ret

使用方法:

n = numbers()  # 封存状态机初始状态
n.send(None)  # 恢复封存的状态
n.send(0)  # 恢复封存的状态 (并传入返回值)

完美适合我们实现操作系统玩具 (os-model.py)

玩具的意义

我们并没有脱离真实的操作系统

  • “简化” 了操作系统的 API
    • 在暂时不要过度关注细节的时候理解操作系统
  • 细节也会有的,但不是现在
    • 学习路线:先 100% 理解玩具,再理解真实系统和玩具的差异
void sys_write(const char *s) { printf("%s", s); }
void sys_sched() { usleep(rand() % 10000); }
int sys_choose(int x) { return rand() % x; }

void sys_spawn(void *(*fn)(void *), void *args) {
    pthread_create(&threads[nthreads++], NULL, fn, args);
}