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 nonzero | RtlCreateHeap 将 ReserveSize 设置为等于 CommitSize,然后将 ReserveSize 向上舍入到最接近的 (PAGE_SIZE * 16) 倍数。 |
ReserveSize nonzero, CommitSize zero | 最初为堆提交一页。 |
ReserveSize nonzero, CommitSize nonzero | 如果 CommitSize 大于 ReserveSize,RtlCreateHeap 将 CommitSize 减少为 ReserveSize。 |
[in, optional] CommitSize
如果 CommitSize 是非零值,则指定为堆提交的初始内存量(以字节为单位)。 RtlCreateHeap 将 CommitSize 舍入到下一页边界,然后在进程的虚拟地址空间中提交该大小的块。
此参数是可选的,可以是零。
[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字节。 堆的虚拟内存阈值等于参数结构中 VirtualMemoryThreshold 成员的最大堆块大小或值,以较小者为准。 堆还可能需要为元数据和对齐目的填充请求大小,以便请求在 4096 字节内分配块 (1 页) VirtualMemoryThreshold ,即使堆的最大大小足以包含块,也可能会失败。 (有关 VirtualMemoryThreshold 的详细信息,请参阅 RtlCreateHeap.) 的参数参数的成员
如果堆可增长,则分配大于堆虚拟内存阈值的块的请求不会自动失败;系统调用 ZwAllocateVirtualMemory 以获取此类大型块所需的内存。
专用堆对象的内存仅可供创建它的进程访问。
系统使用专用堆中的内存来存储堆支持结构,因此并非所有指定的堆大小都可用于进程。 例如,如果 RtlAllocateHeap 从最大大小为 64K 的堆请求 64 KB (K) ,则请求可能会因为系统开销而失败。
如果未指定HEAP_NO_SERIALIZE (简单的默认) ,则堆将在调用过程中序列化访问权限。 当两个或多个线程尝试同时分配或释放同一堆中的块时,序列化可确保相互排除。 序列化的性能成本很小,但每当多个线程从同一堆中分配和释放内存时,都必须使用它。
设置HEAP_NO_SERIALIZE消除了堆上的相互排斥。 如果没有序列化,使用同一堆句柄的两个或多个线程可能会同时尝试分配或释放内存,这可能会导致堆损坏。 因此,HEAP_NO_SERIALIZE只能在以下情况下安全地使用:
进程只有一个线程。
进程有多个线程,但只有一个线程调用特定堆的堆函数。
进程具有多个线程,并且应用程序提供其自己的机制,用于将相互排除到特定堆。
注意
若要防范访问冲突,请使用结构化异常处理来保护写入或读取堆的任何代码。 有关使用内存访问进行结构化异常处理的详细信息,请参阅 处理异常**。
要求
最低受支持的客户端 | Windows XP |
目标平台 | 通用 |
标头 | ntifs.h (包括 Ntifs.h) |
Library | Ntoskrnl.lib |
DLL | NtosKrnl.exe (内核模式) ;Ntdll.dll (用户模式) |
IRQL | < DISPATCH_LEVEL |