本文简要介绍rust语言中 Trait alloc::alloc::GlobalAlloc
的用法。
用法
pub unsafe trait GlobalAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8;
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout);
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { ... }
unsafe fn realloc( &self, ptr: *mut u8, layout: Layout, new_size: usize ) -> *mut u8 { ... }
}
一个内存分配器,可以通过#[global_allocator]
属性注册为标准库的默认值。
一些方法要求当前通过分配器分配内存块。这意味着:
-
该内存块的起始地址先前由先前调用分配方法(例如
alloc
)返回,并且 -
内存块随后未被释放,其中块通过传递给释放方法(例如
dealloc
)或通过传递给返回非空指针的重新分配方法来释放。
示例
use std::alloc::{GlobalAlloc, Layout};
use std::cell::UnsafeCell;
use std::ptr::null_mut;
use std::sync::atomic::{
AtomicUsize,
Ordering::{Acquire, SeqCst},
};
const ARENA_SIZE: usize = 128 * 1024;
const MAX_SUPPORTED_ALIGN: usize = 4096;
#[repr(C, align(4096))] // 4096 == MAX_SUPPORTED_ALIGN
struct SimpleAllocator {
arena: UnsafeCell<[u8; ARENA_SIZE]>,
remaining: AtomicUsize, // we allocate from the top, counting down
}
#[global_allocator]
static ALLOCATOR: SimpleAllocator = SimpleAllocator {
arena: UnsafeCell::new([0x55; ARENA_SIZE]),
remaining: AtomicUsize::new(ARENA_SIZE),
};
unsafe impl Sync for SimpleAllocator {}
unsafe impl GlobalAlloc for SimpleAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let size = layout.size();
let align = layout.align();
// `Layout` contract forbids making a `Layout` with align=0, or align not power of 2.
// So we can safely use a mask to ensure alignment without worrying about UB.
let align_mask_to_round_down = !(align - 1);
if align > MAX_SUPPORTED_ALIGN {
return null_mut();
}
let mut allocated = 0;
if self
.remaining
.fetch_update(SeqCst, SeqCst, |mut remaining| {
if size > remaining {
return None;
}
remaining -= size;
remaining &= align_mask_to_round_down;
allocated = remaining;
Some(remaining)
})
.is_err()
{
return null_mut();
};
(self.arena.get() as *mut u8).add(allocated)
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
}
fn main() {
let _s = format!("allocating a string!");
let currently = ALLOCATOR.remaining.load(Acquire);
println!("allocated so far: {}", ARENA_SIZE - currently);
}
安全性
出于多种原因,GlobalAlloc
trait 是 unsafe
trait,实现者必须确保他们遵守这些约定:
-
如果全局分配器展开,这是未定义的行为。将来可能会取消此限制,但目前任何这些函数的Panics都可能导致内存不安全。
-
Layout
查询和计算通常必须正确。允许此特征的调用者依赖在每个方法上定义的协定,实施者必须确保此类协定保持真实。 -
即使源中有明确的堆分配,您也不能依赖实际发生的分配。优化器可能会检测到未使用的分配,它可以完全消除或移动到堆栈中,因此永远不会调用分配器。优化器可能会进一步假设分配是无误的,因此过去由于分配器故障而失败的代码现在可能会突然工作,因为优化器解决了分配的需要。更具体地说,无论您的自定义分配器是否允许计算发生了多少分配,以下代码示例都是不合理的。
drop(Box::new(42)); let number_of_heap_allocs = /* call private allocator API */; unsafe { std::intrinsics::assume(number_of_heap_allocs > 0); }
请注意,上述优化并不是唯一可以应用的优化。如果可以在不改变程序行为的情况下删除它们,您通常可能不会依赖发生的堆分配。分配是否发生并不是程序行为的一部分,即使它可以通过通过打印或其他方式跟踪分配的分配器检测到。
相关用法
- Rust Generator用法及代码示例
- Rust UdpSocket.set_multicast_loop_v6用法及代码示例
- Rust i64.overflowing_add_unsigned用法及代码示例
- Rust Box.downcast用法及代码示例
- Rust BTreeMap.last_key_value用法及代码示例
- Rust str.make_ascii_uppercase用法及代码示例
- Rust u128.checked_pow用法及代码示例
- Rust usize.wrapping_mul用法及代码示例
- Rust AtomicU8.fetch_sub用法及代码示例
- Rust PanicInfo.payload用法及代码示例
- Rust MaybeUninit.assume_init_mut用法及代码示例
- Rust String.try_reserve用法及代码示例
- Rust Mutex.new用法及代码示例
- Rust f32.exp用法及代码示例
- Rust Result.unwrap_or_else用法及代码示例
- Rust slice.sort_unstable_by_key用法及代码示例
- Rust Formatter.precision用法及代码示例
- Rust i128.log2用法及代码示例
- Rust OsStr.to_ascii_uppercase用法及代码示例
- Rust f32.hypot用法及代码示例
- Rust RefCell.try_borrow_unguarded用法及代码示例
- Rust i16.log10用法及代码示例
- Rust LowerExp用法及代码示例
- Rust HashSet.get_or_insert_with用法及代码示例
- Rust Bound.cloned用法及代码示例
注:本文由纯净天空筛选整理自rust-lang.org大神的英文原创作品 Trait alloc::alloc::GlobalAlloc。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。