#allocator #safe #no-std #calloc #heap-allocation #custom

no-std bin+lib alloc-stdlib

可用于与 stdlib 一起使用的动态分配器示例

4 个版本

使用旧的 Rust 2015

0.2.2 2022 年 9 月 12 日
0.2.1 2018 年 10 月 30 日
0.2.0 2018 年 10 月 30 日
0.1.0 2018 年 10 月 28 日

内存管理 中排名第 162

Download history 508833/week @ 2024-03-14 493727/week @ 2024-03-21 524041/week @ 2024-03-28 549291/week @ 2024-04-04 532292/week @ 2024-04-11 539919/week @ 2024-04-18 535132/week @ 2024-04-25 523264/week @ 2024-05-02 537299/week @ 2024-05-09 558390/week @ 2024-05-16 530509/week @ 2024-05-23 621785/week @ 2024-05-30 649912/week @ 2024-06-06 616254/week @ 2024-06-13 630911/week @ 2024-06-20 562200/week @ 2024-06-27

每月下载量 2,588,513
301 个 Crates (2 直接) 中使用

BSD-3-Clause

63KB
1K SLoC

为 #![no_std] 模块提供内存分配框架。

Build Status

要求

  • Rust 1.6

文档

目前没有标准方法可以在 no_std 模块中分配内存。这提供了一个机制,通过 rust-alloc-no-stdlib 描述的与 stdlib 无关的内存分配系统来分配内存,该系统可以通过不安全地链接到 calloc,或者通过不安全地引用可变的全局变量,完全在堆栈上满足内存分配。如果未在内存上调用 free_cell,则此库当前会泄漏内存。

然而,如果由实际上可以依赖 stdlib 的库链接,则该库可以简单地传递一些分配器,并使用标准的 Box 分配,并将自动释放。

此库还应使通过预分配最大数据量(使用 calloc)和使用 seccomp 禁止未来系统调用来完全隔离需要动态分配的 rust 应用程序成为可能。

用法

使用 stdlib 分配内存有 3 种模式,每种都有其优缺点

在堆上

这使用标准的 Box 功能来分配内存,并假设给定类型的默认构造函数

let mut halloc = StandardAlloc::new(0);
for _i in 1..10 { // heap test
    let mut x = <StandardAlloc as Allocator<u8>>::alloc_cell(&mut halloc, 100000)
    x[0] = 4;
    let mut y = <StandardAlloc as Allocator<u8>>::alloc_cell(&mut halloc, 100000)
    y[0] = 5;
    let mut z = <StandardAlloc as Allocator<u8>>::alloc_cell(&mut halloc, 100000)
    z[0] = 6;
    assert_eq!(y[0], 5);
    halloc.free_cell(y);
    assert_eq!(x[0], 4);
    assert_eq!(x[9], 0);
    assert_eq!(z[0], 6);
}

在堆上

这使用标准的 Box 功能来分配内存,但假设默认用户提供的值

let mut halloc = HeapAlloc::<u8>::new(8);
for _i in 1..10 { // heap test
    let mut x = halloc.alloc_cell(100000);
    x[0] = 4;
    let mut y = halloc.alloc_cell(110000);
    y[0] = 5;
    let mut z = halloc.alloc_cell(120000);
    z[0] = 6;
    assert_eq!(y[0], 5);
    halloc.free_cell(y);
    assert_eq!(x[0], 4);
    assert_eq!(x[9], 8);
    assert_eq!(z[0], 6);
}

在堆上,但未初始化

每次请求时都会分配数据,但不会分配内存,因此它是不可安全的。调用者必须正确地初始化内存

let mut halloc = unsafe{HeapAllocUninitialized::<u8>::new()};
{ // heap test
    let mut x = halloc.alloc_cell(100000);
    x[0] = 4;
    let mut y = halloc.alloc_cell(110000);
    y[0] = 5;
    let mut z = halloc.alloc_cell(120000);
    z[0] = 6;
    assert_eq!(y[0], 5);
    halloc.free_cell(y);
    assert_eq!(x[0], 4);
    assert_eq!(x[9], 0);
    assert_eq!(z[0], 6);
    ...
}

