Виртуальная память в Microsoft Windows
Зздесь мырассмотрим архитектуру памяти, применяемую в Microsoft Windows.
Виртуальное адресное пространство процесса
Каждому процессувыделяется собственное виртуальное адресное пространство. Для 32-разрядныхпроцессов его размер составляет 4 Гб. Соответственно 32-битный указатель можетбыть любым числом от 0x00000000 до 0xFFFFFFFF. Всего, таким образом, указательможет принимать 4 294 967 296 значений, что как раз и перекрываетчетырехгигабайтовый диапазон. Для 64-разрядных процессов размер адресногопространства равен 16 экзабайтам, поскольку 64-битный указатель может бытьлюбым числом от 0x00000000 00000000 до 0xFFFFFFFF FFFFFFFF и принимать 18 446744 073 709 551 616 значений, охватывая диапазон в 16 экзабайтов.Посколькукаждому процессу отводится закрытое адресное пространство, то, когда в процессевыполняется какой-нибудь поток, он получает доступ только к той памяти, котораяпринадлежит его процессу. Память, отведенная другим процессам, скрыта от этогопотока и недоступна ему. В Windows 2000 память, принадлежащая собственнооперационной системе, тоже скрыта от любого выполняемого потока. Иными словами,ни один поток не может случайно повредить ее данные.В Windows 2000, ни одинпоток не может получить доступ к памяти чужого процесса. Итак, адресноепространство процесса закрыто. Отсюда вытекает, что процесс А в своем адресномпространстве может хранить какую-то структуру данных по адресу 0x12345678, иодновременно у процесса В по тому же адресу — но уже в его адресномпространстве — может находиться совершенно иная структура данных. Обращаясь кпамяти по адресу 0x12345678, потоки, выполняемые в процессе А, получают доступк структуре данных процесса А, Но, когда по тому же адресу обращаются потоки,выполняемые в процессе В, они получают доступ к структуре данных процесса В.Иначе говоря, потоки процесса А не могут обратиться к структуре данных вадресном пространстве процесса В, и наоборот
Как адресное пространство разбивается наразделы
Виртуальноеадресное пространство каждого процесса разбивается на разделы. Их размер иназначение в какой-то мере зависят от конкретного ядра Windows (таблица 13-1)
Раздел
32-разрядная Windows 2000 (на х86 и Alpha)
32-разрядная Windows 2000 (на х86 с ключом /3GB)
64-разрядная Windows 2000 (на Alpha и А-64)
Windows 98
Для выявления
0x00000000
0x00000000
0x00000000 00000000
0x00000000
нулевых указателей
0x0000FFFF
0x0000FFFF
0x00000000 0000FFFF
0x00000FFF
Для совместимости с программами DOS и 16-разрядной Windows
Hет
Нет
Нет
0x00001000 0x003FFFFF
Для кода и данных
0x00010000
0x00010000
0x00000000 00010000
0x00400000
пользовательского режима
0x7FFEFFFF
0xBFFFFFFF
0x000003FF FFFEFFFF
0x7FFFFFFF
Закрытый,
0x7FFF0000
0xBFFF0000
0x000003FF FFFF0000
Нет
размером 64 Кб
0x7FFFFFFF
0xBFFFFFFF
0x000003FF FFFFFFFF
Для общих MMF (файлов, проецируемых в память)
Нет
Нет
Нет
0x80000000 0xBFFFFFFF
Для кода и данных
0x800000000
0xC0000000
0x00000400 00000000
0xC0000000
режима ядра
0xFFFFFFFF
0xFFFFFFFF
0xFFFFFFFF FFFFFFFF
0xFFFFFFFF
Таблица13-1. Так адресное пространство процесса разбивается на разделыРаздел для выявления нулевыхуказателей (Windows 2000 и Windows 98)
Этот разделадресного пространства резервируется для того, чтобы программисты могливыявлять нулевые указатели. Любая попытка чтения или записи в память по этимадресам вызывает нарушение доступа. Довольно часто в программах, написанных наС/С++, отсутствует скрупулезная обработки ошибок. Например, в следующемфрагменте кода такой обработки вообще нет:
int* pnSomeInteger= (int*) malloc(sizeof(int));
*pnSomeInteger = 5;
При нехватке памяти malloc вернет NULL. Ho код не учитывает этувозможность и при ошибке обратится к памяти по адресу 0x00000000 А посколькуэтот раздел адресного пространства заблокирован, возникнет нарушение доступа иданный процесс завершится Эта особенность помогает программистам находить«жучков* в своих приложениях. В Windows 2000 программыдля MS-DOS и 16-разрядной Windows выполняются в собственных адресныхпространствах; 32-разрядные приложения повлиять на них не могут. Раздел для кода и данныхпользовательского режима (Windows 2000 и Windows 98)
В этом разделерасполагается закрытая (неразделяемая) часть адресного пространства процесса.Ни один процесс не может получить доступ к данным другого процесса, размещеннымв этом разделе. Основной объем данных, принадлежащих процессу, хранится именноздесь (это касается всех приложений) Поэтому приложения менее зависимы отвзаимных «капризов», и вся система функционирует устойчивее. В Windows 2000 сюдазагружаются все EXE- и DLL-модули В каждом процессе эти DLL можно загружать поразным адресам в пределах данного раздела, но так делается крайне редко. Наэтот же раздел отображаются все проецируемые в память файлы, доступные данномупроцессу. В 64-разрядной Windows 2000 ядро наконец получит то пространство, котороеему нужно на самом деле.
Увеличение разделадля кода и данных пользовательского режима до 3 Гб на процессорах x86 (толькоWindows 2000)
Microsoft предусмотрела в версиях Windows 2000Advanced Server и Windows 2000 Data Center для процессоров x86 возможностьувеличения этого пространства до 3 Гб. Чтобы все процессы использовали разделдля кода и данных пользовательского режима размером 3 Гб, а раздел для кода иданных режима ядра — объемом 1 Гб, Вы должны добавить ключ /3GB к нужной записив системном файле Boot.ini. Как выглядит адресное пространство процесса в этомслучае, показано в графе «32-разрядная Windows 2000 (на x86 сключом /3GB)» таблицы 13-1.
Уменьшение разделадля кода и данных пользовательского режима до 2 Гб в 64-разрядной Windows 2000
Многиеразработчики захотят как можно быстрее перенести свои 32-разрндные приложения в64-разрядную среду. Но в исходном коде любых программ полно таких мест, гдепредполагается, что указатели являются 32-разрядными значениями. Простаяперекомпиляция исходного кода приведет к ошибочному усечению указателей инекорректному обращению к памяти. Однако, если бы система как-то гарантировала,что память никогда не будет выделяться по адресам выше 0x00000000 7FFFFFFF,приложение работало бы нормально. И усечение 64-разрядного адреса до32-разрядного, когда старшие 33 бита равны 0, не создало бы никаких проблем.Так вот, система дает такую гарантию при запуске приложения в «адреснойпесочнице» (address space sandbox), которая ограничивает полезное адресноепространство процесса до нижних 2 Гб. По умолчанию, когда Вы запускаете64-разрядное приложение, система резервирует все адресное пространствопользовательского режима, начиная с 0x0000000 80000000, что обеспечиваетвыделение памяти исключительно в нижних 2 Гб 64-разрядного адресногопространства. Это и есть «адресная песочница». Большинству приложенийэтого пространства более чем достаточно. А чтобы 64-разрядное приложение моглоадресоваться ко всему разделу пользовательского режима (объемом 4 Тб), егоследует скомпоновать с ключом /LARGEADDRESSAWARE. Закрытый раздел размером 64 Кб (толькоWindows 2000)
Этот разделзаблокирован, и любая попытка обращения к нему приводит к нарушению доступаMicrosoft резервирует этот раздел специально, чтобы упростить внутреннююреализацию операционной системы. Вспомните, когда Бы передаете Windows-функцииадрес блока памяти и его размер, то она (функция), прежде чсм приступить кработе, проверяет, действителен ли данный блок. Допустим, Вы написали код:
BYTE bBuf[70000]; DWORDdwNumBytesWritTen; WriteProcessMemory(GetCurrentProcess(), (PVOID) 0x7FFEEE90, bBuf,sizeof(bBuf), &dwNumBytesWntten);
В случае функцийтипа WriteProcessMemory область памяти, в которую предполагается запись,проверяется кодом, работающим в режиме ядра, — только он имеет право обращатьсяк памяти, выделяемой под код и данные режима ядра (в 32-разрядных системах — поадресам выше 0x80000000). Если по этому адресу есть память, вызов WriteProcessMemory,показанный выше, благополучно запишет данные в ту область памяти, которая, поидее, доступна только коду, работающему в режиме ядра. Чтобы предотвратить этои в то же время ускорить проверку таких областей памяти, Microsoft предпочлазаблокировать данный раздел, и поэтому любая попытка чтения или записи в немвсегда вызывает нарушение доступа. Раздел для кода иданных режима ядра (Windows 2000 и Windows 98)
В этот разделпомещается код операционной системы, в том числе драйверы устройств и коднизкоуровневого управления потоками, памятью, файловой системой, сетевойподдержкой. Все, что находится здесь, доступно любому процессу. В Windows 2000эти компоненты полностью защищены. Поток, который попытается обратиться поодному из адресов памяти в этом разделе, вызовет нарушение доступа, а этоприведет к тому, что система в конечном счете просто закроет его приложение.64-разрядной Windows 2000 раздел пользовательского режима (4 Тб) выглядитнепропорционально малым по сравнению с 16 777 212 Тб, отведенными под разделдля кода и данных режима ядра. Дело не в том, что ядру так уж необходимо всеэто виртуальное пространство, a просто 64-разрядное адресное пространствонастолько огромно, что его большая часть не задействована. Система разрешаетнашим программам использовать 4 Тб, а ядру — столько, сколько ему нужно. Ксчастью, какие-либо внутренние структуры данных для управлениянезадействованными частями раздела для кода и данных режима ядра не требуются. Регионы в адресном пространстве
Адресноепространство, выделяемое процессу в момент создания, практически все свободно(незарезервировано). Поэтому, чтобы воспользоваться какой-нибудь его частью,нужно выделить в нем определенные регионы через функцию WirtualAlloc.Операциявыделения региона называется резервированием (reserving). Прирезервировании система обязательно выравнивает начало региона с учетом такназываемой гранулярности выделения памяти (allocation granularity).Последняя величина в принципе зависит от типа процессора, но для процессоров,рассматриваемых в книге (x86, 32- и 64-разрядных Alpha и IA-64), — онаодинакова и составляет 64 Кб.Резервируя регион в адресном пространстве, системаобеспечивает еще и кратность размера региона размеру страницы. Такназывается единица объема памяти, используемая системой при управлении памятью.Как и гранулярность выделения ресурсов, размер страницы зависит от типапроцессора В частности, для процессоров x86 он равен 4 Кб, а для Alpha(под управлением как 32-разрядной, так и 64-разядной Windows 2000) — 8 Кб.Иногда система сама резервирует некоторые регионы адресного пространства винтересах.Если Вы попытаетесь зарезервировать регион размером 10Кб, система автоматически округлит заданное Вами значение до большей кратнойвеличины. А зто значит что на x86 будет выделен регион размером 12 Кб, ана Alpha — 16 Кб.Когда зарезервированный регион адресного пространствастановится не нужен, ею следует вернуть в общие ресурсы системы.Эта операция — освобождение(releasing) региона — осуществляется вызовом функции VirtualFree Передача региону физической памяти
Чтобызарезервированный регион адресного пространства можно было использовать, Выдолжны выделить физическую память и спроецировать её на этот регион. Такаяоперация называется передачей физической памяти (committing physicalstorage). Чтобы передать физическую память зарезервированному региону, Выобращаетесь все к той же функции VirtualAlloc.Передавая физическуюпамять регионам, нет нужды отводить ее целому региону. Можно, скажем,зарезервировать регион размером 64 Кб и нередать физическую память только еговторой и четвертой страницам. Когда физическая память, переданнаязарезервированному региону, больше не нужна, ее освобождают. Эта операция — возвратфизической памяти (decommitting physical storage) — выполняется вызовомфункции VirtualFree.
Физическую память следует рассматривать как данные, хранимые в дисковом файлесо страничной структурой. Поэтому, когда приложение передает физическую памятькакому-нибудь региону адресного пространства (вызывая VirtualAttoc), онана самом деле выделяется из файла, размещенного на жестком диске. Размерстраничного файла в системе — главный фактор, определяющий количествофизической памяти, доступное приложениям. Реальный объем оперативной памятиимеет гораздо меньшее значение. Физическая память в страничном файле не хранится Windows 2000может использовать несколько страничных файлов, и, если они расположены наразных физических дисках, операционная система работает гораздо быстрее, посколькуспособна вести запись одновременно на нескольких дисках. Чтобы добавить илиудалить страничный файл, откройте в Control Panel апплет System, выберитевкладку Advanced и щелкните кнопку Performance Options. Нa экране появитсяследующее диалоговое окно.
Однако системадействует не так, иначе на загрузку и подготовку программы к запуску уходило быслишком много времени.При запуске приложения система открывает его исполняемыйфайл и определяет объем кода и данных. Затем резервирует регион адресногопространства и помечает, что физическая память, связанная с этим регионом, —сам ЕХЕ-файл, то есть вместо выделения какого-то пространства из страничногофайла система использует истинное содержимое, или образ (image) ЕХЕ-файлакак зарезервированный регион адресного пространства программы. Благодаря этомуприложение загружается очень быстро, а размер страничного файла удается заметноуменьшить. Образ исполняемого файла (т. e. EXE- или DLL-файл), размещенный нажестком диске и применяемый как физическая память для того или иного регионаадресного пространства, называется проецируемым в память файлом(memory-mapped file). При загрузке EXF, или DLL система автоматическирезервирует регион адресного пространства и проецирует на него образ файла.Помимо этого, система позволяет (с помощью набора функций) проецировать нарегион адресного пространства еще и файлы данных
Когда EXE- или DLL-файл загружается с дискеты Windows 2000 целиком копируют егов оперативную память, а в страничном файле выделяют такое пространство, чтобы внем мог уместиться образ загружаемого файла. Если нагрузка на оперативнуюпамять в системе невелика, EXE- или DLLфайл всегда запускается непосредственноиз оперативной памяти.Так сделано для корректной работы программ установки.Обычно программа установки запускается с первой дискеты, потом поочередновставляются следующие диски, на которых собственно и содержится устанавливаемоеприложение. Если системе понадобится какой-то фрагмент кода EXE- или DLLмодуляпрограммы установки, на текущей дискете его, конечно же, пет. Но, посколькусистема скопировала файл в оперативную память (и предусмотрела для него место встраничном файле), у нее не возникнет проблем с доступом к нужной части кодапрограммы установки Атрибуты защитыОтдельным страницам физической памяти можноприсвоить свои атрибуты защиты показанные в следующей таблице.
Атрибут защиты
Описание
PAGE_NOACCESS
Попытки чтения, записи или исполнения содержимого памяти на этой странице вызывают нарушение доступа
PAGE_READONLY
Попытки записи или исполнения содержимого памяти на этой странице вызывают нарушение доступа
PAGE_READWRITE
Попытки исполнения содержимого памяти на этой странице вызывают нарушение доступа
PAGE_EXECUTE
Попытки чтения или записи на этой странице вызывают нарушение доступа
PAGE_EXECUTE_READ
Попытки записи на этой странице вызывают нарушение доступа
PAGE_EXECUTE_READWRITE
На этой странице возможны любые операции
PAGE_WRITECOPY
Попытки исполнения содержимого памяти на этой странице выбывают нарушение доступа, попытка записи приводит к тому, что процессу предоставляется «личная» копия данной страницы
PAGE_EXECUTE_WRITECOPY
На этой странице возможны любые операции, попытка записи приводит к тому, что процессу предоставляется «личная» копия данной страницы Защита типа «копирование призаписи»
Атрибуты защиты,перечисленные в предыдущей таблице, достаточно понятны, кроме двух последних:PAGE_WRITECOPY и PAGE_EXECUTE_WRITECOPY. Они предназначены специально дляэкономного расходования оперативной памяти и места в страничном файле. Windowsподдерживает мехянизм, позволяющий двум и более процессам разделять один и тотже блок памяти. Например, если Вы запустите 10 экземпляров программы Notepad,все экземпляры будут совместно использовать одни и те же страницы с кодом иданными этой программы. И обычно никяких проблем не возникает — пока процессыничего не записывают в общие блоки памяти. Только представьте, что творилось быв системе, если потоки из разных процессов начали бы одновременно записывать водин и тот же блок памяти!
Чтобыпредотвратить этот хаос, операционная система присваивает общему блоку памятиатрибут защиты «копирование при записи» (copy-on-write). Когда потокв одном процессе попытается что-нибудь записать в общий блок памяти, в дело тутже вступит система и проделает следующие операции:Найдет свободную страницу в оперативной памяти. Заметьте, что при первом проецировании модуля на адресное пространство процесса эта страница будет скопирована на одну из страниц, выделенных в страничном файле. Поскольку система выделяет нужное пространство в страничном файле еще при первом проецировании модуля, сбои на этом этапе маловероятны. Скопирует страницу с данными, которые поток пытается записать в общий блок памяти, на свободную страницу оперативной памяти, полученную на этапе 1. Последней присваивается атрибут защиты PAGE_WRITECOPY или PAGE_EXECUTE_WRITECOPY. Атрибут защиты и содержимое исходной страницы не меняются. Отобразит виртуальный адрес этой страницы в процессе на новую страницу в оперативной памяти.
Когда системавыполнит эти операции, процесс получит свою копию нужной страницы памяти.
Кроме того, прирезервировании адресного пространства или передаче физической памяти через VirtualAllocнельзя указывать атрибуты PAGE_WRITECOPY или PAGE_EXECUTE_WRITECOPY. ИначевызовVirtualAllocдастошибку, a GetLastError вернеткодERROR_INVALID_PARAMETER. Дело в том, что эти два атрибута используютсяоперационной системой, только когда она проецирует образы EXE- или DLL-файлов.
Базовый адрес
Тип
Размер
Блоки
Атрибут( ы) защиты
Описание
00000000
Free
65536
00010000
Private
4096
1
-RW-
00011000
Free
G1440
00020000
Private
4096
1
-RW-
000? 1000
Free
61440
00030000
Private
1048576
3
-HW-
Стек потока
00130000
Private
1048576
2
-RW-
00230000
Mapped
65536
2
-RW-
00240000
Mapped
90112
1
-R-
DeviceHarddiskVolume1WINN7system32unicode.nls
00256000
Free
40960
00260000
Mapped
208896
1
-R-
DeviceHarddiskVolume1WINNTsystem32locale.nIs
00293000
Free
53248
002A0000
Happed
266240
1
-R-
PeviccHarddiskVolume1WINNTsystem32sortkey.nls
002E1000
Free
61440
002F0000
Mapped
16384
1
-R-
DeviceHarddiskVolume1WINNTsystem32sorttbls.nls
002F4000
Free
49152
00300000
Mapped
819200
4
ER-
0003С8000
Free
229376
00400000
Image
106496
5
ERWC
С CDx86Debug14_VMMap.ехе
0041A000
Free
24576
00420000
Mapped
274432
1
-R-
00463000
Free
53248
00470000
Mapped
3145728
2
ER
00770000
Private
4096
1
-RW-
00771000
Free
61440
00780000
Private
4096
1
-RW-
00781000
Free
61440
00790000
Private
65536
2
-RW-
007A0000
Mapped
8192
1
-R-
DeviceHarddiskVolume1WINNTsystem32ctype.nls
007А2000
Free
1763893248
699D0000
Image
45056
4
ERWC
C:WINNTSystpm32PSAPI dll
6990В000
Free
238505984
77D50000
Image
450560
4
ERWC
С:WINNTsystem32RPCRT4 DLL
770ВЕ000
Free
8192
770С0000
Image
344064
5
ERWC
С:WINNTsystem32ADVAPI32 dll
77Е14000
Free
49152
77E20000
Image
401408
4
ERWC
C:WINNTsystem32USER32 dll
77Е82000
Free
57344
77Е90000
Image
720896
5
ERWC
СWINNTsystem32KERNEL32.dll
77F40000
Image
241664
4
ERWC
СWINKTsystem32GUI32 DLL
77F7В000
Free
20480
77FB0000
image
483328
5
ERWC
СWINNTSystem32tdll.dll
77FF000
Free
40960
78000000
Image
290816
6
bMWC
СWINNTsystem32MSVCRI.dll
78047000
Free
124424192
7F6F0000
Mapped
1048576
2
ER--
7F7F0000
Free
8126464
7FFB0000
Mapped
147456
1
-R--
7FFD4000
Free
40960
7FFDE000
Private
4096
1
ERW-
7FFDF000
Private
4096
1
ERW-
7FFE0000
Private
65536
2
-R--
Таблица13-2. Образец карты адресного пространства процесса в Windows 2000 на32-разрядном процессоре типа x86
Во втором полепоказывается тип региона Free (свободный), Private (закрытый), Image (образ)или Mapped (проецируемый). Эти типы описаны в следующей таблице,
Тип
Описание
Free
Этот диапазон виртуальных адресов не сопоставлен ни с каким типом физической памяти. Его адресное пространство не зарезервировано, приложение может зарезервировать регион по указанному базовому адресу или в любом месте в границах свободного региона
Private
Этот диапазон виртуальных адресов сопоставлен со страничным файлом.
Image
Этот диапазон виртуальных адресов изначально был сопоставлен с образом ЕХЕ- или DLL-файла, проецируемого в память, но теперь, возможно, уже нет. Например, при записи в глобальную переменную из образа модуля механизм поддержки «копирования при записи» выделяет соответствующую страницу памяти из страничного файла, а не исходною образа файла.
Mapped
Этот диапазон виртуальных адресов изначально был сопоставлен с файлом данных, проецируемым в память, но теперь, возможно, уже нет. Например, файл данных мог быть спроецирован с использованием механизма поддержки «копирования при записи». Любые операции записи в этот файл приведут к тому, что соответствующие страницы памяти будут выделены из страничного файла, а не из исходного файла данных.
В третьем полесообщается размер региона в байтах. Например, система спроецировала образUser32.dll по адресу 0x77E20000. Когда она резервировала адресное пространстводля этого образа, ей понадобилось 401 408 байтов. Не забудьте, что в третьемполе всегда содержатся значения, кратные размеру страницы, характерному дляданного процессора (4096 байтов для x86).В четвертом поле показаноколичество блоков в зарезервированном регионе. Блок — это неразрывная группастраниц с одинаковыми атрибутами защиты, связанная с одним и тем же типомфизической памяти.Для свободных регионов это значение всегда равно 0, так каким не передается физическая память. (Поэтому в четвертой графе никаких данныхдля свободных регионов не приводится.) Но для занятых регионов это значениеможет колебаться в пределах от 1 до максимума (его вычисляют делением размерарегиона на размер страницы). Скажем, у региона, начинающегося с адресаOx77E20000, размер — 401 408 байтов. Поскольку процесс выполняется напроцессоре x86 (страницы памяти по 4096 байтов), максимальное количество блоковв этом регионе равно 98 (401 408/4096); ну а, судя по карте, в нем содержится 4блока.
В пятом поле —атрибуты защиты региона. Здесь используются следующие сокращения: E — execute (исполнение), R — read (чтение), W — write (запись), С- copy-onwrite (копирование при записи). Если ни один из атрибутов в этой графене указан, регион доступен без ограничений. Атрибуты защиты не присваиваются исвободным регионам. Кроме того, здесь Вы никогда не увидите флагов атрибутовзащиты PAGE_ GUARD или PAGE_NOCACHE — они имеют смысл только для физическойпамяти, а не для зарезервированного адресного пространства. Атрибуты защитыприсваиваются регионам только эффективности ради и всегда замещаются атрибутамизащиты, присв