RtlCreateHeap 函数 (ntifs.h)
RtlCreateHeap 例程创建可由调用进程使用的堆对象。 此例程在进程的虚拟地址空间中保留空间,并为此块的指定初始部分分配物理存储。
语法
NTSYSAPI PVOID RtlCreateHeap(
[in] ULONG Flags,
[in, optional] PVOID HeapBase,
[in, optional] SIZE_T ReserveSize,
[in, optional] SIZE_T CommitSize,
[in, optional] PVOID Lock,
[in, optional] PRTL_HEAP_PARAMETERS Parameters
);
参数
[in] Flags
指定堆的可选属性的标志。 这些选项通过调用堆函数(RtlAllocateHeap 和 RtlFreeHeap)影响对新堆的后续访问。
如果没有请求可选属性,调用方应将此参数设置为零。
此参数可以是以下一个或多个值。
价值 | 意义 |
---|---|
HEAP_GENERATE_EXCEPTIONS | 指定系统将通过引发异常(如STATUS_NO_MEMORY)来指示堆失败,而不是返回 NULL。 |
HEAP_GROWABLE | 指定堆可增长。 如果 heapBase 为 NULL,则必须指定 |
HEAP_NO_SERIALIZE | 指定在堆函数分配和释放此堆中的内存时,不会使用相互排除。 如果未指定HEAP_NO_SERIALIZE,则默认值是序列化对堆的访问。 堆访问的序列化允许两个或多个线程同时分配和释放同一堆中的内存。 |
[in, optional] HeapBase
指定以下两个操作之一:
如果 HeapBase 为非 NULL 值,则它指定用于堆的调用方分配内存块的基址。
如果 HeapBase 为 NULL,RtlCreateHeap 从进程的虚拟地址空间为堆分配系统内存。
[in, optional] ReserveSize
如果 ReserveSize 为非零值,则指定为堆保留的初始内存量(以字节为单位)。 RtlCreateHeap 将 ReserveSize 舍入到下一页边界,然后保留堆大小的块。
此参数是可选的,可以是零。 下表汇总了 ReserveSize 和 CommitSize 参数的交互。
值 | 结果 |
---|---|
ReserveSize 零,CommitSize 零 | 最初为堆保留 64 页。 最初提交一页。 |
ReserveSize 零,CommitSize 非零 | RtlCreateHeap 将 ReserveSize 设置为等于 CommitSize,然后将 ReserveSize 向上舍入到最近的倍数(PAGE_SIZE * 16)。 |
ReserveSize 非零,CommitSize 零 | 最初为堆提交一页。 |
ReserveSize 非零,CommitSize 非零 | 如果 CommitSize 大于 ReserveSize,则 RtlCreateHeap 可减少 CommitSizeReserveSize。 |
[in, optional] CommitSize
如果 CommitSize 为非零值,则指定为堆提交的初始内存量(以字节为单位)。 RtlCreateHeapCommitSize 向上舍入到下一页边界,然后在进程的虚拟地址空间中提交该大小的块。
此参数是可选的,可以是零。
[in, optional] Lock
指向要用作资源锁的不透明 ERESOURCE 结构的指针。 此参数是可选的,可以是 NULL。 由调用方提供时,必须从非分页池分配结构,并通过调用 ExInitializeResourceLite 或 ExReinitializeResourceLite进行初始化。 如果设置了HEAP_NO_SERIALIZE标志,此参数必须为 NULL。
[in, optional] Parameters
指向 RTL_HEAP_PARAMETERS 结构的指针,该结构包含创建堆时要应用的参数。 此参数是可选的,可以是 NULL。
返回值
RtlCreateHeap 返回用于访问已创建的堆的句柄。
言论
RtlCreateHeap 创建一个专用堆对象,调用进程可以通过调用 RtlAllocateHeap来分配内存块。 初始提交大小确定最初为堆分配的页数。 初始保留大小确定最初为堆保留的页数。 保留但未提交的页面会在进程的虚拟地址空间中创建块,堆可以扩展到其中。
如果 RtlAllocateHeap 发出的分配请求 超过堆的初始提交大小,则系统会提交堆的物理存储的其他页面,最大大小。 如果堆不可增长,则其最大大小限制为初始保留大小。
如果堆可增长,则其大小仅受可用内存的限制。 如果 RtlAllocateHeap 的请求超过提交的页面的当前大小,则系统会调用 ZwAllocateVirtualMemory 以获取所需的内存,前提是物理存储可用。
此外,如果堆不可增长,则会出现绝对限制:堆中内存块的最大大小为0x7F000字节。 堆的虚拟内存阈值等于最大堆块大小或 VirtualMemoryThresholdParameters 结构的成员的值,以较小者为准。 堆还可能需要填充元数据和对齐目的的请求大小,以便请求在 VirtualMemoryThres hold 的 4096 字节(1 页)内分配块,即使堆的最大大小足以包含块,也可能会失败。 (有关
如果堆可增长,则分配大于堆虚拟内存阈值的块的请求不会自动失败;系统调用 ZwAllocateVirtualMemory 以获取此类大型块所需的内存。
专用堆对象的内存只能访问创建它的进程。
系统使用专用堆中的内存来存储堆支持结构,因此并非所有指定的堆大小都可用于进程。 例如,如果 RtlAllocateHeap 从最大大小为 64K 的堆请求 64 KB(K),则请求可能会因为系统开销而失败。
如果未指定HEAP_NO_SERIALIZE(简单默认值),堆将在调用过程中序列化访问权限。 当两个或多个线程尝试同时分配或释放同一堆中的块时,序列化可确保相互排斥。 序列化的性能成本很小,但每当多个线程从同一堆中分配和释放内存时,都必须使用它。
设置HEAP_NO_SERIALIZE可消除堆上的相互排斥。 如果没有序列化,使用同一堆句柄的两个或多个线程可能会同时尝试分配或释放内存,这可能会导致堆损坏。 因此,HEAP_NO_SERIALIZE只能在以下情况下安全地使用:
进程只有一个线程。
进程有多个线程,但只有一个线程调用特定堆的堆函数。
进程具有多个线程,并且应用程序提供自己的机制来相互排除到特定堆。
注意
若要防范访问冲突,请使用结构化异常处理来保护写入堆或从堆读取的任何代码。 有关内存访问的结构化异常处理的详细信息,请参阅 处理异常**。
要求
要求 | 价值 |
---|---|
最低支持的客户端 | Windows XP |
目标平台 | 普遍 |
标头 | ntifs.h (include Ntifs.h) |
库 | Ntoskrnl.lib |
DLL | NtosKrnl.exe(内核模式):Ntdll.dll(用户模式) |
IRQL | < DISPATCH_LEVEL |