NJUOS-9-操作系统的状态机模型
本文最后更新于:2 年前
并发的内容,包括Debug的,在这里就已经全部够啦!!!可以做点小的习题+Model Checker看看代码执行的过程,接下来回到操作系统,继续学习!!!
大学&“专业”
大学的意义:将已有的知识和方法重新消化,为大家建立好 “台阶”,在有限的时间里迅速赶上数十年来建立起的学科体系。
- 操作系统真的就是个 C 程序
- 你只是需要 “被正确告知” 一些额外的知识
- 然后写代码、吃苦头
- 从而建立正确的 “专业世界观”
专业的素质:
“专业世界观” 的例子 (这些都没啥,paper 都发不了)
- 写 x86 模拟器的时候,不知道哪条指令错了,怎么办?
- 做操作系统实验的时候,如果遇到神秘 CPU Reset,怎么办?
- 做实验做不下去的时候,该实现什么工具?
“专业世界观” 的学习方法
- 经典研究论文 (OSDI, SOSP, ATC, EuroSys, …)
- 久经考验的经典教学材料 (xv6, OSTEP, CSAPP, …)
- 海量的开源工具 (GNU 系列, qemu, gdb, …)
- 第三方资料,慎用 (tutorials, osdev wiki, …)
如果难顶,可能还要自己写一点工具,能够帮助我们解决对应的问题(聪明的,有效率的)
操作系统的启动
我们已经知道如何写一个 “最小” 的 C 程序了:
- minimal.S
- 不需要链接任何库,就能在操作系统上运行
“程序 = 状态机” 没问题
- 带来更多的疑问
- 但谁创建的这个状态机???
- 当然是操作系统了……呃……
- 这个程序可以在没有操作系统的硬件上运行吗?
- “启动” 状态机是由 “加载器” 完成的
- 加载器也是一段程序 (状态机)
- 这个程序由是由谁加载的?
- 但谁创建的这个状态机???
egg or chicken first?
Bare-metal 与程序员的约定(硬件和软件之间的约定!)
为了让计算机能运行任何我们的程序,一定存在软件/硬件的约定。比如按下了CPU的电源,发生了什么。
CPU reset 后,处理器处于某个确定的状态(约定好的,固定的状态)-> 硬件厂商固定了硬件初始设置和一些固定的启动代码,然后固定开始执行(例如扫描外设)。这才有了操作系统的启动。
- PC 指针一般指向一段 memory-mapped ROM
- ROM 存储了厂商提供的 firmware (固件)
- 处理器的大部分特性处于关闭状态
- 缓存、虚拟存储、……
- PC 指针一般指向一段 memory-mapped ROM
Firmware (固件,厂商提供的代码)
- 将用户数据加载到内存
- 例如存储介质上的第二级 loader (加载器)
- 或者直接加载操作系统 (嵌入式系统)
- 将用户数据加载到内存
In computing, firmware is a specific class of computer software that provides the low-level control for a device’s specific hardware. Firmware, such as the BIOS of a personal computer, may contain basic functions of a device, and may provide hardware abstraction services to higher-level software such as operating systems.
- Legacy BIOS: 约定
- Firmware(硬件上写死的那些东西) 必须提供机制,将用户数据载入内存
- Legacy BIOS 把第一个可引导设备的第一个扇区加载到物理内存的
7c00
位置- 此时处理器处于 16-bit 模式
- 规定
CS:IP = 0x7c00
,(R[CS] << 4) | R[IP] == 0x7c00
- 可能性1:
CS = 0x07c0, IP = 0
- 可能性2:
CS = 0, IP = 0x7c00
- 可能性1:
- 其他没有任何约束
操作系统和Firmware(Legacy Bios)的约定是:启动磁盘的第一个512个字节,叫主引导扇区(Master Boot Record, MBR)。512个字节,由firmware帮助我们自动搬到内存中的某个固定位置,并且开始执行。
上面就是我们的操作系统和Firmware的第一次,也是唯一一次握手。
- 怎么表示,一个磁盘的前512个字节是可以启动的?
为啥Windows从C盘开始编号?因为A, B盘是软盘,会从软盘驱动器读出对应的内容,看看能不能启动昂!
- BIOS: 内存的7C00,存储的是磁盘的前512个字节,PC也指向这个位置。 -> BIOS就是帮助把代码搬过来,然后执行,开始拉起操作系统。
能不能看一下代码?
qemu给了gdb接口,我们可以在gdb中调试,这样就相当于给操作系统一个watchpoint,可以一步一步的看昂!!!
Gdb -x bootloader.gdb -> -x可以预先执行一些脚本,这样可以做很多初始化的配置昂!
鸡和蛋的问题解决(Firmware)
有个原始的鸡:Firmware
- 代码直接存在于硬件里
- CPU Reset 后 Firmware 会执行
- 加载 512 字节到内存 (Legacy Boot)
- 然后功成身退
Firmware 的另一用处
放置一些 “绝对安全的代码”
笑死ROM可写!!!植入病毒!!!
ROM给你更新了,在ROM里面写入了一个死循环??? -> 开机都开不了。。。这个ROM没有写保护,可以更新,就造成了firmware并植入了病毒昂!!!
操作系统的状态机模型
Firmware 和 boot loader 共同完成 “操作系统的加载”
- 初始化全局变量和栈;分配堆区 (
heap
) - 为main函数传递参数
- 谁给操作系统传递了参数?
- 如何实现参数传递?
进入 C 代码之后
- 完全遵循 C 语言的形式语义
- 但有一些行为 “补充” —— AbstractMachine API
一个迷你 “操作系统” thread-os.c
- make 会得到一个 “磁盘镜像”,好像魔法一样
- 就跟你们第一次用 IDE 的时候按一个键就可以编译运行一样
1 |
|
你怎么写一个操作系统?要知道并理解各个API的作用:
例如:
- 上面的mpe_init:将一个单线程的C程序,变成一个多线程的C程序。
- cpu_count():获得CPU的数量
mp_entry就是创建了cpu_count()个线程,每个线程都只有一个调用栈的栈帧。就是mp_entry,没有参数,PC就在代码的最头上。然后在CPU上执行调用就行
并发,多进程的状态机 -> 就是多进程处理器!!!
- 状态机是真的强啊。。。
所有的并发 + 系统调用(虚拟内存的翻译,中断),都可以在状态机中进行表示,本质就是状态机的转换!!!
上面这些API,就是状态机正确流转的最小的API啊!!!无非就是实现一个状态机嘛!!!
- 就上面这幅图,从BIOS, bootloader, main, mpe_init, CPU多线程执行,开中断之类的。从计算机一开始到最后多线程运行,本质就是一整个状态机的轮转哇!!!
- 例如CPU1开中断之后。变成了CPU1执行,CPU1中断和CPU2执行三条路径,状态机分叉增加,中断后面又跟着中断响应的处理,比如把CPU1的线程状态存入全局Context,然后改变指针指向去执行别的!!!
- GDB调试代码,本质上就是走了上面这幅图中的一根线!!!你跟着代码动态的,从状态机上走了一次。
- 这里可以看看原视频,讲的非常的好!!!1h 15min左右昂!!!
RTFSC时间
GDB调试代码,本质上就是走了上面这幅图中的一根线!!!你跟着代码动态的,从状态机上走了一次。正确阅读代码,就应该顺着状态机走一次,理解一次,停下来看看情况!!!而不是一味干读代码昂!!!
例如代码上有注释,注释不就是#,诶嘿!这个不就是markdown里面,不同的标题记号吗?输出成Markdown,不就可以方便的根据注释作为标题,来主动找代码逻辑吗?
过滤一些杂七杂八的内容!!! -> 而且,空格换成换行也很酷!!!
代码一下就清楚了昂!!!
用命令行,都是在编程(上面这种替换,就可以用正则表达式!!!)
- 老师刚刚给的那个例子,就可以调试,建议看看原视频。看看make是如何编译的!!!就和按一下Clion的按键一样,怎么一下子就跑起来了??? -> blackbox就会给人失控的感觉!
- 可以看看,我们是怎么去make,构造磁盘镜像过程的昂!!!
References
- 这个视频前几分钟特有意思,笑死我了,小学生 pk 大学生:https://www.bilibili.com/video/BV1yP4y1M7FE/?spm_id_from=333.999.0.0&vd_source=ff957cd8fbaeb55d52afc75fbcc87dfd
小学生很强orz