c++ - When I reserve memory with VirtualAlloc() and MEM_RESERVE, shouldn't I be able to grow my allocation on a 64K boundary? -
first of all, know how virtualalloc()
works: when reserve blocks of memory, addresses aligned 64k boundaries, (a value can obtained getsysteminfo()
), when commit pages, them on page size boundary, 4k.
the thing can't get, why if call virtualalloc()
mem_reserve
flag (so i'm reserving pages) , specify size, let's 4096, won't able grow further region 64k?
what i'm saying is: when commit pages can use memory 4k because windows alignes these commits page size (of course, i'm commiting pages!), when i'm reserving regions of memory, shouldn't windows align size of region pass virtualalloc()
64k? "wasted" 15 pages go?
so if reserve 4096 bytes, shouldn't able commit more pages until 65536 bytes? doesn't seem so, because if try this, virtualalloc()
fails error_invalid_address
last error code.
but why? if windows reserve pages on 64k boundaries, , reserve pages on less size, loose pages don't reserve forever? because seems there no way commit them again, or resize region fit 64k boundaries i've missed lower reservation.
so, process' virtual space have holes? avoid this, have reserve memory always on 64k boundaries, giving virtualalloc()
64k-aligned value always when i'm reserving pages?
what when use mem_reserve|mem_commit
? shoulnd't pass 64k-aligned sizes there, because of mem_reserve
flag?
i include little code example i've tried. can see here, first functions succeeds, because reserve more pages, commits have enough "reserved region" committed, but in case, region <64k, "lost" pages go?
in second case, i've mem_reserve|mem_commit
, committing other pages fails error_invalid_address
last error code. fair enough, here, why can't commit more pages, @ least on 64k boundary? not waste addresses , create these "holes", should reserve virtual memory on 64k boundaries? if don't follow principle? see a lot of code around calls virtualalloc()
mem_commit|mem_reserve
flags, not caring 64k align thing. allocating memory in wrong way? thoughts?
#include <stdlib.h> #include <stdio.h> #include <windows.h> #define page_sz 4096 bool reserve_and_commit() { memory_basic_information mem_info; void * mem, * mem2; bool result = true; mem = virtualalloc(0, page_sz * 1, mem_reserve | mem_commit, page_readwrite); if (!mem) { result = false; printf("virtualalloc1: error '%d'\n", (unsigned int)getlasterror()); } else printf("virtualalloc1: mem_reserve|mem_commit ok. address: %p\n", mem); printf("\n-------------------------------------\n\n"); if (!virtualquery(mem, &mem_info, sizeof mem_info)) { result = false; printf("virtualquery: error '%d'\n", (unsigned int)getlasterror()); } else printf("virtualquery: ok. baseaddress:%p allocationbase:%p allocationprotect:%08x " "regionsize:%d state:%08x protect:%08x type:%08x\n", mem_info.baseaddress, mem_info.allocationbase, mem_info.allocationprotect, (unsigned int)mem_info.regionsize, (unsigned int)mem_info.state, (unsigned int)mem_info.protect, (unsigned int)mem_info.state); printf("\n-------------------------------------\n\n"); mem2 = virtualalloc(mem, page_sz * 2, mem_commit, page_readwrite); if (!mem2) { result = false; printf("virtualalloc2: error '%d'\n", (unsigned int)getlasterror()); } else printf("virtualalloc2: mem_commit ok. address: %p\n", mem2); printf("\n-------------------------------------\n\n"); if (!virtualfree(mem, 0, mem_release)) { result = false; printf("virtualfree: error '%d'\n", (unsigned int)getlasterror()); } else printf("virtualfree: ok.\n"); return result; } bool first_reserve_and_then_commit() { memory_basic_information mem_info; void * mem_reserved, * mem_committed; bool result = true; mem_reserved = virtualalloc(0, page_sz * 8, mem_reserve, page_readwrite); if (!mem_reserved) { result = false; printf("virtualalloc1: error '%d'\n", (unsigned int)getlasterror()); } else printf("virtualalloc1: mem_reserve ok. address: %p\n", mem_reserved); printf("\n-------------------------------------\n\n"); if (!virtualquery(mem_reserved, &mem_info, sizeof mem_info)) { result = false; printf("virtualquery1: error '%d'\n", (unsigned int)getlasterror()); } else printf("virtualquery1: ok. baseaddress:%p allocationbase:%p allocationprotect:%08x " "regionsize:%d state:%08x protect:%08x type:%08x\n", mem_info.baseaddress, mem_info.allocationbase, mem_info.allocationprotect, (unsigned int)mem_info.regionsize, (unsigned int)mem_info.state, (unsigned int)mem_info.protect, (unsigned int)mem_info.state); printf("\n-------------------------------------\n\n"); mem_committed = virtualalloc(mem_reserved, page_sz * 1, mem_commit, page_readwrite); if (!mem_committed) { result = false; printf("virtualalloc2: error '%d'\n", (unsigned int)getlasterror()); } else printf("virtualalloc2: mem_commit ok. address: %p\n", mem_committed); printf("\n-------------------------------------\n\n"); if (!virtualquery(mem_committed, &mem_info, sizeof mem_info)) { result = false; printf("virtualquery2: error '%d'\n", (unsigned int)getlasterror()); } else printf("virtualquery2: ok. baseaddress:%p allocationbase:%p allocationprotect:%08x " "regionsize:%ul state:%08x protect:%08x type:%08x\n", mem_info.baseaddress, mem_info.allocationbase, mem_info.allocationprotect, (unsigned int)mem_info.regionsize, (unsigned int)mem_info.state, (unsigned int)mem_info.protect, (unsigned int)mem_info.state); printf("\n-------------------------------------\n\n"); mem_committed = virtualalloc(mem_committed, page_sz * 8, mem_commit, page_readwrite); if (!mem_committed) { result = false; printf("virtualalloc3: error '%d'\n", (unsigned int)getlasterror()); } else printf("virtualalloc3: mem_commit ok. address: %p\n", mem_committed); printf("\n-------------------------------------\n\n"); if (!virtualfree(mem_reserved, 0, mem_release)) { result = false; printf("virtualfree: error '%d'\n", (unsigned int)getlasterror()); } else printf("virtualfree: ok.\n"); return result; } int main() { first_reserve_and_then_commit(); reserve_and_commit(); return 0; }
as demonstrated program, virtual pages aren't automatically reserved when allocated. when reserve single page virtualalloc entire 64k block of pages allocated, single page reserved. can commit pages have been reserved, when program tries commit allocated unreserved pages call virtualalloc fails.
as why works way, simple answer way it's documented work. no in documentation state virtualalloc ever reserve more pages ask to. don't have insight on why microsoft chose implement way, seems meets principle of least astonishment. in particular means less programs break if decide change allocation granularity size since it's hidden implementation detail. (however, @ point don't think it's possible microsoft change this.) might reduce memory needed keeping track of reserved pages.
as what's best practice when using virtualalloc recommendation should used allocate memory in sizes greater 64k , ideally bigger. since no physical memory lost when allocating regions smaller 64k, virtual address space, many programs doesn't matter. debugging aid, once used custom version of malloc in program used virtualalloc allocations, of smaller 4k, let alone 64k.
Comments
Post a Comment