Адресное пространство процесса в Windows

Метки:  , , , , , , , , , , , ,

Оперативная память (ОЗУ) является тем компонентом персональных компьютеров, важность которого, при современной архитектуре вычислительных систем, сложно переоценить, и без которого работа их (в силу архитектурных особенностей) не представляется возможной. Было бы интересно посмотреть, как именно ОС управляет доступной ей памятью? Как она распределяет её между загруженными приложениями? Как происходит организация (создание) в памяти нового процесса, как код программы получает управление и как процессу выделяется дополнительная память по запросу, в случае, когда выделенная изначально память заканчивается? Как организовано адресное пространство процесса? Подобные вопросы возникали и продолжают возникать у многих довольно часто, но далеко не на все из них находятся вразумительные ответы.
Поскольку круг вопросов, касающихся оперативной памяти настолько велик, что не может быть освещен в одной статье, здесь мы коснемся лишь части огромного механизма управления памяти в ОС Microsoft Windows, а именно изучим адресное пространство процесса, увидим, что же размещается [системой] в пространстве памяти, выделяемой процессу.

Память (общее определение) - физическое устройство или среда для хранения данных, используемая в вычислениях в течении определенного времени.

Код приложений (программ), исполняемый в произвольный момент времени на процессоре, оперирует данными. Данные, которые в текущий момент необходимы коду для выполнения, должны быть размещены в физической (оперативной) памяти или регистрах общего назначения. Если данные размещены в памяти, то их положение нужно каким-либо образом определить - проще всего сделать это при помощи некоего числа (порядкового номера), называемым адресом [в массиве]. Иными словами, оперативную память легче всего себе представить в виде массива байт. Чтобы обратиться к конкретному байту данных (массива), или адресовать его, логично было бы использовать порядковый номер. Подобным образом можно поступить со всеми байтами, пронумеровав их целыми положительными числами, установив ноль за начало отсчёта. Индекс байта в этом огромном массиве и будет его адресом.

Адресация на уровне процессора

В первых микропроцессорах компании Intel (архитектуры x86) был доступен единственный режим работы процессора, впоследствии названный реальным режимом. Адресация памяти в процессорах того времени была достаточно простой и носила название сегментной. Суть её заключалась в том, что ячейка памяти адресовалась при помощи двух составляющих: сегмент : смещение (сегмент - область адресного пространства фиксированного размера, смещение — адрес ячейки памяти относительно начала сегмента). Специфика архитектуры упомянутых [первых] микропроцессоров накладывала ограничения на размер физического адресного пространства (16 килобайт, 64 килобайта, 1 мегабайт...), и память, доступная программно, была не более размера оперативной (физически установленной) памяти компьютера. Это было просто, логично и понятно. Тем не менее, описанная архитектура имела ряд недостатков, к тому же в индустрии появились тенденции дальнейшего развития:

  • Была актуальна проблема согласования выделения памяти различным приложениям. Размещение кода/данных приложений в едином для всех программ пространстве памяти требовало от операционной системы (а иногда и от самой программы) сложного механизма постоянного отслеживания занятого пространства.
  • Наметился переход к многозадачным операционным системам, в которых большое количество задач должно было выполняться [псевдо]параллельно, что затрудняло использование общего пространства памяти.
  • В условиях множества одновременно выполняющихся задач встала проблема безопасности, необходимости ограничения доступа к "чужим" процессам в памяти.

Эти, а так же некоторые другие, проблемы явились отправной точкой для работы над усовершенствованием, в следствии чего в процессоре 80286 появился защищенный режим и концепция сегментной адресации памяти была значительно расширена для обеспечения новых требований. Например в защищенном режиме сегменты могли располагаться (начинаться) в памяти в произвольном месте (база), иметь нефиксированный размер (лимит), уровни доступа, типы содержимого и прочее. И наконец, по прошествии некоторого времени была создана новая архитектура, получившая название IA-32, в которой были введены несколько новых моделей организации оперативной памяти:

  • Базовая плоская модель (basic flat model) - наиболее простая модель памяти [системы], операционная система и приложения получают в своё распоряжение непрерывное, не сегментированное адресное пространство.
  • Защищенная плоская модель (protected flat model) - более сложная модель памяти [системы], может применяться страничный механизм изоляции пользовательского/системного кода/данных, описываются четыре сегмента: кода/данных (для уровня привилегий 3, пользовательский уровень) и кода/данных (для уровня привелегий 0, ядро).
  • Мульти-сегментная модель (multi-segment Model) - самая сложная модель памяти [системы], предоставляется аппаратная защита кода, данных, программ и задач. Каждой программе (или задаче) назначаются их собственные таблицы [сегментных] дескрипторов и собственные сегменты.
И главным завоеванием защищенного режима явилось появление механизма страничной организации/адресации памяти.

Страничная организация памяти это альтернативный тип управления памятью, разработанный для обеспечения организации виртуального адресного пространства (виртуальной памяти) в многозадачных операционных системах. В отличие от сегментной адресации, которая делила адресное пространство на сегменты определенной длины, страничная адресация делит пространство на множество страниц равного размера. Изменения коснулись и принципов адресации: если в реальном режиме работы процессора пара сегментный_регистр : смещение могла адресовать ячейку памяти, то в защищенном режиме сегмент был заменен на селектор, который содержал индекс в таблице дескрипторов и биты вида таблицы дескрипторов + биты привилегий.

Механизм трансляции адресов (преобразование адреса)

Поскольку одним из нововведений защищенного режима было создание виртуальной адресации, потребовались механизм трансляции виртуальных адресов в физические, а так же механизм трансляции страниц. Именно благодаря трансляции операционная система имеет возможность создавать для каждого процесса иллюзию полноразмерного обособленного адресного пространства и обеспечивать защиту собственного ядра [от пользовательских процессов]. Адрес, по которому код программы (процесса) пытается обратиться к данным (где-то в оперативной памяти), изначально закодирован в исполняемой инструкции (пример фрагмента кода):

