B. N. Bershad, S. Savage, P. Pardyak, E. G. Sirer, M. E. Fiuczynski, D. Becker, C. Chambers, and S. Eggers. 1995. Extensibility safety and performance in the SPIN operating system. SIGOPS Oper. Syst. Rev. 29, 5 (December 1995), 267-283. DOI: http://dx.doi.org/10.1145/224057.224077
问题和背景
动机和目的
-
不同的应用有不同的需求. 通用系统可能各种应用都能跑, 但效率都很差, 故需要 dynamic specialization 来类似做一个 system tuning 的工作, 使得系统特别适合某一种应用.
-
(dzy) 又如专注运行数据库 (让数据库自己处理磁盘调度, 改进分页) 或网路服务器 (sendfile, poll) 的系统. 又比如像 tlp, 自称安装了这个 package 就让系统变得省电.
-
传统通用系统一定程度上可以 dynamic specialize, 但是非常麻烦 (可能他们认为
EXPORT_SYMBOL
暴露的接口不好用吧) 易错 (比如垃圾驱动搞崩整个系统) 而且容易顾此失彼. (dzy: 并且效果也不好, 自称省电的 tlp 给我的系统 dynamic specialize 之后续航毫无提升) -
希望是, SPIN 自身可以是一个通用系统, 但可以很好的 dynamic specialize. 这里是通过给 kernel 加上 extension 的方式. (dzy: 可以理解为类似 kernel module 的, 具体后述)
关键问题
- 可拓展性: 以完成 dynamic specialization
- 安全: 保证 specialization 这个过程不会损坏系统
- 效率: specialization 若开销很大就可能抵消其好处
工作介绍
基础技术
为了实现可拓展性, SPIN 的方法是 内核定义一系列接口, 然后 extension 实现这些接口. (dzy: 接口就类似回调函数, 这样内核就可以主动调用 extension 中代码了)
因此, 接口需要细粒度, 而且需要权限管理. (dzy: 防止 faulty extension 崩掉整个系统)
-
Extension 动态链接入 kernel. 目的是低代价. 也就是说 kernel 内部有一个 linker.
-
要求 extension 使用 Modula 3 编写. 通过编译器来完成严格的模块化, i.e. 模块边界的保证. (当然用户应用可以用任何语言)
-
Extension 的保护隔离通过 logical protection domain, 实际上就是一个命名空间, 包含接口 (代码和数据). 跨域通信的代价降到一个函数调用.
-
只有发生 “事件” 的时候, 才会调用 extension. extension 可以监听事件. 事件在内核提供的接口中定义. 动态分派的代价也是一个函数调用.
Modula 3
SPIN 依赖 Modula 3 的安全和模块封装措施
- 类型安全: 这一块内存保存的数据的类型是 A, 访问就必须按照 A 来访问. (dzy: 似乎这也包含了 按名访存 的思想? 的确 按址访存 很不安全…)
- 内存安全: 自动内存管理 (dzy: GC 完成)
- 模块和接口
架构
第 3, 4, 5 节讲的就是架构.
SPIN 内核有两个组成部分
- core services
- extension services 它们在同样的内核地址空间运行.
连接这两部分的软件 infrastructure 包含
- Protection model: 高效细粒度的访问控制
- Extension model: 如何让 extension 能够集成到内核中
Protection Model
-
Capability: 内核资源, 如设备 / 内存区域, 用 capability 表示. (dzy: 也可以称为 handle / access token) Capability 就是一个引用 / 指针, 非常轻量级, 指向内核资源. 但是类型安全的编译器保证引用的用法是安全的, 也不能有人随便就造出一个 capability 出来 (不可伪造). (dzy: e.g. through some insane pointer arithmetic) 这个概念似乎来自 mach, 参见.
-
Protection Domain: 针对一个执行流, 包含一系列可用的名. 只有有权访问该域的代码才能访问这些名. (dzy: c.f. 传统按址访存的系统的 protection domain 就是地址空间) 不同 domain 可以互相交叉, 完成共享. (dzy: c.f. 传统系统的 shared memory) 编译器检查.
-
Domain 由目标文件 object file 表示, 安全的目标文件应当被 Modula 3 编译器签名. 目标文件导出的符号是该 domain 导出的, 导入的默认不被解析. 有相关方法
Create(objFile): T
,Resolve(src, mut tgt: T)
等.
Extension Model
有两个关键组成部分
- Event: 系统的状态发生变化, 或者系统需要某项服务.
如 IP 模块定义了事件
IP.PacketArrived(pkt: IP.Packet)
, - Handler: 接受处理 event 的函数/过程
实现中, event 是接口函数 (原型), handler 实现对应函数 (实现), 这样, 无论是表示系统状态变化的事件, 还是系统内部请求一个服务, 都可以直接调用这个接口函数 (event), 然后分派到 handler 执行.
(dzy: 这样命名让人觉得是强行面向事件, 简单的 interface / service function 就可以了啊)
权限控制就延续 protection model 的方法
- 要有权限处理某 event, 一个 extension 必须有权限实现它所在的接口
- 要有权限引起 (raise) 某 event, 一个 extension 必须有权限调用其所在的接口
这种架构当然还需要 dispatcher, registeration 等等概念. 一个 event 能有多个 handler, 默认有一个主 handler, 在其 default impl 模块中. 其他 extension 可以请求替换它或者注册额外的 handler. 主 handler 负责批准或拒绝这些请求, 以及给这些请求加上约束如这个额外 handler 是异步的, 需要在一定时间内执行完成等等.
Core Services
管理处理器和内存资源, 向 extensions 暴露细粒度的接口 (i.e. event).
管理内存包含地址分配和映射. 有三个底层接口分别是实页框分配, 虚页分配, 以及虚实映射控制. 这些接口由内核实现 (dzy: 显然为了安全), 用户可以提供 service (不是 extension 而是 core) 在其之上构建高层语义, 如 Unix 地址空间, Mach task 等.
管理处理器就是进程管理. 由简单的 Strand 接口完成, 用户应用可以提供自己的线程模型/线程实现 (如 C-Thread, Modula-3 Thread, goroutine) 和自己的 scheduler. 当然为了安全, 应用的 scheduler 只管用户态的线程, 进到内核态之后切换还是内核来做.
Core Services 应当是可信的, 它们需要符合 Spec. 如线程管理中 CheckPoint 之后不切换是违反语义的. 不过即使它们出错, 也只有使用它们的应用会受影响.