越大的系统潜在的bug就越多,所以微内核在减少bug方面很有优势,seL4是世界上最小的内核之一。但是seL4的性能可以与当今性能最好的微内核相比。
作为微内核,seL4为应用程序提供少量的服务,如创建和管理虚拟内存地址空间的抽象,线程和进程间通信IPC。这么少的服务靠8700行C代码搞定。seL4是高性能的L4微内核家族的新产物,它具有操作系统所必需的服务,如线程,IPC,虚拟内存,中断等。
除了微内核,seL4另一大特色是完全的形式验证。
seL4的实现总是严格满足上一抽象层内核行为的规约,它在任何情况下都不会崩溃以及执行不安全的操作,甚至可以精确的推断出seL4 在所有情况下的行为,这是了不起的。
研究发现常用的攻击方法对seL4无效,如恶意程序经常采用的缓存溢出漏洞。
使用面向过程语言Haskell实现了一个内核原型,用它来参与形式验证,最后根据它,用C语言重新实现内核,作为最终内核。 顺便提一句,seL4有两只team,kernel team和verification team,而连接这两个team的是 Haskell prototype。
在用C开发内核的过程中,seL4对使用C进行了如下限制:
1. 栈变量不得取引用,必要时以全局变量代替
2. 禁止函数指针
3. 不支持union
对seL4的formal verification(形式验证)分为两步:abstract specification(抽象规范)和executable specification(可执行规范)之间,executable specification和implementation(实现)之间。有两个广泛的方法来进行formal verification: model checking(全自动)和交互式数学证明(interactive mathematical proof ),后者需要手工操作。seL4验证使用的形式数学证明来自Isabelle/HOL,属于后者。
具体来说seL4的形式验证步骤:
1. 写出IPC、syscall、调度等所有微内核对象(kernel object)的abstract specification(in Isabelle)
2. 写出如上对象的executable specification(in Haskell),并证明其正确实现了第一步的abstract specification,利用状态机的原理,abstract specification的每一步状态转换,executable specification都产生唯一对应的状态转换。
3. 写C实现。通过一个SML写的C-Isabelle转换器,和Haskabelle联合形式证明C代码和第二步的Haskell定义语义一致。
seL4的实现被证明是bug-free(没有bug)的,比如不会出现缓冲区溢出,空指针异常等。还有一点就是,C代码要转换成能直接在硬件上运行的二进制代码,seL4可以确保这个转换过程不出现错误,可靠。seL4是世界上第一个(到目前也是唯一一个)从很强程度上被证明是安全的OS。
seL4 is the first (and still only) protected-mode OS kernel with a sound and complete timeliness analysis. Among others this means that it has provable upper bounds on interrupt latencies (as well as latencies of any other kernel operations). It is therefore the only kernel with memory protection that can give you hard real-time guarantees. 从这段英文可以看出,seL4的硬实时性很强。
实际上OS的verification(验证)早在40年前就开始了,而seL4是振奋人心的,一是它拥有很强的属性(properties):功能正确性(functional correctness),完整性(integrity)和机密性(confidentiality),二是这些属性已经被形式验证到代码级别,先是C,现在又到了二进制。相比于之前人们对于OS的验证,seL4做得更彻底,但正是借助前人的工作,seL4才能如此优秀。
seL4的API调用分两步:第一,checking,验证参数,然后确定是否授权执行这个调用; 第二,execute,执行调用,永不失败。
The combined checking phases of all kernel calls are a substantial fraction of the kernel. 看来对内核调用的检查是很重要的部分。
接下来来了解一下kernel objects:
我们去了解一个OS,它的内存管理是必不可少的。内存分配模块可以被单独验证。在重新使用一块内存之前,所有对这个内存的引用必须要无效。seL4不动态为内核对象分配内存,内核对象要由应用程序控制的内存区域通过Untyped Memory来创建,这样可以精确控制应用程序使用的物理内存,而且可以做到程序与程序之间物理内存的isolation。
seL4是一个单内核栈的操作系统,在启动时期,seL4会预先为内核需要的内存如代码区,数据区和栈分配内存。
seL4实现了一个capability-based的访问控制模型。每一个用户空间的线程有一个相关联的capability space (CSpace),这个空间包含了这个线程处理的capabilities,也就是说它管理着这个线程访问的资源。
CNode有一些slot,每个slot需要16字节的物理内存,它可以恰恰保存一个capability。同其他对象一样,CNode必须要通过seL4 Untyped Retype()在合适数量的untyped memory上来创建。
seL4用TCB(thread control block)描述一个线程,每一个TCB对一个CSpace和VSpace(它们可与其他线程共享),一个TCB一般有一个IPC buffer。
seL4提供了消息传递机制用于线程间的通信。
seL4采用256个优先级的抢占式轮转调度机制,当一个线程创建或修改了另一个线程,它只能将另一线程的优先级设为比它低或跟它一样,线程优先级用函数seL4 TCB Configure() 和 seL4 TCB SetPriority() 来设置。