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 内存管理
2,590,683 每月下载量
在 1,629 个 crates 中使用 (8 直接使用)
41KB
823 行
在 #![no_std] 模块中分配内存的框架。
要求
- 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