Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows

Функция из библиотеки С/С++ для контроля стека


Библиотека С/С++ содержит функцию, позволяющую контролировать стек. Трансли руя исходный код программы, компилятор при необходимости генерирует вызовы этой функции. Она обеспечивает корректную передячу страниц физической памяти стеку потока.

Возьмем, к примеру, небольшую функцию, требующую массу памяти под свои локальные переменные:

void SomeFunction()
{

int nValues[4000];

// здесь что-то делаем с массивом

nValuesjOj = 0; // а тут что-то присваиваем

}

Для размещения целочисленного массива функция потребует минимум 16 000 байтов стекового пространства, так как каждое целое значение занимает 4 байта. Код, генерируемый компилятором, обычно выделяеттакое пространство в стеке простым уменьшением указателя стека процессора на 16 000 байтов. Однако система не пере даст физическую память этой нижней области стека, пока не произойдет обращения по данному адресу

В системе с размерим страниц по 4 или 8 Кб это могло бы создать проблему. Если первое обращение к стеку проходит по адресу, расположенному ниже сторожевой страницы (как в показанном выше фрагменте кода), поток обратится к зарезервиро ванной памяти, и возникнет нарушение доступа. Поэтому, чтобы можно было спокой но писать функции вроде приведенной выше, компилятор и вставляет в код вызовы библиотечной функции для контроля стека.

При трансляции программы компилятору известен размер страниц памяти, ис пользуемых целевым процессором (4 Кб для x86 и 8 Кб для Alpha). Встречая в про грамме ту или иную функцию, компилятор определяет требуемый для нее объем сте ка и, если он превышает размер одной страницы, вставляет вызов функции, контро лирующей стек.

Нижс показан псевдокод, который иллюстрирует, что именно делает функция, контролирующая стек. (Я говорю «псевдокод» потому, что обычно эта функция реа лизуется поставщиками компиляторов на языке ассемблера.)

// стандартной библиотеке С "известен" размер страницы в целевой системе

#ifdef _M_ALPHA



#define PAGESIZE (8 * 1024) // страницы по 8 Кб

#else

#define PAGESIZE (4 * 1024) // страницы по 4 Кб


#endif



void StackCheck(int nBytesNeededFromStack)
{

// Получим значение указателя стека. В этом месте указатель стека
// еще НЕ был уменьшен для учета локальных переменных функции.
PBYTE pbStackPfr = (указатель стека процессора);

while (nBytesNeededFromStack >= PAGESIZE)
{

// смещаем страницу вниз по стеку - должна быть сторожевой
pbStackPtr -= PAGESIZE;

// обращаемся к какому-нибудь байту на сторожевой странице, вызывая
// тем самым передачу новой страницы и сдвиг сторожевой страницы вниз
pbSTackPtr[0] = 0;

// уменьшаем требуемое количество байтов в стеке
nBytesNeededFromStack -= PAGESIZE;

}

// перед возвратом управления функция StackCheck устанавливает регистр
// указателя стека на адрес, следующий за локальными переменными функции

}

В компиляторе Microsoft Visual C++ предусмотрен параметр, позволяющий конт ролировать пороговый предел числа страниц, начиная с которого компилятор авто матически вставляет в программу вызов функции StackCheck. Используйте этот пара метр, только если Вы точно знаете, что делаете, и если это действительно нужно. В 99,99999 процентах из ста приложения и DLL не требуют применения упомянутого параметра.


Содержание раздела