..и проходит через некоторое количество преобразований, начиная от декодирования инструкции процессором и заканчивая выставлением адреса на шину. Давайте посмотрим, какие же этапы проходит преобразование адреса:

  1. Эффективный адрес - адрес, задаваемый в аргументах машинной инструкции при помощи регистров, смещений, коэффициентов. Фактически эффективный адрес представляет собой смещение от начала сегмента (базы). Как раз для нашего примера (выше): эффективный адрес = EDI + ECX * 4 + 4;
  2. Логический адрес – адрес, представляющий собой пару селектор : смещение. Традиционно селектор (левая часть) располагается в сегментном регистре, смещение (правая часть) в регистре общего назначения или указывается непосредственно, для нашего примера это: DS:[EDI+ECX*4+4]. Как мы видим, часто сегментный регистр (левая часть) не указывается (выбирается неявно). Фактически с логическими адресами и имеет дело программист в своих программах;
  3. Линейный адрес - это 32-/64-разрядный адрес, получаемый путем использования селектора (содержащегося в левой части виртуального адреса, в сегментном регистре, для нашего примера задан неявно, в DS) в качестве индекса в таблице дескрипторов (для вычисления базы сегмента) и добавления к ней смещения (правая часть виртуального адреса, в нашем случае значение, вычисляемое на основе выражения EDI+ECX*4+4). Линейный адрес = база сегмента + эффективный адрес.
  4. Гостевой физический адрес - при использовании аппаратной виртуализации. В случае, когда в системе работают виртуальные машины, физические адреса (получаемые в каждой из них), необходимо транслировать ещё раз.
  5. Физический адрес - это финальная часть преобразований адреса внутри процессора. Физический адрес:
    • (для сегментной адресации) полностью совпадает с линейным адресом;
    • (для сегментно-страничной адресации) получается путем преобразования трех частей значения линейного адреса на основании: каталога страниц, таблицы страниц и смещения внутри страницы;

    И наконец этот получившийся физический адрес выставляется на адресную шину процессора; может как совпадать с адресом ячейки оперативной памяти, так и не совпадать с ним;

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

Механизм трансляции страниц (страничное преобразование)

Алгоритмы преобразования линейного адреса в физический (этапы 3 → 5) варьируются в зависимости от множества причин (состояния определенных регистров). В некоторых режимах линейный адрес делится на несколько частей, при этом каждая часть является индексом в специализированной системной таблице (все они расположены в памяти), а число и размер описанных таблиц различаются в зависимости от режима работы процессора. Запись в таблице первого уровня представляет собой адрес начала таблицы следующего уровня, а для последнего уровня — информация о физическом адресе страницы в памяти и её свойствах. Иначе говоря:

Страничная память — способ организации виртуальной памяти, при котором виртуальные адреса отображаются на физические постранично.

Соответственно, на данном этапе, мы уже имеем дело уже с разбиением адресного пространства на страницы (определенного размера) или со страничной организацией памяти. Иными словами, мы имеем дело с виртуальной памятью. Процессор как бы делит линейное адресное пространство на блоки (страницы) фиксированного размера (в зависимости от установок - 4Кб, 2Мб, 4Мб), которые уже могут отображаются в физической памяти или на жестком диске. И вот тут стоит обратить внимание на один крайне важный аппаратный механизм:

В произвольный момент времени та или иная страница может "находиться" (быть сопоставлена) в физической памяти, а может и не находиться в ней.

Иными словами, преобразование линейного адреса в физический может закончиться неудачей, если:

  • страницы в данный момент нет в физической памяти;
  • таблицы не содержат необходимых данных;
  • недостаточно прав доступа;

