1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
#![deny(unsafe_op_in_unsafe_fn)] use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ffi::c_void; use crate::ptr; use crate::sync::atomic::{AtomicPtr, Ordering}; use crate::sys::c; use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; #[cfg(test)] mod tests; // Windows 上的堆内存管理是通过使用系统 Heap API (heapapi.h) 完成的 // See https://docs.microsoft.com/windows/win32/api/heapapi/ // 指示 `HeapAlloc` 返回的内存应清零的标志。 const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008; extern "system" { // 获取当前进程的默认堆的句柄,如果操作失败,则为 null。 // // SAFETY: 假设在同一进程中成功调用此函数会返回相同的句柄,该句柄在整个进程生命周期内始终有效。 // // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap fn GetProcessHeap() -> c::HANDLE; // 从给定的堆 `hHeap` 分配一块 `dwBytes` 字节的内存。 // 如果 `dwFlags` 设置为 `HEAP_ZERO_MEMORY`,分配的内存可能未初始化或归零。 // // // 返回指向新分配内存的指针,如果操作失败,则返回 null。 // 返回的指针将至少与 `MIN_ALIGN` 对齐。 // // SAFETY: // - `hHeap` 必须是 `GetProcessHeap` 返回的非空句柄。 // - `dwFlags` 必须设置为零或 `HEAP_ZERO_MEMORY`。 // // 请注意,与其他一些分配器相反,`dwBytes` 可以为零。 // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapalloc fn HeapAlloc(hHeap: c::HANDLE, dwFlags: c::DWORD, dwBytes: c::SIZE_T) -> c::LPVOID; // 将给定指针 `lpMem` 后面的一块内存从给定的堆 `hHeap` 重新分配到至少 `dwBytes` 字节的块,要么将块缩小到位,要么在新位置分配,复制内存并释放原始位置。 // // // 返回指向重新分配的内存的指针,如果操作失败,则返回 null。 // 返回的指针将至少与 `MIN_ALIGN` 对齐。 // 如果操作失败,给定的块将永远不会被释放。 // // SAFETY: // - `hHeap` 必须是 `GetProcessHeap` 返回的非空句柄。 // - `dwFlags` 必须设置为零。 // - `lpMem` 必须是一个非空指针,指向由 `HeapAlloc` 或 `HeapReAlloc` 返回的尚未被释放的已分配块。 // 如果块在新位置成功重新分配,则不能再次解引用指向已释放内存的指针,例如 `lpMem`。 // // 请注意,与其他一些分配器相反,`dwBytes` 可以为零。 // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heaprealloc // // // fn HeapReAlloc( hHeap: c::HANDLE, dwFlags: c::DWORD, lpMem: c::LPVOID, dwBytes: c::SIZE_T, ) -> c::LPVOID; // 从给定的堆 `hHeap` 中释放给定指针 `lpMem` 后面的一块内存。 // 如果操作成功则返回一个非零值,如果操作失败则返回零。 // // SAFETY: // - `hHeap` 必须是 `GetProcessHeap` 返回的非空句柄。 // - `dwFlags` 必须设置为零。 // - `lpMem` 必须是 `HeapAlloc` 或 `HeapReAlloc` 返回的一个尚未被释放的已分配块的指针。 // 如果块被成功释放,指向被释放内存的指针,如 `lpMem`,不得再次解引用。 // // // 注意 `lpMem` 允许为空,这不会导致操作失败。 // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree // fn HeapFree(hHeap: c::HANDLE, dwFlags: c::DWORD, lpMem: c::LPVOID) -> c::BOOL; } // 缓存到当前进程的默认堆的句柄。 // `GetProcessHeap` 返回的非空句柄,或在尚未初始化或 `GetProcessHeap` 失败时为空。 static HEAP: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut()); // 获取当前进程的默认堆的句柄,如果操作失败,则为 null。 // 如果此操作成功,则 `HEAP` 将成功初始化并包含 `GetProcessHeap` 返回的非空句柄。 // #[inline] fn init_or_get_process_heap() -> c::HANDLE { let heap = HEAP.load(Ordering::Relaxed); if heap.is_null() { // `HEAP` 尚未成功初始化 let heap = unsafe { GetProcessHeap() }; if !heap.is_null() { // SAFETY: 不需要锁定,因为在同一个进程中,成功调用 `GetProcessHeap` 将始终返回相同的值,即使在不同的线程上也是如此。 // HEAP.store(heap, Ordering::Release); // SAFETY: `HEAP` 包含一个由 `GetProcessHeap` 返回的非空句柄 heap } else { // 无法获取当前进程堆。 ptr::null_mut() } } else { // SAFETY: `HEAP` 包含一个由 `GetProcessHeap` 返回的非空句柄 heap } } // 获取当前进程默认堆的非空句柄。 // SAFETY: `HEAP` 必须已成功初始化。 #[inline] unsafe fn get_process_heap() -> c::HANDLE { HEAP.load(Ordering::Acquire) } // 包含指向已分配块开头的指针的标头。 // SAFETY: 大小和对齐方式必须 <= `MIN_ALIGN`。 #[repr(C)] struct Header(*mut u8); // 为给定的 `layout` 分配一块可选为零的内存。 // SAFETY: 返回一个满足 `System` 关于已分配指针的保证的指针,如果操作失败,则返回 null。 // 如果返回非空,那么 `HEAP` 将被成功初始化。 // #[inline] unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 { let heap = init_or_get_process_heap(); if heap.is_null() { // 分配失败,无法获取当前进程堆。 return ptr::null_mut(); } // 分配的内存将被清零或未初始化。 let flags = if zeroed { HEAP_ZERO_MEMORY } else { 0 }; if layout.align() <= MIN_ALIGN { // SAFETY: `heap` 是 `GetProcessHeap` 返回的非空句柄。 // 返回的指针指向已分配块的开始。 unsafe { HeapAlloc(heap, flags, layout.size()) as *mut u8 } } else { // 分配额外的填充以便能够满足对齐。 let total = layout.align() + layout.size(); // SAFETY: `heap` 是 `GetProcessHeap` 返回的非空句柄。 let ptr = unsafe { HeapAlloc(heap, flags, total) as *mut u8 }; if ptr.is_null() { // 分配失败。 return ptr::null_mut(); } // 从分配的块的开头创建一个正确对齐的指针偏移量,并在它之前写入一个头。 // let offset = layout.align() - (ptr as usize & (layout.align() - 1)); // SAFETY: `MIN_ALIGN` <= `offset` <= `layout.align()`,分配块的大小为 `layout.align() + layout.size()`。 // `aligned` 因此将是分配块内正确对齐的指针,其后面至少有 `layout.size()` 字节,前面至少有 `MIN_ALIGN` 字节的填充。 // // let aligned = unsafe { ptr.add(offset) }; // SAFETY: 因为标头的大小和对齐方式 <= `MIN_ALIGN` 并且 `aligned` 至少与 `MIN_ALIGN` 对齐并且在它之前至少有 `MIN_ALIGN` 个字节的填充,所以直接在它之前写入标头是安全的。 // // unsafe { ptr::write((aligned as *mut Header).offset(-1), Header(ptr)) }; // SAFETY: 返回的指针不指向已分配块的开始,但在它包含块开始位置之前有一个直接可读的标头。 // // aligned } } // 此分配器返回的所有指针除了 `GlobalAlloc` 的保证外,还具有以下属性: // // 如果使用 `layout` 指定对齐 <= `MIN_ALIGN` 分配或重新分配指针,则指针将至少与 `MIN_ALIGN` 对齐并指向已分配块的开头。 // // 如果使用 `layout` 指定对齐 > `MIN_ALIGN` 分配或重新分配指针,则指针将与指定对齐对齐,而不指向已分配块的开头。 // // 相反,在返回的指针之前会有一个直接可读的标头,包含块开始的实际位置。 // // // #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { // SAFETY: `allocate` 返回的指针满足 `System` 的保证 let zeroed = false; unsafe { allocate(layout, zeroed) } } #[inline] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { // SAFETY: `allocate` 返回的指针满足 `System` 的保证 let zeroed = true; unsafe { allocate(layout, zeroed) } } #[inline] unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { let block = { if layout.align() <= MIN_ALIGN { ptr } else { // 块开始的位置存储在 `ptr` 之前的填充中。 // SAFETY: 由于 `System` 的约定,`ptr` 被保证为非空并且在它之前有一个直接可读的标头。 // unsafe { ptr::read((ptr as *mut Header).offset(-1)).0 } } }; // SAFETY: 因为 `ptr` 已经用这个分配器成功分配了,所以 `HEAP` 一定已经成功初始化了。 // let heap = unsafe { get_process_heap() }; // SAFETY: `heap` 是 `GetProcessHeap` 返回的非空句柄,`block` 是指向已分配块开头的指针。 // unsafe { HeapFree(heap, 0, block as c::LPVOID) }; } #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { if layout.align() <= MIN_ALIGN { // SAFETY: 因为 `ptr` 已经用这个分配器成功分配了,所以 `HEAP` 一定已经成功初始化了。 // let heap = unsafe { get_process_heap() }; // SAFETY: `heap` 是 `GetProcessHeap` 返回的非空句柄,`ptr` 是指向已分配块开头的指针。 // // 返回的指针指向已分配块的开始。 unsafe { HeapReAlloc(heap, 0, ptr as c::LPVOID, new_size) as *mut u8 } } else { // SAFETY: `realloc_fallback` 使用 `dealloc` 和 `alloc` 实现,它们将正确处理 `ptr` 并返回满足 `System` 保证的指针 // unsafe { realloc_fallback(self, ptr, layout, new_size) } } } }