在堆上的单个池分配

在堆上执行单个大分配,之后将不再发生对 stdlib 的进一步使用。这对于希望在此点限制系统调用的隔离应用程序很有用

use alloc_no_stdlib::HeapPrealloc;
...
let mut heap_global_buffer = define_allocator_memory_pool!(4096, u8, [0; 6 * 1024 * 1024], heap);
let mut ags = HeapPrealloc::<u8>::new_allocator(4096, &mut heap_global_buffer, uninitialized);
{
    let mut x = ags.alloc_cell(9999);
    x.slice_mut()[0] = 4;
    let mut y = ags.alloc_cell(4);
    y[0] = 5;
    ags.free_cell(y);

    //y.mem[0] = 6; // <-- this is an error (use after free)
}

在堆上,未初始化

该功能在堆上执行一次大的分配,之后不再使用stdlib。这对于希望在此点限制系统调用的受限制应用可能很有用。此选项不会将内存设置为有效值,因此必须标记为不安全。

use alloc_no_stdlib::HeapPrealloc;
...
let mut heap_global_buffer = unsafe{HeapPrealloc::<u8>::new_uninitialized_memory_pool(6 * 1024 * 1024)};
let mut ags = HeapPrealloc::<u8>::new_allocator(4096, &mut heap_global_buffer, uninitialized);
{
    let mut x = ags.alloc_cell(9999);
    x.slice_mut()[0] = 4;
    let mut y = ags.alloc_cell(4);
    y[0] = 5;
    ags.free_cell(y);

    //y.mem[0] = 6; // <-- this is an error (use after free)
}

使用calloc

这是在没有stdlib的情况下获取零初始化的动态大小缓冲区的最高效方式。它确实调用了C的calloc函数,因此必须调用不安全代码。在这个版本中,单元格的数量固定为结构定义中指定的参数(本例中为4096)。

extern {
    fn calloc(n_elem : usize, el_size : usize) -> *mut u8;
    fn malloc(len : usize) -> *mut u8;
    fn free(item : *mut u8);
}

declare_stack_allocator_struct!(CallocAllocatedFreelist4096, 4096, calloc);
...

// the buffer is defined with 200 megs of zero'd memory from calloc
let mut calloc_global_buffer = unsafe {define_allocator_memory_pool!(4096, u8, [0; 200 * 1024 * 1024], calloc)};
// and assigned to a new_allocator
let mut ags = CallocAllocatedFreelist4096::<u8>::new_allocator(&mut calloc_global_buffer.data, bzero);
{
    let mut x = ags.alloc_cell(9999);
    x.slice_mut()[0] = 4;
    let mut y = ags.alloc_cell(4);
    y[0] = 5;
    ags.free_cell(y);
    //y.mem[0] = 6; // <-- this is an error (use after free)
}

使用静态可变缓冲区

如果在整个应用程序运行期间需要单个数据缓冲区,那么在不执行内存零操作和不使用stdlib的情况下完成此操作的最简单方法是简单地拥有一个全局分配的结构。访问可变静态变量需要不安全代码;但是,因此此代码将调用不安全块。

确保在代码中仅从单一位置、单一时间引用全局_buffer。如果它从两个地方或不同时间使用,则可能导致未定义的行为,因为多个分配器可能访问全局_buffer。

declare_stack_allocator_struct!(GlobalAllocatedFreelist, 16, global);
define_allocator_memory_pool!(16, u8, [0; 1024 * 1024 * 100], global, global_buffer);

...
// this references a global buffer
let mut ags = GlobalAllocatedFreelist::<u8>::new_allocator(bzero);
unsafe {
    bind_global_buffers_to_allocator!(ags, global_buffer, u8);
}
{
    let mut x = ags.alloc_cell(9999);
    x.slice_mut()[0] = 4;
    let mut y = ags.alloc_cell(4);
    y[0] = 5;
    ags.free_cell(y);

    //y.mem[0] = 6; // <-- this is an error (use after free)
}

贡献者

  • 丹尼尔·雷伊特·霍恩

依赖项