Во всех этих случаях возникает аппаратное событие - так называемое исключение Page Fault (#PF), которое предписывает обработчику исключения произвести дополнительные действия по устранению возникшей проблемы: подгрузить (отсутствующую) страницу с диска (либо скинуть (ненужную) страницу на диск). Как только страница была подгружена, то выполнение прерванного кода продолжится с инструкции, которая вызвала #PF. Именно механизм страничной адресации (преобразования) и позволяет операционной системе организовать виртуальное адресное пространство, о котором речь пойдет далее. К сожалению, подробное описание механизмов преобразования адресов и типов адресации выходит за рамки данной статьи, далее мы переходим к "программному" уровню, то есть непосредственно к механизмам операционной системы.

Адресация на уровне ОС

Сами понимаете, что было бы не совсем корректно называть излагаемое в данной главе некоей "программной" частью адресации в операционной системе, поскольку:

Механизмы, используемые операционными системами, имеют аппаратную поддержку на уровне процессора.

..поэтому операционная система Windows эксплуатирует особенности той архитектуры, на которой она в данный момент функционирует (выполняется) и всего-лишь использует аппаратные механизмы [процессора]. Становится очевидным, что если Windows исполняется на станциях, построенных на базе процессоров архитектуры IA-32, то используется защищенный режим работы процессора. Версии операционной системы Windows для архитектуры IA-32, пользуются механизмом сегментации защищенного режима лишь в минимальном объёме:

  • используются всего два уровня привилегий: 0 и 3;
  • и из всех доступных способов организации памяти используется защищенная плоская модель со страничной адресацией (protected flat model);
Защищенная плоская модель в Windows имеет свои особенности: память представляется программе [задаче] в виде единого непрерывного адресного пространства (линейное адресное пространство). Код, данные и стек - всё содержатся в этом адресном пространстве, то есть объединены в один физический сегмент.

Есть соглашение, что все селекторы для процесса идентичны, это значит что адресация внутри процесса фактически базируется на смещении (а селекторы сегментов остаются неизменными). Исходя из этого, если каждый процесс [в системе] использует собственное адресное пространство линейным способом, то есть адресация базируется (фактически) на смещении, селекторы сегментов остаются неизменными, то сегментные регистры не нужны и их можно "опустить", "упразднить". На основе плоской модели памяти базируется часто упоминаемый в литературе механизм виртуальной памяти.

Виртуальная память – стратегия организации памяти [операционной системой], основанная на идее создания единого виртуального адресного [псевдо]пространства, состоящего из физической памяти (ОЗУ) и дисковой памяти (жесткий/твердотельный диск).

Возникает вопрос: почему это пространство называется виртуальным? А потому что виртуальный адрес может и не присутствовать в физической памяти, все механизмы [защищенного режима] созданы лишь для имитации (создания иллюзии для программы) его существования, ведь используются селекторы:смещения (которые могут ссылаться на любой адрес) совместно со страничным преобразованием (страницы могут быть сопоставлены с физической памятью, а могут и не быть), то есть все сущности по сути эфемерны, пользователь не знает как и где они размещены!!

Размерность адресных пространств

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

Физическое адресное пространство

Итак, размерность физического (процессорного) адресного пространства зависит от особенностей аппаратной архитектуры:

  • 32-бита: используются 32-битные указатели (размерность 4 байта), и размер адресного пространства равен 232 = 4294967296 байт (4 гигабайта, Гб). Шестнадцатеричное представление диапазона: 00000000 - FFFFFFFF.
  • 64-бита: используются 64-битные указатели (размерность 8 байт) и размер адресного пространства процесса равен 264 = 18446744073709551616 байт (16 экзабайт, Эб. ~17 миллиардов гигабайт). Шестнадцатеричное представление диапазона: 0000000000000000 - FFFFFFFFFFFFFFFF.
Разрядность (битность) приложения Разрядность указателя Размер адресного пространства [процесса] Адреса диапазона (шестнадцатеричные)
32 бита 32 бита (4 байта) 232 (4294967296 байт = 4 гигабайта) 00000000 - FFFFFFFF
64 бита 64 бита (8 байт) 264 (18446744073709551616 байт = ~17 миллиардов гигабайт = 16 экзабайт) 0000000000000000 - FFFFFFFFFFFFFFFF

Но это, опять же, теоретическая адресация на основе разрядности.

Линейное адресное пространство

Линейное адресное пространство процесса теоретически могло бы быть идентично физическому адресному пространству, но на практике вступают в действие ограничения операционной системы, которые зависят от: версии операционной системы, [определенных] настроек (флагов) операционной системы и приложений, типа запуска: 32-битное приложение на 32-битной ОС, 32-битное приложение на 64-битной ОС, 64-битное на 64-битной ОС.

  • 32-бита: размер линейного адресного пространства процесса равен 4Гб, верхние 2Гб (или 1Гб, в зависимости от флагов) из которых защищены на уровне страниц. Поэтому для пользовательского приложения в 32-битной ОС определен лимит в 2Гб (или 3, в зависимости от флагов), за пределы которого процесс выбраться не может (без использования специализированных технологий вида AWE).
  • 64-бита: размер линейного адресного пространства процесса равен 16Тб или 256Тб, из которых (верхняя) часть защищена на уровне страниц. Поэтому 32/64-битному пользовательским приложениям может быть определен лимит в 2Гб, 4Гб, 8Тб и 128Тб (в зависимости от разрядности/версии/флагов).

Виртуальные адреса используются приложениями, однако сама операционная система (равно как и процессор) не способна по этим виртуальным адресам непосредственно обращаться к данным, потому как виртуальные адреса не являются адресами физического устройства хранения информации (ОЗУ/ДИСК), другими словами физически по этим адресам информация не хранится.

Для того, чтобы код, располагающийся по виртуальным адресам можно было выполнить, эти адреса должны быть отображены на физические адреса, по которым действительно могут храниться код и данные. Это особенности аппаратной архитектуры x86 (проще: так устроен центральный процессор).

Или, если выразиться иначе, выполняемый код или используемые данные должны находиться в физической памяти, только в этом случае они будут выполнены/обработаны процессором.

Еще раз: код и данные, которые в данный момент обрабатываются/исполняются, физически располагаются в ОЗУ.

Использование страничной организации операционной системой

Получается интересная ситуация: с одной стороны, для каждого процесса в операционной системе Windows выделяется адресное пространство, которое фактически эквивалентно размерности теоретического адресного пространства; с другой стороны размер физически установленной оперативной памяти (ОЗУ) компьютера может быть в разы меньше суммы всех адресных пространств процессов, исполняемых в данный момент в системе. Как нам в подобной ситуации обеспечить нормальное функционирование операционной системы? А очень просто, поскольку:

  1. виртуальные адреса суть иллюзия, они могут не ссылаться на физическую память;
  2. [в подавляющем большинстве случаев] процесс не использует всё виртуальное адресное пространство, отведенное для него; то есть адресное пространство процесса не обязательно заполнено [под завязку] данными;
  3. общие для всех процессов данные могут разделяться множеством процессов (экономия оперативной памяти);
  4. не обязательно код и данные всех процессов [постоянно] держать в ОЗУ (экономия оперативной памяти);

И в обеспечении всех этих механизмов нам на помощь приходит страничная организация (о которой говорилось выше): она позволяет операционной системе прозрачно (незаметно) для пользователя/приложения выполнять ряд очень нужных системе манипуляций:

  • подгружать/выгружать неиспользуемые страницы с/на носитель информации (жесткий диск: HDD, SSD);
  • проецировать общие страницы [общих ключевых библиотек] в несколько адресных пространств одновременно;

Страницы, которые в определенный промежуток времени не используются, из ОЗУ переносятся (перепроецируются) на любой физический носитель, установленный в системе - в файл (файл подкачки, страничный файл, page file, swap-файл, "своп") либо [в некоторых ОС] в область подкачки (специализированный раздел).
Сопоставлением (отображением) виртуальных адресов на физические адреса ОЗУ или файла подкачки занимается так называемый диспетчер виртуальной памяти (VMM, Virtual Memory Manager).

Диспетчер виртуальной памяти (Virtual memory manager, Kernel-mode memory manager) - модуль ядра ОС Windows, предназначающийся для организации подсистемы виртуальной памяти: создания таблицы адресов для процессов, организации общего доступа к памяти, осуществления защиты на уровне страниц, поддержки возможность отображения файлов на память, распределения физической памяти между процессами, организации выгрузки/загрузки страниц между физической памятью и файлом подкачки, обеспечения всех процессов достаточным для функционирования объемом физической памяти.

Упрощенная схема процесса "отображения" выглядит следующим образом:

сопоставление виртуальных страниц процесса

Схема действительно упрощена, поскольку на деле в процессе сопоставления участвует множество структур: таблица указателей на каталог страниц, каталог страниц, таблица страниц. Как видно из нашего рисунка, виртуальные адреса могут проецироваться на физическую намять, файл подкачки или любой файл, располагающийся в файловой системе. На основе приведенной схемы мы может сделать довольно-таки важный вывод:

Виртуальное адресное пространство создается для каждого процесса, работающего в операционной в системе и напрямую не связано с адресацией физической памяти (ОЗУ).

и теперь вы, надеюсь, понимаете, что:

Виртуальный адрес может быть просто не сопоставлен с физической памятью!!

Получается, что у виртуальных страниц адреса одни, аппаратные страницы выделяются по мере необходимости из имеющихся в наличии свободных страниц физической памяти, и, понятное дело, что физические адреса будут случайными и, в большинстве случаев, не будут совпадать с виртуальными. Напомню, что синхронизация между виртуальными и физическими страницами памяти обеспечивается аппаратно (на уровне процессора), называется страничным преобразованием и была нами описана выше. Наиболее значимые особенности виртуальной памяти таковы:

  • Виртуальная память, доступная программе, напрямую не связана с физической памятью.
  • Каждая программа работает в своей виртуальном адресном пространстве. Размер этого пространства может быть больше размера фактически установленной в машине оперативной памяти.
  • Адресное пространство [каждого] процесса (программы), исполняющегося в ОС, изолировано [от подобных адресных пространств других процессов].

Когда какая-либо программа обращается к своим данным, которые [в этот момент] отсутствуют в ОЗУ, то функция обработчика страничного нарушения диспетчера виртуальной памяти производит следующие манипуляции:

  1. сохраняет в стек адрес инструкции, следующей за инструкцией, вызвавшей #PF;
  2. производит поиск свободной (незанятой) физической страницы;
  3. создает новый элемент в таблице страниц;
  4. подгружает недостающие данные из файла подкачки в ОЗУ;
  5. производит проецирование виртуальной страницы на физическую;
  6. производит восстановление адреса из стека и выполняет "перезапуск" инструкции (следующей за той, на которой было прервано выполнение);

Все эти процессы происходят на уровне ядра операционной системы, поэтому они "прозрачны" или "неразличимы" для пользовательского приложения (а программисту, в реалиях высокоуровневого программирования, зачастую и вовсе не интересны).
Теперь несколько слов об изоляции или закрытости [адресного пространства] процесса. Виртуальное пространство [каждого] процесса изолировано, или, можно сказать по-другому - процессы отделены друг от друга в своем собственном виртуальном адресном пространстве. Поэтому любой поток в рамках некоего процесса получает доступ только лишь к той памяти, которая принадлежит родительскому процессу. Наглядно, изолированность выражается в том, что некая программа A в своем адресном пространстве может хранить запись данных по условному адресу 12345678h, и в то же время у программы B по абсолютно тому же адресу 12345678h (но уже в его собственном адресном пространстве) могут находиться совершенно другие данные. Изолированность, к тому же подразумевает, что код одной программы (если быть точным, то потока в рамках процесса) не может получить доступ к памяти другой программы (без дополнительных манипуляций). Достоинства виртуальной памяти:

  • Упрощается программирование. Программисту больше не нужно учитывать ограниченность памяти, или согласовывать использование памяти с другими приложениями.
  • Повышается безопасность. Адресное пространство процесса изолировано.
  • Однородность массива. Адресное пространство линейно.

[пример] Виртуальное адресное пространство процесса

Я думаю, после некоторого количества теоретических выкладок, самое время перейти ближе к рассмотрению основной темы статьи. Напомню, что мы будем исследовать структуру памяти 32-битного процесса Windows. Для исследования памяти процесса нам потребуется специализированное программное средство, которое поможет нам увидеть адресное пространство процесса в деталях. Использовать мы будем утилиту VMMap от Марка Руссиновича, отличное приложение, которое выводит подробную информацию по использованию памяти в рамках того или иного процесса. Однако, не обошлось, что называется, и без ложки дегтя. Бытует мнение, что данное ПО отражает карту процесса не достаточно подробно, игнорируя кое-какие структуры памяти, однако, как отправная точка для понимания принципов размещения объектов в памяти вполне нас устроит.
Для практического эксперимента я буду использовать самописный модуль test2.exe, написанный на ассемблере, код которого предельно прост, отображает всего-лишь некоторые оконные элементы и выводит информационное окно перед выходом. В модуле используются (импортируются) функции SetFocus, SendMessageA, MessageBoxA, CreateWindowExA, DefWindowProcA, DispatchMessageA, ExitProcess, GetMessageA, GetModuleHandleA, LoadCursorA, LoadIconA, PostQuitMessage, RegisterClassA, ShowWindow, TranslateMessage, UpdateWindow из библиотек user32.dll и kernel32.dll. Итак, запускаем на исполнение тестовый файл test2.exe а затем, пока приложение исполняется, загружаем программу VMMap, указывая ей открыть наш целевой процесс. Вот что мы наблюдаем:

Карта памяти процесса Windows

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

Наименование столбца Описание
Address Стартовый адрес региона в виртуальном пространстве процесса. Шестнадцатеричное представление.
Type Тип региона (см. таблицу далее).
Size Полный размер выделенной области. Отражает максимальный размер физической памяти, которая необходима для хранения региона. Так же включает зарезервированные области.
Committed Количество памяти региона, которое "отдано", "передано" или "зафиксировано" — то есть эта память уже связана с ОЗУ, страничным файлом [подкачки], или с отображенным файлом (mapped file) [на диске].
Private Часть всей памяти, выделенной для региона, которая приватна, то есть принадлежит исключительно процессу-владельцу и не может быть разделена с другими процессами.
Total WS Общее количество физической памяти, выделенной для региона (ОЗУ + файл подкачки).
Private WS Приватная часть физической памяти [региона/файла]. Принадлежит исключительно владельцу и не может быть разделена (использована совместно) с другими процессами.
Shareable WS Общедоступная часть физической памяти [региона/файла]. Может быть использована совместно другими процессами, которым так же необходим данный регион (файл).
Shared WS Общедоступная часть физической памяти [региона/файла]. Уже используется совместно с другими процессами.
Locked WS Часть физической памяти [региона/файла], которая гарантированно находится в ОЗУ и не вызывает ошибок страниц (необходимость подгрузки из файла подкачки), когда к ней пытаются получить доступ.
Blocks Количество выделенных в регионе блоков памяти. Блок - неразрывная группа страниц с идентичными атрибутами защиты, сопоставленная с одним регионом физической памяти. Если Вы посмотрите внимательно то заметите, что значения параметра "Blocks", отличные от нуля, встречаются в регионах, которые разбиты на несколько частей (подрегионы, блоки). Обычно имеется несколько подрегионов: основной регион + резервные.
Protection Типы операций, которые могут быть применены к региону. Для регионов, которые подразделяются на подблоки (+), колонка указывает общую (сводную) информацию по типам защиты в подблоках. В случае применения к региону неразрешенного типа операций, возникает "Ошибка доступа". Ошибка доступа происходит в случаях: когда происходит попытка запустить код из региона, который не помечен как исполняемый (если DEP включена), или при попытке записи в регион, который не помечен как предназначенный для записи или для "копирования-при-записи" (copy-on-write), или в случае попытки доступа к региону, который маркирован как "нет доступа" или просто зарезервирован, но не подтвержден. Атрибуты защиты присваиваются регионам виртуальной памяти на основе атрибутов сопоставленных регионов физической памяти.
Details Дополнительная информация по региону. Тут могут отображаться: путь файла бэкапа, идентификатор кучи (для региона heap), идентификатор потока (для стека), указатель на .NET-домен и прочее.
WS (Working Set) - так называемый рабочий набор, то есть множество (массив) страниц физической памяти (ОЗУ), уже выделенных для процесса и использующихся для фактического хранения кода/данных. Когда требуется доступ к каким-либо адресам виртуальной памяти, фактически с этими адресами должна быть связана физическая память (потому что операции чтения/записи/выполнения могут производиться только с физической памятью). Поэтому, когда с данными адресами будет сопоставлена физическая память, она добавляется как раз к рабочему набору процесса (working set).

Ну и необходимо описать все виды типов (type) регионов. Типы регионов у можно наблюдать на карте процесса в столбце Type:

Тип региона Описание
Free Диапазон виртуальных адресов не сопоставленных с физической памятью. Это память, которая еще не занята. Регион или часть региона доступны для резервирования (выделения).
Shareable Регион, который может быть разделен с другими процессами и забекаплен в физической памяти либо файле подкачки. Подобные регионы обычно содержат данные, которые разделены между процессами, то есть используются несколькими программами, через общие, специально оформленные, секции DLL или другие объекты.
Private Data Частные данные. Это регион, выделенный через функцию VirtualAlloc. Эта часть памяти не управляется менеджером кучи (Heap Manager), функциями .NET и не выделяется стеку. Обычно содержит данные приложения, которые используются только нашей программой и не доступны другим процессам. Так же содержит локальные структуры процесса/потока, такие как PEB или TEB. Типичная "память программы". Регион сопоставлен со страничным файлом.
Unusable Виртуальная память, которая не может быть использована из-за фрагментации. Это осколки, которые уже закреплены за регионом. Гранулярность выделения памяти в Windows - регионы по 64Кб. Когда Вы пытаетесь выделить память с помощью функции VirtualAlloc и запрашиваете, к примеру 8 килобайт, VirtualAlloc возвращает адрес региона в 64 килобайта. Оставшиеся 56Кб помечаются как неиспользуемые (unusable). Обратите внимание на то, что области Unusable "следуют" в карте за не кратными 64Кб регионами, на самом же деле, это всего-лишь память, которая входит в регион (принадлежит региону-владельцу), но на данный момент не используется.
Image Регион сопоставлен с образом исполняемого EXE- или DLL-файла, проецируемого в память. Это именно тот регион, куда загружается образ пользовательского приложения со всеми его секциями (в нашем случае test2.exe).
Image (ASLR) Образы системных библиотек, загружаемые с использованием механизма безопасности ОС под названием ASLR (Address Space Layout Randomization). ASLR - рандомизация расположения в адресном пространстве процесса таких структур как: образ исполняемого файла, подгружаемая библиотека, куча и стек. Вкратце, ОС игнорирует предпочитаемый базовый адрес загрузки, который задан в заголовке PE и загружает библиотеку в адрес по выбору "менеджера загрузки". Для поддержки ASLR, библиотека должна быть скомпилирована со специализированной опцией, либо без неё, когда используется принудительная рандомизация (ForceASLR). Таким образом, усиливается безопасность процесса и исключаются конфликты базовых адресов образов [подгружаемых модулей]. Применяется начиная с Windows Vista. Технология так же известна под псевдонимом Rebasing.
Thread Stack Стек. Регион сопоставлен со стеком потока. Каждый поток имеет свой собственный стек, соответственно под каждый поток выделяется регион для хранения его собственного стека. Когда в процессе создается новый поток, система резервирует регион адресного пространства для стека потока. Для чего обычно используется стек? Ну как и все стеки, стек потока предназначается для хранения локальных переменных, содержимого регистров и адресов возврата из функций.
Mapped File Проецируемые файлы. Это немного не то же, что "проецирование" образа самой программы и необходимых библиотек. Все отображаемые в адресное пространство процесса файлы могут быть трех видов: самой программой, библиотеками, и рабочими объектами. Проецируемые (mapped) файлы это и есть вот эти самые рабочие объекты, которые может создавать и использовать код программы. Обычно это файлы, которые содержат какие-либо требующиеся приложению данные и с которыми приложение работает напрямую. Проецирование файлов - наиболее удобный способ обработки внешних данных, поскольку данные из файла становятся доступны непосредственно в адресном пространстве процесса (регион памяти сопоставлен с файлом или частью файла), а на самом деле они размещаются на диске. Таким образом программе файл доступен в виде большого массива, нет необходимости писать собственный код загрузки файла в память, на лицо экономия на операциях ввода-вывода и операциях с блоками памяти. ОС делает всё это прозрачно для разработчика, собственными механизмами, получается для кода область проецируемых файлов - это обычная память. Проецируемые файлы предназначены для операций с файлами из кода основной программы, ведь рано или поздно подобные операции с файлами приходится использовать практически во всех проектах, и зачастую это влечет за собой большое количество дополнительной работы, поскольку пользовательское приложение должно уметь работать с файлами: открывать, считывать и закрывать файлы, переписывать фрагменты файла в буфер и оттуда в другую область файла. В Windows все подобные проблемы решаются как раз при помощи проецируемых в память файлов (memory-mapped files). Проецируемый в память файл может иметь имя и быть разделяемым, то есть совместно использоваться несколькими приложениями. Работа с проецируемыми файлами в пользовательском режиме обеспечивается функциями CreateFileMapping и MapViewOfFile.
Heap (Private Data) Куча. Это регион зарезервированного адресного пространства процесса, предназначенный для динамического распределения небольших областей памяти. Представляет из себя закрытую область памяти, которая управляется так называемым "Менеджером кучи" (Heap Manager). Данные в этой области хранятся "в куче" (или "свалены в кучу"), то есть друг за другом, разнородные, без какой-либо систематизации. Смысл кучи сводится к обработке множества запросов на создание/уничтожение множества мелких объектов (блоков памяти). Куча используется различными функциями WinAPI, вызываемыми кодом Вашего приложения, либо функциями самого приложения, для выделения различных временных буферов хранения строк, переменных, структур, объектов. Память в куче выделяется участками (индексами), которые имеют фиксированный размер (8 байт).

Как Вы видите из карты процесса, всё адресное пространство процесса разбито на множество неких зон различного назначения, называемых регионами. Регионов в адресном пространстве достаточно много. Однако, для начала, давайте посмотрим на "общее" разбиение адресного пространства процесса, дабы возникло понимание, как что и где может размещаться. Разбиение адресного пространства в определенной степени зависит от версии ядра Windows.
Общая концепция разбиения виртуального адресного пространства 32-битных программ:

Начало Конец Размер Описание
00000000 0000FFFF 64Кб Область нулевых указателей. Зарезервировано. Данная область всегда маркируется как свободная (Free). Попытка доступа к памяти по этим адресам вызывает генерацию исключения нарушения доступа STATUS_ACCESS_VIOLATION. Область применяется для выявления программистами некорректных, нулевых указателей, тем самым позволяя выявлять некорректно работающий код. Если по каким-то причинам (напр.: возврат значения функцией) переменная или регистр вдруг принимает нулевое (неинициализированное) значение, то дальнейшая попытка обращения к памяти (запись/чтение) с использованием данной переменной/регистра приведет к генерации исключения (напр.: mov eax, dword ptr [esi], где ESI=0).
00010000 7FFEFFFF 2Гб (3Гб) Пользовательский режим (User mode). Пользовательская часть кода и данных. В это пространство загружается пользовательское приложение, с разбивкой по секциям. Отображаются все проецируемые в память файлы, доступные данному процессу. В этом пространстве создаются пользовательская часть стеков потоков приложения. Тут присутствуют основные системные библиотеки ntdll.dll, kernel32.dll, user32.dll, gdi32.dll.
7FFF0000 7FFFFFFF 64Кб Область некорректных указателей. Зарезервировано. Данная область всегда маркируется как свободная (Free). Попытка доступа к памяти по этим адресам вызывает генерацию исключения нарушения доступа STATUS_ACCESS_VIOLATION. Хотя эта область формально и относится к области памяти пользовательского режима, она является "пограничной", то есть имеется риск при операциях с большими блоками памяти выйти за границы пользовательского режима и перезаписать данные режима ядра, поэтому Microsoft предпочла заблокировать доступ к данной области. Область применяется для выявления некорректных (вышедших за пределы пользовательской памяти) указателей (переменные/регистры) в коде (например: mov eax, dword ptr [esi], где ESI=значение, входящее в диапазон 7FFF0000-7FFFFFFF).
80000000 FFFFFFFF 2Гб (1Гб) Режим ядра (Kernel mode). Код и данные модулей ядра, код драйверов устройств, код низкоуровневого управления потоками, памятью, файловой системой, сетевой подсистемой. Размещается кеш буферов ввода/вывода, области памяти, не сбрасываемые в файл подкачки. Таблицы, используемые для контроля страниц памяти процесса (PTE?). В этом пространстве создаются ядерная часть стеков для каждого потока в каждом процессе. Пространство недоступно из пользовательского режима, и попытка обращения из кода режима пользователя приведет к исключению нарушения доступа. Пространство "общее", то есть идентично (одинаково) для всех процессов системы.

Ну, регионы мы бегло рассмотрели, давайте разберемся, как же происходит построение адресного пространства для конкретного процесса? Ведь должен существовать в системе механизм выделения и заполнения регионов. Все начинается с того, что пользователь либо некий код инициируют выполнение исполняемого модуля (программы). Одни из примеров подобного действия может быть двойной щелчок в проводнике по имени исполняемого файла. В этом случае код инициирующего выполнение потока вызывает функцию CreateProcess либо родственную из набора функций, предназначающихся для создания нового процесса. Какие же действия выполняет ядро после вызова данной функции:

  • Находит исполняемый файл (.exe), указанный в параметре функции CreateProcess. В случае каких проблем просто возвращает управление со статусом false.
  • Создает новый объект ядра "процесс".
  • Создает адресное пространство процесса.
  • Во вновь созданном адресном пространстве резервирует регион (набор страниц). Размер региона выбирается с расчетом, чтобы в него мог уместиться исполняемый .exe-файл. Загрузчик образа смотрит на параметр заголовка .exe-файла, который указывает желательное расположение (адрес) этого региона. По-умолчанию = 00400000, однако может быть изменен при компиляции.
  • Отмечает, что физическая память, связанная с зарезервированным регионом это сам .exe-файл на диске.
  • После окончания процесс проекции .exe-файла на адресное пространство процесса, система анализирует секцию import directory table, в которой представлен список DLL-библиотек (которые содержат функции необходимые коду исполняемого файла) и список самих функций.
  • Для каждой найденной DLL-библиотеки производится "отображение", то есть вызывается функция LoadLibrary, которая выполняет следующие действия:
    • Резервирует регион в адресном пространстве процесса. Размер выбирается таковым, чтобы в регион мог поместиться загружаемый DLL-файл. Желаемый адрес загрузки DLL указывается в заголовке. Если размер региона по желаемому адрес меньше размера загружаемого DLL, либо регион занят, ядро пытается найти другой регион.
    • Отмечает, что физическая память, связанная с зарезервированным регионом это DLL-файл на диске.
    • Производится настройка образа библиотеки, сопоставление функций. Результатом этого является заполненная таблица (массив) адресов импортируемых функций, чтобы в процессе работы код обращается к своему массиву для определения точки входа в необходимую функцию.

Очевидно, что в момент создания процесса, адресное пространство практически все свободно (незарезервировано). Поэтому, для того, что бы воспользоваться какой-либо частью адресного пространства, надо эту самую часть для начала выделить (зарезервировать) посредством специализированных функций.

Резервирование (reserving) - операция выделения региона (выделения блока памяти по те или иные нужды).

Резервирование региона происходит вместе с выравниванием начала региона с учетом так называемой гранулярности (зависит от процессора, 64Кб). При резервировании обеспечивается дополнительно еще и кратность размера региона размеру страницы (зависит от процессора, 4Кб), поэтому если вы пытаетесь зарезервировать регион некратной величины, система округлит значение до ближайшей большей кратной величины.

Страница (page) - минимальная единица [объема памяти], используемая системой при управлении памятью (как мы и писали выше).

Размещение в адресном пространстве структур и библиотек

Далее по тексту я буду приводить описание непосредственно регионов памяти, делая цветовую маркировку для удобства сопоставления типа региона фактически размещаемым данным.

Адрес Модуль Описание
00040000 apisetschema.dll Предназначена для организации и разделения на уровни выполнения огромного количества функций базовых DLL системы. Подобная технология была названа "Наборами API" (API Sets), появилась в Windows 7 и предназначалась для группировки всех [многочисленных] функций в 34 различных типа и уровня выполнения, с целью предотвратить циклические зависимости между модулями и минимизировать проблемы с производительностью, которые обусловлены обеспечением зависимости новых DLL от набора Win32 API в адресном пространстве процесса. Перенаправляет вызовы, адресованные базовым DLL к их новым копиям, разделенным на уровни.
00050000 Стек потока [64-битный] стек потока.
00090000 Стек потока. Одномерный массив элементов с упорядоченными адресами (организованный по принципу "последний пришел - первым ушел" (LIFO)), предназначенный для хранения небольших объемов данных фиксированного размера (слово/двойное слово/четверное слово): стековых фреймов, передаваемых в функцию аргументов, локальных переменных функций, временно сохраняемых значений регистров. Для каждого потока выделяется собственный (отдельный) стек. Каждый раз при создании нового потока в контексте процесса, система резервирует регион адресного пространства для стека потока и передает данному региону определенный объем [физической] памяти. Для стека система резервирует 1024Кб (1Мб) адресного пространства и передает ему всего две страницы (2х8Кб?) памяти. Но перед фактическим выполнением потока система устанавливает указатель стека на конец верхней страницы региона стека, это именно та страница, с которой поток начнет использовать свой стек. Вторая страница сверху называется сторожевой (guard page). Как только активная страница "переполняется", поток вынужден обратиться к следующей (сторожевой) странице. В этом случае система уведомляется о данном факте и передает память еще одной странице, расположенной непосредственно за сторожевой. После чего флаг PAGE_GUARD переходит к странице, которой только что была передана память. Благодаря описанному механизму объем памяти стека увеличивается исключительно по мере необходимости.
00280000 msctf.dll.mui Файл локализации библиотеки msctf.dll, описанной ниже. В общем смысле представляет собой переведенные на русский язык текстовые строки/константы, используемые библиотекой.
00400000 test2.exe Собственно образ нашей программы. Отображается в виртуальном адресном пространстве благодаря системному механизму проецирования файлов. Исполняемый .exe-файл проецируется на адресное пространство программы по определенным адресам и становится его частью. Проецирование состоит в том, что данные [из файла] не копируются в память, а как бы связываются с данными на физическом носителе, то есть любое обращение к памяти по этим адресам инициирует чтение данных с диска, память как бы "читается" из файла на диске. Виртуальный адрес 00400000 является "предпочитаемой базой образа" (Image base), константой, которую можно изменять при компиляции. По традиции, никто этим не заморачивается, и, в большинстве случаев, данный адрес актуален для подавляющего большинства программ (но встречаются и исключения).

Не путайте "базу образа" (image base) с "точкой входа" (entry point). Вторая представляет из себя адрес, с которого начинается исполнение кода программы. Обычно лежит по некоторому смещению относительно "базы образа".

Образ исполняемого файла (test2.exe) содержит в себе секции. Данный факт можно подтвердить, раскрыв (+) содержимое образа. Объясняется это тем, что exe-файл состоит из множества частей: непосредственно секция кода, секция данных, секция ресурсов, констант. Все эти секции загрузчик размещает по собственным областям памяти и назначает различные атрибуты доступа.

00410000 locale.nls NLS предоставляет поддержку местной раскладки клавиатуры и шрифтов. NLS позволяет приложениям устанавливать локаль для пользователя и получать (отображать) местные значения времени, даты, и других величин, отображаемых в формате региональных настроек.
01F80000 SoftDefault.nls
02250000 StaticCache.dat
735A0000 uxtheme.dll Тема оформления. Функционал библиотеки позволяет менять визуальное представление интерфейса (вид многочисленных элементов управления) программ без необходимости менять базовый (в ядре) функционал операционной системы.
73A30000 comctl.dll Библиотека реализует готовые элементы управления (контролы), которые используются в графическом интерфейсе.
74B20000 dwmapi.dll Интерфейс диспетчера окон рабочего стола (DWM, Desktop Windows Manager). DWM - графический интерфейс рабочего стола, использующийся в Windows Aero. Управляет объединением различных выполняющихся и визулизируемых окон с рабочим столом. В своей программе я никаких специфических функций Windows Aero не использую, но, могу предположить, что образ библиотеки dwmapi.dll отображается на адресное пространство процесса по причине включенного на уровне системы интерфейса Aero.
751F0000 75250000 752D0000 wow64win.dll wow64.dll wow64cpu.dll В адресном пространстве процесса по данным адресам находятся библиотеки DLL пользовательского режима, отвечающие за работу подсистемы Wow64. Они появились в нашем адресном пространстве не случайно, поскольку, напомню, что наш 32-разрядный процесс test2.exe запущен в 64-разрядной ОС Windows 7 Professional.

Wow64 (Windows 32-bit on Windows 64-bit) - эмуляция Win32 приложений на 64-разрядной ОС Windows.

Представляет из себя программную среду, позволяющую исполнять 32-разрядные приложения на 64-разрядной версии Windows. Механизм используется в 64-разрядных версиях Windows в виде набора библиотек DLL пользовательского режима. Помимо данных библиотек в 64-разрядной версии ОС присутствует поддержка со стороны ядра (изменение контекста). Перехватывает системные вызовы 32-битных версий ntdll.dll и user32.dll, поступающих от 32-битных приложений и транслирует их в 64-битные вызовы ядра. С помощью Wow64 создаются 32-разрядные версии структур данных для процесса, например PEB, TEB и другие, на основе их 64-разрядных прототипов.

wow64win.dll Библиотека, предназначенная для эмуляции системных вызовов графической оболочки пользователя (GUI), экспортируемых Win32k.sys.
wow64.dll Обеспечивает инфраструктуру эмуляции (преобразования) системных вызовов, экспортируемых ядром ntoskrnl.exe. По сути, организует перенаправление всего основного функционала, включающего операции с файловой системой и реестром. Управляет созданием процесса и потоков в нём.
wow64cpu.dll Библиотека, отвечающая за эмуляцию x86-инструкций на процессорах Itanium. Управляет 32-разрядным контекстом процессора для каждого потока, запущенного в рамках Wow64. Поддерживает переключение режима работы с 32-разрядного в 64-разрядный и наоборот, обеспечивая поддержку всей логики. Не обязательна для x64-процессоров, поскольку они выполняют x86-32-инструкции напрямую.
75500000 msctf.dll Библиотека расширяет функционал, предоставляемый службами Microsoft для работы с текстом (Microsoft Text Services). Среди функций библиотеки имеются функции усовершенствованной текстовой обработки и ввода текста. Функционал библиотеки msctf.dll предоставляет двунаправленный обмен между приложением и службами работы с текстом. Предоставляет поддержку различных языков.
75810000 msvcrt.dll Microsoft Visual C++ Runtime. Библиотека времени выполнения языка C, обеспечивающая вспомогательные функций для работы с памятью, устройствами ввода/вывода, математическими функциями. Довольно много прототипов функций, используемых в языках C/C++ содержится в данной библиотеке.
758C0000 gdi32.dll Одна из четырех основных библиотек поддержки Win32 API. Часть интерфейса графического устройства (GDI, Graphics Device Interface) или интерфейса между приложениями и графическими драйверами видеокарты, работающая в режиме пользователя. Содержит функции и методы для представления графических объектов и вывода их на устройство отображения, отвечает за отрисовку линий, кривых, обработку палитры и управление шрифтами, можно сказать полностью отвечает на графику. Приложения посылают запросы коду GDI, работающему в режиме пользователя, который пересылает их GDI режима ядра, а тот уже перенаправляет данные запросы драйверам графического адаптера. Моя программа [напрямую] не импортирует функции из gdi32.dll напрямую, однако библиотека проецируется в адресное пространство любого процесса, использующего оконный интерфейс.
75A80000 advapi32.dll Одна из четырех основных библиотек поддержки Win32 API. Содержит большое количество часто востребованных функций: работа с реестром, сервисами, выключение (перезагрузка) ПК, и прч. В системе присутствует огромное количество библиотек, которые статически слинкованы с библиотекой advapi32.dll. Поэтому, без проецирования её в адресное пространство процесса никак не обойтись.
76С00000 kernel32.dll Одна из четырех основных библиотек поддержки Win32 API. В библиотеке содержатся основные подпрограммы для поддержки работы подсистемы Win32. Много ключевых процедур и функций, которые используются в пользовательских программах, содержатся в библиотеке kernel32.dll. Это работа с процессами (GetModuleHandle, GetProcAddress, ExitProcess), вводом-выводом, памятью, синхронизацией. Ранее kernel32.dll загружался во всех контекстах процесса по одному и тому же адресу. Теперь, думаю именно из-за ASLR, в адресному пространстве каждого процесса он загружается по разным адресам? Большинство экспортируемых библиотекой kernel32.dll функций используют «родной» API ядра напрямую.
77070000 user32.dll Одна из четырех основных библиотек поддержки Win32 API. Эта библиотека проецируется практически в каждый процесс Win32. Библиотека содержит часто используемые функции для обработки сообщений, меню, взаимодействия. Напомню, что в моей программе используются такие функции как: SetFocus, SendMessage, MessageBox, CreateWindowEx, DefWindowProcA, DispatchMessageA, GetMessageA, LoadCursorA, LoadIconA, PostQuitMessage, RegisterClassA, ShowWindow, TranslateMessage, UpdateWindow. Все эти функции предоставляются системной библиотекой user32.dll, поэтому без проецирования её в адресное пространство моего процесса моя программа (test2.exe) работать не будет.
771F0000 kernelbase.dll Результат технологии разделения на уровни выполнения базовых функций DLL. Содержит так называемые низкоуровневые функции, которые ранее помещались в библиотеках kernel32.dll и advapi32.dll. Теперь код направляет запросы к этой библиотеке низкоуровневых функций, вместо того, чтобы, как раньше, выполнять их напрямую.
77C00000 ntdll.dll Библиотека, обеспечивающая "родной" интерфейс (Native API) функций ядра как для приложений раннего этапа загрузки ОС, так и для функций интерфейса WinAPI. Все функции подсистемы Win32 можно разделить на две части: функции, требующие перехода в режим ядра и функции не требующие перехода в режим ядра. Для обработки API-функций пользовательского режима, которые требуют перехода в режим ядра и существует библиотека ntdll.dll. По своей структуре ntdll.dll представляет собой обычную библиотеку пользовательского режима, представляющую собой своеобразный "мост" (переходник) между функциями библиотек пользовательского режима и кодом, который реализует соответствующий функционал в ядре. Пользовательский режим (user mode) и режим ядра (kernel mode) существенно отличаются в реализации, однако пользовательский режим должен максимально сохранять совместимость с привычными (старыми) форматами входных/выходных данных функций, в то время как режим ядра может потребовать существенного видоизменения кода от версии Windows к версии. С этой точки зрения, ntdll.dll играет роль интерфейса совместимости, именно благодаря ему разработчики Microsoft могут свободно менять [при выпуске новых версий/пакетов обновлений Windows] внутреннюю реализацию функций в ядре, сохраняя, при этом, формат параметров функций пользовательского режима. Можно сказать, что Native API создан с единственной целью - вызывать функции ядра, код которого располагается в нулевом кольце защиты. Большинство точек входа в Native API являются "заглушками", которые передают параметры и управление коду режима ядра.
77DE0000 ntdll.dll То же самое, что и описанный ntdll.dll, только для 32-битного процесса.
7EFDB000 TEB Блок переменных окружения потока (Thread Environment Block). Структура данных, размещаемая в адресном пространстве процесса, которая содержит информацию о конкретном потоке в пределах основного (текущего) процесса (в нашем случае - test2.exe). Каждый поток имеет свой TEB. Заполняется через функцию MmCreateTeb и заполняется загрузчиком потока. Создается, контролируется и разрушается исключительно самой ОС. Подобные регионы создаются и уничтожаются по мере появления/уничтожения потоков в процессе. Wow64 процессы имеют два TEB для каждого потока?
7EFDE000 PEB Блок переменных окружения процесса (Process Environment Block). Структура данных, размещенная в адресном пространстве процесса, которая содержит информацию о загруженных модулях (LDR_DATA), окружении, базовой информации и другие данные, которые требуются для нормального функционирования процесса. Создается через функцию MmCreatePeb и заполняется загрузчиком процесса на этапе создания адресного пространства процесса. PEB создается, контролируется и уничтожается исключительно самой ОС. Wow64 процессы имеют два PEB для каждого процесса?
80000000 Ядро Память выше данного значения принадлежит ядру. В этой области памяти находятся модули ядра, объекты ядра и пользовательские объекты, доступные всем процессам - проекции системных файлов. Но это все уже тема отдельной статьи.

Выводы

К каким выводам можно придти после изучения адресного пространства процесса? Первый состоит в том, что понятие "памяти" для пользовательских программ это достаточно условное обозначение, поскольку регионы адресного пространства могут по разному отображаться на различные объекты операционной системы. Второй состоит в том, что адресное пространство процесса это огромный линейный массив байтов, в котором хранится всё, с чем непосредственно работает процесс (программа). Массив этот виртуален, не ограничен физической памятью, уникален для каждого приложения и обладает достаточной размерностью, дабы программист не задумывался о его ограничениях. Механизм создания адресного пространства процесса достаточно сложен, и в статье удалось рассмотреть лишь малую часть его логики. В добавок, мы вовсе не касались 64-битных реалий, грозно смотрящих на нас из недалекого будущего :) но, пожалуй это тема отдельной статьи.

