Windows не фиксирует стековую память немедленно; вместо этого он резервирует адресное пространство для него и фиксирует его постранично при обращении к нему. Прочитайте эту страницу для получения дополнительной информации.
В результате адресное пространство стека состоит из трех смежных областей:
- Зарезервированная, но незафиксированная память, которую можно использовать для наращивания стека (но к ней еще не обращались);
- Защитная страница, к которой еще никогда не обращались, и служит для запуска роста стека при обращении;
- Выделенная память, то есть стековая память, к которой когда-либо обращался поток.
Это позволяет нам построить функцию, которая получает размер стека (с гранулярностью размера страницы):
static size_t GetStackUsage()
{
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(&mbi, &mbi, sizeof(mbi));
// now mbi.AllocationBase = reserved stack memory base address
VirtualQuery(mbi.AllocationBase, &mbi, sizeof(mbi));
// now (mbi.BaseAddress, mbi.RegionSize) describe reserved (uncommitted) portion of the stack
// skip it
VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));
// now (mbi.BaseAddress, mbi.RegionSize) describe the guard page
// skip it
VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));
// now (mbi.BaseAddress, mbi.RegionSize) describe the committed (i.e. accessed) portion of the stack
return mbi.RegionSize;
}
Следует учитывать одну вещь: CreateThread
позволяет указать начальный размер фиксации стека (с помощью параметра dwStackSize
, когда флаг STACK_SIZE_PARAM_IS_A_RESERVATION
не установлен). Если этот параметр не равен нулю, наша функция вернет правильное значение только тогда, когда использование стека станет больше, чем dwStackSize
значение.