3个稳定版本
使用旧的Rust 2015
1.2.4 | 2019年7月25日 |
---|---|
1.2.3 | 2018年8月15日 |
1.2.2 | 2018年6月24日 |
#93 在 内存管理 中
54 每月下载量
用于 3 个crate(2个直接)
77KB
1K SLoC
libfringe
libfringe是一个实现安全、轻量级上下文切换的库,不依赖于内核服务。它可以在托管环境中(使用 std
)以及在裸机(使用 core
)上使用。
它提供了以下安全抽象
- 生成器实现,Generator。
它还提供了必要的底层构建块
- 一个可以被栈分配器实现的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版(LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可证(LICENSE-MIT 或 http://opensource.org/licenses/MIT)
任由您选择。
贡献
除非您明确声明,否则任何有意提交以包含在您的工作中的贡献,如Apache-2.0许可证所定义,应如上双授权,不附加任何额外条款或条件。