Функция из библиотеки С/С++ для контроля стека
Библиотека С/С++ содержит функцию, позволяющую контролировать стек. Транслируя исходный код программы, компилятор при необходимости генерирует вызовы этой функции. Она обеспечивает корректную передячу страниц физической памяти стеку потока.
Возьмем, к примеру, небольшую функцию, требующую массу памяти под свои локальные переменные:
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 не требуют применения упомянутого параметра.