操作系统设计
设计问题的本质
目标
- 定义抽象概念:使用一些抽象概念来具象化操作系统
- 提供基本操作
- 确保隔离:进程隔离 故障隔离 用户隔离
- 管理硬件:驱动硬件来完成软件功能
操作系统设计的困难性
- 庞大
- 并发问题
- 安全性
- 通用性
- 可移植性
接口设计
- 从一个系统对外提供的接口开始设计,以此延伸到底层
设计原则
关于接口的设计原则,UNIX编程艺术提供了许多,诸如
- 简单:接口的简单性
- 完备:以最少的接口集合实现尽可能多的功能
- 效率:接口实现的执行效率
范型
选择操作系统对外提供的接口的表现形式
- 用户界面范型:接口对用户的表现形式 如 UI驱动
- 执行范型:应用程序代码的执行形式
- 过程性范型
- 事件驱动范型
- 数据范型:系统的结构对设备对外的表现形式 这种统一的形式并不代表一切操作都能用固定的几个系统调用来完成 还是需要特殊化处理
- unix的一切皆文件
- 而windows则是使用了句柄达到更通用的效果
系统调用接口
更多的代码意味着更多的 BUG 不要将牛逼的能力隐藏在抽象的接口后面
实现
系统结构
- 分层系统
- 外内核:能在用户进程实现的功能就不要放在内核里
- 基于微内核的CS系统:内核提供通信能力使各个插件(进程)能相互通信以完成功能
- 可扩展的系统:以可信的方式将更多的代码置入内核之中
- 内核线程
机制与策略
机制(如何做)与策略(做什么)分离有利于系统保持小巧和良好的结构
正交性
不同概念之间没有交集,可以独立组合,达到更强的能力
命名
- 使用名字或标识符来引用操作系统的一些数据结构,比如文件
绑定的时机
命名绑定的时机影响实现的简单与否以及灵活与否
- 早期绑定:在程序编译后 变量的位置就已经确定了
- 晚期绑定:在运行时,动态确定变量位置
静态与动态结构
对于内核必须在内存存储的一些数据,使用静态的数据结构,也就是内存是固定的,会简单许多
但对于通用应用,这种数据结构肯定得要是动态的,静态的结构可以预先知道进程如何被调度,而动态的结构则必须待到运行时动态决定
自顶向下与自底向上
自顶向下的问题在于首先可用的只有顶层的过程,但这些过程在没有底层支持的情况下基本没法测试
自底向上可以隐藏保护底层的细节,从底部逐层封装,逐层测试
同步通信与异步通信
同步的思想很简单,发起一个请求,阻塞直到回复到达,但一定引入并发,事情就复杂了,需要同步、需要锁
异步请求的简单性在于客户端发起请求,然后不阻塞,待请求异步回来时,再处理,这是把复杂性部分从服务端转移到了客户端
实用技术
隐藏硬件
使用统一的抽象概念屏蔽硬件细节
使用条件编译来处理针对不同硬件的编程
引用
所谓引用,就是标识符的数值与其表现含义相分离,如1代表A 在另外一种情况下 1 代表 B
映射关系可以改变,这样能获得尽可能大的解耦
可重用性
- 代码的复用
重入
代码具备同时被执行多次的能力
对于无状态代码,当然无所谓,但在操作系统级别,由于寄存器等的存在,好像没那么简单,需要通过互斥量或者锁来对临界资源进行保护
暴力法
对于数据量很少的问题,直接暴力解决就好了,这种问题不值得优化,并且解决方式很简单
检查可能的错误
- 操作系统需要做防御式编程,操作之前要检查所有可能的错误,然后再执行真正操作
性能
为什么慢
- 更多的特性、做更多的事
优化什么
- 足够好就够了,不要追求极致
空间时间权衡
无论是空间换时间,还是时间换空间,都需要对算法及数据结构做好深入考量
缓存
- 空间换时间的一种形式
线索(hint)
缓存总是正确的,线索并不保证正确性,需要调用者自行验证
局部性原理
- 最近使用越有可能被使用
- 最常访问越有可能被访问
- 使用的空间周围被使用概率更高
项目管理
设计趋势
- 虚拟化云化
- 多核化
- 内存越来越大化
- 移动化
- 嵌入式