本文簡要介紹rust語言中 Trait std::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 GlobalAlloc用法及代碼示例
- 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-lang.org大神的英文原創作品 Trait std::alloc::GlobalAlloc。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。