Когда ОС, такая как Windows, хочет запустить исполняемый файл, сначала она должна загрузить его в ОЗУ. Из-за того, что память не расходуется впустую, ее частичная загрузка в память кажется более интеллектуальной, чем загрузка ее целиком.
Итак, при таком условии, мои вопросы таковы:
Что именно происходит, когда элементы управления прибывают к инструкции, такой как JMP, содержащей адрес вне диапазона загруженного кода? Другими словами, как ОС распознает, что она должна прекратить выполнение инструкции, чтобы избежать перехода на нерелевантный адрес, и как она вычисляет, на какой странице находится соответствующий адрес?
Сколько страниц кода ОС копирует в ОЗУ перед переходом к точке входа программы? Я имею в виду, всегда ли ОС копирует фиксированный объем кода или фиксированное количество страниц в оперативную память обязательно или это может быть неопределенным?
Если ОС принимает решение о том, сколько кода или сколько страниц следует загрузить в память, то какие условия учитываются до принятия такого решения?
Спасибо всем.
Я так не думаю. Для того, чтобы быть достаточно опытным в программировании на ассемблере X86, важно иметь хорошее представление о структуре операционных систем, архитектуре системы и ее деталях, и наоборот.
Да, но вопрос вовсе не в программировании на ассемблере.
Загрузчик программ современной ОС в основном использует mmap
, а не read
.. https://en.wikipedia.org/wiki/Memory-mapped_file#Common_uses говорит:
Perhaps the most common use for a memory-mapped file is the process loader in most modern operating systems (including Microsoft Windows and Unix-like systems.)
Это создает частное сопоставление с файловой поддержкой. (https://en.wikipedia.org/wiki/Virtual_memory).
- ... In other words how does the OS recognize that it must stop executing the instruction to avoid jumping to a irrelevant address and how does it calculate which page the related address situated in?
В этом случае выборка кода вызывает ошибку страницы., как если бы ваш код загружался из части большого статического массива, который еще не был загружен с диска. После возможной загрузки страницы с диска (если ее еще не было в кеше страниц) и обновления таблиц страниц выполнение возобновляется по адресу, на котором произошел сбой, чтобы повторить выполнение инструкции.
Аппаратное обеспечение виртуальной памяти ЦП («MMU», хотя на самом деле это не отдельная вещь в современных ЦП) обрабатывает обнаружение загрузки / сохранения / выборки кода с несопоставленных адресов. (Несопоставлено в соответствии с фактическими таблицами страниц, которые может видеть HW. Когда процесс "логически" отображает некоторую память, но ОС ленивается в этом отношении, мы говорим, что память не "подключена" к таблицам страниц, поэтому Ошибка страницы перенесет его в память, если это еще не сделано, и подключит его к таблицам страниц, чтобы аппаратное обеспечение могло получить к нему доступ (после промаха TLB для запуска аппаратного обхода страниц).
Если есть какая-либо среда выполнения перемещение символов, также известное как исправления, для учета программы, загружаемой по базовому адресу, отличному от того, на который она была связана, если ей нужны какие-либо абсолютные адреса в памяти, они могут потребовать записи страниц кода или данных, предназначенных только для чтения, загрязнение страницы виртуальной памяти, чтобы она поддерживалась файлом подкачки, а не исполняемым файлом на диске. например если ваш источник C включает int *foo = &bar;
в глобальном масштабе, или int &foo = bar;
- How many pages of code does the OS copy into RAM before jumping to the entry point of the program?
Загрузчик программы, вероятно, имеет некоторую эвристику, чтобы убедиться, что точка входа и, возможно, некоторые другие страницы отображаются перед первой попыткой. Помимо этого IDK, если есть какие-либо специальные эвристики в коде виртуальной памяти для исполняемых файлов / библиотек по сравнению с неисполняемыми сопоставлениями.
О, я не видел вашего ответа, когда отправлял свой. Надеюсь, это не сделает мой дубликат :)
@MargaretBloom: похоже, мы использовали разные подходы. Я не искал возможных повторяющихся вопросов; ты сделал? (С нашими ответами здесь, возможно, следует закрыть некоторые другие вопросы как дубликаты этого: P)
re: эвристика загрузчика: Да, и Linux, и Windows используют механизмы предварительной выборки, чтобы уменьшить влияние аппаратных ошибок страниц на производительность.
Да, существуют механизмы опережающего чтения для жестких ошибок (чтобы избежать большого количества операций ввода-вывода на малых дисках), а также есть отдельная оптимизация для устранения ошибок при программных сбоях страниц. Последнее помогает в общем случае, когда страницы существуют в кэше страниц, но не отображаются в текущий процесс: когда возникает ошибка страницы при доступе к данной странице, Linux проверяет, присутствуют ли уже «соседние» страницы в ОЗУ. , и в таком случае приведет к добавлению (добавлению в таблицы страниц) нескольких из этих страниц с одной и той же ошибкой. Значение по умолчанию для устранения неисправности - 16, и вы можете увидеть это простым способом в ...
... любой процесс, который обращается к большой области отображения поля, которая уже кэширована: количество мягких ошибок будет 1/16 от количества страниц. Эта оптимизация делает менее нужными трюки вроде MAP_POPULATE
.
Процессор делит адресное пространство на наборы адресов, называемых страницы.
.
На x86 размер стандартной страницы составляет 4 КиБ, но возможны и другие размеры (например, 1 ГБ, 2 МБ).
Страницы являются непрерывными, поэтому первая страница находится от адреса 0x00000000 до адреса 0x00000fff, для каждого адреса есть уникальная страница, связанная с ним.
Страница имеет набор атрибутов, весь смысл разбиения по страницам состоит в том, чтобы связать набор атрибутов с каждым адресом. Поскольку делать это для каждого отдельного адреса было бы слишком непозволительно, вместо этого используются страницы. Все адреса на странице имеют один и тот же атрибут.
Я несколько упростил историю, не проводя различия между виртуальными адресами (те, которые на самом деле разбиты на страницы, т.е. они могут иметь атрибуты) и физическими адресами (реальные адреса для использования, виртуальный адрес может быть сопоставлен с другим физическим адресом).
Среди различных атрибутов есть:
Помните, что эти атрибуты относятся к странице, они применяются ко всему диапазону адресов на странице (например, они влияют на адреса 4 KiB для страницы 4 KiB).
Имея это в виду:
Когда процесс создается, все его страницы помечаются как отсутствующие. Доступ к ним приведет к сбою ЦП. Когда ОС загружает программу, загружается минимальный набор страниц (например, ядро, его часть, общие библиотеки, часть программного кода и данные), которые помечаются как присутствующие. Когда программа обращается к незагруженной странице, ОС проверяет, был ли адрес назначен программой, и если да (это действительная ошибка страницы), она загружает страницу и возобновляет выполнение. Если адрес не был назначен, возникает ошибка неверной страницы, и исключение сообщается самой программе.
Я не знаю точного количества загруженных страниц, это можно было проверить разными способами, в том числе взглянув на ядро Linux (для случая Linux) .
Я не делаю этого, потому что фактическая используемая стратегия может быть сложной, и я не считаю ее особенно актуальной: ОС может загрузить всю программу, если она достаточно мала и нагрузка на память невысока.
Там могут быть настройки для выбора той или иной стратегии.
В общем, разумно предположить, что оптимистично загружается только фиксированное количество страниц.
Факторы, влияющие на решение, могут быть следующими: объем доступной памяти, приоритет загруженного процесса, политика системы, созданная системным администратором (для предотвращения ее раздувания), тип процесса (такая служба, как СУБД, может быть помечена как с интенсивным использованием памяти), ограничение программы (например, на машине NUMA процесс может быть помечен для использования преимущественно локальной памяти, тем самым имея доступ к меньшему объему памяти, чем общий доступный), эвристика, реализованная ОС (например, она знает, что последнее выполнение потребовало K страниц кода / данных в пределах M миллисекунд от начала) .
Проще говоря, алгоритм, используемый для загрузки оптимального количества страниц, должен немного предсказывать будущее, поэтому принимаются обычные соображения для случая (т.е. предположения, упрощения, сбор данных и тому подобное).
Программные сбои страницы включают случай, когда код или данные были горячими в кэше страниц для сопоставлений с файловой поддержкой. Это не ограничивается анонимными сопоставлениями для таких вещей, как malloc.
@BeeOnRope Вы правы, у меня создалось впечатление, что программный сбой страницы произошел, когда ОС требовалось загрузить выделенную, но отсутствующую страницу. Спасибо :)
Тег
assembly
не подходит для этого вопроса. Вы можете использовать тегpaging
.