当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


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。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。