Во-первых, это не имеет ничего общего с оперативной памятью. Мы говорим здесь об адресном пространстве - даже если у вас есть только 16 МБ памяти, у вас все еще есть 32-битное адресное пространство на 32-битном процессоре.
Это уже отвечает на ваш первый вопрос, на самом деле - в то время, когда это было спроектировано, ПК реального мира не имели почти полных 4 ГБ памяти; их было больше в диапазоне 1-16 мегабайт памяти. Адресное пространство было, по сути, свободным.
Теперь, почему именно 0xFFFFFFF0? Процессор не знает, сколько там BIOS. Некоторые BIOS могут занимать всего несколько килобайт, в то время как другие могут занимать полные мегабайты памяти - и я даже не вхожу в различные дополнительные RAM. Процессор должен быть жестко привязан к какому-либо адресу для запуска - нет необходимости настраивать процессор. Но это только сопоставление адресного пространства - адрес отображается непосредственно в микросхему ПЗУ BIOS (да, это означает, что вы не получите доступ к полному 4 ГБ ОЗУ в этот момент, если у вас есть так много - но в этом нет ничего особенного, многим устройствам требуется свой диапазон адресов). На 32-битном процессоре этот адрес дает вам полные 16 байтов для выполнения базовой инициализации - этого достаточно для настройки сегментов и, при необходимости, режима адресации (помните,настоящая «процедура» загрузки. На данный момент вы вообще не используете ОЗУ - все это просто ПЗУ. На самом деле, ОЗУ даже не готово к использованию на этом этапе - это одна из задач BIOS POST! Теперь вы можете подумать - как 16-битный реальный режим получает доступ к адресу 0xFFFFFFF0? Конечно, есть сегменты, поэтому у вас есть 20-битное адресное пространство, но этого все еще недостаточно. Ну, есть хитрость - 12 старших бит адреса устанавливаются до тех пор, пока вы не выполните свой первый длинный прыжок, предоставляя вам доступ к высокому адресному пространству (при этом отказывая в доступе к чему-либо ниже 0xFFF00000 - пока вы не выполните длинный прыжок) ,
Все это вещи, которые в основном скрыты от программистов (не говоря уже о пользователях) в современных операционных системах. Обычно у вас нет доступа к чему-либо столь низкому уровню - некоторые вещи уже не поддаются спасению (вы не можете переключать режимы процессора вольно или невольно), некоторые обрабатываются исключительно ядром ОС.
Таким образом, более хороший взгляд приходит из старой школы на MS DOS. Другой типичный пример того, как память устройства напрямую сопоставляется с адресным пространством, - это прямой доступ к видеопамяти. Например, если вы хотите быстро выводить текст на дисплей, вы пишете непосредственно по адресу B800:0000
(плюс смещение - в текстовом режиме 80x25, это означало, (y * 80 + x) * 2
что моя память правильно меня обслуживает - два байта на символ, строка за строкой). Если вы хотите рисовать попиксельно, вы использовали графический режим и начальный адрес A000:0000
(обычно 320x200 при 8 битах на пиксель). Делать что-то высокопроизводительное обычно означало погрузиться в руководства по устройствам, чтобы выяснить, как получить к ним прямой доступ.
Это сохранилось до наших дней - оно просто скрыто. В Windows вы можете видеть адреса памяти, сопоставленные с устройствами, в диспетчере устройств - просто откройте свойства чего-то вроде вашей сетевой карты, перейдите на вкладку «Ресурсы» - все элементы диапазона памяти являются отображениями из памяти устройства в ваше основное адресное пространство. А в 32-разрядной системе вы увидите, что большинство этих устройств отображаются выше отметки 2 ГиБ (позднее 3 ГиБ) - опять же, чтобы минимизировать конфликты с используемой пользователем памятью, хотя на самом деле это не проблема виртуальной памяти ( приложения не приближаются к реальному аппаратному адресному пространству - у них есть собственный виртуализированный кусок памяти, который может быть сопоставлен, например, с ОЗУ, ПЗУ, устройствами или файлом подкачки).
Что касается стека, ну, это должно помочь понять, что по умолчанию стек растет сверху. Таким образом, если вы сделаете a push
, новый указатель стека будет в 0xFFFFFEC
- другими словами, вы не пытаетесь записать в адрес инициализации BIOS :) Это, конечно, означает, что подпрограммы инициализации BIOS могут безопасно использовать стек, прежде чем переназначить его где-то полезнее. В программировании старой школы до того, как пейджинг стал де-факто по умолчанию, стек обычно начинался с конца ОЗУ, и «переполнение стека» происходило, когда вы начинали перезаписывать память приложения. Защита памяти во многом изменилась, но в целом она максимально поддерживает обратную совместимость - обратите внимание, что даже самый современный процессор x86-64 все еще может загружать MS DOS 5 - или как Windows все еще может запускать многие приложения DOS, которые не имеют представления о подкачке страниц.