Memory_Windows_Ch3_2015.ppt
- Количество слайдов: 88
Архитектура памяти в Win 32 API Организация «статической» виртуальной памяти (Virtual Memory API)
Получение информации о ВАП VOID Get. System. Info ( LPSYSTEM_INFO lp. System. Info ); размер страницы младший и старший адрес ВАП доступного процессу гранулярность блоков адресов typedef struct _SYSTEM_INFO { union { DWORD dw. Oem. Id; struct { WORD w. Processor. Architecture; WORD w. Reserved; }; DWORD dw. Page. Size; LPVOID lp. Minimum. Application. Address; LPVOID lp. Maximum. Application. Address; DWORD_PTR dw. Active. Processor. Mask; DWORD dw. Number. Of. Processors; DWORD dw. Processor. Type; DWORD dw. Allocation. Granularity; WORD w. Processor. Level; WORD w. Processor. Revision; } SYSTEM_INFO;
Дескрипторы виртуальных адресов Для управления виртуальным адресным пространством процесса менеджер виртуальной памяти поддерживает набор структур данных, которые позволяют вести учет зарезервированных и свободных блоков (диапазонов) виртуальных адресов. Эти структуры данных называются дескрипторами виртуальных адресов (virtual address descriptors, VAD). Для каждого процесса диспетчер памяти поддерживает свой набор VAD. Для большей эффективности поиска VAD организованы в виде двоичного дерева с автоматической балансировкой.
Пример схемы дерева VAD
Работа приложений с виртуальной памятью Резервирование и выделение памяти производится блоками адресов (VAD). Начальный адрес блока должен быть выровнен на значение dw. Allocation. Granularity (обычно равно 64 K), при необходимости адрес округляется «вниз» . Размер блока должен быть кратен размеру страницы dw. Page. Size, при необходимости округляется «вверх» . Блок адресов в адресном пространстве процесса может находиться в одном из трех состояний : выделен (committed) – блоку адресов назначена физическая память либо часть файла подкачки; зарезервирован (reserved) – блок адресов помечен как занятый, но физическая память не распределена; свободен (free) – блок адресов не выделен и не зарезервирован.
Функции API для работы виртуальной памятью Для резервирования региона памяти в адресном пространстве процесса или выделения ее используется функция Virtual. Alloc (), а для освобождения – функция Virtual. Free (). Для работы в адресном пространстве произвольного процесса необходимо использовать функции Virtual. Alloc. Ex () и Virtual. Free. Ex (). Выделенные страницы можно заблокировать в памяти, т. е. запретить их вытеснение в файл подкачки. Для этих целей служит пара функций Virtual. Lock () и Virtual. Unlock (). Процессу не разрешается блокировать более 30 страниц. Для изменения атрибутов защиты регионов используются функции Virtual. Protect () и Virtual. Protect. Ex ().
Функции Win 32 API для работы виртуальной памятью Virtual. Alloc. Ex Virtual. Free. Ex Virtual. Lock Virtual. Unlock Virtual. Protect. Ex
Состояния страниц ВП и функции Win 32 API
Функция Virtual. Alloc LPVOID Virtual. Alloc ( LPVOID lp. Address, // адрес для резервирования или выделения памяти DWORD dw. Size, // размер выделяемого региона DWORD fl. Allocation. Type, // тип распределения памяти DWORD fl. Protect // тип защиты доступа );
Функция Virtual. Alloc. Ex LPVOID Virtual. Alloc. Ex ( HANDLE h. Process, // дескриптор процесса LPVOID lp. Address, // адрес для резервирования или выделения памяти DWORD dw. Size, // размер выделяемого региона DWORD fl. Allocation. Type, // тип распределения памяти DWORD fl. Protect // тип защиты доступа );
Параметр fl. Allocation. Type MEM_RESERVE – резервирует блок адресов без выделения памяти. MEM_COMMIT – отображает ранее зарезервированный блок адресов на физическую память или файл подкачки, выделяя при этом память. Может комбинироваться с флагом MEM_RESERVE для одновременного резервирования и выделения. MEM_TOP_DOWN – выделяет память по наибольшему возможному адресу. Имеет смысл только при lp. Address = NULL. MEM_DECOMMIT – освободить выделенную память. MEM_RELEASE – освободить зарезервированный регион. При использовании этого флага параметр dw. Size должен быть равен нулю.
Параметр fl. Protect PAGE_READONLY – допускается только чтение. PAGE_READWRITE – допускается чтение и запись. PAGE_EXECUTE – допускается только выполнение. PAGE_EXECUTE_READ – допускается исполнение и чтение. PAGE_EXECUTE_READWRITE – допускается выполнение, чтение и запись. PAGE_GUARD – дополнительный флаг защиты, который комбинируется с другими флагами. При первом обращении к странице этот флаг сбрасывается и возникает исключение STATUS_GUARD_PAGE. Этот флаг используется для контроля размеров стека с возможностью его динамического расширения. PAGE_NOCACHE – запрещает кэширование страниц. Может быть полезен при разработке драйверов устройств (например, данные в видеобуфер должны переписываться сразу, без кэширования).
Работа с «большими» страницами 1. 2. 3. Установите для процесса уровень привилегий Se. Lock. Memory. Privilege с помощью функции Adjust. Token. Privileges (). Получите минимально допустимый размер для «больших» страниц с помощью функции Get. Large. Page. Minimum (). Используйте параметр MEM_LARGE_PAGES при вызове функции Virtual. Alloc (). Выравнивание начального адреса и размера блока должно быть выполнено по минимально допустимому размеру «больших» страниц.
Пример – получение привилегий HANDLE h. Token; if (!Open. Process. Token (Get. Current. Process(), TOKEN_ADJUST_PRIVILEGES, &h. Token)) return 0; LUID luid; if (!Lookup. Privilege. Value (NULL, SE_LOCK_MEMORY_NAME, &luid)) return 0; TOKEN_PRIVILEGES tp; tp. Privilege. Count = 1; tp. Privileges[0]. Luid = luid; tp. Privileges[0]. Attributes = SE_PRIVILEGE_ENABLED; if(!Adjust. Token. Privileges (h. Token, FALSE, &tp, sizeof(tp), NULL) || (Get. Last. Error() != ERROR_SUCCESS)) return 0;
Пример – выделение «большой» страницы char *p. Array = (char *) Virtual. Alloc ( NULL, 1 * Get. Large. Page. Minimum(), MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
Функция Virtual. Free BOOL Virtual. Free ( LPVOID lp. Address, // адрес освобождаемого региона DWORD dw. Size, // размер освобождаемого региона DWORD dw. Free. Type // тип освобождения ); В случае успеха функция возвращает TRUE, FALSE – в случае неудачи.
Параметры функции Virtual. Free dw. Size – размер, если мы будем использовать тип освобождения, как MEM_RELEASE, то размер должен быть установлен в 0. dw. Free. Type – будет определять какая операция произойдет с памятью: MEM_RELEASE – освобождена; MEM_DECOMMIT – зарезервирована, но не используется. При успешном выполнении функция вернет TRUE в случае успеха и FALSE – в случае неудачи.
Функция Virtual. Free. Ex BOOL Virtual. Free. Ex ( HANDLE h. Process, // дескриптор процесса LPVOID lp. Address, // адрес освобождаемого региона DWORD dw. Size, // размер освобождаемого региона DWORD dw. Free. Type // тип освобождения );
Изменение атрибутов защиты Для изменения атрибутов защиты блоков адресов используются функции Virtual. Protect () и Virtual. Protect. Ex (). Virtual. Protect () позволяет изменять атрибуты защиты в адресном пространстве текущего процесса. Virtual. Protect. Ex () позволяет изменять атрибуты защиты в адресном пространстве произвольного процесса.
Функция Virtual. Protect BOOL Virtual. Protect ( LPVOID lp. Address, // адрес региона для установки флага DWORD dw. Size, // размер региона DWORD fl. New. Protect, // флаги защиты PDWORD lpfl. Old. Protect // адрес для сохранения // прежних флагов );
Инспектирование ВАП Функции Virtual. Query () и Virtual. Query. Ex () позволяют определить статус указанного региона адресов. DWORD Virtual. Query( LPCVOID Ip. Address, // адрес области PMEMORY_BASICONFORMATION Ip. Buffer, // адрес // информационного буфера DWORD dw. Length // размер буфера );
Структура MEMORY_BASIC_INFORMATION typedef struct _MEMORY_BASIC_INFORMATION { PVOID Base. Address; // базовый адрес области PVOID Allocation. Base; // базовый адрес выделенной области DWORD Allocation. Protect; // исходная защита от доступа DWORD Region. Size; // размер области в байтах DWORD State; // состояние блока DWORD Protect; // текущая защита от доступа DWORD Type; // тип страниц // (MEM_IMAGE, MEM_MAPPED, MEM_PRIVATE) } MEMORY_BASIC_INFORMATION;
Настройка алгоритма замещения Фиксация (блокировка) страниц в оперативной памяти – функции Virtual. Lock() и Virtual. Un. Lock(). Процессу разрешается блокировать не более 30 страниц. Для настройки рабочего множества процесса используется функция Set. Process. Working. Set. Size (), которая может снять ограничение блокировки не более 30 страниц. Также Set. Process. Working. Set. Size () позволяет задать для процесса минимальный и максимальный размер рабочего множества процесса.
Блокировка страниц в памяти: Virtual. Lock и Virtual. Unlock Функция Virtual. Lock () позволят зафиксировать (заблокировать) страницы в ОП и предотвратить их вытеснение на диск. BOOL Virtual. Lock ( LPVOID lp. Address, // адрес начала памяти SIZE_T dw. Size // количество байтов ); Если фиксация больше не нужна, то ее можно убрать функцией Virtual. Unlock(). BOOL Virtual. Unlock ( LPVOID lp. Address, // адрес начала памяти SIZE_T dw. Size // количество байтов ); При успешном выполнении функции возвращают TRUE.
Настройка рабочего множества: Set. Process. Working. Set. Size BOOL Set. Process. Working. Set. Size ( HANDLE h. Process, // дескриптор процесса DWORD dw. Minimum. Working. Set. Size, // мин. размер рабочего мн-ва процесса, в байтах DWORD dw. Maximum. Working. Set. Size // макс. размер рабочего мн-ва процесса, в байтах );
Урезание рабочего множества процесса Если и dw. Minimum. Working. Set. Size и dw. Maximum. Working. Set. Size имеют значение «– 1» , функция временно урезает рабочее множество процесса до нуля. Аналогичный результат достигается с помощью функции Empty. Working. Set ().
Особенности настройки рабочего множества процесса Дескриптор процесса должен иметь права доступа PROCESS_SET_QUOTA. Если значения dw. Minimum. Working. Set. Size или dw. Maximum. Working. Set. Size больше, чем текущий размер рабочего множества памяти процесса, данный процесс должен иметь привилегию SE_INC_BASE_PRIORITY_NAME.
Получение справочной информации по ВП процесса Get. Process. Working. Set. Size () – получение текущих значений минимального и максимального размера рабочего множества процесса. Get. Process. Memory. Info () – получение расширенной статистики по использованию ВП процесса, например: количество страничных прерываний; текущий и пиковый размер рабочего множества процесса; текущее и пиковое использование файла подкачки.
Структура MEMORYSTATUS typedef struct _MEMORYSTATUS { DWORD dw. Length; // размер структуры DWORD dw. Memory. Load; // процент используемой памяти SIZE_T dw. Total. Phys; // количество байтов физической памяти SIZE_T dw. Avail. Phys; // кол-во свободных байтов физ. памяти SIZE_T dw. Total. Page. File; // размер в байтах файла подкачки SIZE_T dw. Avail. Page. File; // количество свободных байтов файла подкачки SIZE_T dw. Total. Virtual; // количество байтов ВАП доступных // пользователю SIZE_T dw. Avail. Virtual; // количество свободных байтов // ВАП, доступных пользователю } MEMORYSTATUS, *LPMEMORYSTATUS;
Архитектура памяти в Win 32 API Организация «динамической» виртуальной памяти (Heap Memory API)
«Кучи» (heaps) Кучи (heaps) – это динамически распределяемые области данных. При порождении процесса ему предоставляется куча размером 1 Мбайт по умолчанию. Ее размер может изменяться параметром /HEAP при построении исполняемого модуля. Функции библиотеки времени исполнения компилятора CRT (malloc(), free() и т. д. ) используют возможности куч.
Функции создания и использования «куч» HANDLE Get. Process. Heap (VOID ) – получение дескриптора кучи по умолчанию; LPVOID Heap. Alloc (HANDLE h. Heap, DWORD dw. Flags, DWORD dw. Size) – для выделения из кучи блока памяти заданного размера и возвращения указателя; LPVOID Heap. Re. Alloc (HANDLE h. Heap, DWORD dw. Flags, LPVOID lp. Old. Block, DWORD dw. Size) – для изменения размера выделенного блока памяти с возможностью перемещения блока при необходимости (флаг запрета на перемещение HEAP_REALLOC_IN_PLACE_ONLY); BOOL Heap. Free (HANDLE h. Heap, DWORD dw. Flags, LPVOID lp. Mem) – для освобождения выделенного блока памяти кучи.
Создание дополнительных «куч» для повышения эффективности управления памятью; для уменьшения рабочего множества процесса; для повышения производительности многопоточных приложений; для устранения дискриминации между потоками; для быстрого освобождение всей памяти в куче.
Повышение эффективности управления памятью В системах со страничной организацией отсутствует проблема фрагментации физической памяти. Однако существует проблема фрагментации адресного пространства. В 4 Gb адресном пространстве эта проблема не актуальна, но она имеет значение в 1 Mb куче. Если элементы какой-либо структуры имеют один размер, а элементы другой структуры – другой размер, то полезно размещать эти структуры в разных кучах.
Уменьшение рабочего множества процесса В соответствии с принципом локальности, работа с разными структурами, чаще всего, происходит не одновременно. Границы элементов разных структур не выровнены на границу страницы. Обращение к элементам одной структуры вызывает подкачку всей страницы, а, значит и элементов другой структуры. Это увеличивает рабочее множество процесса.
Повышение производительности многопоточных приложений Предоставление собственной кучи для каждого потока многопоточного приложения уменьшает состязательность между ними, в результате чего общая производительность программы может значительно повыситься.
Устранение дискриминации между потоками Ни один из потоков не сможет получить больше памяти, чем распределено для его кучи.
Создание и уничтожение «кучи» HANDLE Heap. Create ( DWORD dw. Flags, // флаги создания кучи DWORD dw. Initial. Size, // начальный размер кучи DWORD dw. Maximum. Size // макс. размер кучи // 0 – размер всей виртуальной памяти системы ); BOOL Heap. Destroy ( HANDLE h. Heap);
Параметр dw. Flags HEAP_GENERATE_EXCEPTIONS – указывает системе на то, что в случае возникновения ошибки необходимо генерировать исключительную ситуацию. Это будет происходить во всех случаях, когда функция должна была бы возвратить значение NULL. HEAP_NO_SERIALIZE – указывает, что пока выполняется текущий вызов Heap. Alloc (), к куче не будут происходить обращения из других потоков. HEAP_ZERO_MEMORY – указывает, что выделяемая память должна инициализироваться нулями. В противном случае память не обязательно инициализируется нулями.
Определения размера блока памяти «кучи» DWORD Heap. Size( HANDLE h. Heap, // дескриптор кучи DWORD dw. Flags, // управляющие флаги LPCVOID lp. Mem // адрес проверяемого блока памяти ); Зная адрес блока памяти, полученного из пула, вы можете определить его размер при помощи функции Heap. Size (). В случае ошибки эта функция возвращает значение 0 x. FFFF. Если блоком памяти пользуется только один поток, то можно передать через параметр dw. Flags значение HEAP_NO_SERIALIZE.
Блокировка совместно используемой «кучи» BOOL Heap. Lock (HANDLE h. Heap) – блокирует кучу, при использовании данной функции только один поток имеет к ней доступ. Другие потоки, запрашивающие доступ, переводятся в состояние ожидания до тех пор, пока поток, владеющий кучей, не разблокирует ее. Это одна из форм синхронизации потоков, которым система реализует упорядоченность доступа к ресурсу. BOOL Heap. Unlock (HANDLE h. Heap) – разблокирует кучу, которая до этого была заблокирована функцией Heap. Lock ().
Дополнительные возможности по управлению «кучами» UINT Heap. Compact (HANDLE h. Heap, DWORD fdw. Flags); BOOL Heap. Walk (HANDLE h. Heap, PPROCESS_HEAP_ENTRY p. Heap. Entry); BOOL Heap. Validate (HANDLE h. Heap, DWORD dw. Flags, LPCVOID lp. Mem);
Архитектура памяти в Win 32 API Файлы, проецируемые в память (memory-mapped file API)
Проецирование файлов в память “Как и виртуальная память, проецируемые файлы позволяют резервировать регион адресного пространства и передавать ему физическую память. Различие между этими механизмами состоит в том, что в последнем случае физическая память не выделяется из системного страничного файла, а берется из файла, уже находящегося на диске. Как только файл спроецирован в память, к нему можно обращаться так, как будто он в нее целиком загружен. ” (Джеффри Рихтер. Windows для профессионалов. )
Проецируемые файлы Использование проецируемых файлов стирает для программиста грань между оперативной и дисковой памятью. С точки зрения классической теории кэш, оперативная память и дисковое пространство – это три вида памяти, отличающиеся скоростью доступа и размером. Но если перемещением данных между кэшем и ОП занимаются процессор и ОС, то перемещение данных между ОП и диском обычно выполняет прикладной процесс с использованием функций файлового ввода-вывода. В ОС Windows менеджер виртуальной памяти занимается загрузкой и перемещением страниц адресного пространства процесса, которые могут находиться не только в файле подкачки, но и в любом спроецированном файле. Механизм проецирования файлов может быть использован процессами, работающими только на локальном компьютере; он не может быть использован для передачи данных по сети.
Применение проецируемых файлов Для запуска исполняемых файлов (EXE) и динамически связываемых библиотек (DLL). Для работы с файлами. Для одновременного использования одной области данных двумя процессами.
Использование проецируемого файла при запуске нового процесса 1. 2. 3. 4. Создание виртуального адресного пространства процесса (размером 4 Gb). Резервирование в ВАП региона размером, достаточным для размещения исполняемого файла. Начальный адрес региона определяется в заголовке EXE-модуля. Обычно он равен 0 x 00400000. Проецирование исполняемого файла на зарезервированное адресное пространство. Аналогичным образом выполняется проецирование на ВАП процесса необходимых ему динамически связываемых библиотек. Информация о необходимых библиотеках находится в заголовке EXEмодуля. Желательное расположение региона адресов описано внутри библиотеки.
Пример: совместное использование файла подкачки и DLL-библиотек
Пример: одновременное использование области данных двумя процессами
Механизм проецирования файла в память Проецирование файла данных в память: Создается объект типа «файл» – функция Create. File (). Создается объект типа «проецируемый файл» – функция Create. File. Mapping (). При этом используется дескриптор файла, возвращенный функцией Create. File (). Производится отображение объекта «проецируемый файл» или его части на ВАП процесса – функция Map. View. Of. File (). Завершение проецирования файла данных: Выполняется открепление файла от адресного пространства процесса – функция Unmap. View. Of. File (). Выполняется уничтожение объектов «файл» и «проецируемый файл» – функция Close. Handle ().
Функция Create. File. Mapping HANDLE Create. File. Mapping ( HANDLE h. File, // дескриптор проецируемого файла LPSECURITY_ATTRIBUTES lp. Attributes, // атрибуты защиты объекта DWORD fl. Protect, // флаги защиты страниц DWORD dw. Maximum. Size. High, // старшее слово макс. размера DWORD dw. Maximum. Size. Low, // младшее слово макс. размера LPCTSTR lp. Name // имя объекта );
Параметр fl. Protect PAGE_READONLY SEC_COMMIT – по умолчанию PAGE_READWRITE PAGE_WRITECOPY PAGE_EXECUTE_READWRITE SEC – проецирование исполняемого файла SEC_NOCACHE – все страницы раздела определяются как некэшируемые SEC_RESERVE – резервирует все страницы раздела, не выделяя физическую память
Функция Open. File. Mapping HANDLE Open. File. Mapping ( DWORD dw. Desired. Access, // режим доступа BOOL b. Inherit. Handle, // флажок наследования LPCTSTR lp. Name // имя объекта );
Функция проецирования области LPVOID Map. View. Of. File ( HANDLE h. File. Mapping. Object, // дескриптор проецируемого файла DWORD dw. Desired. Access, // режим доступа DWORD dw. File. Offset. High, // старшее DWORD смещения DWORD dw. File. Offset. Low, // младшее DWORD смещения SIZE_T dw. Number. Of. Bytes. To. Map // количество байт отображения //, 0 – отображать файл целиком );
Параметр dw. Desired. Access FILE_MAP_WRITE – доступ к операциям чтения-записи, проецируемый файл должен быть создан с защитой PAGE_READWRITE. FILE_MAP_READ – доступ только для чтения, проецируемый файл должен быть создан с защитой PAGE_READWRITE или PAGE_READONLY. FILE_MAP_ALL_ACCESS – то же самое, что FILE_MAP_WRITE. FILE_MAP_COPY – копирование при доступе для записи, проецируемый файл должен создаваться с флажком защиты PAGE_WRITECOPY. FILE_MAP_EXECUTE – доступ к исполнению кода из отображаемой памяти, проецируемый файл должен быть создан с доступом PAGE_EXECUTE_READWRITE или PAGE_EXECUTE_READ.
Функция проецирования области по определенному адресу LPVOID Map. View. Of. File. Ex ( HANDLE h. File. Mapping. Object, // дескриптор отображаемого объекта DWORD dw. Desired. Access, // режим доступа DWORD dw. File. Offset. High, // старшее DWORD смещения DWORD dw. File. Offset. Low, // младшее DWORD смещения SIZE_T dw. Number. Of. Bytes. To. Map, // число отображаемых байтов LPVOID lp. Base. Address // начальный адрес );
Функция отмены проецирования области BOOL Unmap. View. Of. File ( LPCVOID lp. Base. Address // начальный адрес );
Механизм совместного использования проецируемого файла Один процесс создает объект «проецируемый файл» с помощью функции Create. File. Mapping (), передавая в параметре lp. Name имя объекта. Другой процесс открывает уже созданный объект «проецируемый файл» по имени. Теперь два процесса могут совместно использовать объект «проецируемый файл» . При этом, при помощи функции Map. View. Of. File () каждый процесс проецирует этот объект на свое ВАП и используют эту часть адресного пространства как разделяемую область данных.
Взаимодействие процессов через общую область данных Если один процесс меняет разделяемую область данных, то она меняется и для другого процесса. Операционная система обеспечивает когерентность разделяемой области данных для всех процессов. Для обеспечения когерентности процессы должны работать с одним объектом “проецируемый файл”, а не с одним файлом.
Использование файла подкачки Совместного использования несколькими процессами области файла подкачки необходимо в функцию Create. File. Mapping () передать в качестве параметра не дескриптор ранее открытого файла, а значение 0 x. FFFF (или -1).
Пример проецирования файла подкачки //создание объекта «проецируемый файл» HANDLE h. File. Mapping = Create. File. Mapping( (HANDLE)0 x. FFFF, NULL, PAGE_READWRITE, 0, 100, lp. File. Share. Name ); Close. Handle( h. File ); //проецирование файла подкачки целиком PVOID p. Massive = Map. View. Of. File( h. File. Mapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); //здесь производится работа с массивом p. Massive … //завершение работы с проецированной областью Unmap. View. Of. File( p. Massive );
Архитектура памяти в Win 32 API Доступ к ВП другого процесса
Доступ к ВП другого процесса Ранее говорилось, что менеджер ВП изолирует ВАП процессов для защиты от несанкционированного доступа. Для решения задачи обмена информацией между процессами следует использовать проецируемые файлы или другие специальные технологии, которые будут рассмотрены в разделе «Межпроцессное взаимодействие» . Однако есть еще один способ получить доступ к памяти другого процесса – использовать функции Read. Process. Memory () и Write. Process. Memory (). Недостаток этого метода – процесс-писатель должен установить разрешение PROCESS_VM_READ, которое предоставит ВСЕМ процессам системы право читать ВСЕ свое адресное пространство!
Функции Read. Process. Memory и Write. Process. Memory Read. Process. Memory () – читает данные из области памяти в заданном процессе. Write. Process. Memory () – пишет данные в область памяти в заданном процессе. Пример для самостоятельной работы: http: //faceh 0 r. narod. ru/doc/Game. Trainer. html
Функция Write. Process. Memory BOOL Write. Process. Memory ( HANDLE h. Process, // дескриптор процесса LPVOID lp. Base. Address, // указатель на базовый адрес в // ВАП процесса, по которому должны быть записаны данные LPCVOID lp. Buffer, // указатель на буфер, который содержит // данные, записываемые в ВАП заданного процесс SIZE_T n. Size, // число записываемых байтов SIZE_T* lp. Number. Of. Bytes. Written // указатель на переменную для // записи количество реально записанных байтов );
Функции Read. Process. Memory BOOL Read. Process. Memory( HANDLE h. Process, // дескриптор процесса LPCVOID lp. Base. Address, // указатель на базовый адрес в // ВАП процесса, по которому выполняется чтение LPVOID lp. Buffer, // указатель на буфер, в который должны // быть записаны читаемые данные из ВАП заданного процесса SIZE_T n. Size, // число читаемых байтов SIZE_T* lp. Number. Of. Bytes. Read // указатель на переменную для // записи количество реально записанных байтов );
Архитектура памяти в Win 32 API Адресация расширенного адресного пространства
Address Windowing Extensions (AWE) – программный интерфейс в ОС семейства Microsoft Windows, позволяющий 32 -битному приложению получить доступ к оперативной памяти, размер которой превышает размеры доступного приложению виртуального адресного пространства (2 -3 Гб). Процесс отображения дополнительной памяти в адресное пространство приложения при помощи AWE называется «windowing» (оконный доступ) и сходен с концепцией «overlay» используемой, например, в DOS. AWE не зависит от расширения Physical Address Extension и не препятствует его использованию. В статье 2004 года, опубликованной в Dr. Dobb's Journal, отмечалось, что память, выделенная при помощи AWE не подлежит сохранению в swap-файле, и было высказано предположение, что в регионы AWE памяти удобно использовать для защиты данных, например, ключей шифрования.
Функции AWE API Virtual. Alloc () – резервирует диапазон виртуального адресного пространства для использования под AWE (атрибут MEM_PHYSICAL). Allocate. User. Physical. Pages () – выделяет физическую память для использования с AWE. Map. User. Physical. Pages () – проецирует (или отключает) виртуальное пространство AWE на произвольный набор физических страниц, полученных при помощи вызова Allocate. User. Physical. Pages (). Map. User. Physical. Pages. Scatter () – выполняет групповое проецирование (или отключение) виртуального пространства AWE на несколько блоков физических страниц. Free. User. Physical. Pages () – освобождает физическую память, которая была выделена под AWE.
Пример использования AWE В AWE-окно в виртуальном адресном пространстве приложения проецируется блок физической памяти, предварительно выделенный с помощью функции Allocate. User. Physical. Pages ().
Функция Allocate. User. Physical. Pages BOOL Allocate. User. Physical. Pages( HANDLE h. Process, // дескриптор процесса PULONG_PTR Number. Of. Pages, // на входе: количество страниц на выделение, // на выходе: количество выделенных страниц RAM PULONG_PTR User. Pfn. Array // массив номеров фреймов, // идентифицирующий выделенные окну страницы );
Функция Map. User. Physical. Pages BOOL Map. User. Physical. Pages( PVOID lp. Address, // адресного окна ULONG_PTR Number. Of. Pages, // число элементов в массиве PULONG_PTR User. Pfn. Array // массив фреймов страниц ); Функция отключает текущий блок оперативной памяти от адресного окна, если вместо параметра User. Pfn. Array передается NULL.
Функция Free. User. Physical. Pages BOOL Free. User. Physical. Pages( HANDLE h. Process, // дескриптор процесса PULONG_PTR Number. Of. Pages, // на входе: количество страниц на освобождение, // на выходе: количество освобожденных страниц RAM PULONG_PTR User. Pfn. Array // массив номеров фреймов, // идентифицирующий освобождаемые страницы RAM );
Пример кода программы по иcпользованию AWE // сначала резервируем для адресного окна регион размером 1 Мб ULONG_PTR ul. RAMBytes = 1024 * 1024; PVOl. D pv. Window = Virtual. Alloc(NULL, ul. RAMBytes, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE); // получаем размер страниц на данной процессорной платформе SYSTEM_INFO sinf; Get. System. Info (&sinf); // вычисляем, сколько страниц памяти нужно для нашего количества байтов ULONG_PTR ul. RAMPages = (ul. RAMBytes + sinf. dw. Page. Size – 1) / sinf. dw. Page. Size; // создаем соответствующий массив для номеров фреймов страниц ULONG_PTR a. RAMPages[ul. RAMPages]; // выделяем страницы оперативной памяти (пользователь должен иметь права // на блокировка страниц в памяти) Allocate. User. Physical. Pages( Get. Current. Process(), &ul. RAMPages, a. RAMPages); Map. User. Physical. Pages( pv. Window, ul. RAMPages, a. RAMPages); // обращаемся к этим страницам через виртуальный адрес pv. Window. . . // освобождаем блок страниц оперативной памяти Free. User. Physical. Pages( Get. Current. Process(), &ul. RAMPages, a. RAMPages); // уничтожаем адресное окно Virtual. Free(pv. Window, 0, MEM_RELEASE);
Ограничения AWE Страницы такой памяти нельзя разделять между процессами. Одну и ту же физическую страницу нельзя проецировать по более чем одному виртуальному адресу в рамках одного процесса. B старых версиях Windows страницы такой памяти могут иметь единственный атрибут защиты – «для чтения и записи» . B Windows Server 2003 Service Pack 1 и выше также поддерживаются атрибуты «нет доступа» и «только для чтения» .
AWE и архитектура x 64 64 -разрядная Windows полностью поддерживает AWE, так что перенос 32 -битных приложений, использующих этот механизм, не вызывает никаких проблем. Однако AWE не столь полезен для 64 -битных приложений, поскольку размеры их адресных пространств намного больше. Однако AWE дает возможность приложению выделять физическую память, которая никогда не сбрасывается на диск.
Архитектура памяти в Win 32 API Локальная память потока (TLS)
Локальная память потока (TLS) Для решения ситуаций, когда есть данные, которые должны быть связаны индивидуально с каждым потоком, необходимо использовать механизм локальной памяти потока TLS (Thread Local Storage). Поток имеет доступ лишь к своим TLS-переменным, и не может обратиться к TLS-переменным любого другого потока.
Назначение TLS Например, пусть процесс обрабатывает некоторый массив данных. Каждый отдельных элемент массива должен быть поставлен в соответствие на обработку отдельному потоку. Встает задача передачи каждому потоку индекса обрабатываемого элемента массива: Для этого можно использовать передачу параметров через стек функции потока Thread. Proc (). Тогда индекс будет храниться в локальной переменной функции Thread. Proc (). Но представьте, что Thread. Proc () вызывает другую функцию потом еще одну, и так поток может вызывать сотни функций с разными уровнями вложенности. Куда денется индекс элемента массива, который должен обработать поток? Да, можно передавать индекс каждой вложенной функции через стек, но это очевидно будет сказываться на эффективности программы. Решением подобной задачи является использование каждого потока своей локальной памяти – TLS.
Виды TLS динамическая TLS: размер ячейки локальных данных – 4 байт количество локальных данных – ограничено требует использования функций API статическая TLS: размер ячейки локальных данных – не ограничен количество локальных данных – не ограничено не требует использования функций API
Динамическая TLS Каждому потоку выделяется определенное количество ячеек размером 4 байта. Количество ячеек зависит от версии Windows, самое маленькое – это 64 ячейки на процесс, в ОС Windows 2000 и старше – 1088 ячеек на процесс. Количество ячеек можно определить с помощью константы TLS_MINIMUM_AVAILABLE. Для работы с динамической TLS поток может использовать четыре функции: Tls. Alloc () Tls. Get. Value () Tls. Set. Value () Tls. Free ()
Функции для работы с динамической TLS: Tls. Alloc DWORD Tls. Alloc (VOID); Данная функция резервирует очередную 4 -х байтную ячейку в локальной памяти потока и возвращает ее индекс. Далее этот индекс передают в функции Tls. Set. Value () и Tls. Get. Value (). Если массив ячеек TLS полностью использован, возвращаемое значение будет равно TLS_OUT_OF_INDEXES, что сообщает об ошибке выделения ячейки.
Функции для работы с динамической TLS: Tls. Set. Value BOOL Tls. Set. Value ( DWORD dw. Tls. Index, // TLS индекс для сохранения значения LPVOID lpv. Tls. Value // адрес значения для сохранения ); Функция Tls. Set. Value () записывает значение в ячейку с индексом, который был возвращен функцией Tls. Alloc (). В случае успеха функция возвращает TRUE, иначе – FALSE.
Функции для работы с динамической TLS: Tls. Get. Value LPVOID Tls. Get. Value ( DWORD dw. Tls. Index // TLS индекс для получения значения ); Функция Tls. Get. Value () соответственно возвращает значение указанное данным индексом. В случае успеха функция возвращает TRUE, иначе – FALSE.
Иллюстрация использования динамической TLS
Статическая TLS Статическая локальная память позволяет хранить данные любого фиксированного размера. Статическая локальная память потока опирается на механизмы загрузчика и свои собственные структуры. Статическая TLS не использует функции Win 32 API. Компиляторы высокоуровневых языков предоставляют специальный синтаксис для работы с статической TLS. В программах на ассемблере статическую TLS придется реализовывать ее вручную.
Синтаксис объявления переменной в статической TLS Компилятор Microsoft VC++ позволяет использовать следующий синтаксис для создания переменной специфичной для потока: Пример кода: __declspec(thread) int tls_i = 1; Этим кодом создается переменная tls_i локальная для потока, которая инициализируется значением 1. Переменная может быть любого типа. Переменная, указываемая за __declspec(thread), должна быть либо глобальной, либо статической внутри (или вне) функции. Локальную переменную с модификатором __declspec(thread) объявить нельзя.
Ограничения на использование статической TLS 1. 2. 3. 4. Спецификатор __declspec( thread ) может быть использован только с данными. TLS нельзя применять к локальным переменным. Нельзя получить адрес переменной TLS, т. к. он не является константой. Могут возникнуть проблемы с DLL, которую динамически загружают с помощью Load. Library (). Для DLL, которые могут быть загружены с помощью Load. Library () и которые используют TLS рекомендуется использовать динамическую TLS.


