當前位置: 首頁>>編程示例 >>用法及示例精選 >>正文


Rust GlobalAlloc用法及代碼示例

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