TockOS,一种新安全软件架构的RTOS介绍_tock os
文章目录
-
- 1. TockOS介绍
-
- 详细总结
- 2. TockOS开源项目的目录结构
- 3. 胶囊(Capsules)
-
- 胶囊的本质
- 胶囊的特点
- 胶囊的应用场景
- 4. 胶囊的实现
-
- 模块化设计
- 安全隔离
- 事件驱动
- 可复用性
1. TockOS介绍
Tock 是一款面向 Cortex-M 和 RISC-V 微控制器的安全嵌入式操作系统,依赖硬件内存保护单元(MPU)保障安全。其内核与扩展(胶囊)用 Rust 编写,能运行多种语言的多个独立不可信进程,进程数量受微控制器闪存和内存限制,默认采用抢占式循环调度算法。Tock 采用微内核架构,内核提供 command、subscribe、allow、yield 四个主要系统调用,通过 MPU 保护自身和其他进程免受恶意进程代码侵害,同时借助 Rust 的类型安全特性确保内核和胶囊代码的安全性。
详细总结
- Tock概述:Tock是用于Cortex-M和RISC-V微控制器的安全嵌入式操作系统,要求硬件配备内存保护单元(MPU),内核和胶囊使用Rust编写。
- 进程相关
- 编写语言与数量限制:支持用任意语言编写多个独立的不可信进程,进程数量受微控制器闪存和随机存取存储器(RAM)的限制。
- 调度算法:默认采用抢占式循环调度算法,也可配置使用其他调度算法。
- 系统架构
- 代码分类:代码分为核心内核、胶囊和进程三类。核心内核因需操作特定内存地址外设等功能,可使用“unsafe” Rust代码;胶囊不能使用unsafe特性,以保证代码安全。进程可由任意语言编写。
- 系统调用:内核提供四个主要系统调用,具体如下:
- 安全机制:借助硬件MPU,若进程试图访问非法内存,会触发异常,内核捕获异常并终止该进程,以此保护内核和其他进程。同时,Rust的类型安全特性防止内存错误使用,保障内核和胶囊代码安全。
2. TockOS开源项目的目录结构
TockOS的开源项目的目录结构如下图:
#mermaid-svg-FWZCsqvZo4nMc8HP {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FWZCsqvZo4nMc8HP .error-icon{fill:#552222;}#mermaid-svg-FWZCsqvZo4nMc8HP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FWZCsqvZo4nMc8HP .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-FWZCsqvZo4nMc8HP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FWZCsqvZo4nMc8HP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FWZCsqvZo4nMc8HP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FWZCsqvZo4nMc8HP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FWZCsqvZo4nMc8HP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FWZCsqvZo4nMc8HP .marker.cross{stroke:#333333;}#mermaid-svg-FWZCsqvZo4nMc8HP svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FWZCsqvZo4nMc8HP .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FWZCsqvZo4nMc8HP .cluster-label text{fill:#333;}#mermaid-svg-FWZCsqvZo4nMc8HP .cluster-label span{color:#333;}#mermaid-svg-FWZCsqvZo4nMc8HP .label text,#mermaid-svg-FWZCsqvZo4nMc8HP span{fill:#333;color:#333;}#mermaid-svg-FWZCsqvZo4nMc8HP .node rect,#mermaid-svg-FWZCsqvZo4nMc8HP .node circle,#mermaid-svg-FWZCsqvZo4nMc8HP .node ellipse,#mermaid-svg-FWZCsqvZo4nMc8HP .node polygon,#mermaid-svg-FWZCsqvZo4nMc8HP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FWZCsqvZo4nMc8HP .node .label{text-align:center;}#mermaid-svg-FWZCsqvZo4nMc8HP .node.clickable{cursor:pointer;}#mermaid-svg-FWZCsqvZo4nMc8HP .arrowheadPath{fill:#333333;}#mermaid-svg-FWZCsqvZo4nMc8HP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FWZCsqvZo4nMc8HP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FWZCsqvZo4nMc8HP .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-FWZCsqvZo4nMc8HP .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-FWZCsqvZo4nMc8HP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FWZCsqvZo4nMc8HP .cluster text{fill:#333;}#mermaid-svg-FWZCsqvZo4nMc8HP .cluster span{color:#333;}#mermaid-svg-FWZCsqvZo4nMc8HP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FWZCsqvZo4nMc8HP :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}#mermaid-svg-FWZCsqvZo4nMc8HP .userspace>*{fill:#ff9999!important;stroke:#ff0000!important;}#mermaid-svg-FWZCsqvZo4nMc8HP .userspace span{fill:#ff9999!important;stroke:#ff0000!important;}#mermaid-svg-FWZCsqvZo4nMc8HP .kernel>*{fill:#9999ff!important;stroke:#0000ff!important;}#mermaid-svg-FWZCsqvZo4nMc8HP .kernel span{fill:#9999ff!important;stroke:#0000ff!important;}#mermaid-svg-FWZCsqvZo4nMc8HP .hardware>*{fill:#99ff99!important;stroke:#00ff00!important;}#mermaid-svg-FWZCsqvZo4nMc8HP .hardware span{fill:#99ff99!important;stroke:#00ff00!important;}#mermaid-svg-FWZCsqvZo4nMc8HP .driver>*{fill:#ffff99!important;stroke:#ffff00!important;}#mermaid-svg-FWZCsqvZo4nMc8HP .driver span{fill:#ffff99!important;stroke:#ffff00!important;}#mermaid-svg-FWZCsqvZo4nMc8HP .security>*{fill:#ff99ff!important;stroke:#ff00ff!important;}#mermaid-svg-FWZCsqvZo4nMc8HP .security span{fill:#ff99ff!important;stroke:#ff00ff!important;} Hardware Board_Support Chip_Support Architecture_Support Capsules Kernel_Space User_Space Legend Physical Hardware Board Implementations Common Board Code nRF52 STM32 SAM4L Cortex-M Support RISC-V Support MPU Implementation Core Capsules Extra Capsules System Capsules Kernel Core Scheduler Process Management System Call Interface Hardware Abstraction Layer User Applications User Space Kernel Space Hardware Interface Driver Layer
3. 胶囊(Capsules)
Tock 是一个为物联网(IoT)设备设计的开源、多任务、安全的嵌入式操作系统。在 Tock OS 里,“胶囊(Capsules)”是其核心抽象概念之一,下面为你介绍其本质。
胶囊的本质
胶囊本质上是一种模块化的软件组件,是Tock OS用于实现设备驱动和系统服务的关键机制。它以面向对象的方式来封装设备的功能和状态,能把不同的硬件设备和系统服务进行抽象,从而让它们能在 Tock 内核中以独立、安全且可复用的形式存在。
胶囊的特点
- 模块化设计:胶囊具备模块化特性,每个胶囊都负责特定的功能,像传感器驱动、通信协议栈或者文件系统服务等。如此一来,开发者可以按需添加或者移除胶囊,以此实现系统功能的定制。
- 安全隔离:Tock OS 借助内存保护和能力机制保证各个胶囊间的安全隔离。每个胶囊只能访问其被授权的资源,这样就能防止恶意或者有缺陷的胶囊对系统其他部分造成影响。
- 事件驱动:胶囊采用事件驱动的编程模型。胶囊会对来自硬件或者其他胶囊的事件做出响应,然后执行相应的操作。这种模型有助于降低系统的复杂性,提高系统的响应能力。
- 可复用性:胶囊是可复用的软件组件。开发者可以在不同的项目中复用已有的胶囊,或者基于现有的胶囊开发新的功能。
胶囊的应用场景
- 设备驱动:为各类硬件设备(如传感器、执行器等)实现驱动程序。
- 系统服务:实现诸如网络协议栈、文件系统、电源管理等系统服务。
- 应用程序支持:为应用程序提供必要的接口和服务,确保应用程序能够安全、高效地运行。
4. 胶囊的实现
以下将结合 Tock OS 源代码来解释胶囊的实现,聊聊胶囊的各个特点以及对应的源代码。在 Tock OS 里,胶囊通常是 Rust 语言编写的结构体和相关的特征(trait)实现。
模块化设计
模块化设计体现为每个胶囊负责特定功能,且独立于其他胶囊。以 virtual_alarm
胶囊为例,它实现了一个虚拟闹钟服务。
// 定义一个虚拟闹钟的结构体pub struct VirtualMuxAlarm<\'a, A: Alarm> { alarm: &\'a A, // 其他状态字段 next_alarm: Option<Ticks>, callbacks: VecDeque<(Ticks, Callback)>,}// 为 VirtualMuxAlarm 实现相关的方法和特征impl<\'a, A: Alarm> VirtualMuxAlarm<\'a, A> { // 初始化虚拟闹钟 pub fn new(alarm: &\'a A) -> VirtualMuxAlarm<\'a, A> { VirtualMuxAlarm { alarm: alarm, next_alarm: None, callbacks: VecDeque::new(), } } // 设置闹钟 pub fn set_alarm(&mut self, when: Ticks, callback: Callback) { // 实现设置闹钟的逻辑 // ... }}
在上述代码中,VirtualMuxAlarm
结构体封装了虚拟闹钟的功能和状态。它依赖于一个实现了 Alarm
特征的底层闹钟设备,但本身是一个独立的模块,可被其他模块复用。
安全隔离
Tock OS 通过内存保护和能力机制确保胶囊间的安全隔离。在 Rust 中,这主要通过所有权和借用规则来实现。例如,在 virtual_alarm
胶囊中,VirtualMuxAlarm
结构体持有对底层闹钟设备的不可变引用:
pub struct VirtualMuxAlarm<\'a, A: Alarm> { alarm: &\'a A, // ...}
这里的生命周期参数 \'a
确保了 VirtualMuxAlarm
不会持有底层闹钟设备的引用超过其生命周期,避免了悬空引用和内存安全问题。同时,胶囊只能访问其被授权的资源,通过 Rust 的私有性规则(pub
和非 pub
成员)来限制对内部状态的访问。
事件驱动
胶囊采用事件驱动的编程模型。以 button
胶囊为例,它会对按钮按下和释放事件做出响应:
// 定义按钮事件的回调特征pub trait ButtonClient { fn pressed(&self, btn_num: usize); fn released(&self, btn_num: usize);}// 定义按钮胶囊结构体pub struct Button<\'a, C: ButtonClient> { client: &\'a C, // 其他状态字段 btn_state: [bool; NUM_BUTTONS],}// 实现按钮胶囊的方法impl<\'a, C: ButtonClient> Button<\'a, C> { // 处理按钮事件 pub fn handle_interrupt(&mut self) { for i in 0..NUM_BUTTONS { let new_state = self.read_button_state(i); if new_state != self.btn_state[i] { if new_state { self.client.pressed(i); } else { self.client.released(i); } self.btn_state[i] = new_state; } } } // 读取按钮状态 fn read_button_state(&self, btn_num: usize) -> bool { // 实现读取按钮状态的逻辑 // ... }}
在这个例子中,Button
胶囊会在检测到按钮状态变化时调用 ButtonClient
特征的 pressed
或 released
方法,从而实现事件驱动的编程模型。
可复用性
胶囊的可复用性体现在多个方面。例如,virtual_alarm
胶囊可以在不同的项目中复用,只要提供一个实现了 Alarm
特征的底层闹钟设备即可:
// 在某个项目中使用 VirtualMuxAlarmlet hardware_alarm = get_hardware_alarm();let virtual_alarm = VirtualMuxAlarm::new(&hardware_alarm);// 设置闹钟virtual_alarm.set_alarm(Ticks::from_ms(1000), Callback::new(...));
通过这种方式,开发者可以在不同的项目中复用 VirtualMuxAlarm
胶囊,而无需重新实现闹钟功能。