Во-первых, если вы хотите глубже погрузиться в подобные вещи, я настоятельно рекомендую взять копию Windows Internals, часть 1: Архитектура системы, процессы, потоки, управление памятью и многое другое, 7-е издание. Это хорошо объясняет детали. В качестве альтернативы посмотрите, например, документацию по Linux, которая более общедоступна - она будет немного другой, но будет следовать многим из тех же понятий, что и Windows.
Вот еще одно, возможно, более доступное объяснение.
Вы также можете посмотреть, как работают таблицы страниц , и, возможно, также TLB.
Суть в том, что когда процесс пытается выполнить чтение или запись на определенной странице своего виртуального адресного пространства, модуль управления памятью (MMU) ЦП либо преобразует его в физический адрес (просматривая таблицы страниц через TLB, специальный встроенный кэш, предназначенный только для записей в таблице страниц) или, если в записи таблицы страниц указано "страница отсутствует", уведомляет об этом операционную систему. Это то, что называется ошибкой страницы. В ответ ОС а) при необходимости назначает процессу новую физическую страницу и загружает ее содержимое из "резервного хранилища" (файл подкачки или сопоставленный файл) в эту страницу ОЗУ; б) обновляет запись таблицы страниц, чтобы отразить физическую адрес страницы, и c) говорит процессору повторить попытку ("отклоняет ошибку страницы").
Существует запись таблицы страниц для каждой страницы определенного виртуального адресного пространства. Записи таблицы страниц не только используются ЦП для преобразования адресов. PTE, соответствующие "недействительным" страницам - те, которые могут вызвать сбой страницы при обращении к ним - используются ОС для хранения информации о местонахождении данных страницы. Таким образом, PTE существуют для всех определенных виртуальных адресов. Подмножество обычно "допустимо", то есть соответствует виртуальным адресам, к которым можно получить доступ без ошибки страницы. (Это подмножество называется "рабочим набором" процесса.)
PTE для "недействительных" страниц содержат информацию, которая позволит ОС найти содержимое страницы в случае сбоя на этой странице. Содержимое может находиться в файле страницы, в отображенном файле, в ОЗУ (в одном из кэшей страниц), в рабочем наборе другого процесса (для общих страниц) или иногда вообще нигде (для страниц, которые не имели начального содержимого). определены и еще не были указаны; такие страницы просто распределяются физически и заполняются нулями при первой ссылке; эти события называются "сбоями при нулевой потребности страницы").
Таблицы страниц слишком велики, чтобы полностью поместиться в (очень очень маленький) TLB, поэтому они просто хранятся в обычной памяти.
Таблицы страниц на самом деле слишком велики, чтобы все сразу помещались в ОЗУ, поэтому они организованы в виде древовидной структуры для каждого процесса, и все, кроме таблицы верхнего уровня каждого дерева, сами по себе доступны для просмотра страниц.
В вашей типичной современной ОС каждый процесс будет иметь свою собственную таблицу страниц, сохраняя адресное пространство процесса независимым друг от друга. Ядро отслеживает процессы и их адресные пространства в пределах собственных областей памяти. В текущей Windows это хранится вместе с другими метаданными процесса в памяти в структуре данных « EPROCESS
» и связанных структурах, называемых дескрипторами виртуальных адресов. Windows также поддерживает отдельные структуры таблиц страниц для своего кода и данных режима ядра.
Важно помнить: адресное пространство процесса просто хранится в памяти и никогда не записывается обратно в исполняемые файлы + DLL, из которых был запущен процесс. Адресное пространство отбрасывается при выходе из процесса. Однако изменения в сопоставленных файлах, которые были открыты для доступа для записи, будут сброшены обратно в соответствующие файлы.
Насколько я знаю, фактические адреса виртуальной памяти для процесса сначала сохраняются в файле .exe программы. Это означает, что компоновщик исправит все ссылки на функции и переменные с фактическими адресами, такими как 0x00007fb6.
Нет. Это только для ссылок на программный код; адресное пространство процесса также включает в себя две или более кучи, стек для каждого потока и любые дополнительные диапазоны адресов, которые создаются потоком (-ами) процесса во время их работы.
И в наши дни даже программный код (случайно) переставляется при загрузке; это известно как ASLR и делает определенные типы атак более сложными.
Насколько я могу представить, ОС получает виртуальный адрес отдельного процесса из .exe
Нет. Исполняемый файл указывает только местоположение некоторых исходных данных; а именно, программный код, глобальные переменные и связанные библиотеки. И даже большая часть этой информации переопределяется ASLR в современных ОС.
Другая память (куча программы, стеки потоков, сопоставленные файлы и т.д.) Динамически распределяется программой - их размер, расположение и т.д. Могут и будут изменяться во время выполнения программы.
Как упоминалось выше, каждый процесс имеет свое независимое виртуальное адресное пространство. Часть его отображается в память ядра (которая помечена как не читаемая или изменяемая из пользовательского режима), а остальная часть является приватной для процесса. Ни одно из адресного пространства процесса не используется совместно с другими процессами, если это явно не запрошено ("разделяемая память") или не сделано полностью прозрачно для производительности (копирование при записи, сопоставленные файлы).
Рис. 5-10. Схемы виртуального адресного пространства x86 (2 ГБ слева, 3 ГБ справа) из Windows Internals, Часть 1. Архитектура системы, процессы, потоки, управление памятью и т.д. общая схема адресного пространства процесса. К сожалению, мне не разрешено воспроизводить это здесь.
поэтому, если содержимое другого файла отображается в виртуальном адресном пространстве этого процесса
Это заканчивается как специальная запись в таблице страниц процесса, которая указывает ОС загружать данные из сопоставленного файла. Это фактически перенаправление; "искать в другом месте". Помните, что адресное пространство является виртуальным - оно может ссылаться на файл, но этот файл не будет загружен, пока поток не попытается его прочитать / записать. И даже тогда будет загружаться только одна или несколько страниц за раз.
тогда это содержимое будет сохранено обратно в исполняемый файл
Нет. Исполняемые файлы всегда загружаются только для чтения. После загрузки вся соответствующая информация сохраняется исключительно в памяти и никогда не "сохраняется" ОС. Любые изменения в памяти процесса теряются при выходе из процесса, за исключением операций записи в сопоставленные файлы или общую память.
Или ОС как-то отслеживает добавленное содержимое за кулисами?
Да, какие регионы сопоставлены, какой файл отслеживается в метаданных процесса.