#context #stack #switch #safe #generator #safety #libfringe

nightly no-std fringe

安全,轻量级用户空间上下文切换

9个稳定版本

使用旧版Rust 2015

1.2.1 2016年9月22日
1.2.0 2016年9月9日
1.0.3 2016年8月31日

#222 in 操作系统


用于 fringe-futures

Apache-2.0/MIT

75KB
1K SLoC

crates travis rustdoc irccloud

libfringe

libfringe是一个实现安全、轻量级上下文切换的库,不依赖于内核服务。它可以在托管环境中(使用std)以及裸机(使用core)中使用。

它提供了以下安全抽象

它还提供了必要的低级构建块

  • 一个可以由堆分配器实现的特质,Stack;
  • 用于将切片引用用作堆栈的包装器,SliceStack;
  • 基于Box<[u8]>的堆分配器,OwnedStack;
  • 基于带有保护页的匿名内存映射的堆分配器,OsStack

libfringe强调安全性和正确性,并尽力确保不会违反平台ABI。

使用示例

extern crate fringe;

use fringe::{OsStack, Generator};

fn main() {
  let stack = OsStack::new(1 << 16).unwrap();
  let mut gen = Generator::new(stack, move |yielder, ()| {
    for i in 1..4 { yielder.suspend(i) }
  });

  println!("{:?}", gen.resume(())); // Some(1)
  println!("{:?}", gen.resume(())); // Some(2)
  println!("{:?}", gen.resume(())); // Some(3)
  println!("{:?}", gen.resume(())); // None
}

性能

libfringe在x86和x86_64上执行上下文切换仅需3纳秒!

test swap ... bench:         6 ns/iter (+/- 0)

可调试性

在所有实现上下文切换的库中,libfringe确保调用栈不会突然在生成器边界处结束。让我们考虑以下有缺陷的代码

extern crate fringe;

use fringe::{OsStack, Generator};

fn main() {
  let stack = OsStack::new(1 << 16).unwrap();
  let mut gen = Generator::new(stack, move |yielder, mut index| {
    let values = [1, 2, 3];
    loop { index = yielder.suspend(values[index]) }
  });

  println!("{:?}", gen.resume(5));
}

它将崩溃,并显示以下回溯(为了清晰而省略了部分内容)

thread 'main' panicked at 'assertion failed: index < self.len()', ../src/libcore/slice.rs:531
stack backtrace:
   [... core::panicking internals ...]
   9:     0x559ee50f677b - core::panicking::panic::hbfac80217e56ecbe
  10:     0x559ee50b6b4c - core::slice::_<impl core..ops..Index<usize> for [T]>::index::hcb117ddcc7cf2f33
                        at .../src/libcore/slice.rs:21
  11:     0x559ee50b7288 - crash_test::main::_{{closure}}::hc7da249d76d51364
                        at .../crash_test.rs:9
  12:     0x559ee50b6f23 - _<fringe..generator..Generator<Input, Output, Stack>>::unsafe_new::generator_wrapper::ha2da172d4f041d38
                        at .../libfringe/src/generator.rs:94
  13:     0x559ee50b76d3 - fringe::arch::imp::init::trampoline_2::hdb11eb4bdafcdeb9
                        at .../libfringe/src/arch/x86_64.rs:71
  14:     0x559ee50b76c4 - fringe::arch::imp::init::trampoline_1::h6b071b2a8ea6aab3
                        at .../libfringe/src/arch/x86_64.rs:43
  15:     0x559ee50b7098 - _<fringe..generator..Generator<Input, Output, Stack>>::resume::h8d2b90d386543e29
                        at .../libfringe/src/arch/x86_64.rs:131
                        at .../libfringe/src/context.rs:52
                        at .../libfringe/src/generator.rs:129
  16:     0x559ee50b71c8 - crash_test::main::hfc5e04bc99de7a6a
                        at .../crash_test.rs:12
  [... standard library startup internals ...]

同样,调试器、性能分析器以及使用DWARF调试信息的所有其他工具都可以全面了解调用栈。

请注意,堆栈应该足够深,以便恐慌机制可以存储其状态——在任何时候,至少应有8 KiB的空闲堆栈空间,否则将导致段错误。

限制

目前支持的架构:x86、x86_64、aarch64、or1k。

目前支持的平台:裸机、Linux(任何libc)、FreeBSD、DragonFly BSD、macOS。Windows不受支持(请参阅下面的说明)。

安装

libfringe是一个Cargo包。将以下内容添加到您的Cargo.toml

[dependencies.fringe]
version = "1.2.1"

要在裸机目标上使用 libfringe,请添加 no-default-features

[dependencies.fringe]
version = "1.2.1"
no-default-features = true

功能标志

libfringe 通过 Cargo 的功能标志 提供一些可选功能。目前,它们默认都是启用的。

alloc

此标志启用对 alloc crate 的依赖,这是 OwnedStack 所必需的。

valgrind

此标志启用 Valgrind 集成。libfringe 将将上下文堆栈注册到 Valgrind。

内部机制

libfringe 使用两种关键实现技术。

编译器辅助寄存器溢出

传统上,在用户空间实现上下文切换的库必须溢出所有被调用者保留的寄存器。另一方面,libfringe 将每个最终导致上下文切换的函数的调用完全内联,并使用内联汇编语句标记每个寄存器为破坏性,以实现上下文切换本身。

因此,在上下文切换代码中需要执行的最小工作量(LLVM 不支持溢出帧指针),这对于具有许多被调用者保留寄存器的架构尤为重要。

调用栈拼接

非 Windows 平台使用 DWARF 进行堆栈回溯和调试。DWARF 调用帧信息非常通用,对 ABI 兼容性无关——它定义了一种字节码,描述了需要执行的操作以模拟从函数返回。libfringe 使用此字节码指定,在生成器函数返回后,执行将继续在生成器函数上次恢复的位置继续。

Windows 兼容性

如前所述,libfringe 强调遵循平台 ABI。在 Windows 上,平台 ABI 不允许在创建线程时将堆栈指针移动到操作系统指定的范围之外。因此,libfringe 在 *nix 平台上使用的技术不适用,libfringe 不提供 Windows 支持。

你可能问,“那关于 mioco 呢?”mioco 库使用 context 库来实现上下文切换,这只不过是一个 boost::context 的包装器。boost::context 库在每次上下文切换期间都会更改 TIB 中的未记录字段,以尝试克服 Windows 平台 ABI 施加的限制。这之前已经失败过,而且肯定会再次失败,破坏了使用 boost::context 的现有代码,以意外和复杂的方式。libfringe 的作者认为这是不可接受的。

在 Windows 上实现用户模式上下文切换的唯一支持方式是 fibers。没有理由认为 libfringe 提供的安全抽象不能在此基础上实现;只是还没有完成。这应该是简单的,并且欢迎实现。请注意,虽然 UMS 线程 可以使用用户模式上下文切换,但它们涉及管理调度器线程以运行 UMS 线程,这与 libfringe 的设计不兼容。

许可证

根据您的选择,受以下任一许可证的许可:

贡献

除非您明确表示,否则根据 Apache-2.0 许可证定义,您提交给作品的所有贡献都应双重许可,不得附加任何额外条款或条件。

依赖关系