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

no-std bin+lib alloc-no-stdlib

一个动态分配器,可以与或不与 stdlib 一起使用。这允许具有 nostd 的包动态分配内存,并且可以使用自定义分配器、栈上的项或希望简单地使用 Box<> 的包。它还提供了使用 calloc 或可变全局变量进行预零内存的选项。

9 个稳定版本

使用旧的 Rust 2015

2.0.4 2022年9月12日
2.0.3 2021年7月31日
2.0.1 2018年11月22日
2.0.0 2018年10月30日
1.0.0 2016年5月24日

#15 in 内存管理

Download history 509223/week @ 2024-03-14 494194/week @ 2024-03-21 524813/week @ 2024-03-28 549809/week @ 2024-04-04 532680/week @ 2024-04-11 540380/week @ 2024-04-18 535530/week @ 2024-04-25 523771/week @ 2024-05-02 537766/week @ 2024-05-09 558825/week @ 2024-05-16 530963/week @ 2024-05-23 622167/week @ 2024-05-30 650240/week @ 2024-06-06 616740/week @ 2024-06-13 631693/week @ 2024-06-20 562713/week @ 2024-06-27

2,590,683 每月下载量
1,629 个 crates 中使用 (8 直接使用)

BSD-3-Clause

41KB
823

在 #![no_std] 模块中分配内存的框架。

Build Status

要求

  • Rust 1.6

文档

目前没有标准方法可以在 no_std 模块中分配内存。这提供了一种机制来描述可以在栈上完全满足、通过不安全链接到 calloc 或通过不安全引用可变全局变量来满足的内存分配。如果未在内存上显式调用 free_cell,则该库当前会泄露内存。

然而,如果由可以依赖于 stdlib 的库链接,那么该库可以简单地传递几个分配器,并使用标准的 Box 分配,并将自动释放。

此库还应使可能通过预先使用 calloc 分配最大数据限制并使用 seccomp 禁止未来系统调用来完全隔离需要动态分配的 rust 应用程序。

使用方法

有三种分配内存的模式,每种模式都有其优点和缺点

在栈上

这可以在完全没有 stdlib 的情况下完成。然而,这会消耗栈深度的自然 ulimit,并且通常将程序限制在只有几兆的动态分配数据。

示例

// First define a struct to hold all the array on the stack.
declare_stack_allocator_struct!(StackAllocatedFreelist4, 4, stack);
// since generics cannot be used, the actual struct to hold the memory must be defined with a macro
...

// in the code where the memory must be used, first the array needs to be readied
let mut stack_buffer = define_allocator_memory_pool!(4, u8, [0; 65536], stack);
// then an allocator needs to be made and pointed to the stack_buffer on the stack
// the final argument tells the system if free'd data should be zero'd before being
// reused by a subsequent call to alloc_cell
let mut ags = StackAllocatedFreelist4::<u8>::new_allocator(&mut stack_buffer, bzero);
{
    // now we can get memory dynamically
    let mut x = ags.alloc_cell(9999);
    x.slice_mut()[0] = 4;
    // get more memory
    let mut y = ags.alloc_cell(4);
    y[0] = 5;
    // and free it, consuming the buffer
    ags.free_cell(y);

    //y.mem[0] = 6; // <-- this is an error: won't compile (use after free)
    assert_eq!(x[0], 4);

在堆上

这使用标准的 Box 功能来分配内存

let mut halloc = HeapAlloc::<u8>::new(0);
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], 0);
    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。这对希望在此点限制系统调用的被限制的应用程序非常有用。此选项keep不将内存设置为有效值,因此它必然是不安全的。

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的最简单方法就是简单地有一个全局分配的结构。访问可变静态变量需要不安全代码;然而,所以这段代码将调用不安全块。

请确保在代码中只在单一位置、单一时间引用global_buffer。如果它从两个地方或不同时间使用,则可能导致未定义的行为,因为多个分配器可能获取到global_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)
}

贡献者

  • Daniel Reiter Horn

无运行时依赖

功能