Может ли CreateThread мешать использованию VirtualAlloc?

Возможно ли, чтобы пространство стека, выделенное CreateThread, мешало использованию VirtualAlloc? Я не могу найти какое-либо обсуждение или документацию, объясняющую, где именно разрешено выделять пространство стека...

Следующее более точно иллюстрирует мой вопрос:

uint8_t *baseA = (uint8_t*)VirtualAlloc(NULL,1,MEM_RESERVE,PAGE_NOACCESS);

// Create a thread with the default stack size
HANDLE hThread = CreateThread(NULL,0,SomeThreadProc,NULL,NULL,NULL);

// Possibly create even more threads here.

// Can this ever fail in the absence of other allocators? It doesn't here...
uint8_t *baseB = (uint8_t*)VirtualAlloc(NULL,1,MEM_RESERVE,PAGE_NOACCESS);

// Furthermore, in this test, baseB-baseA == 65536 (unless the debugger did something),
// so nothing appeared between baseA and baseB... not even enough space for the
// full 64kb of wastage, as baseA points to 4096 bytes by itself

Если он действительно использует какой-то аналог VirtualAlloc, есть ли способ изменить то, как Windows распределяет пространство стека в данном процессе?


person defube    schedule 11.08.2012    source источник


Ответы (2)


Пространство стека может быть выделено в любом месте адресного пространства процесса. Документации по этому поводу сейчас нет и вряд ли такая документация появится в будущем.

Вы можете с уверенностью предположить, что создание потока и виртуальное выделение памяти независимы. Если бы этого не было, многое было бы сломано. Распределитель не может выдавать перекрывающиеся диапазоны адресов. Это немыслимо. Проблема в другом.

Единственное, что может выглядеть как корреляция - это объем используемой памяти и фрагментация виртуального адресного пространства. В этом случае последний запрос просто завершится ошибкой.

Я работал над утилитами анализа памяти.

введите здесь описание изображения

На этом рисунке показано распределение количества виртуальных выделений по размеру выделения.

введите здесь описание изображения

Это пример содержимого адресного пространства для 32-битного процесса (синий — зафиксировано, пурпурный — зарезервировано, зеленый — свободная память).

То, что я пишу здесь, основано на реальном опыте.

person Kirill Kobelev    schedule 11.08.2012
comment
+1 за «Это немыслимо» — ОС будет так быстро падать при загрузке, что, вероятно, даже не успеет до BSOD. - person Martin James; 11.08.2012
comment
Спасибо за ответ. Возможно, вмешиваться было суровым словом для этого вопроса. Все, о чем я спрашивал, это, по сути, может ли CreateThread выделять место в стеке где угодно - и вы ответили на это. Под вмешательством я спрашивал, может ли он фрагментировать виртуальное адресное пространство процесса (но теперь, когда я об этом думаю, где еще он сможет его выделить?). Таким образом, похоже, что случайное распределение потоков вызовет фрагментацию, которую не сможет решить ни один специальный распределитель. - person defube; 11.08.2012
comment
Мне нравится концепция AddrSpaceUtilization, которую я представил сам. Это отношение суммы размеров всех VirtualAlloc распределений к размеру самого адресного пространства. Мой опыт работы с 32-битными процессами в Windows показывает, что при этом значении меньше 85-90% все работает более-менее нормально. Когда он превышает 90%, начинают появляться различные проблемы. - person Kirill Kobelev; 11.08.2012

ядро Windows NT обрабатывает операции выделения памяти с высоким приоритетом прерывания, а также в потокобезопасном режиме.

Это означает, что только один поток процесса может одновременно выделять память, что делает все процессы выделения потокобезопасными (теоретически). не должно быть никаких помех между выделением стека и виртуальным выделением.

Также вы должны помнить, что вы можете выделить 1 ГБ пространства, но ваша программа по-прежнему использует только 2 МБ ОЗУ.

Это потому, что Windows «предварительно выделяет» виртуальное пространство, но не назначает его, пока вы его не используете (не записываете на нем).

На самом деле управление памятью намного сложнее, но на данный момент вы можете быть уверены, что никакие операции выделения не должны мешать, поскольку Windows блокирует ваш процесс на одном ядре, задерживая все запросы на выделение памяти другими потоками, пока выполняется выделение. (тупик)

* РЕДАКТИРОВАТЬ: Это также означает, что выделение и освобождение — это своего рода процесс, требующий производительности, если вы выделяете миллионы маленьких битов. Всегда лучше выделять/освобождать большие области памяти из-за этого взаимоблокирующего поведения.

person Alzurana    schedule 11.08.2012