Рихтер Дж., Назар К. - Windows via C C++. Программирование на языке Visual C++ - 2009
.pdf440 Часть III. Управление памятью
Физическая память и страничный файл
В старых операционных системах физической памятью считалась вся оперативная память (RAM), установленная в компьютере. Иначе говоря, если в вашей машине было 16 Мб оперативной памяти, вы могли загружать и выполнять приложения, использующие вплоть до 16 Мб памяти. Современные операционные системы умеют имитировать память за счет дискового пространства. При этом на диске создается страничный файл (paging file), который и содержит виртуальную память, доступную всем процессам.
Разумеется, операции с виртуальной памятью требуют соответствующей поддержки от самого процессора. Когда поток пытается обратиться к какому-то байту, процессор должен знать, где находится этот байт — в оперативной памяти или на диске.
С точки зрения прикладной программы, страничный файл просто увеличивает объем памяти, которой она может пользоваться. Если в вашей машине установлено 1 Гб оперативной памяти, а размер страничного файла на жестком диске составляет также 1 Гб, приложение считает, что объем оперативной памяти равен 2 Гб.
Конечно, 2 Гб оперативной памяти у вас на самом деле нет. Операционная система в тесной координации с процессором сбрасывает содержимое части оперативной памяти в страничный файл и по мере необходимости подгружает его порции обратно в память. Если такого файла нет, система просто считает, что приложениям доступен меньший объем памяти, — вот и все. Но, поскольку страничный файл явным образом увеличивает объем памяти, доступный приложениям, его применение весьма желательно. Это позволяет приложениям работать с большими наборами данных.
Физическую память следует рассматривать как данные, хранимые в дисковом файле со страничной структурой. Поэтому, когда приложение передает физическую память какому-нибудь региону адресного пространства (вызывая VirtualAlloc), она на самом деле выделяется из файла, размещенного на жестком диске. Размер страничного файла в системе — главный фактор, определяющий количество физической памяти, доступное приложениям. Реальный объем оперативной памяти имеет гораздо меньшее значение.
Теперь посмотрим, что происходит, когда поток пытается получить доступ к блоку данных в адресном пространстве своего процесса. А произойти может одно из двух (рис. 13-2).
В первом сценарии данные, к которым обращается поток, находятся в оперативной памяти. В этом случае процессор проецирует виртуальный адрес данных на физический, и поток получает доступ к нужным ему данным.
Во втором сценарии данные, к которым обращается поток, отсутствуют в оперативной памяти, но размещены где-то в страничном файле. Попытка доступа к данным генерирует ошибку страницы (page fault), и процессор таким образом уведомляет операционную систему об этой попытке. Тогда операционная система начинает искать свободную страницу в оперативной памяти;
Глава 13. Архитектура памяти в Windows.docx 441
если таковой нет, система вынуждена освободить одну из занятых страниц. Если занятая страница нс модифицировалась, она просто освобождается; в ином случае она сначала копируется из оперативной памяти в страничный файл. После этого система переходит к страничному файлу, отыскивает в нем запрошенный блок данных, загружает этот блок на свободную страницу оперативной памяти и, наконец, отображает (проецирует) адрес данных в виртуальной памяти на соответствующий адрес в физической памяти.
Чем чаще системе приходится копировать страницы памяти в страничный файл и наоборот, тем больше нагрузка на жесткий диск и тем медленнее работает операционная система. (При этом может получиться так, что операционная система будет тратить все свое время на подкачку страниц вместо выполнения программ.) Поэтому, добавив компьютеру оперативной памяти, вы снизите частоту обращения к жесткому диску и тем самым увеличите общую производительность системы. Кстати, во многих случаях увеличение оперативной памяти дает больший выигрыш в производительности, чем замена старого процессора на новый.
Рис. 13-2. Трансляция виртуального адреса на физический
442 Часть III. Управление памятью
Физическая память в страничном файле не хранится
Прочитав предыдущий раздел, вы, должно быть, подумали, что страничный файл сильно разбухнет при одновременном выполнении в системе нескольких программ, — особенно если вы сочли, будто при каждом запуске приложения система резервирует регионы адресного пространства для кода и данных процесса, передает им физическую память, а затем копирует код и данные из файла программы (расположенного на жестком диске) в физическую память, переданную из страничного файла.
Однако система действует не так, иначе на загрузку и подготовку программы к запуску уходило бы слишком много времени. На самом деле происходит вот что: при запуске приложения система открывает его исполняемый файл и определяет объем кода и данных. Затем резервирует регион адресного пространства и помечает, что физическая память, связанная с этим регионом, — сам ЕХЕ-файл. Да-да, правильно: вместо выделения какого-то пространства из страничного файла система использует истинное содержимое, или образ (image) ЕХЕ-файла как зарезервированный регион адресного пространства программы. Благодаря этому приложение загружается очень быстро, а размер страничного файла удается заметно уменьшить.
Образ исполняемого файла (т. е. EXEили DLL-файл), размещенный на жестком диске и применяемый как физическая память для того или иного региона адресного пространства, называется проецируемым в память файлом (memorymapped file). При загрузке EXE или DLL система автоматически резервирует регион адресного пространства и проецирует на него образ файла. Помимо этого, система позволяет (с помощью набора функций) проецировать на регион адресного пространства еще и файлы данных. (О проецируемых в память файлах мы поговорим в главе 17.)
Примечание. Windows может использовать несколько страничных файлов, и, если они расположены на разных физических дисках, операционная система работает гораздо быстрее, поскольку способна вести запись одновременно на нескольких дисках. Чтобы добавить или удалить страничный файл, откройте в Control Panel аплет System, щелкните ссылку Advanced, Adjust The Appearance And Performance Of Windows. На вкладке Advanced
щелкните в секции Virtual Memory кнопку Change. На экране появится следующее диалоговое окно:
Глава 13. Архитектура памяти в Windows.docx 443
Примечание. Когда EXEили DLL-файл загружается с дискеты, Windows 98 и Windows 2000 целиком копируют его в оперативную память, а в страничном файле выделяют такое пространство, чтобы в нем мог уместиться образ загружаемого файла. Если нагрузка на оперативную память в системе невелика, EXEили DLL-файл всегда запускается непосредственно из оперативной памяти.
Так сделано для корректной работы программ установки. Обычно программа установки запускается с первой дискеты, потом поочередно вставляются следующие диски, на которых собственно и содержится устанавливаемое приложение. Если системе понадобится какой-то фрагмент кода EXEили DLL-модуля программы установки, на текущей дискете его, конечно же, нет. Но, поскольку система скопировала файл в оперативную память (и предусмотрела для него место в страничном файле), у нее не возникнет проблем с доступом к нужной части кода программы установки.
Система не копирует в оперативную память образы файлов, хранящихся на других съемных носителях (CD-ROM или сетевых дисках), если только требуемый файл не скомпонован с использованием ключа /SWAPRUN:CD или
/SWAPRUN:NET.
Атрибуты защиты
Отдельным страницам физической памяти можно присвоить свои атрибуты защиты, показанные в следующей таблице.
444 Часть III. Управление памятью
Табл. 13-3. Атрибуты защиты страниц памяти
Атрибут защиты |
Описание |
PAGE_NOACCESS |
Попытки чтения, записи или исполнения содержимого памяти на |
|
этой странице вызывают нарушение доступа |
PAGE_READONLY |
Попытки записи или исполнения содержимого памяти на этой |
|
странице вызывают нарушение доступа |
PAGE_READWRITE |
Попытки исполнения содержимого памяти на этой странице вы- |
|
зывают нарушение доступа |
PAGE_EXECUTE |
Попытки чтения или записи на этой странице вызывают наруше- |
|
ние доступа |
PAGE_EXECUTE_READ |
Попытки записи на этой странице вызывают нарушение доступа |
PAGE_EXECUTE_ |
На этой странице возможны любые операции |
READWRITE |
|
PAGE_WRITECOPY |
Попытки исполнения содержимого памяти на этой странице вы- |
|
зывают нарушение доступа; попытка записи приводит к тому, что |
|
процессу предоставляется «личная» копия данной страницы |
PAGE_EXECUTE_ |
На этой странице возможны любые операции; попытка записи |
WRITECOPY |
приводит к тому, что процессу предоставляется «личная» копия |
|
данной страницы |
Некоторые вредоносные программы записывают код в области памяти, предназначенные для хранения данных (например, в стек потока), а затем исполняют его. Для защиты от подобных атак предназначен механизм Windows, названный Data Execution Prevention (DEP). Если DEP включен, система назначает атрибуты PAGE_EXECUTE_* только тем регионам памяти, в которых хранится предназначенный для исполнения код. Остальные атрибуты (чаще всего PAGE_READWRITE) назначаются регионам, предназначенных для хранения данных (например, регионам, занятым кучами и стеками потоков). При попытке исполнения кода, хранящегося в странице, не имеющей атрибута защиты PAGE_EXECUTE_*, процессор генерирует исключение «нарушение доступа».
У механизма структурной обработки исключений Windows (см. главы 23-25) защита еще надежнее. При компоновке приложения с ключом /SAFESEH обработчики исключений регистрируются в специальной таблице, хранящейся в файле образа. При вызове обработчика исключений система проверяет, зарегистрирован ли он в таблице, и разрешает исполнение только зарегистрированных обработчиков.
Подробнее о DEP cм. в официальной статье «03_CIF_Memory_Protection. DOC» по ссылке http://go.microsoft.com/fwlink/?LinkId=28022.
Глава 13. Архитектура памяти в Windows.docx 445
Защита типа «копирование при записи»
Атрибуты защиты, перечисленные и предыдущей таблице, достаточно понятны,
кроме двух последних: PAGE_WRITECOPY и PAGE_EXECUTE_ WRITECOPY.
Они предназначены специально для экономного расходования оперативной памяти и места в страничном файле. Windows поддерживает механизм, позволяющий двум и более процессам разделять один и тот же блок памяти. Например, если вы запустите 10 экземпляров программы Notepad, все экземпляры будут совместно использовать одни и те же страницы с кодом и данными этой программы. И обычно никаких проблем не возникает — пока процессы ничего не записывают в общие блоки памяти. Только представьте, что творилось бы в системе, если потоки из разных процессов начали бы одновременно записывать в один и тот же блок памяти!
Чтобы предотвратить этот хаос, операционная система присваивает общему блоку памяти атрибут защиты «копирование при записи» (copy-on-write). Когда
.exe- или .dll-модуль, проецируется в адресное пространство процесса, система подсчитывает число страниц, доступных для записи (хранящие код страницы обычно помечаются атрибутом PAGE_EXECUTE_READ, а страницы, хранящие данные — атрибутом PAGE_READWRITE) и выделяет в страничном файле место для соответствующего числа страниц. Однако это место реально используется, только если в эти страницы будет что-то записано.
Когда поток в одном процессе попытается что-нибудь записать в общий блок памяти, в дело тут же вступит система и проделает следующие операции:
1.Найдет свободную страницу в оперативной памяти. Заметьте, что при первом проецировании модуля на адресное пространство процесса эта страница будет скопирована на одну из страниц, выделенных в страничном файле. Поскольку система выделяет нужное пространство в страничном файле еще при первом проецировании модуля, сбои на этом этапе маловероятны.
2.Скопирует страницу с данными, которые поток пытается записать в общий блок памяти, на свободную страницу оперативной памяти, полученную на этапе 1. Последней присваивается атрибут защиты PAGE_READWRITE ИЛИ PAGE_EXECUTE_READWRITE. Атрибут защиты и содержимое исходной страницы не меняются.
3.Отобразит виртуальный адрес этой страницы в процессе на новую страницу в оперативной памяти.
Когда система выполнит эти операции, процесс получит свою копию нужной
страницы памяти. Подробнее о совместном использовании памяти и о защите типа «копирование при записи» я расскажу в главе 17.
Кроме того, при резервировании адресного пространства или передаче физической памяти через VirtualAlloc нельзя указывать атрибуты PAGE_WRITECOPY или PAGE_EXECUTE_WRITECOPY. Иначе вызов VirtualAlloc даст ошибку, a
GetLastError вернет код ERROR_INVALID_PARAMETER.
446 Часть III. Управление памятью
Дело в том, что эти два атрибута используются операционной системой, только когда она проецирует образы EXEили DLL-файлов.
Специальные флаги атрибутов защиты
Кроме рассмотренных атрибутов защиты, существует три флага атрибутов защи-
ты: PAGE_NOCACHE, PAGE_WRITECOMBINE и PAGE_GUARD. Они комби-
нируются с любыми атрибутами защиты (кроме PAGE_NOACCESS) побитовой операцией OR.
Флаг PAGE_NOCACHE отключает кэширование переданных страниц. Как правило, использовать этот флаг не рекомендуется; он предусмотрен главным образом для разработчиков драйверов устройств, которым нужно манипулировать буферами памяти.
Флаг PAGE_WRITECOMBINE тоже предназначен для разработчиков драйверов устройств. Он позволяет объединять несколько операций записи на устройство в один пакет, что увеличивает скорость передачи данных.
Флаг PAGE_GUARD позволяет приложениям получать уведомление (через механизм исключений) в тот момент, когда на страницу записывается какойнибудь байт. Windows использует этот флаг при создании стека потока. Подробнее на эту тему см. раздел «Стек потока» в главе 16.
Подводя итоги
А теперь попробуем осмыслить понятия адресных пространств, разделов, регионов, блоков и страниц как единое целое. Лучше всего начать с изучения карты виртуальной памяти, на которой изображены все регионы адресного пространства в пределах одного процесса. В качестве примера мы воспользуемся программой VMMap из главы 14. Чтобы в полной мере разобраться в адресном пространстве процесса, рассмотрим его в том виде, в каком оно формируется при запуске VMMap (см. главу 14) под управлением Windows на 32-разрядной процессорной платформе x86. Образец карты адресного пространства VMMap показан в таблице
13-4.
Табл. 13-2. Образец карты адресного пространства процесса в Windows 2000 на 32-разрядном процессоре типа x86
Базовый |
Тип |
Размер |
Блоки |
Атрибут(ы) |
Описание |
|
адрес |
защиты |
|||||
|
|
|
|
|||
00000000 |
Free |
65536 |
|
|
|
|
00010000 |
Mapped |
65536 |
1 |
-RW- |
|
|
00020000 |
Private |
4096 |
1 |
-RW- |
|
|
00021000 |
Free |
61440 |
|
|
|
|
00030000 |
Private |
1048576 |
3 |
-RW- |
Стек потока |
|
00130000 |
Mapped |
16384 |
1 |
-R- |
|
Глава 13. Архитектура памяти в Windows.docx 447
Табл. 13-2. (продолжение)
Базовый |
Тип |
Размер |
Блоки |
Атрибут(ы) |
Описание |
||
адрес |
защиты |
||||||
|
|
|
|
|
|||
00134000 " |
Free |
49152 |
|
|
|
|
|
00140000 |
Mapped |
12288 |
1 |
|
-R- |
|
|
00143000 |
Free |
53248 |
|
|
|
|
|
00150000 |
Mapped |
819200 |
4 |
|
-R- |
|
|
00218000 |
Free |
32768 |
|
|
|
|
|
00220000 |
Mapped |
1060864 |
1 |
|
-R- |
|
|
00323000 |
Free |
53248 |
|
|
|
|
|
00330000 |
Private |
4096 |
1 |
|
-RW- |
|
|
00331000 |
Free |
61440 |
|
|
|
|
|
00340000 |
Mapped |
20480 |
1 |
|
-RWC |
\Device\ |
|
|
|
|
|
|
|
HarddiskVolume1\ |
|
|
|
|
|
|
|
Windows\System32\ |
|
|
|
|
|
|
|
en-US\user32.dll.mui |
|
00345000 |
Free |
45056 |
|
|
|
|
|
00350000 |
Mapped |
8192 |
1 |
|
-R- |
|
|
00352000 |
Free |
57344 |
|
|
|
|
|
00360000 |
Mapped |
4096 |
1 |
|
-RW- |
|
|
00361000 |
Free |
61440 |
|
|
|
|
|
00370000 |
Mapped |
8192 |
1 |
|
-R- |
|
|
00372000 |
Free |
450560 |
|
|
|
|
|
003E0000 |
Private |
65536 |
2 |
|
-RW- |
|
|
003F0000 |
Free |
65536 |
|
|
|
|
|
00400000 |
Image |
126976 |
7 |
|
ERWC |
C:\Apps\14 VMMap.exe |
|
0041F000 |
Free |
4096 |
|
|
|
|
|
00420000 |
Mapped |
720896 |
1 |
|
-R- |
|
|
004D0000 |
Free |
458752 |
|
|
|
|
|
00540000 |
Private |
65536 |
2 |
|
-RW- |
|
|
00550000 |
Free |
196608 |
|
|
|
|
|
00580000 |
Private |
65536 |
2 |
~" |
-RW- |
|
|
00590000 |
Free |
196608 |
|
|
|
|
|
005C0000 |
Private |
65536 |
2 |
|
-RW- |
|
|
005D0000 |
Free |
262144 |
|
|
|
|
|
00610000 |
Private |
1048576 |
2 |
|
-RW- |
|
|
00710000 |
Mapped |
3661824 |
1 |
|
-R- |
\Device\ |
|
|
|
|
|
|
|
HarddiskVolume1\ |
|
|
|
|
|
|
|
Windows\System32\ |
|
|
|
|
|
|
|
locale.nls |
|
00A8E000 |
Free |
8192 |
|
|
|
|
448 Часть III. Управление памятью
Табл. 13-2. (продолжение)
Базовый |
Тип |
Размер |
Блоки |
Атрибут(ы) |
Описание |
|
адрес |
защиты |
|||||
|
|
|
|
|||
00A90000 |
Mapped |
3145728 |
2 |
-R-- |
|
|
00D90000 |
Mapped |
3661824 |
1 |
-R-- |
\Device\ |
|
|
|
|
|
|
HarddiskVolume1\ |
|
|
|
|
|
|
Windows\System32\ |
|
|
|
|
|
|
locale.nls |
|
0110E000 |
Free |
8192 |
|
|
|
|
01110000 |
Private |
1048576 |
2 |
-RW- |
|
|
01210000 |
Private |
524288 |
2 |
-RW- |
|
|
01290000 |
Free |
65536 |
|
|
|
|
012A0000 |
Private |
262144 |
2 |
-RW- |
|
|
012E0000 |
Free |
1179648 |
|
|
|
|
01400000 |
Mapped |
2097152 |
1 |
-R-- |
|
|
01600000 |
Mapped |
4194304 |
1 |
-R-- |
|
|
01A00000 |
Free |
1900544 |
|
|
|
|
01BD0000 |
Private |
65536 |
2 |
-RW- |
|
|
01BE0000 |
Mapped |
4194304 |
1 |
-R-- |
|
|
01FE0000 |
Free |
235012096 |
|
|
|
|
739B0000 |
Image |
634880 |
9 |
ERWC |
C:\Windows\WinSxS\ |
|
|
|
|
|
|
x86_microsoft.vc80. |
|
|
|
|
|
|
crt_lfc8b3b9alel8e3b_ |
|
|
|
|
|
|
8.0.50727.312_none_ |
|
|
|
|
|
|
10b2ee7b9bffc2c7\ |
|
|
|
|
|
|
MSVCR80.dll |
|
73A4B000 |
Free |
24072192 |
|
|
|
|
75140000 |
Image |
1654784 |
7 |
ERWC |
C:\Windows\ |
|
|
|
|
|
|
WinSxS\x86_ |
|
|
|
|
|
|
microsoft.windows. |
|
|
|
|
|
|
common-controls_ |
|
|
|
|
|
|
6595b64144ccfldf_ |
|
|
|
|
|
|
6.0.6000.16386_none_ |
|
|
|
|
|
|
5d07289e07eldl00\ |
|
|
|
|
|
|
comctl32.dll |
|
752D4000 |
Free |
1490944 |
|
|
|
|
75440000 |
Image |
258048 |
5 |
ERWC |
C:\Wlndows\ |
|
|
|
|
|
|
system32\uxtheme.dll |
|
7547F000 |
Free |
15208448 |
|
|
|
|
76300000 |
Image |
28672 |
1 |
ERWC |
C:\Windows\ |
|
|
|
|
|
system32\PSAPI.dll |
||
|
|
|
|
|
||
76307000 |
Free |
626688 |
|
|
|
|
763A0000 |
Image |
512000 |
7 |
ERWC |
C:\Wmdows\ |
|
|
|
|
|
|
system32\USP10.dll |
|
7641D000 |
Free |
12288 |
|
|
|
Глава 13. Архитектура памяти в Windows.docx 449
Табл. 13-2. (продолжение)
Базовый |
Тип |
Размер |
Блоки |
Атрибут(ы) |
Описание |
|
адрес |
защиты |
|||||
|
|
|
|
|||
76420000 |
Image |
307200 |
1 |
ERWC |
C:\Windows\ |
|
|
|
|
|
|
system32\GDI32.dll |
|
7646B000 |
Free |
20480 |
|
|
|
|
76470000 |
Image |
36864 |
4 |
ERWC |
C:\Windows\ |
|
|
|
|
|
|
system32\LPK.dll |
|
76479000 |
Free |
552960 |
|
|
|
|
76500000 |
Image |
348160 |
4 |
ERWC |
C:\Windows\system32\ |
|
|
|
|
|
|
SHLWAPI.dll |
|
76555000 |
Free |
1880064 |
|
|
|
|
76720000 |
Image |
696320 |
7 |
ERWC |
C:\Windows\ |
|
|
|
|
|
|
system32\msvcrt.dll |
|
767CA000 |
Free |
24576 |
|
|
|
|
767D0000 |
Image |
122880 |
4 |
ERWC |
C:\Windows\ |
|
|
|
|
|
|
system32\IMM32.dll |
|
767EE000 |
Free |
8192 |
|
|
|
|
767F0000 |
|
647168 |
5 |
ERWC |
C:\Windows\ |
|
|
|
|
|
|
system32\USER32.dll |
|
7688E000 |
Free |
8192 |
|
|
|
|
76890000 |
Image |
815104 |
4 |
ERWC |
C:\Windows\ |
|
|
|
|
|
|
system32\MSCTF.dll |
|
76957000 |
Free |
36864 |
|
|
|
|
76960000 |
Image |
573440 |
4 |
ERWC |
C:\Windows\system32\ |
|
|
|
|
|
|
OLEAUT32.dll |
|
769EC000 |
Free |
868352 |
|
|
|
|
76AC0000 |
Image |
798720 |
4 |
ERWC |
C:\Windows\ |
|
|
|
|
|
|
system32\RPCRT4.dll |
|
76B83000 |
Free |
2215936 |
|
|
|
|
76DA0000 |
Image |
884736 |
5 |
ERWC |
C:\Windows\ |
|
|
|
|
|
|
system32\kernel32.dll |
|
76E7S000 |
Free |
32768 |
|
|
|
|
76E80000 |
Image |
1327104 |
5 |
ERWC |
C:\Windows\ |
|
|
|
|
|
|
system32\ole32.dll |
|
76FC4000 |
Free |
11649024 |
|
|
|
|
77AE0000 |
Image |
1171456 |
9 |
ERWC |
C:\Windows\ |
|
|
|
|
|
|
system3Antdll,dH |
|
77BFE000 |
Free |
8192 |
|
|
|
|
77C00000 |
Image |
782336 |
7 |
ERWC |
C:\Windows\system32\ |
|
|
|
|
|
|
ADVAPI32.dll |
|
77CBF000 |
Free |
128126976 |
|
|
|
|
7F6F0000 |
Mapped |
1048576 |
2 |
-R-- |
|
|
7F7F0000 |
Free |
8126464 |
|
|
|