Windows 编程/内存子系统
C 程序员无疑会熟悉 stdlib 内存分配函数,例如 malloc、realloc、calloc 等。这些函数基于 Win32 API 中的许多其他函数,这些函数处理内存段。
在谈论内存子系统时,Windows 管理着 4 种不同类型的内存,每种类型的内存都有许多不同的函数来分配和释放该内存。
- 虚拟内存
- 虚拟内存子系统通过页面分配和管理内存。这意味着内存只能一次以 4 KB(或更大,取决于处理器架构)的块分配。对于大多数应用程序来说,这是一个非常大的内存量,并且在大多数程序中使用虚拟内存函数是过度的。但是,一些程序确实需要使用整个页面进行存储,因此虚拟内存子系统可以用于此目的。
- 堆内存
- 堆是内存区域,通常占一个页面或页面的几分之一。Windows 立即为每个进程分配一个堆,称为进程堆。stdlib 函数(如 malloc)将从此内存区域分配内存。堆可以被划分为小的内存段,用于变量和动态存储。
- 全局内存
- Windows 至少维护 1 页内存,用于作为通用全局内存。任何在计算机上运行的进程都可以使用全局内存函数读取或写入此内存。全局内存空间中的数据可以在各种程序之间共享,发送到 Windows 剪贴板的项目通常存储在全局内存中(以便可以将其“粘贴”到任何程序中)。全局内存是有限的,因此应在没有特定需要的情况下使用。
- 局部内存
- 局部内存与全局内存和堆内存都有相似之处。它是针对进程的,但内存由 Windows 内存管理器管理,而不是由程序管理。我们将在后面讨论这一点。
如上所述,虚拟内存函数以页为单位分配内存。页面通常是 4 KB 内存,因此大多数应用程序不需要分配整个页面(更不用说超过 1 个页面)。虚拟内存系统本质上是其他内存函数用于执行其任务的原始函数基础。例如,堆包含一个或多个页面,堆函数将在需要时使用虚拟内存系统分配页面。
当分配虚拟内存块时,它们实际上并没有被使用,它们只是被系统预留以备将来使用。其他函数需要用于将虚拟内存页面分割成有用的段。由于虚拟内存是按页面分配的,因此可以在虚拟内存上使用许多特殊分页功能,而这些功能不能在其他类型的内存上使用。例如,页面可以被锁定(以防止读/写访问),或者可以从任何特定访问模式(读、写、执行)中保护它们。
也就是说,虚拟内存子系统中有很多函数可以使用
- VirtualAlloc
- VirtualFree
- VirtualProtect
- VirtualLock
- VirtualQuery
每个程序都提供了一个默认的进程堆,但一个进程可以选择分配任意数量的其他堆,如果需要更多存储空间。堆函数将自动管理它们的虚拟内存使用,因此堆可以设置为在被数据填满时增长。如果允许堆自动增长,堆函数将在需要时自动分配额外的页面。在 x86 架构上,堆的大小向更高的内存地址增长。
要使用堆内存,必须首先分配堆(或者必须获得对默认堆的句柄)。一旦你获得了对堆的句柄,你就可以将该句柄传递给内存分配函数,以便从该特定堆分配内存。
stdlib 内存函数(malloc、realloc、calloc、free)的使用方式与堆函数非常相似,因此熟悉 stdlib 函数的程序员可以通过检查它们的名字来弄清楚许多堆函数的作用
- HeapCreate
- 分配一个堆,并返回该堆的句柄。所有其他堆函数都将使用此句柄来唯一标识你正在访问的特定堆。通过维护多个句柄,你的程序可以与多个独立的堆进行交互。
- HeapDestroy
- 此函数关闭堆句柄,并释放堆内存,以便其他进程可以使用它。
- GetProcessHeap
- 此函数返回对默认进程堆的句柄。每个程序在加载时都会获得一个默认堆,在大多数应用程序中,这应该足以存储数据项。
- HeapAlloc
- 类似于 STDLIB 的“malloc”函数,HeapAlloc 在堆上分配存储空间,并返回指向该空间的指针。
- HeapReAlloc
- 类似于 STDLIB 的“realloc”函数,HeapReAlloc 重新分配变量的存储空间以使其大小不同。
- HeapFree
- 释放堆上的内存对象。尝试使用同一个指针访问已释放的内存会导致错误。
Windows 维护一定量的全局堆内存。与常规进程堆相比,此内存有限,应仅在特别需要全局内存时访问。
当数据被写入全局内存时,你不会获得指向该数据的指针,而是获得该数据的句柄。一旦你将数据交给全局内存管理器,系统就会负责它。请记住,句柄不是指针,也不应该用作指针。系统将管理全局内存部分中的内存,将其在页面之间移动,对其进行碎片整理等。数据不驻留在单个段中
- GlobalAlloc
- GlobalFree
- GlobalDiscard
- GlobalLock
- GlobalUnlock
- GlobalFlags
- GlobalSize
- GlobalHandle
在本例中,局部内存不是程序在内部使用的那种存储,例如在堆栈上以及其他位置。相反,Windows 管理一个它称为“局部内存”的特殊内存部分,并且它提供了一些函数来分配和管理这种特殊内存。局部内存与全局内存类似,数据被写入局部位置,系统返回指向该数据的句柄。系统将管理数据,就像在全局内存中一样。局部函数的命名与全局内存函数非常相似。但是,全局内存函数和局部内存函数绝不应该混用。例如,全局内存句柄绝不应该用 LocalFree 函数关闭。
- LocalAlloc
- LocalFree
- LocalDiscard
- LocalFlags
- LocalLock
- LocalReAlloc
- LocalUnlock
- LocalHandle