#分配器 #Redox #malloc #alloc #ralloc

nightly ralloc_shim

rallc内存分配器的绑定层

1 个不稳定版本

使用旧的Rust 2015

0.1.1 2016年12月21日

#538 in 内存管理


ralloc 中使用

MIT 许可证

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使用多阶段验证模型

  1. 类型检查器。验证的大部分工作都是完全静态完成的,并通过类型检查器强制执行。我们大量使用Rust的安全性功能,特别是归约类型。
  2. 单元测试。ralloc具有完整的单元测试覆盖率,甚至针对私有接口。
  3. 集成测试套件。ralloc使用一种生成性测试,其中测试通过一组固定函数“展开”。这使得相对较少的测试(例如,几百行)可以成倍增加并变得更有效。
  4. 运行时检查。ralloc试图避免运行时测试,只要可能,但这并不总是可能的。当确定安全性收益是显著的,而性能损失很小,我们使用运行时检查(例如缓冲区溢出检查)。
  5. 调试断言。ralloc包含许多调试断言,在调试模式下启用。这允许对双重释放、内存损坏、泄漏以及对齐进行检查等非常仔细的测试。
  6. 手动审查。一个人或多人审查补丁以确保高安全性。

通过类型系统实现安全

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