Комментарии: 4

  1. Я.В.

    Уважаемый автор/авторы, в таблице у вас приводится вычисление теоретического значения адресного пространства: "32 бита 32 бита (4 байта) 2^32 = 4294967296 байт (4 гигабайта, Гб)". Вы возводите 2 в 32 степень, что соответствует разрядности адресной шины, которая у вас же в соседней колонке указана в битах. Почему же результат вычислений у вас уже в байтах, а не в битах? Если учесть, что полученный результат "4294967296" - это биты, то получится не 4 Гбайта, а 512 Мбайт или 0, 5 Гбайт.

    1. einaare

      наверное я где-то упустил смысл вашего высказывания. двоичная система счисления: двойное слово, 4 байта, 32 бита. максимальное возможное число = 4294967296 байт. Размерность проставить в таблице?

    2. Колобок

      2 в 32 степени - это количество уникальных комбинаций битов, которое можно получить в 32ух разрядах. Если адрес ячейки памяти занимает 32 бито, то мы можем адресовать 2 в 32ой ячеек. Размер ячейки равен 1 байту. Поэтому максимальный размер адресного пространства будет равен 4ем гигабайтами.
      Если бы размер ячейки был равен одному биту, то вы были бы правы.

      1. einaare

        верно! адресуется байт, а не бит.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *