第四讲 Python 实现操作系统模型

可以说从这一讲开始,才真正迈入操作系统的殿堂 https://jyywiki.cn/OS/2023/build/lect4.ipynb 一个由 sys_write(),sys_spawn(),sys_sched(),sys_choose() 四个系统调用 组成的简单操作系统

jyy在2023年使用python重写了这个threads.h,几乎在50行里实现了一个解释器 因为我太菜了,所以这段代码看了我一下午才理解

 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
#os-model.py
import sys
import random
from pathlib import Path

class OperatingSystem():
    """A minimal executable operating system model."""

    SYSCALLS = ['choose', 'write', 'spawn', 'sched']
    class Thread:
        """A "freezed" thread state."""

        def __init__(self, func, *args):
            self._func = func(*args)
            self.retval = None

        def step(self):
            """Proceed with the thread until its next trap."""
            syscall, args, *_ = self._func.send(self.retval)# 第一次调用send 到达第一个yield,获得返回值 "choose",并将syscall: choose返回,交给match比较
            # 注意,这里同时传递了args参数,可以解答下面的xs的疑问
            self.retval = None
            return syscall, args

    def __init__(self, src):
        variables = {}
        exec(src, variables)
        self._main = variables['main']

    def run(self):
        threads = [OperatingSystem.Thread(self._main)]
        print(threads[0])
        while threads:  # Any thread lives
            try:
                match (t := threads[0]).step():
                    case 'choose', xs:  # Return a random choice
                        t.retval = random.choice(xs)
                    case 'write', xs:  # Write to debug console
                        print(xs, end='')
                    case 'spawn', (fn, args):  # Spawn a new threadL:::
                        threads += [OperatingSystem.Thread(fn, *args)]
                    case 'sched', _:  # Non-deterministic schedule
                        random.shuffle(threads)
            except StopIteration:  # A thread terminates
                threads.remove(t)
                random.shuffle(threads)  # sys_sched()

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print(f'Usage: {sys.argv[0]} file')
        exit(1)

    src = Path(sys.argv[1]).read_text()
    for syscall in OperatingSystem.SYSCALLS:
        src = src.replace(f'sys_{syscall}',        # sys_write(...)
                          f'yield "{syscall}", ')  #  -> yield 'write', (...)

    OperatingSystem(src).run()

前置知识yield

Generator Object

1
2
3
4
5
6
7
8
def numbers():
    i = 0
    while True:
        ret = yield f'{i:b}'
        i += ret
n=numbers()
n.send(None)
s.send(0)

在调用生成器函数的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息(保留局部变量),返回yield的值, 并在下一次执行next()方法时从当前位置继续运行,直到生成器被全部遍历完。

https://www.liaoxuefeng.com/article/895920356978720

send()的意思是,传入给ret值,让迭代器能接受外部的值,从而改变状态机的状态

1
2
3
4
5
6
7
def fab(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        print("fab:"+n+"val:"+a)
        a, b = b, a + b
        n = n + 1

如果你大致明白了 yield 是做什么的,再来看一下os-model.py究竟做了什么。

基础的玩具

读入thread.py的内容,这里有一个巧妙的地方,把threads.py替换成下面代码,通过exec运行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
count = 0
def Tprint(name):
    global count
    for i in range(3):
        count += 1
        yield "write", (f'#{count:02} Hello from {name}{i+1}\n')
        yield "sched", ()
def main():   
    n = yield "choose", ([3, 4, 5])
    yield "write", (f'#Thread = {n}\n')
    for name in 'ABCDE'[:n]:
        yield "spawn", (Tprint, name)
        yield "sched", ()

创建迭代器,然后选择迭代器执行下一步,就相当于选择状态机执行。在外部针对syscall做了传递

这个模型还可以更加简化,比如现在有3个迭代器,斐波那契迭代器,倍增迭代器,和倒数迭代器,每次通过random选择迭代器执行一步,然后转交控制权,等待调度执行。感谢python,让写代码变得这么容易。

 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
import random
def fab(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        print(f"fab:{n} val:{b}")
        a, b = b, a + b
        n = n + 1

def declin(max):
    n, a= 0, 0
    while n < max:
        yield a
        print(f"dec:{n} val:{a}")
        a = a - 1
        n = n + 1

def doubl(max):
    n, a= 0, 1
    while n < max:
        yield a
        print(f"dou:{n} val:{a}")
        a *= 2
        n = n + 1

threads = [fab(10),doubl(10),declin(10)]


while threads:
    try:
        random.shuffle(threads)
        next(threads[0])
    except StopIteration:  # A thread terminates
            threads.remove(threads[0])
            random.shuffle(threads) 

当你熟悉了python构建的状态机,来试试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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

static pthread_t threads[64];
static int nthreads = 0;

static inline void
sys_write(const char *s) {
  printf("%s", s);
  fflush(stdout);
}

static inline void
sys_sched() {
  usleep(rand() % 10000);
}

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

static inline int
sys_choose(int x) {
  return rand() % x;
}

// Constructor called before main()
static inline void __attribute__((constructor))
srand_init() {
  srand(time(0));
}

// Destructor called after main()
static inline void __attribute__((destructor))
thread_join() {
  for (int i = 0; i < nthreads; i++) {
    pthread_join(threads[i], NULL);  // Wait for thread terminations
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "os-real.h"

int count = 0;

void *Tprint(void *s) {
  char buf[64];
  for (int i = 0; i < 3; i++) {
    sprintf(buf, "#%02d Hello from %c%d\n", ++count, *(const char *)s, i);
    sys_write(buf);
    sys_sched();
  }
  return NULL;
}

int main() {
  int n = sys_choose(3) + 3;
  char buf[64];
  sprintf(buf, "#Thread = %d\n", n);
  sys_write(buf);
  for (int i = 0; i < n; i++) {
    sys_spawn(Tprint, &"ABCDE"[i]);
  }
}

将上面的调度改写成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
void *fab(int max){
  int n=0, a=0, b=1;
  char buf[64];
  while(n < max){
    sys_sched();
    sprintf(buf,"fab: %d val:%d\n",n,b);
    sys_write(buf);
    int tmp=a;
    a=b;
    b=tmp + b;
    n++;
  }
  return NULL;
}
void *declin(int max){
  int n=0,a=0;
  char buf[64];
  while (n<max){
    sys_sched();
    sprintf(buf,"dec: %d val:%d\n",n,a);
    sys_write(buf);
    a--;
    n++;
  }
  return NULL;
}
void *doubl(int max){
  int n=0,a=1;
  char buf[64];
  while (n<max){
    sys_sched();
    sprintf(buf,"dou: %d val:%d\n",n,a);
    sys_write(buf);
    a*=2;
    n++;
  }
  return NULL;
}
int main() {
  sys_spawn(doubl,10);
  sys_spawn(fab,10);
  sys_spawn(declin,10);
}

更完善的玩具

所以这节课学了什么呢?一个简化的操作系统模型,如果你对一个简化的系统了解非常深刻,再去学习实际的操作系统,去比较其中的差异,就不会那么困难了。