1 个不稳定版本
使用旧的Rust 2015
0.1.1 | 2016年12月21日 |
---|
#538 in 内存管理
在 ralloc 中使用
7KB
84 行
ralloc
一个快速且内存高效的用户空间分配器。
此分配器是Redox的默认分配器。
关于其状态的说明。
它完全工作,尽管它比jemalloc慢一些,因为它尚未优化。
我认为代码质量的状态非常好。
开箱即用的平台支持
- BSD
- Linux
- Mac OS X
- Redox
- Windows
使用ralloc
确保您有Rust nightly。
将 ralloc
添加到 Cargo.toml
[dependencies.ralloc]
git = "https://github.com/redox-os/ralloc.git"
然后在主文件中导入它
extern crate ralloc;
ralloc
现在可以使用了!
请注意,ralloc
不能与另一个分配器共存,除非它们故意兼容。
功能
线程局部分配
Ralloc使用全局-局部模型,允许在不使用锁、同步或原子写的情况下分配或释放。这提供了合理的性能,同时保留了灵活性和多线程的能力。
一流的调试器(默认:valgrind)支持
当启用 debugger
功能时,ralloc
将数据提供给 ralloc_shim
中指定的两个调试器符号。默认的 shim
实现已连接到 valgrind
,因此可以与 ralloc
一起使用以检测内存泄漏和未初始化的默认使用。
一切都可以自定义
您几乎可以配置、调整和自定义 ralloc
中的几乎所有内容。通过更改 shim
模块,这很容易实现。
例如,您可以更改重分配策略、memtrim限制、日志目标等。
日志记录
如果您启用 log
功能,您将获得分配器的详细日志,例如:
| : BRK'ing a block of size, 80, and alignment 8. (at bookkeeper.rs:458)
| : Pushing 0x5578dacb2000[0x0] and 0x5578dacb2050[0xffb8]. (at bookkeeper.rs:490)
|x : Freeing 0x1[0x0]. (at bookkeeper.rs:409)
x| : BRK'ing a block of size, 4, and alignment 1. (at bookkeeper.rs:458)
x| : Pushing 0x5578dacc2008[0x0] and 0x5578dacc200c[0xfffd]. (at bookkeeper.rs:490)
x|x : Reallocating 0x5578dacc2008[0x4] to size 8 with align 1. (at bookkeeper.rs:272)
x|x : Inplace reallocating 0x5578dacc2008[0x4] to size 8. (at bookkeeper.rs:354)
_|x : Freeing 0x5578dacb2058[0xffb0]. (at bookkeeper.rs:409)
_|x : Inserting block 0x5578dacb2058[0xffb0]. (at bookkeeper.rs:635)
在左侧,您可以看到块池的状态。x
表示非空块,_
表示空块,|
表示光标。
《a[b]
》是表示在地址a
上大小为b
的块的语法。
您可以在shim
中设置日志级别(例如,避免过多信息)。
自定义内存不足处理器
您可以通过以下方式设置自定义的OOM处理器:
extern crate ralloc;
fn my_handler() -> ! {
println!("Oh no! You ran out of memory.");
}
fn main() {
ralloc::set_oom_handler(my_handler);
// Do some stuff...
}
线程特定的OOM处理器。
您可以为当前线程覆盖全局OOM处理器。启用thread_oom
功能,然后进行以下操作:
extern crate ralloc;
fn my_handler() -> ! {
println!("Oh no! You ran out of memory.");
}
fn main() {
ralloc::set_thread_oom_handler(my_handler);
// Do some stuff...
}
部分分配
许多分配器将解分配限制为已分配的块,这意味着您不能进行算术运算或分割它。ralloc
没有这种限制。
extern crate ralloc;
use std::mem;
fn main() {
// We allocate 200 bytes.
let vec = vec![0u8; 200];
// Cast it to a pointer.
let ptr = vec.as_mut_ptr();
// To avoid UB, we leak the vector.
mem::forget(vec);
// Now, we create two vectors, each being 100 bytes long, effectively
// splitting the original vector in half.
let a = Vec::from_raw_parts(ptr, 100, 100);
let b = Vec::from_raw_parts(ptr.offset(100), 100, 100);
// Now, the destructor of a and b is called... Without a segfault!
}
顶级安全
如果您愿意牺牲一点性能以换取额外的安全性,可以带security
标志编译ralloc
。这将使释放为零,以及其他事情。
换句话说,攻击者不能注入恶意代码或数据,这可能会在忘记初始化您分配的数据时被利用。
代码验证
分配器极其安全关键。如果同一地址分配给两个不同的调用者,您将面临各种漏洞。因此,代码的审查和验证非常重要。
ralloc
使用多阶段验证模型
- 类型检查器。验证的大部分工作都是完全静态完成的,并通过类型检查器强制执行。我们大量使用Rust的安全性功能,特别是归约类型。
- 单元测试。
ralloc
具有完整的单元测试覆盖率,甚至针对私有接口。 - 集成测试套件。
ralloc
使用一种生成性测试,其中测试通过一组固定函数“展开”。这使得相对较少的测试(例如,几百行)可以成倍增加并变得更有效。 - 运行时检查。
ralloc
试图避免运行时测试,只要可能,但这并不总是可能的。当确定安全性收益是显著的,而性能损失很小,我们使用运行时检查(例如缓冲区溢出检查)。 - 调试断言。
ralloc
包含许多调试断言,在调试模式下启用。这允许对双重释放、内存损坏、泄漏以及对齐进行检查等非常仔细的测试。 - 手动审查。一个人或多人审查补丁以确保高安全性。
通过类型系统实现安全
ralloc
大量使用Rust的类型系统来保证安全性。内部,ralloc
有一个名为Block
的原语。这相当简单,表示内存的连续段,但有趣的是,它是如何通过归约类型系统在编译时进行检查以确保唯一的。
这只是许多例子中的一个。
平台无关性
ralloc
是平台无关的。它依赖于ralloc_shim
,这是一个用于平台相关函数的最小接口。提供了一个默认的ralloc_shim
实现(支持Mac OS、Linux和BSD)。
强制就地重新分配
就地重新分配可能比memcpy重新分配快得多。libc的一个限制是您不能只进行就地重新分配(这是一种有故障的方法,保证缓冲区不进行memcpy)。
有一种方法可以实现就地重新分配,这提供了一些有趣的选项。
extern crate ralloc;
fn main() {
let buf = ralloc::alloc(40, 1);
// BRK'ing 20 bytes...
let ptr = unsafe { ralloc::inplace_realloc(buf, 40, 45).unwrap() };
// The buffer is now 45 bytes long!
}
安全的SBRK
ralloc
提供了一个 sbrk
,可以安全使用而不会破坏分配器
extern crate ralloc;
fn main() {
// BRK'ing 20 bytes...
let ptr = unsafe { ralloc::sbrk(20) };
}
无用的对齐
对齐不一定是2的幂。
计划中的功能
可失败分配
通常情况下,您可能希望针对每种情况分别处理OOM。当处理非常大的分配时,这一点尤为重要。
ralloc
允许这样做
extern crate ralloc;
fn main() {
let buf = ralloc::try_alloc(8, 4);
// `buf` is a Result: It is Err(()) if the allocation failed.
}
lib.rs
:
ralloc
所依赖的符号和externs。
此crate为Linux、BSD和Mac OS提供了这些的实现/导入。
重要
由于POSIX规范中函数的分配没有提供保证,因此您不能使用libc库调用。因此,我们直接使用系统调用。
依赖项
~135KB