#context #stack #switch #generator #safe #userland #context-switching

nightly no-std deploy-temp-fringe

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

3个稳定版本

使用旧的Rust 2015

1.2.4 2019年7月25日
1.2.3 2018年8月15日
1.2.2 2018年6月24日

#93内存管理

Download history 30/week @ 2024-02-19 17/week @ 2024-02-26 7/week @ 2024-03-04

54 每月下载量
用于 3 个crate(2个直接)

Apache-2.0/MIT

77KB
1K SLoC

crates travis rustdoc irccloud

libfringe

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

它提供了以下安全抽象

它还提供了必要的底层构建块

  • 一个可以被栈分配器实现的trait,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 'index out of bounds: the len is 3 but the index is 5', /checkout/src/libcore/slice.rs:658
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
   0: <usize as core::slice::SliceIndex<T>>::index
             at /checkout/src/libcore/slice.rs:658
   1: core::slice::<impl core::ops::Index<I> for [T]>::index
             at /checkout/src/libcore/slice.rs:560
   2: crash_test::main::{{closure}}
             at ./src/main.rs:9
   3: <fringe::generator::Generator<'a, Input, Output, Stack>>::unsafe_new::generator_wrapper
             at /home/edef/src/github.com/edef1c/libfringe/src/generator.rs:137
   4: fringe::arch::imp::init::trampoline_2
             at /home/edef/src/github.com/edef1c/libfringe/src/arch/x86_64.rs:116
   5: fringe::arch::imp::init::trampoline_1
             at /home/edef/src/github.com/edef1c/libfringe/src/arch/x86_64.rs:61
   6: <fringe::generator::Generator<'a, Input, Output, Stack>>::resume
             at /home/edef/src/github.com/edef1c/libfringe/src/arch/x86_64.rs:184
             at /home/edef/src/github.com/edef1c/libfringe/src/generator.rs:171
   7: crash_test::main
             at ./src/main.rs:12

同样,调试器、分析器以及所有使用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 包的依赖,这是 OwnedStack 所必需的。

valgrind

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

内部结构

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

编译器辅助寄存器溢出

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

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

调用栈拼接

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

Windows 兼容性

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

您可能会问,“但是关于 mioco 呢?”mioco 库使用 context 库来实现上下文切换,这只不过是对 boost::context 的包装。boost::context 库在每次上下文切换期间更改未记录的字段,以尝试绕过 Windows 平台 ABI 施加的限制。这以前 失败过,而且注定还会再次失败,破坏使用 boost::context 的现有代码,以出人意料和复杂的方式工作。libfringe 的作者认为这是不可接受的。

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

许可证

根据以下任一许可证授权

任由您选择。

贡献

除非您明确声明,否则任何有意提交以包含在您的工作中的贡献,如Apache-2.0许可证所定义,应如上双授权,不附加任何额外条款或条件。

依赖关系