Управление памятью в операционной системе WINDOWS, страница 8

Ситуация меняется в случае, если программа пытается обратиться к странице, которая зарезервирована, но не выделена (такое происходит, в частности, при| самом первом обращении к буферу). Возникает исключение 0хС0000005, на которое реагирует специальный код функции putbuffer. Этот код выделяет соответствующую страницу для того, чтобы программа смогла продолжить работу. После того как страница выделена, происходит рекурсивное обращение к функции putbuffer. He стоит забывать, что обработчик исключения реагирует на любые, возникающие исключения, поэтому обязательно следует проверить, не превышает ли значение Index допустимого предела и не вызвано ли исключение какими-либо иными причинами,

Возникающее исключение невидимо для нормального обработчика исключений C++. Чтобы осуществить преобразование исключений С в исключения unsignedint в стиле C++, используется простая функция, указатель на которую передается функции _set_se_translator. Функция _set_se_translator возвращает адрес функции преобразования исключений по умолчанию, поэтому подпрограмма putbuffer имеет возможность восстановить адрес этой функции перед тем, как управление будет передано вызвавшей программе. Для хранения указателей на подобные функции служит специальный тип _se_translator_function (определенный в заголовочном файле eh.h).

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

Чтобы реализовать это же самое, но с использованием инструментария Builder C++ надо использовать программный код наподобие следующего:

#pragma hdrstop

#pragma argsused

#include <stdio.h>

#include <windows.h>

#include <conio.h>

//#include <winnt.h>

static int maxindex;

// процедура – фильтр системных исключений. Использует ряд системных

// структур, описанных в файле winnt.h

static int xfilter(EXCEPTION_POINTERS *xp)

{  int rc;

EXCEPTION_RECORD *xr = xp->ExceptionRecord;

CONTEXT *xc = xp->ContextRecord;

// распечатаем код системной ошибки

printf("Code %ul  ", (unsigned long)xr->ExceptionCode);

//в зависимости от кода исключения пошлем обработчику исключения

//соответствующие системные константы:

switch (xr->ExceptionCode) {

case EXCEPTION_BREAKPOINT:

// кто-то оставил внедренную контрольную точку отладки

// just step over it (1 byte on x86)

++xc->Eip;

rc = EXCEPTION_CONTINUE_EXECUTION;

break;

case EXCEPTION_ACCESS_VIOLATION:

rc = EXCEPTION_EXECUTE_HANDLER;

break;

default:

// give up

rc = EXCEPTION_CONTINUE_SEARCH;

break;

};

return rc;

}

char *getbuffer()

{SYSTEM_INFO info;

// определение размера страницы

GetSystemInfo(&info);

char *rv;

rv=(char *)VirtualAlloc(NULL, maxindex=info.dwPageSize*1010,

MEM_RESERVE,PAGE_READWRITE);

return rv;

}

void putbuffer(char *p,int index, char value)

{ int code;

try

{

p[index]=value;

}

//обработчик системного исключения

__except(code = xfilter(GetExceptionInformation()))

{

printf("Index %d\n", index);

if (code!=EXCEPTION_EXECUTE_HANDLER)

{     printf("Неизвестное исключение\n");

exit(1);

}

// проверка: находится ли индекс в нужном диапазоне

if (index>=maxindex)

{  printf("Попытка доступа к ячейку за границами буфера\n");

exit(2);

}

// Выделяем один байт

// на самом деле система выделяет целую страницу

VirtualAlloc(p+index, 1, MEM_COMMIT, PAGE_READWRITE);

// еще одна попытка записать в буфер

putbuffer(p,index,value);

}

}

void freebuffer(char *p)

{

VirtualFree(p,0,MEM_RELEASE);

}

void main()

{  char *p=getbuffer();

for (int i=0;i<4000000;i++)   putbuffer(p, i, i&0xFF);

freebuffer(p);

printf("Pass #2\n");