系统与数据库2017sing.stanford.edu
Ownership is Theft:用Rust构建嵌入式操作系统的经验(2015)
Ownership is Theft: Experiences Building an Embedded OS in Rust (2015)
进阶 · 约 60 分钟读完前置知识:Rust 所有权与借用、嵌入式系统基础、操作系统内核概念
导读
这篇 2015 年的论文(Tock 操作系统)报告了在嵌入式系统上用 Rust 构建操作系统的经验。作者发现 Rust 的所有权模型主要基于线程假设,而嵌入式平台高度事件驱动,导致所有权机制阻碍了嵌入式领域常见的安全资源共享、与硬件资源的现实冲突,并妨碍用闭包进行异步编程。
HN 关注点:Rust 所有权模型在嵌入式/事件驱动场景下的局限性,以及这些 2015 年提出的问题如今是否已被语言或库层面的方案解决。
后来怎么样
Tock 成为嵌入式 Rust 的代表性项目并持续发展,论文揭示的所有权与事件驱动冲突推动了 embedded-hal、lazy_static 等嵌入式 Rust 生态的改进,是嵌入式 Rust 领域的早期重要经验报告。
HN 评论精选
这几条评论里:支持 1 质疑 2 补充 1
补充论文已过时,许多当年提出的问题如今已有语言或库层面的解决方案
我是一名嵌入式 Rust 开发者,看到这是 2015 年的论文才明白为何有些困惑。如今论文中提出的许多问题在语言和库层面都已有解决方案。比如论文提到静态初始化无法调用 size_of<T>() 函数来为闭包分配缓冲区,这可以用 lazy_static crate 解决——它允许在运行时执行代码来初始化静态变量,且兼容 no_std,可在内核类环境中使用。所以对这篇论文要有所保留,它已经过时了。
bascule查看英文原文
I'm an embedded Rust developer, and was a little confused by this paper until I noticed it was written in 2015. There are now solutions to a number of the problems they pose both at the language level and the library level: Here's one in particular: In principal, this would allow us to statically allocate a buffer sized for each particular closure. Unfortunately, there is no size_of keyword in Rust, and static initialization cannot invoke functions— in this case the function size_of<T>() -> usize provided by the Rust core library. This can be solved by using the lazy_static crate: https://github.com/rust-lang-nursery/lazy-static.rs From the description in the README: Using this macro, it is possible to have statics that require code to be executed at runtime in order to be initialized. Hey look, it's exactly what they need! It's also "no_std" compatible and can work just fine in a kernel-like context. Take this paper with a grain of salt: it's a bit dated at this point, and there are solutions for a number of the problems posed.
质疑问题根源是 OOP 式封装和回调副作用,改用纯函数式状态建模即可避开借用检查冲突
我认为这些问题部分源于过度依赖面向对象实践,尤其是把状态封装进对象、在回调中触发副作用。以论文中'通过闭包回调'的例子为例:嵌入式系统中不能有多个事实来源,activityLed 不能在多个回调间共享不只是 Rust 所有权的原因——多个事件影响同一输出时就应显式同步。更好的做法是把新状态的计算建模为纯函数,副作用通过把期望效果存为数据、由专门函数实现来分离,时间也只是数据。这大幅提升可测试性,且完全符合 Rust 的借用检查。
jononor查看英文原文
I feel that some of these issues occur due to an over-reliance on object-oriented practices, in particular 'encapsulation' of state into objects and triggering side-effects in callbacks. The usage of Rust brings painpoints to the fore, but I'll argue that the code is better done otherwise, independently of borrow checker. For instance in the 'Callback through closures' example, there is code like: setTimeout(|| { activityLed.toggle(); }, 2000); First, in an embedded system, you cannot have multiple sources of truth. So its not just because of Rust ownership reasoning that `activityLed` cannot be shared between callbacks - if multiple events is to be considered for a given output they should be syncronized explicitly. Instead I think the code should be more like the following, which models calculation of new state as a pure function. Side-effects are separated out by storing the desired effect as data, dedicated function for realizing. Time is just data, time-based logic without depending on hidden state. This increases testability significantly. And it can be fully reasoned about by Rust, no conflicts with the borrow checker. # for each tick # calculate new state if (now >= state.toggleLedTime) { state.activityLed = !state.activityLed; state.toggleLedTime = now + 2000; } # and realize it updatePin(config.ledPin, state.activityLed);
质疑论文误解了 &mut 唯一性的动机(与线程安全无关),并探讨了放宽保证的替代语言设计
论文中关于执行上下文的讨论基于一个错误假设:&mut 的唯一性与线程安全有关。其实无关——在 scoped threads 出现之前的多年里,它与 Rust 的线程安全毫无关系。论文虽有所提及(我也和作者讨论过),但 IMO 低估了这一保证的重要性。我曾与一位作者讨论过调整保证的可能性:允许在不含运行时类型信息(如枚举和向量)的情况下使用多个 &mut。这会造就一门差异很大、与生态不兼容的语言,但仍是安全的,且可能有用。我甚至开始改编译器 fork 来实现,但没来得及完成。
Manishearth查看英文原文
The whole execution contexts thing is sort of based off an assumption that the unique-&mut stuff has to do with thread safety. It doesn't. In fact, it had no bearing on thread safety in Rust for the many years before scoped threads were made possible by removing the 'static bound on Send. http://manishearth.github.io/blog/2015/05/17/the-problem-with-shared-mutability/ details why it's necessary. The article does sort of mention this (and I have talked with the authors about this before) but IMO it underrepresents the importance of it. One thing I did discuss with one of the authors at one point was swapping around the guarantees a bit -- allowing multiple &mut for cases not involving any form of runtime typing (enums and vectors are both cases of runtime typing -- in a vector the number of elements is runtime dependent). This would create a significantly different language and be incompatible with the vast majority of the ecosystem; however, it would still be safe, and has the potential to be useful. I even started hacking on a fork of the compiler that does this, but never got the chance to finish it. The idea in essence is not too hard to implement.
支持引用摘要:Rust 所有权模型以线程为前提,与事件驱动的嵌入式场景冲突
摘要指出:嵌入式平台高度事件驱动,而 Rust 的内存安全机制基本以线程为前提。在用 Rust 开发嵌入式操作系统的过程中,我们发现 Rust 的所有权模型阻碍了嵌入式领域常见的安全资源共享,与硬件资源的现实冲突,并妨碍使用闭包进行异步编程。
jononor查看英文原文
From the abstract: "...embedded platforms are highly event-based, and Rust’s memory safety mechanisms largely presume threads. In our experience developing an operating system for embedded systems in Rust, we have found that Rust’s ownership model prevents otherwise safe resource sharing common in the embedded domain, conflicts with the reality of hardware resources, and hinders using closures for programming asynchronously..."
相关资源
从这篇论文所有 HN 讨论帖的全部评论里整理出来的链接。
历次讨论
- 2017Hacker News 讨论帖171 分 · 116 评
同领域还值得读
I/O 比 CPU 更快——让我们划分资源并消除操作系统抽象
I/O Is Faster Than CPU – Let’s Partition Resources and Eliminate OS Abstractions
指出在现代服务器硬件上 I/O 速度已接近甚至超过 CPU,传统操作系统抽象反而成为瓶颈。
系统与数据库2019 · 817 分 · 277 评进阶 · 约 30 分钟编辑精选使用 io_uring 的高效 I/O
Efficient IO with io_uring
由 Linux 内核开发者 Jens Axboe 撰写,介绍 Linux 新的异步 I/O 接口 io_uring。
系统与数据库2019 · 426 分 · 96 评进阶 · 约 45 分钟编辑精选线程对大多数用途而言是个坏主意(1995)
Threads Are a Bad Idea for Most Purposes (1995)
这篇 1995 年的文章由 John Ousterhout 撰写,讨论为什么线程对大多数用途而言是坏主意,并主张事件驱动编程通常更简单、更高效。
系统与数据库2017 · 423 分 · 382 评进阶 · 约 25 分钟编辑精选学习型索引结构之论证
The Case for Learned Index Structures
提出把索引看作一种"模型":既然 B-tree 等结构本质上是在学习数据的分布,就可以用机器学习模型(如回归模型、神经网络)来近似键到位置的映射,从而替代 B-tree、哈希索引…
系统与数据库2017 · 398 分 · 66 评 · 引用 25进阶 · 约 60 分钟编辑精选FoundationDB:一个分布式、解耦的事务型键值存储
FoundationDB: A distributed, unbundled, transactional key value store
介绍 FoundationDB:一个分布式、解耦设计的事务性键值存储。
系统与数据库2021 · 283 分 · 99 评进阶 · 约 90 分钟编辑精选