Пост создан для публикации одной единственной картинки (кликабельна). На мой взгляд, она как нельзя лучше передаёт атмосферу того как устроена память в компьютере. Я изобразил её на 3-4 курсе института на A4 при объяснении положения дел товарищу. Так она мне тогда понравилась, что дал себе обещание нарисовать её в электронном виде.
Исходник в формате VSD (Microsoft Visio) лежит у меня в DropBox. Если имеются уточнения, поправки, комментарии — прошу под кат.
Отношение между ВАП и ВП:
Образ загружается в виртуальную память системы (ВП) единожды и проецируется в те виртуальные адресные пространства (ВАП), в которых он востребован. Образ EXE или DLL идентифицируется уникальным путём к файлу. Соответственно, если к одному и тому же файлу ведут несколько путей, можно произвести множественную загрузку одного и того же модуля. В системе имеется набор DLL, которые подгружаются во все программы, такие как kernel32.dll и прочие…
Отношение между ВП и ФП:
Физическая память (ФП) состоит из страниц. Любой модуль EXE или DLL занимает некоторое число таких страниц. При загрузке системой модулей для них ищутся свободные страницы, в которые загружается образ файла. Модули проецируются в виртуальную память и загружаются в физическую по мере необходимости средствами ОС. Также проецирование можно использовать для пользовательских файлов (также называется маппинг файлов — mapping).
Отношение между ФП и железом:
Операционная система может по своему усмотрению перемещать страницы физической памяти в файл подкачки и обратно. При обращении исполняемого кода к странице, находящейся в файле подкачки, производится выгрузка страницы в ОЗУ. Если к странице долгое время не осуществлялся доступ и возникла потребность в памяти, ОС может перемещение страницы в файл подкачки.
-
Распределение оперативной памяти в современных ос для пк
-
Microsoft
Windows 95/98
Это 32-разрядные,
многопотоковые ОС с вытесняющей
многозадачностью. Основной пользовательский
интерфейс – графический.
В системе фактически
действует только страничный механизм
преобразования виртуальных адресов в
физические, т.к. используется плоская
модель памяти (имеется только один
сегмент). Этот сегмент отображается
непосредственно в область виртуального
линейного адресного пространства,
состоящего из 4 Кбайт страниц. Каждая
страница может располагаться где угодно
в оперативной памяти или может быть
перемещена на диск, если не запрещено
использовать страничный файл.
Каждая 32-разрядная
программа выполняется в своем адресном
пространстве, но все они используют
совместно один и тот же системный
32-разрядный код. Виртуальные адресные
пространства не используют всех
аппаратных средств защиты микропроцессора,
поэтому неправильно написанная программа
может привести к аварийному сбою всей
системы.
Рис.
3. Модель памяти ОС Windows
95/98
Системный
код размещается выше границы 2 Гб. Здесь
размещаются системные библиотеки DLL
(dynamic
link
library
– динамически загружаемый библиотечный
модуль), используемые несколькими
программами. В микропроцессорах i80x86
имеется четыре уровня защиты (кольца с
номерами от 0 до 3). В наиболее
привилегированном кольце с номером 0
находится ядро ОС, модули файловой
системы, подсистема управления
виртуальными машинами, виртуальные
драйверы.
-
Microsoft
Windows NT
Здесь
тоже используется плоская модель памяти.
В отличии от Windows
95/98 в гораздо большей степени используется
ряд серьезных аппаратных средств защиты,
имеющихся в микропроцессоре, а также
применено принципиально другое логическое
распределение адресного пространства.
Все системные программные модули и
остальные программные модули самой ОС,
выступающие как серверные процессы по
отношению к прикладным программам
(клиентам), находятся в своих собственных
виртуальных адресных пространствах и
доступ к ним со стороны прикладных
программ невозможен. Логическое
распределение адресных пространств
приведено на рис.4.
Прикладным
программам выделяется 2 Гб локального
(собственного) пространства от 64 Кб до
2 Гб (первые 64 Кб полностью недоступны).
Они изолированы друг от друга, хотя
могут общаться через буфер обмена c
помощью механизмов DDE
– Dynamic
Data
Exchange
(динамический обмен данными) и OLE
– Object
Linking
and
Embedding
(связь и внедрение объектов).
Между
отметками 2 и 4 Гб находятся ядро ОС,
планировщик потоков и диспетчер
виртуальной памяти VMM
(Virtual
Memory
Manager
– выделение памяти, резервирование,
освобождение, подкачка).
Рис.
4. Модель распределения виртуальной
памяти в Windows
NT
Вся
виртуальная память в Windows
NT
подразделяется на классы:
-
зарезервированная
(reserved)
– набор непрерывных адресов, которые
диспетчер виртуальных адресов (VMM)
выделяет для процесса, но не учитывает
в общей квоте памяти процесса, пока она
не будет фактически использована; -
память
выделена
(committed)
– если VMM
резервирует для нее место в специальном
файле Pagefile.sys
на тот случай, когда потребуется
выгрузить содержимое памяти на диск.
Выделенная память ограничивается
файлом подкачки; -
доступная
(available)
– вся остальная память.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
The memory management in the operating system is to control or maintain the main memory and transfer processes from the primary memory to disk during execution. Memory management keeps track of all memory locations, whether the process uses them or not. Determines how much memory should be allocated to each process. Specifies how much memory each process should be given. It decides which processes will be remembered and when. It tracks when memory is released or when it is shared and changes the status accordingly.
Windows Memory Management
Microsoft Windows has its own virtual address space for each 32-bit process, allowing up to 4 gigabytes of memory to be viewed. Each process has 8-terabyte address space on 64-bit Windows. All threads have access to the visible address space of the process. Threads, on the other hand, do not have access to the memory of another process, which protects one process from being damaged by another.
Architecture for 32-bit Windows: The automatic configuration of the 32-bit Windows Operating System (OS) allocates 4 GB (232) of accessible memory space to the kernel and user programs equally. With 4 GB physical memory available, the kernel will receive 2 GB and the app memory will receive 2 GB. Kernel-mode address space is shared by all processes, but application mode access space is provided for each user process.
Architecture for 64-bit Windows: The automatic configuration of the 64-bit Windows Operating System (OS) allocates up to 16 TB (254) of accessible memory space to the kernel and user programs equally. As 16 TB real memory is available, the kernel will have 8 TB of virtual address (VA) space and user application memory will have 8 TB of VA space. Visible address space in the kernel is allocated for all processes. Each 64-bit functionality gets its place, but each 32-bit system works on a 2 GB (Windows) virtual machine.
Virtual Address Space
The process’ visible address space is the range of memory addresses that you can use. The address area of each process is private, and can only be accessed through other processes if it is shared.
A virtual address does not reflect the actual location of an object in memory; instead, the system stores a table for each process, which is an internal data structure that converts visible addresses into local addresses. The program converts the virtual address into a local address every time the chain refers to it.
The virtual address area of Windows is divided into two parts: one for process use and the other for system usage.
Virtual Memory Functions
A process can alter or determine the state of pages in its virtual address space using virtual memory functions.
The width of the visible address space is reserved for the process. Although saving address space does not provide material storage, it prevents scope from using other sharing processes. It does not affect other active address spaces for other processes. Page storage reduces unnecessary use of virtual storage while allowing the process of setting aside part of its address space for the flexible data structure. As required, the procedure can provide a physical repository for this area.
Provide a set of cached pages in the address of the process so that only a shared process can access real storage (either RAM or disk).
For the most dedicated pages, specify read/write, read-only, or no access. This differs from the general distribution procedures, which often provide read/write access to the pages.
Release a set of saved pages, making the visible address set accessible for the following call process sharing actions.
We can withdraw a group of committed pages, freeing up portable storage that can be assigned to any process in the future.
To prevent the program from changing pages in the file, lock one or more memory pages bound to the virtual memory (RAM). Find information about a set of pages in a call process or the address space of a specific process. It may change the access protection of a set of pages bound to the physical address of the call process.
Heap Functions
The system provides a default heap for each process. Private heaps can help applications that make frequent allocations from the heap perform better. A private heap is a block of one or more pages in the caller process’s address space. After constructing the private heap, the process manages the memory in it via operations like HeapAlloc and HeapFree.
File Mapping
The association of file content with a piece of visible address space in the process is known as a file map. To track this relationship, the system creates a file map maker (also known as a category object). File view is the physical address area are used for the file content access process. The process may use both input and outgoing sequences (I/O) thanks to the file map. It also allows the process to work effectively with large data files, such as websites, without requiring the entire file to be mapped to memory. Files with a memory map can be used with many processes to exchange data.
The memory management in the operating system is to control or maintain the main memory and transfer processes from the primary memory to disk during execution. Memory management keeps track of all memory locations, whether the process uses them or not. Determines how much memory should be allocated to each process. Specifies how much memory each process should be given. It decides which processes will be remembered and when. It tracks when memory is released or when it is shared and changes the status accordingly.
Windows Memory Management
Microsoft Windows has its own virtual address space for each 32-bit process, allowing up to 4 gigabytes of memory to be viewed. Each process has 8-terabyte address space on 64-bit Windows. All threads have access to the visible address space of the process. Threads, on the other hand, do not have access to the memory of another process, which protects one process from being damaged by another.
Architecture for 32-bit Windows: The automatic configuration of the 32-bit Windows Operating System (OS) allocates 4 GB (232) of accessible memory space to the kernel and user programs equally. With 4 GB physical memory available, the kernel will receive 2 GB and the app memory will receive 2 GB. Kernel-mode address space is shared by all processes, but application mode access space is provided for each user process.
Architecture for 64-bit Windows: The automatic configuration of the 64-bit Windows Operating System (OS) allocates up to 16 TB (254) of accessible memory space to the kernel and user programs equally. As 16 TB real memory is available, the kernel will have 8 TB of virtual address (VA) space and user application memory will have 8 TB of VA space. Visible address space in the kernel is allocated for all processes. Each 64-bit functionality gets its place, but each 32-bit system works on a 2 GB (Windows) virtual machine.
Virtual Address Space
The process’ visible address space is the range of memory addresses that you can use. The address area of each process is private, and can only be accessed through other processes if it is shared.
A virtual address does not reflect the actual location of an object in memory; instead, the system stores a table for each process, which is an internal data structure that converts visible addresses into local addresses. The program converts the virtual address into a local address every time the chain refers to it.
The virtual address area of Windows is divided into two parts: one for process use and the other for system usage.
Virtual Memory Functions
A process can alter or determine the state of pages in its virtual address space using virtual memory functions.
The width of the visible address space is reserved for the process. Although saving address space does not provide material storage, it prevents scope from using other sharing processes. It does not affect other active address spaces for other processes. Page storage reduces unnecessary use of virtual storage while allowing the process of setting aside part of its address space for the flexible data structure. As required, the procedure can provide a physical repository for this area.
Provide a set of cached pages in the address of the process so that only a shared process can access real storage (either RAM or disk).
For the most dedicated pages, specify read/write, read-only, or no access. This differs from the general distribution procedures, which often provide read/write access to the pages.
Release a set of saved pages, making the visible address set accessible for the following call process sharing actions.
We can withdraw a group of committed pages, freeing up portable storage that can be assigned to any process in the future.
To prevent the program from changing pages in the file, lock one or more memory pages bound to the virtual memory (RAM). Find information about a set of pages in a call process or the address space of a specific process. It may change the access protection of a set of pages bound to the physical address of the call process.
Heap Functions
The system provides a default heap for each process. Private heaps can help applications that make frequent allocations from the heap perform better. A private heap is a block of one or more pages in the caller process’s address space. After constructing the private heap, the process manages the memory in it via operations like HeapAlloc and HeapFree.
File Mapping
The association of file content with a piece of visible address space in the process is known as a file map. To track this relationship, the system creates a file map maker (also known as a category object). File view is the physical address area are used for the file content access process. The process may use both input and outgoing sequences (I/O) thanks to the file map. It also allows the process to work effectively with large data files, such as websites, without requiring the entire file to be mapped to memory. Files with a memory map can be used with many processes to exchange data.
В прошлый раз мы рассмотрели вопросы использования глобальных переменных. Прежде, чем двинуться дальше, нужно дать небольшой вводный курс по памяти в Windows.
Любая вещь в вашей программе занимает «память компьютера». Это может быть строка, число, открытый файл, запись, объект, форма и даже сам код. Даже хотя вы явно никого не просили выделять память, она всё равно выделена автоматически — либо компилятором, либо операционной системой.
Адресное пространство и все, все, все…
Кратко говоря, память программы может рассматриваться как один очень-очень длинный ряд байтов. Байт — это единица измерения количества информации, в мире Delphi и Windows он равен восьми битам и может хранить одно из 256
различных значений (от 0
до 255
). На память можно смотреть как на массив байт. Что именно содержат эти байты — зависит от того, как интерпретировать их содержимое, т.е. от того, как их используют. Значение 97 может означать число 97
, или же ANSI букву 'a'
. Если вы рассматриваете вместе несколько байт, то вы можете хранить и большие значения. Например, в 2-х байтах вы можете хранить одно из 256*256 = 65536
различных значений, две ANSI буквы 'ab'
или Unicode букву 'a'
— и т.д.
Чтобы обратиться к конкретному байту в памяти (адресовать его), можно присвоить каждому байту номер, пронумеровав их целыми положительными числами, включив ноль за начало отсчёта. Индекс байта в этом огромном массиве и называется его адресом, а весь массив целиком — памятью программы. Диапазон адресов от 0
до максимума называется адресным пространством программы. А максимум (длина) массива называется размером адресного пространства.
(примечание: ну, на самом деле, есть тысяча и один способ адресовать память, но в рамках современного мира и этой статьи мы ограничимся только этим способом).
Адресное пространство (вернее, его размер) определяет способность программы работать с данными. Чем оно больше — тем с большим количеством данных программа сможет работать (в один момент времени). Если у программы заканчивается свободное место в адресном пространстве (т.е. все адреса в нём выделены под какие-то объекты в программе) — то у программы заканчивается память (out of memory).
Как адресное пространство соотносится с вашим исходным кодом
С точки зрения языка высокого уровня (Паскаль) все вещи в вашей программе характеризуются именем (идентификатором), типом («сколько памяти выделять») и семантикой («что с этим можно делать»). Например, целое число занимает 4
байта и их можно читать, писать, складывать и т.п. И число A — это не то же самое, что число B. Строки же занимают переменный объём памяти, их, к примеру, можно соединять и редактировать. И так далее.
Но на уровне машинного языка, железа и операционной системы все они характеризуются только местоположением, размером (в байтах) и атрибутами доступа. Местоположение — это адрес объекта. К примеру, число A может иметь адрес 1234
, а число B — 1238
. И поэтому это два разных числа — потому что у них разный адрес, т.е. они лежат в разных местах. Атрибут доступа является упрощённой «семантикой», которая определяет то, что можно делать с памятью. А таких вещей всего три: читать, писать и выполнять. Последнее означает исполнение машинного кода. Тут нужно пояснить, что ваши данные (числа, строки, формы и т.п.) находятся в одном «контейнере» (том самом «массиве памяти из байт») вместе с кодом программы — .exe файлом. Иными словами, код рассматривается наравне с данными, а чтобы их отличать и служат атрибуты доступа.
Можно увидеть, как понятия языка высокого уровня («имя», «тип» и «семантика») проецируются в понятия низкого уровня («адрес», «размер» и «атрибуты доступа»).
Древний мир
В давние времена память программы была тождественно равна оперативной памяти машины (т.н. ОЗУ или RAM — Random Access Memory). Иными словами, размер адресного пространства программы был равен размеру установленной оперативной памяти. Вот, установлено на вашей машине две планки памяти по 64
Кб — значит, у вашей программы есть 128
Кб памяти. Ну, за вычетом той памяти, что уже занята, конечно же. Адрес объекта программы был равен адресу физической ячейке оперативной памяти (физическому адресу). И если у вас заканчивалось место в ОЗУ, то у вас заканчивалась память в программе.
Конечно, такой способ хотя и весьма прост, имеет две проблемы:
- Память программы ограничена оперативной памятью. А раньше эта память была дорогой и её было очень мало.
- Если нужно запустить две программы, то они будут работать «в одной песочнице»: и первая и вторая программа будут размещать свои данные в одном месте — оперативной памяти. И если первая программа по ошибке запишет что-то в данные второй, то… ой.
Виртуальная память и виртуальное адресное пространство
Поэтому в современном мире используется совершенно другая схема: во-первых, память программы теперь больше не тождественна оперативной памяти. Теперь программа работает исключительно с так называемой «виртуальной памятью». Виртуальная память — это имитация реальной памяти. Она позволяет каждой программе:
- считать, что установлено максимальное теоретически возможное количество оперативной памяти;
- считать, что она является единственной программой, запущенной на машине.
Иными словами, адресное пространство программы более не ограничено размером физической памяти (так называют оперативную память компьютера, чтобы специально указать на её отличие от виртуальной памяти) — адресное пространство имеет теперь максимально возможный размер. К примеру, если для адресации используются 32-битные указатели (4 байта), то размер адресного пространства равен 2^32 = 4'294'967'296
байт. Т.е. 4 миллиарда (если угодно: биллионов) или 4 Гб. А размерность адресного пространства — равна 32
.
В связи с новомодным «переходом на 64
бита» нужно упомянуть, что этот переход заключается в замене 4
-байтных (32
-битных) указателей на 8-байтные (64-битные) — что увеличивает размер адресного пространства программы аж до 2^64 = 18'446'744'073'709'551'616
байт. Т.е. 18
с лишним квинтиллионов байт или 16
Эб (эксабайт) для краткости. Соответственно, 32
-битный указатель может быть любым числом от 0
до 4'294'967'296
(от $00000000
до $FFFFFFFF
). 64
-разрядный указатель может варьироваться от $00000000'00000000
до $FFFFFFFF'FFFFFFFF
.
А из второго пункта следует, что 4
Гб или 16
Эб есть у каждой программы. Т.е. каждой программе отводится своё личное закрытое адресное пространство. Такая изолированность означает, что программа А в своем адресном пространстве может хранить какую-то запись данных по адресу $12345678
, и одновременно у программы В по тому же адресу $12345678
(но уже в его адресном пространстве) может находиться совершенно иная запись данных. Если программа A попробует прочитать данные по адресу $12345678
, то она получит доступ к своей записи (записи программы A), а не данным программы B. Но если к адресу $12345678
обратится программа B, то она получит свою запись, а не запись программы А. Иными словами, программа A не может обратиться в памяти (адресному пространству) программы B и наоборот.
Таким образом, при использовании виртуальной памяти упрощается программирование, так как программисту больше не нужно учитывать ограниченность памяти, или согласовывать использование памяти с другими приложениями. Для программы выглядит доступным и непрерывным всё допустимое адресное пространство, вне зависимости от наличия в компьютере соответствующего объема ОЗУ. Если программы выделяют в их адресных пространствах больше памяти, чем есть в системе физической памяти, то часть памяти из ОЗУ переносится на диск («винчестер») — в т.н. файл подкачки (его ещё называют страничным файлом, page file, SWAP-файлом или «свопом»). Когда программа обращается к своим данным, которые были выгружены на диск, то операционная система автоматически загрузит данные из файла подкачки в ОЗУ. И всё это происходит под капотом — т.е. совершенно незаметно для программы. С точки зрения программы, ей кажется, что она работает с 4
Гб или 16
Эб RAM.
Применение механизма виртуальной памяти позволяет:
- упростить адресацию памяти программами;
- рационально управлять оперативной памятью компьютера (хранить в ней только активно используемые области памяти);
- изолировать программы друг от друга (программа полагает, что монопольно владеет всей памятью).
А теперь, пока вы не перевозбудились от колоссального объема адресного пространства, предоставляемого вашей программе: вспомните, что оно — виртуальное, а не физическое. Другими словами, (виртуальное) адресное пространство — всего лишь диапазон адресов памяти. Конечно, нехватка памяти теперь не происходит, когда заканчивается свободное место в оперативной памяти. И на машине с 256
Мб ОЗУ, любая программа может выделить, скажем, один кусок в 512
Мб памяти. Конечно же, это не означает, что вы можете выделить аж 16
эксабайт — ведь реальный размер ограничен размером диска. И не факт, что в системе будет диск на 16
эксабайт. Тем не менее, это значительно лучше, чем просто 256
Мб оперативной памяти, установленные на вашем «старичке».
(примечание: по непонятной мне причине, некоторые люди не верят в тот простой факт, что программа может спокойно выделить больше памяти, чем установлено физической памяти в системе; звучит как сюжет для разрушителей легенд (MythBusters)).
Чем чаще системе приходится копировать данные из оперативной памяти в файл подкачки и наоборот, тем больше нагрузка на жесткий диск и тем медленнее работает операционная система (при этом может получиться так, что операционная система будет тратить всё свое время на подкачку памяти, вместо выполнения программ). Поэтому, добавив компьютеру оперативной памяти, вы снизите частоту обращения к жёсткому диску и, тем самым, увеличите общую производительность системы. Кстати, во многих случаях увеличение оперативной памяти дает больший выигрыш в производительности, чем замена старого процессора на новый. А с падением цен на память уже не проблема собрать систему с 16
или 32
Гб оперативной памяти по доступной цене.
Факты о виртуальном адресном пространстве
Хотя в самом начале мы рассматривали память программы (адресное пространство) как один непрерывный однородный блок, сейчас настало время сделать уточнение, что я вам наврал: таковым он не является. Адресное пространство, хотя действительно однородно и непрерывно более чем на 99%
, но в нём есть несколько специальных областей. Я не буду подробно разбирать их все, скажу только о самых важных.
Во-первых, это область для отлова нулевых указателей. Это, определённо, самая важная специальная часть адресного пространства. Начинается она в нуле и заканчивается на адресе 65'535
. Т.е. имеет размер в 64
Кб и расположена в диапазоне $00000000-$0000FFFF
— самом начале адресного пространства. Специальна эта область тем, что она всегда заблокирована: в ней нельзя выделить память, а любое обращение по этим адресам всегда возбуждает исключение access violation (примечание: это не единственная причина возбуждения access violation). Эта область сделана исключительно для нашего удобства. Как вы узнаете потом (или уже знаете), нулевой указатель nil
по числовому значению равен 0
. И если вы случайно (по ошибке) обратитесь к нулевому указателю — то эта область поможет вам возбудить исключение и поймать вашу ошибку.
А что такого особенного в числе 65'535
? Ну, 64
Кб — это гранулярность выделения памяти. Гранулярность выделения памяти определяет, блоками каких размеров вы можете оперировать при выделении и освобождении памяти. Т.е. гранулярность выделения памяти в 64
Кб означает, что вы можете выделять только блоки памяти, размер которых кратен 64
Кб. Зачем так делается? Ну, если вы попробуете вести учёт «выделенности» каждого байта в программе, то размер управляющих структур у вас превысит размер самих данных. Поэтому память выделяют «кластерами». Иными словами, если вы хотите расположить область в начале адресного пространства, то вы не можете выделить меньше, чем 64
Кб. А больше? Больше — можно. Например, 64 + 64 = 128
Кб. Но большого смысла в этом нет.
Почему гранулярность выделения памяти равна именно 64
Кб, а не, скажем, 8
Кб? Ну, на это есть исторические причины.
(примечание: полностью аналогичный блок расположен на границе 2
Гб — но уже по совершенно другим причинам).
Далее, что вам ещё нужно знать про виртуальное адресное пространство — оно доступно вам не полностью. Грубо говоря, в виртуальном адресном пространстве каждой программы сосуществуют сама программа и операционная система. Та часть, где работает ваша программа (и о котором мы говорили всё это время выше), называется разделом для кода и данных пользовательского режима (user mode). Та часть, где работает операционная система, называется разделом для кода и данных режима ядра (kernel mode). Обе эти части находятся в едином адресном пространстве программы.
Чем они отличаются? Про пользовательский раздел мы уже много чего сказали: он свой у каждой программы и это полностью ваш раздел — делайте что хотите. Раздел ядра является здесь особенным в двух моментах: во-первых, у вашей программы нет к нему никакого доступа. Вообще и в принципе это невозможно. Там орудует только операционная система, но не вы. Если вы попробуете обратиться к памяти в этом разделе, то получите просто access violation. Во-вторых, особенность раздела в том, что он разделяется между всеми программами. Да, вот так: пользовательская часть у каждого адресного пространства своя, но часть ядра — одна и та же, общая. По сути, раздел ядра является «адресным пространством режима ядра».
Какой размер имеют эти две части адресного пространства? Ну, если мы говорим про 32
-разрядную программу, то пользовательский раздел занимает от 2
до 4
Гб (по умолчанию — 2
Гб). Соответственно, режим ядра занимает от 0
до 2
Гб (ибо суммарно должно быть 4
Гб). Конечно же, это за вычетом уже упоминаемых специальных областей. Итого: по умолчанию адресное пространство 32
-разрядной программы делится пополам. Половина — вам, и половина — операционной системе.
(примечание: 0 Гб под режим ядра — это специальный особый случай, достижимый только при запуске 32-битной программы на 64-битной машине. В обычных условиях граница между разделами может двигаться от 2 до 3 Гб).
Если говорить совсем точно, то раздел для ваших данных в случае 32
-х бит имеет диапазон $0000FFFF-$7FFEFFFF
(или $BFFFFFFF
в максимуме на 3 Гб, с дыркой на 64
Кб в районе 2
Гб), а раздел режима ядра — $80000000-$FFFFFFFF
(или $C0000000-$FFFFFFFF
в максимуме для user mode). В случае 64
-разрядной программы ситуация будет несколько иная. На сегодняшний день в Windows соотношение выглядит так: user mode — $00000000'00010000-$000003FF'FFFEFFFF
(8
Тб); kernel mode — $00000400'00000000-$FFFFFFFF'FFFFFFFF
. Ну, это всё ещё недостаточно точно, ведь, на самом деле, режим ядра в случае 64
-х бит использует только максимум несколько сотен Гб, оставляя большую часть адресного пространства попросту неиспользуемой. Т.е. у нас в дополнение к двум областям (user mode и kernel mode) появляется ещё и третья: зарезервированная область. Которую, впрочем, со стороны user mode удобно считать частью kernel mode. Сделано это по той простой причине, что 64
-битное адресное пространство настолько огромно, что user mode и kernel mode выглядели бы в нём тонюсенькими полосочками, вздумай бы вы изобразить их графически и в масштабе. А если место просто зарезервировано, то и не нужно делать для него управляющих данных. Даже 8
Тб памяти для user mode — это очень много. Если бы вы выделяли мегабайт памяти в секунду, у вас бы ушло три месяца, чтобы исчерпать такое адресное пространство.
Это что касается изолированности одной программы от других и от операционной системы. Внутри программы её модули (exe, DLL, bpl) друг от друга, вообще говоря, никак не изолированы. Однако на практике граница всё же появляется, но связана она с языковыми различиями и особенностью управления памятью в разных языках программирования. Но это разговор для другого раза.
Если вы забудете всё то, что я тут говорил, то вот факт, который вы должны вынести из этого обсуждения: размер памяти программы ограничен 2 Гб (32-битная программа) или 8 Тб (64-битная программа), либо суммарным размером оперативной памяти и файлом подкачки — смотря что меньше, а что больше. Т.е. на практике вы получаете «out of memory» только когда превышаете размер в 2 Гб.
Операции, производимые с виртуальной памятью
Ну, вполне очевидно, что прежде чем использовать память, вы должны её выделить (commit), а после окончания работы — освободить (release). Конечно, вам не обязательно делать это в явном виде — как я уже сказал, часто за вас это делает кто-то другой автоматически. Но об этом в следующий раз.
Помимо двух операций (выделения и освобождения памяти) существует и третья операция — резервирование (reserve) памяти. Смысл её заключается в том, что под зарезервированную виртуальную память не выделяется никакой реальной памяти (будь то оперативная память или файл подкачки), но при этом память считается занятой, как если бы она была выделена. Позднее, вы можете выделить реальную память этому зарезервированному блоку (полностью или частями).
Зачем нужна такая операция? Ну, предположим, вам нужен непрерывный блок памяти для работы (к примеру, чтобы обработать его за один проход одним циклом), но вы выделяете память не сразу а частями — по мере надобности. Вы не можете просто выделить первый блок, а потом — второй: ведь тогда нет гарантии, что они будут идти друг за другом. Вот поэтому и придумали операцию резервирования: вы резервируете достаточно большой регион. Это — «бесплатно». Потом вы выделяете в нём реальную память. Обе цели достигнуты: вы и выделяете память по мере необходимости (а не сразу целиком), и вы получаете свою непрерывную область памяти.
Кстати, все три операции выполняются функциями VirtualAlloc
и VirtualFree
. Не забудьте только, что мы говорили про гранулярность выделения памяти в 64
Кб.
И снова: какое это имеет отношение к Delphi?
Ну, почти самое прямое. Ведь программа на Delphi должна выделять и освобождать память. Это значит, что ей нужно вызывать функции VirtualAlloc
и VirtualFree
. А выделять память она будет в своём (виртуальном) адресном пространстве — причём, только в пользовательской его части.
Операции с памятью в Delphi проводятся через функции GetMem
и FreeMem
. Конечно же, кроме этих функций в Delphi существует и много других — но они являются лишь обёртками или переходниками к GetMem
и FreeMem
. Эти обёртки (например: AllocMem
, New
, Dispose
, SetLength
и т.п.) предоставляют дополнительную функциональность и удобство (кстати, в системе тоже есть обёртки к вызовам VirtualAlloc
и VirtualFree
). В некоторых случаях, эти вызовы и вовсе скрыты и происходят автоматически под капотом языка. Например, при сложении двух строк:
var S1, S2, S3: String; begin S1 := S2 + S3;
вы не видите вызов GetMem
, но он здесь есть.
Зачем нужны «свои» подпрограммы управления памятью? Почему нельзя просто использовать VirtualAlloc
и VirtualFree
? Ну, Delphi тут не уникальна — большинство языков используют т.н. менеджеры памяти — это код, который в Delphi стоит за вызовами GetMem
и FreeMem
, который служит переходником к VirtualAlloc
и VirtualFree
. А делается это по причине всё той же гранулярности выделения в 64
Кб. Т.е. если вы создаёте 100
объектов по, скажем, 12
байт, то вместо двух килобайт (12
б * 100 = 1.2
Кб + служебные данные менеджера памяти) вы занимаете уже почти 6.5
Мб (64 * 100 = 6'400
Кб) — на несколько порядков больше! Использовали бы вы VirtualAlloc — вы бы очень быстро исчерпали свободную память. Менеджер памяти же «упаковывает» несколько запросов на выделение памяти в один блок.
(примечание: «упаковка» ни в коем случае не означает «сжатие» или «кодирование» — это просто размещение нескольких маленьких кусочков памяти в одном 64 Кб блоке).
Заметьте, что операции резервирования памяти у Delphi нет, т.к. подобная операции не имеет большого смысла при «упаковке» запросов менеджером памяти. Для работы с резервированием используются функции операционной системы.
Продолжение следует…
Вот и все базовые сведения про устройство памяти в Windows, которые вам нужно знать для начала. В следующий раз мы более близко посмотрим на то, как архитектура памяти соотносится с переменными в ваших программах.
См. также: Архитектура памяти в Windows: мифы и легенды (spin-off).
Читать далее: Адресное пространство под микроскопом.
Цель работы: получение практических навыков по использованию Win32 API для исследования памяти Windows
Типы памяти
На рисунке ниже представлена взаимосвязь виртуального адресного пространства процесса с физической и внешней памятью.
Физическая память
Физическая память (Physical memory) — это реальные микросхемы RAM, установленные в компьютере. Каждый байт физической памяти имеет физический адрес (Physical Address), который представляет собой число от нуля до числа на единицу меньшего, чем количество байтов физической памяти. Например, ПК с установленными 64 Мб RAM, имеет физические адреса &Н00000000-&Н04000000 в шестнадцатеричной системе счисления, что в десятичной системе будет 0-67 108 863.
Физическая память (в отличие от файла подкачки и виртуальной памяти) является исполняемой (Executable), то есть памятью, из которой можно читать и в которую центральный процессор может посредством системы команд записывать данные.
Виртуальная память
Виртуальная память (Virtual Memory) — это просто набор чисел, о которых говорят как о виртуальных адресах. Программист может использовать виртуальные адреса, но Windows не способна по этим адресам непосредственно обращаться к данным, поскольку такой адрес не является адресом реального физического запоминающего устройства, как в случае физических адресов и адресов файла подкачки. Для того чтобы код с виртуальными адресами можно было выполнить, такие адреса должны быть отображены на физические адреса, по которым действительно могут храниться коды и данные. Эту операцию выполняет диспетчер виртуальной памяти (Virtual Memory Manager — VMM). Операционная система Windows обозначает некоторые области виртуальной памяти как области, к которым можно обратиться из программ пользовательского режима. Все остальные области указываются как зарезервированные. Какие области памяти доступны, а какие зарезервированы, зависит от версии операционной системы (Windows 9x или Windows NT).
Страничные блоки памяти
Как известно, наименьший адресуемый блок памяти — байт. Однако самым маленьким блоком памяти, которым оперирует Windows VMM, является страница (Page) памяти, называемая также страничным блоком (Page Frame) памяти. На компьютерах с процессорами Intel объем страничного блока равен 4 Кб.
Память файла подкачки
Страничный файл (Pagefile), который называется также файлом подкачки (Swap File) в Windows находится на жестком диске. Он используется для хранения данных и программ точно так же, как и физическая память, но его объем обычно превышает объем физической памяти. Windows использует файл подкачки (или файлы, их может быть несколько) для хранения информации, которая не помещается в RAM, производя, если нужно, обмен страниц между файлом подкачки и RAM.
Таким образом, диапазон виртуальных адресов скорее согласуется с адресами в файле подкачки, чем с адресами физической памяти. Когда такое согласование достигается, говорят, что виртуальные адреса спроецированы (Backed) на файл подкачки, или являются проецируемыми на файл подкачки (Pagefile—Backed).
Набор виртуальных адресов может проецироваться на физическую намять, файл подкачки или любой файл.
Файлы, отображаемые в память
В лабораторной работе ЛАБ_ОС-2 обсуждались файлы, отображаемые в память, там же был приведен пример отображения файла. Любой файл применяется для проецирования виртуальной памяти так же, как для этих целей используется файл подкачки. Фактически единственное назначение файла подкачки — проецирование (Backing) виртуальной памяти.
Поэтому файлы, проецируемые в память подобным образом, называются отображаемыми в память (Memory—Mapped). На предыдущем рисунке изображены именно такие файлы. Соответствующие виртуальные страницы являются спроецированными на файл (File—Backed).
Функция CreateFileMapping объявляется так:
Function CreateFileMapping(hFile: THandle; lpFileMappingAttributes: PSecurityAttributes; flProtect, dwMaximumSizeHigh, dwMaximumSizeLow: DWORD; lpName: PChar): THandle; stdcall;
Function CreateFileMapping; external kernel32 name ‘CreateFileMappingA’;
Она создает объект «отображение файла» (File—Mapping Object), используя дескриптор открытого файла, и возвращает дескриптор этого объекта. Дескриптор может использоваться с функцией MapViewOf File, отображающей файл в виртуальную память:
Function MapViewOfFile(hFileMappingObject: THandle; dwDesiredAccess: DWORD; dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap: DWORD): Pointer; stdcall;
Function MapViewOfFile; external kernel32 name ‘MapViewOfFile’;
Начальный адрес объекта «отображение файла» в виртуальной памяти возвращает функция MapViewOfFile. Можно также сказать, что представление проецируется на файл с дескриптором HFile.
Если параметр HFile, передаваемый функции CreateFileMapping, установлен в -1, то объект «отображение файла» (любые представления, созданные на основе этого объекта) проецируем на файл подкачки, а не на заданный файл.
Совместно используемая физическая память
О физической памяти говорят, что она совместно используется (shared), если она отображается на виртуальное адресное пространство нескольких процессов, хотя виртуальные адреса в каждом процессе могут отличаться. Следующий рисунок иллюстрирует это утверждение.
Если файл, такой как DLL, находится в совместно используемой физической памяти, то о нем можно говорить как о совместно используемом.
Одно из преимуществ файлов, отображаемых в память, заключается в том, что их легко использовать совместно. Присвоение имени объекту «отображение файла» делает возможным совместное использование файла несколькими процессами. В этом случае его содержимое отображено на совместно используемую физическую память (см. рис. ниже). Возможно также совместное пользование содержимого файла подкачки с помощью механизма отображения файла.
В частности, можно создать такой объект, проецируемый на файл подкачки,
Просто установив параметр hFile функции CreateFileMapping в -1.
Адресное пространство процесса
Каждый процесс Win32 получает виртуальное адресное пространство (virtual address space), называемое также адресным пространством, или пространством процесса (process space), объем которого равен 4 Гб. Таким образом, код процесса может ссылаться на адреса с &Н00000000 по &HFFFFFFFF (или с 0 по 232 — 1 = 4 294 967 295 в десятичной системе счисления). Конечно, так как виртуальные адреса — это просто числа, заявление о том, что каждый процесс получает свое собственное виртуальное адресное пространство, выглядит довольно бессмысленным. (Это все равно, что сказать, что каждый человек получает свой собственный диапазон возраста от 0 до 150).
Тем не менее, это утверждение должно означать, что Windows не видит ни какой взаимосвязи в том, что и процесс А, и процесс В используют один и тот же виртуальный адрес, например &Н40000000. В частности, Windows может сопоставить (или не сопоставить) виртуальным адресам каждого процесса разные физические адреса.
Использование адресного пространства в Windows 9X
На рисунке показана общая схема использования адресного пространства процесса в Windows 9x.
Область А
Как следует из рисунка, Windows 9х резервирует область А, объем которой всего лишь 4 Кб, для того же, что и Windows NT первые 64 Кб памяти, — с целью предупреждения о нулевых указателях. Эта область защищена, и попытка обращения к ней из программы пользовательского режима приводит к ошибке нарушения доступа.
Область В
Данная область памяти используется для поддержания совместимости с приложениями DOS и 16-разрядными приложениями Windows. Несмотря на потенциальную доступность, она не должна использоваться для программирования.
Область С
Область С — это адресное пространство, используемое прикладными программами и их DLL. Здесь размещаются также и модули Windows. Например, если приложению требуется управляющий элемент OCX, его модуль будет находиться в этой области.
Область D
Windows 9х отображает системные DLL Win32 (KERNEL32.DLL, USER32.DLL и т. д.) в это адресное пространство. Данные файлы используются совместно, то есть несколько процессов могут обращаться к единственной копии такого файла в физической памяти.
Область D доступна для программ пользовательского режима (однако размещать их здесь не рекомендуется).
Область Е
Данная область также содержит совместно используемые файлы Windows, такие как исполнительная система Windows и ядро, драйверы виртуальных устройств, файловая система, программы управления памятью.
Она также доступна для программ пользовательского режима.
Распределение виртуальной памяти
Каждая страница виртуального адресного пространства может находиться в одном из трех состояний:
· Reserved (зарезервирована) — страница зарезервирована для использования;
· Committed (передана) — для данной виртуальной страницы выделена физическая память в файле подкачки или в файле, отображаемом в память;
· Free (свободна) — данная страница не зарезервирована и не передана, и поэтому в данный момент она недоступна для процесса.
Виртуальная память может быть зарезервирована или передана с помощью вызова API-функции VirtualAlloc:
LPVOID VirtualAlloc(
LPVOID IpAddress, //Адрес резервируемой или выделяемой области.
DWORD dwSise, //Объем области.
DWORD flAllocationType, // Тип распределения.
DWORD flProtect // Тип защиты от доступа.
);
Параметр flAllocationType может принимать значения следующих констант (помимо других);
· MEM_RESERVE — параметр, резервирующий область виртуального адресного пространства процесса без выделения физической памяти. Тем не менее, память может быть выделена при следующем вызове этой же функции;
· МЕМ_СОММIТ — параметр, выделяющий физическую память в оперативной памяти или в файле подкачки на диске для заданного зарезервированною набора страниц.
Эти две константы могут объединяться для того, чтобы зарезервировать и выделить память одной операцией.
Разделение процедур резервирования и передачи памяти имеет некоторые преимущества. Например, резервирование памяти является очень полезной процедурой с точки зрения практичности. Если приложению требуется большой объем памяти, можно зарезервировать всю память, а выделить только ту часть, которая нужна в данный момент, раздвигая, таким образом, временные рамки более трудоемкой операции выделения физической памяти.
Windows тоже использует этот подход, когда выделяет память под стек каждого вновь создаваемого потока. Система резервирует 1 Мб виртуальной памяти под стек каждого потока, но выделяет первоначально только две страницы (8 Кб).
Защита памяти
Параметр flProtect функции virtualAlloc используется для задания типа защиты от доступа, соответствующего вновь выделенной (committed) виртуальной памяти (это не относится к резервируемой памяти). Существуют следующие методы защиты:
· PAGE_READONLY присваивает доступ «только для чтения» выделенной виртуальной памяти;
· PAGE_READWRITE назначает доступ «чтение-запись» выделенной виртуальной памяти;
· PAGE_WRITECOPY устанавливает доступ «запись копированием» (сору-оnwrite) выделенной виртуальной памяти.
· PAGE_EXECUTE разрешает доступ «выполнение» выделенной виртуальной памяти. Тем не менее, любая попытка чтения — записи этой памяти приведет к нарушению доступа;
· PAGE_EXECUTE_READ назначает доступ «выполнение» и «чтение»;
· PAGE_EXECUTE_READWRITE разрешает доступ «выполнение», «чтение» и «запись»;
· PAGE_EXECUTE_WRITECOPY присваивает доступ «выполнение», «чтение» и «запись копированием»;
· PAGE_NOACCESS запрещает все виды доступа к выделенной виртуальной памяти.
Любые из этих значений, за исключением PAGE_NOACCESS, могут комбинироваться при помощи логического оператора OR со следующими двумя флагами:
· PAGE_GUARD определяет помеченные страницы как защищенные (guard page). При любой попытке обращения к защищенной странице система возбуждает исключительную ситуацию STATUS_GUARD_PAGE и снимает с данной страницы статус защищенной. Таким образом, защищенные страницы предупреждают только о первом обращении к ним;
· PAGE_NOCACHES запрещает кэширование выделенной памяти.
Следует объяснить, что такое доступ «запись копированием». Допустим, некоторая страница физической памяти совместно используется двумя процессами. Если она помечена как «только для чтения», то два процесса без проблем могу совместно пользоваться этой страницей. Однако возможны ситуации, когда каждому процессу требуется разрешить запись в эту память, но без воздействия и другой процесс. После установки защиты «запись копированием» при попытке записи в совместно используемую страницу система создаст ее копию специально для процесса, которому нужно осуществить запись. Таким образом, данная страница перестает быть совместно используемой, а представление ее данных в других процессах остается неизменным.
Необходимо отметить, что атрибуты защиты страницы могут быть изменены с помощью API-функции Virtual Protect.
Гранулярность при распределении памяти
Если параметр IpAddress не является кратным 64 Кб, то система округляет указанный адрес в меньшую сторону до ближайшего числа, кратного 64 Кб. Windows всегда выравнивает начальный адрес виртуальной памяти на границу гранулярности распределения (allocation granularily), которая является числом кратным 64 Кб (при использовании процессоров Intel). Другими словами, начальный адрес любого блока зарезервированной памяти представляет собой число кратное 64 Кб.
Кроме того, объем выделяемой памяти всегда кратен объему системной страницы, то есть 4 Кб. Поэтому функция VirtualAlloc будет округлять любое запрашиваемое количество байтов в большую сторону до ближайшего числа, кратного объему страницы.
Дескриптор виртуальных адресов
Система отслеживает, какие из виртуальных страниц являются зарезервированными, при помощи структуры, называемой дескриптором виртуальных адресов (Virtual Address Descriptor — VAD). Другого способа определения не существует.
Пример использования функции GlobalMemoryStatus
API-функция GlobalMemoryStatus, записывающаяся таким образом:
В Delphi:
Procedure GlobalMemoryStatus(var lpBuffer: TMemoryStatus); stdcall;
Procedure GlobalMemoryStatus; external kernel32 name ‘GlobalMemoryStatus’;
Выводит множество данных, имеющих отношение к памяти, в составе следующей структуры:
Struct_MEMORYSTATUS {
DWORD dwLength; // Размер структуры MEMORYSTATUS.
DWORD dwMernoryLoad; // Процент используемой памяти.
DWORD dwTotalPhys; // Количество байтов физической памяти.
DWORD dwАvailPhys; // Количество свободных байтов физической памяти.
DWORD dwTotalPageFile; // Размер в байтах файла подкачки.
DWORD dwAvailPageFile; // Количество свободных байтов файла подкачки.
DWORD dwTotalVirtual; // Количество байтов адресного пространства,
// доступного пользователю.
DWORD dwAvailvirtual; // Количество свободных байтов памяти,
// доступных пользователю.
End Type
В Delphi:
_MEMORYSTATUS = record
dwLength: DWORD;
dwMemoryLoad: DWORD;
dwTotalPhys: DWORD;
dwAvailPhys: DWORD;
dwTotalPageFile: DWORD;
dwAvailPageFile: DWORD;
dwTotalVirtual: DWORD;
dwAvailVirtual: DWORD;
end;
Управление виртуальной памятью
Рассмотрим, как диспетчер виртуальной памяти Windows преобразует адреса виртуальной памяти и физические.
Преобразование виртуальных адресов в физические: попадание
На рисунке показан процесс преобразования при отображении виртуальных адресов в физические. Он называется попаданием в (физическую) страницу (Page Hit).
Все виртуальные адреса делятся на три части. Самая левая часть (биты 22-31) содержит индекс каталога страниц процесса. Windows поддерживает отдельный каталог страниц для каждого процесса. Его адрес хранится в одном из регистров центрального процессора, который называется CR3. (В операцию переключения задач входит переведение CR3 в состояние, когда он указывает на каталог страниц процесса, на который осуществляя переключение.) Каталог страниц одержит 1024 четырехбайтовых элемента.
Windows поддерживает для каждого процесса совокупность таблиц страниц (page table). Каждый элемент каталога страниц содержит уникальный номер. Поэтому Windows поддерживает до 1024 таблиц страниц. ( В действительности таблицы страниц создаются только при попытке обращения к данным или коду конкретному виртуальному адресу, а не когда выделяется виртуальная память).
Следующая часть виртуального адреса (биты 12-21) используется в качестве индекса в таблице страниц, соответствующей выбранному элементу каталога страниц. Каждый элемент таблицы, соответствующий указанному индексу, содержит в 20 старших разрядах номер страничного блока, который задает конкретный страничный блок физической памяти.
Третья, и последняя, часть виртуального адреса (биты 0-11) представляет собой смещение в данном страничном блоке. Сочетание номера страничного блока и смещения дают в совокупности адрес физической памяти.
Так как каждая таблица страниц состоит из 1024 элементов и количество таблиц равно 1024, общее количество страничных блоков, которое можно определить, таким образом, будет 1024 х 1024 = 210 х 210 = 220. Так как каждый страничный блок имеет объем 4 Кб = 4 х 210 байт, то теоретический предел физического адресного пространства будет 4 х 230 = 4 Гб.
У этой довольно сложной схемы преобразования есть несколько важных преимуществ. Одно из них — очень небольшой объем страничных блоков, которые легко могут быть размещены в памяти. Гораздо легче найти непрерывный блок памяти размером 4 Кб, чем, скажем, 64 Кб.
Но основное преимущество заключается в том, что адреса виртуальной памяти двух процессов могут быть сознательно преобразованы в разные или в одни и те же физические адреса.
Предположим, что Process1 и Process2 обращаются в программе к одному и тому же виртуальному адресу. При преобразовании виртуальных адресов в физические для каждого из процессов используются их собственные каталоги страниц. Поэтому, хотя индексы в каталогах страниц одинаковы и в том, и в другом случаях, они все же представляют собой индексы из разных каталогов. Таким способом VMM может гарантировать, что виртуальные адреса каждого процесса будут преобразованы в разные физические адреса.
С другой стороны, VMM может также дать гарантию, что виртуальные адреса двух процессов, независимо от того являются ли они одинаковыми или нет, будут преобразованы в один и тот же физический адрес. Один из способов добиться этого — установить соответствующий элемент в обоих каталогах страниц на одну и ту же таблицу страниц и, следовательно, на один и тот же страничный блок. Таким образом, процессы могут совместно использовать физическую память.
Каталог и таблицы системных страниц
Нужно также упомянуть, что Windows поддерживает каталог системных страниц (System Page Directory) для работы с виртуальной памятью, зарезервированной Windows, так же, как и соответствующую совокупность таблиц системных страниц.
Совместно используемые страницы
Ситуация с совместно используемой физической памятью является значительно более сложной, не будем углубляться в детали, а отметим только, что VMM использует концепцию, называемую прототипированием элементов таблицы страниц. Идея заключается в том, что обычные элементы таблицы каждого из совместно использующих память процессов указывают не на физическую память, а на общий прототип элемента таблицы страниц. А тот, в свою очередь, может ссылаться на совместно используемую физическую память.
Рабочие наборы
Каждая страница виртуального адресного пространства процесса объемом 4 Гб существует в одном из трех состояний — свободном (free), зарезервированном (reserved) или переданном (committed). Теперь можно также сказать, что каждая переданная страница (Committed Page) является или действительной, или недействительной. Совокупность действительных страниц, то есть спроецированных на физическую память, называют рабочим наборам (Working Set) процесса. Рабочий набор постоянно меняется по мере того, как страницы подкачиваются в память или выполняется обратное действие.
Системный рабочий набор (System Working Set) характеризует виртуальные страницы системной памяти, которые в данный момент отображены на физическую память.
Размер рабочего набора процесса ограничен теми установками, которые определяет Windows в зависимости от объема физической памяти. Эти значения приведены в следующей таблице.
Модель памяти |
Объем памяти |
Минимальный размер рабочего набора процесса |
Максимальный размер рабочего набора процесса |
Small Medium Large |
<=19Мб 20-32 Мб >= 33 Мб |
20 страниц (80 Кб) 30 страниц(120Кб) 50 страниц (200 Кб) |
45 страниц (180 Кб) 145 страниц (580 Кб) 345страниц(1380Кб) |
Эти пределы могут быть изменены с помощью API-функции
SetProcessWorkingSetSize:
BOOL SetProcessWorkingSetSize(
HANDLE hProcess, // Открытый дескриптор интересующего процесса.
DWORD dwMinimumWorkingSetSize,
// Задает мин. размер рабочего набора в байтах.
DWORD dwMaximumWorkingSetSize
// Задает максимальный размер рабочего набора в байтах.
);
Присвоение каждому из двух параметров размера значения -1 приведет к тому, что функция сожмет размер рабочего набора до 0 и тем самым временно удалит данный процесс из физической памяти.
Действительный размер рабочего набора процесса может изменяться во времени, так как Windows увеличивает рабочий набор, если замечает, что у процесса большое количество страничных промахов.
Пределы размера системного рабочего набора приведены в следующей таблице.
Модель памяти |
Объем памяти |
Минимальный размер рабочего набора процесса |
Максимальный размер рабочего набора процесса |
Small Medium Large |
<=19Мб 20-32 Мб >= 32 Мб |
388страниц(1,5 Мб) 688 страниц (2,7 Мб) 1188 страниц (4,6 Мб) |
500 страниц (2,0 Мб) 1150 страниц (4,5 Мб) 2050 страниц (8 Мб) |
База данных страничных блоков
Windows фиксирует coстояние каждой физической страницы памяти в структуре данных, называемой базой данных страничных блоков (Page Frame Database). Каждая физическая страница может находиться в одном из восьми различных стояний:
· активная, или действительная (Active, Valid). Страница в текущий момент отображается на виртуальную память, входя, таким образом, в рабочий набор страниц;
· переходная (Transition). Страница в процессе перехода к активному состоянию;
· резервная (Standby). Страница только что вышла из состояния «активная», но осталась неизменной;
· измененная (Modified). Страница вышла из состояния «активная». Ее содержание, пока она находилась в указанном состоянии, было изменено, но еще не записано на диск;
· измененная незаписанная (Modified No Write). Страница находится в состоянии «измененная», но особо помечена как страница, содержимое которой не сброшено на диск. Используется драйверами файловой системы Windows;
· свободная (Free). Страница свободна, но содержит произвольные записи и, следовательно, не может использоваться процессом;
· обнуленная (Zeroed). Страница свободна и инициализирована нулями потоком нулевой страницы. Может быть выделена процессу;
· плохая (Bad). В странице были отмечены ошибки четности или какие-то другие аппаратные ошибки, поэтому она не должна использоваться.
Кучи памяти в 32-разрядной Windows
При создании процесса Windows назначает ему кучу по умолчанию (Default Heap), то есть изначально резервирует область виртуальной памяти объемом 1 Мб. Тем не менее, при необходимости система будет регулировать размер кучи, которая используется самой Windows для различных целей.
API-функция GetProcessHeap используется для получения дескриптора кучи. При помощи функции HeapCreatЕ, возвращающей дескриптор кучи, программист может создавать дополнительные кучи.
Есть несколько причин создавать дополнительные кучи вместо того, чтобы использовать кучу по умолчанию. Например, те кучи, которые предназначены для конкретных задач, часто оказываются более эффективными. Кроме того, ошибки записи данных в кучу, память для которой выделена из специализированной кучи, не затронут данных других куч. Наконец, выделение памяти из специализированной кучи в общем случае будет означать, что данные в памяти упакованы более плотно друг к другу, а это может уменьшить потребность в загрузке страниц из файлa подкачки. Следует также упомянуть, что доступ к куче упорядочен (Serialized), то есть система заставляет каждый поток, пытающийся обратиться к памяти кучи, дожидаться своей очереди, пока другие потоки не закончат производимые операции. Следовательно, только один поток в каждый момент времени может выделять или освобождать память кучи во избежание неприятных конфликтов.
16-разрядная Windows поддерживает и глобальную, и локальную кучи. Соответственно в данной системе реализованы функции GlobalAlloc и LocalAlloc. Они выполняются, но не очень эффективны, поэтому следует избегать их применения в Win32. Однако их все-таки приходится использовать для некоторых целей, таких как создание окна просмотра буфера обмена.
Функции работы с кучей
Для работы с кучами используются следующие функции:
· GetProcessHeap возвращает дескриптор кучи процесса по умолчанию;
· GetProcessHeaps возвращает список дескрипторов всех куч, используемых в данный момент процессом;
· HeapAlloc выделяет блок памяти из заданной кучи;
· HeapCompact дефрагментирует кучу, объединяя свободные блоки. Может также освобождать неиспользуемые страницы памяти кучи;
· HeapCreatE создает новую кучу в адресном пространстве процесса;
· HeapDestroy удаляет заданную кучу;
· HeapFree Освобождает предварительно выделенные блоки памяти кучи;
· HeapLock блокирует кучу, при использовании данной функции только один поток имеет к ней доступ. Другие потоки, запрашивающие доступ, переводятся в состояние ожидания до тех пор, пока поток, владеющий кучей, не разблокирует ее. Это одна из форм синхронизации потоков, то есть тот прием, которым система реализует упорядоченность доступа;
· HeapReAlloc перераспределяет блоки памяти кучи. Используется для изменения размера блока;
· Heapsize возвращает размер выделенного блока памяти кучи;
· HeapUnlock разблокирует кучу, которая до этого была заблокирована функцией HeapLock;
· HeapValidate проверяет пригодность кучи (или отдельного ее блока), если имеются ли какие-либо повреждения;
· HeapWalk позволяет программисту просматривать содержимое кучи. Обычно используется при отладке.
Отображения виртуальной памяти
Функция Win32 API VirtualQuery может использоваться для получения информации о состоянии адресов виртуальной памяти. Синтаксис ее таков:
DWORD VirtualQuery(
LPCVOID IpAddress, // Адрес области.
PMEMORY_BASICONFORMATION IpBuffer, // Адрес информационного буфера
DWORD DwLength // Размер буфера
);
Используется также функция VirtualQueryEx, расширенная версия VirtualQuery, которая позволяет получать информацию о внешних виртуальных адресных пространствах:
DWORD VirtualQueryEx(
HANDLE hProcess // Дескриптор процесса
LPCVOID IpAddress, // Адрес области
MEMORY_BASIC_INFORMATION IpBuffer, // Адрес информационного буфера
DWORD DwLength // Размер буфера
);
Параметр HProcess — это дескриптор процесса. Параметр IpAddress — это начальный адрес для записи результирующих данных, который будет округляться в меньшую сторону до ближайшего кратного размеру страницы (4 Кб). Обе функции возвращают информацию в следующую структуру.
Struct MEMORY_BASlC_INFORMATION {
PVOID BaseAdciress; // Базовый адрес области
PVOID AllocationBase; // Базовый адрес выделенной области
DWORD AllocationProtect; // Первоначальная защита от доступа
DWORD RegionSize; // Размер области в байтах
DWORD State; // Передана зарезервирована, свободна
DWORD Protect; // Текущая защита от доступа
DWORD Type; // Тип страниц
}
Чтобы понять принцип действия членов этой структуры, необходимо знать о назначении данной функции. Чтобы сделать определение более понятным, назовем страницу, которой принадлежит адрес IpAddress, заданной (Specified) Следующий рисунок поможет разобраться в новой терминологии.
Функция VirtualQueryEx всегда заполняет следующие члены структуры
MEMORY_BASIC_INFORMATION:
· BaseAddress, которая возвращает базовый адрес заданной страницы;
· RegionSize, представляющая собой количество байтов от начала заданной страницы до вершины заданной области.
Если страница, содержащая адрес IpAddress, свободна (не зарезервирована и не передана), член структуры Stаte содержит символьную константу MEM_FREE. Остальные члены (кроме BaseAddress и RegionSize) не имеют значения.
Если страница, содержащая адрес IpAddress, не свободна, функция определяет выделенную область (allocation region), то есть область виртуальной памяти, которая включает заданную страницу и была, первоначально выделена с помощью вызова функции VirtualAlloc.
Начиная с базового адреса заданной страницы, функция последовательно просматривает все страницы выделенной области, проверяя, совпадают ли их типы выделения (Allocation Type) и защиты (Protection Type) с аналогичными типами заданной страницы. Совокупность всех совпадающих упорядоченных страниц представляет собой заданную область. К ней относятся значения структуры MEMORY_BASIC_INFORMATION. Cтраница считается совпадающей с заданной страницей, если она удовлетворяет двум следующим условиям:
· страница имеет тот же тип выделения, что и первоначальная страница, в соответствии со следующими значениями флага: MEM_COMMIT, MEM_RESERVE, MEM_FREE, MEM_PRIVATE, MEM_MAPPED Или MEM_IMAGE;
· страница имеет тот же тип зашиты, что и первоначальная страница, в соответствии со следующими значениями флага: PAGE_READONLY, PAGE_READWRITE, PAGE_NOACCESS, PAGE_WRITECOPY, PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY, PAGE_GUARD Или PAGE_NOCACHE.
Рассмотрим остальные члены структуры MEMORY_BASIC_INFORMATION:
· AllocationBase — базовый адрес выделенной области;
· AllocationProtect — первоначальный тип защиты выделенной области;
· State — одно из трех значений: MEM_FREE, MEM_RESERVE или МЕМ_СОММIТ. Относится к заданной области;
· Protect — текущий тип защиты заданной области;
· Туре — одно из трех значений: MEM_IMAGE, MEM_MAPPED Или MEM_PRIVATE. Относится к заданной области. Эти константы имеют следующий смысл: MEM_IMAGE указывает, что область отображена на файл образа задачи (Image File), то есть на загрузочный; MEM_MAPPED указывает, что область отображена на не загрузочный отображаемый в память файл (например, файл данных); MEM__PRIVATE указывает, что область используется одним процессом, а не совместно.
СОДЕРЖАНИЕ ОТЧЕТА
8. Наименование лабораторной работы, ее цель.
9. Разработанное программное обеспечение для приложения, которое:
· выдает информацию, получаемую при использовании API GlobalMemoryStatus. При выводе информации использовать диаграммы.
· Составляет карту виртуальной памяти для любого процесса.
10. Примеры разработанных приложений (результаты и тексты программ).