4.状态机模型的应用

虚拟化的最后两课,强化思想,看看状态机到底如何帮我们深入理解操作系统。

应用视角的操作系统。

在第二讲第一次接触状态机模型和 minimal.S 的时候,有同学会对 “为什么这么做” 感到困惑;但随着课程的深入,状态机不断帮助我们以原理性的思路去理解操作系统这个复杂、承载了计算机发展历史的概念。

从课程的角度讲,状态机对于理解是有好处的。但是到底还有什么应用呢?使得这门操作系统课如此强调状态机这个东西。这里再顺着状态机往前走一点点。

本讲内容:回顾操作系统对象、API 上是如何构建应用程序世界的:

  • 操作系统和应用程序的状态机模型

  • 状态机模型的应用

Everything is a State Machine

操作系统课的一个思路:一切都是状态机。

遇到的第一个状态机

#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:

启示:状态机是有初始状态的。程序是有初始状态的,代码是由指令组成的,不管是什么指令,对于底层机器来说,状态有内存和寄存器组成。每一次状态的迁移就是执行一条指令。指令有两类,一种是改变寄存器和内存状态的计算指令,还有一种是特别的 syscall 把状态机的执行交给操作系统的指令,操作系统可以帮装填机完成进程本身无法完成的事情如访问输入输出设备,获得时间等。

我们花了相当多的时间,把上面这段话展开来学习。其中也遇到了很多有意思的问题,如什么是初始状态。

对于 minimal.S 来说,指令载入内存,然后把起始地址放入 PC,这就能正常运行了。

更复杂的,比如静态链接的 ELF 用调试器 starti 调试第一条指令。动态链接的 ELF,有个 interpreter 来解析依赖库,可以用 ldd 命令查看。他们各自都描述了状态机初始状态的一部分,此外初始状态还有一部分是由 main 函数的参数 argc argv envp ,这几个参数是由 execve 系统调用传给进程的,这些都可以从手册上看到。ABI 手册上甚至规定了初始的栈上应该有什么,用这些规范告诉操作系统、libc、应用程序的实现者,只要遵循规范,自己写的程序就可以启动起来。

第2个状态机,非递归汉诺塔,C程序也是状态机,这个事情不显然。其状态是所有数据。C 的迁移,单步调试的每一步,只要能调试起来,就有个状态机模型。更进一步,能想清楚什么是函数调用。

我们把编程语言、编译器、计算机体系结构、二进制代码,全部连接起来了,连接的东西就是状态机。可以试着用状态机解释再 linux 里遇到的一切东西。

比如 linux 里的进程,我们可以看到内存里的各种东西 cat /proc/,我们可以对状态机做任何事情,状态机里所有的东西都是看得见摸得着的。

从应用的角度看操作系统,并不是那么困难。

第3个状态机,多线程程序,共享内存、独立堆栈的状态机。每一步可以任选一个线程往前走一步。多线程带来的新现象:状态的迁移变得不确定了,这带来了理解并发程序上的困难。

从简单的状态机,慢慢的建立起整个计算机系统的概念。从状态机出发,我们还可以理解系统调用。

系统调用:把一个外面的状态往状态机里送,或者改变状态机外部世界的状态。从这个角度,对操作系统另一种理解:操作系统是一个状态机的管理者。状态机瞬时状态的集合,提供 syscall 这样的状态迁移。这就是操作系统,操作系统是应用的容器,体现了操作系统虚拟化的属性。

应用发起 syscall ,不仅可以读写数据,还可以管理操作系统里的状态机,如 fork,execve,这就很容易理解了。在状态机的视角下,fork 一句话就讲清楚了:所以内存和寄存器的原样复制。

到这里,从应用视角的角度已经可以比较好的理解什么是操作系统了。

我们严格的知道了什么是程序,什么是系统调用,原则上只需要去看手册,就可以完整的写成想要实现的任何程序。

学完操作系统,在编程的方面就可以毕业了,这个世界上别人能实现出来的程序,我们都有办法实现了。当然,工程量如果太大,可能没时间,但是功能全部都是可以实现的了的。

比如,想要实现一个即时的编译器,,动态生成一段可以执行的代码,解释执行的模拟器性能会稍微差一点,有没有可能像编译器一样,把执行的指令,编译成本地的x86指令,不要像解释器一样,解释执行。

编译器做的事情是输入一段文本,转化一下,输出成另一段文本。当然实现起来,还是会有很多困难。总的来说,目前已经掌握了足够多的工具,游戏修改器,java虚拟机。

状态机:建模理解我们的世界

最后更新于