Регистры и модель памяти
Вступление: понятие о конвециях
Регистры
Регистр |
Программное имя |
Регистр |
Программное имя |
r0 |
zero |
r16 |
s0 |
r1 |
at |
r17 |
s1 |
r2 |
v0 |
r18 |
s2 |
r3 |
v1 |
r19 |
s3 |
r4 |
a0 |
r20 |
s4 |
r5 |
a1 |
r21 |
s5 |
r6 |
a2 |
r22 |
s6 |
r7 |
a3 |
r23 |
s7 |
r8 |
t0 |
r24 |
t8 |
r9 |
t1 |
r25 |
t9 |
r10 |
t2 |
r26 |
k0 |
r11 |
t3 |
r27 |
k1 |
r12 |
t4 |
r28 |
gp |
r13 |
t5 |
r29 |
sp |
r14 |
t6 |
r30 |
s8, fp |
r15 |
t7 |
r31 |
ra |
Только два регистра особенные — zero (r0, всегда равен 0) и ra (r31, автоматически заполняется некоторыми командами)
Теоретически можно использовать любой, но это сильно затрудняет написание работающих программ
⇒ возникает понятие конвенции (договорённости)
Регистр at (r1) используется псевдоинструкциями (например, для адреса при косвенной адресации)
Регистры a0 - a3 (r4 - r7) используются для передачи параметров подпрограммам
- Очевидно, у подпрограмм может быть более 4 параметров, так что здесь тоже вступают в силу конвенции, и очень разнообразные
Регистры v0, v1 (r2, r3) используются для возврата значений (почему два?)
Регистры t0 - t9 (r8-r15, r24,r25) можно использовать без ограничений
Регистры s0 - s8 (r16 - r23, r30) по договорённости необходимо восстанавливать в исходные значения перед выходом из подпрограммы. При этом даже если они используются вне подпрограммы, код сохранения и восстановления обязан присутствовать.
Регистры k0, k1 (r26, r27) используются для взаимодействия с ядром
Регистр sp (r29) содержит ссылку на вершину стека (stack pointer)
Регистр gp (r28) хранит адрес области глобальных данных (global pointer). Нужен, например, для хранения «глобальных переменных», доступных в том числе и из подпрограмм (конвенция!), или для передачи данных со стороны операционной системы
Регистр s8 (r30) в некоторых конвенциях организации подпрограмм используется для хранения ссылки на область данных текущей подпрограммы, поэтому он носит ещё одно название — fp (frame pointer)
Ещё два регистра — HI и LO — используются в командах деления и умножения
Плоская модель памяти
0xffffffff |
highest address in kernel (and memory) |
Память устройств |
Последний адрес, доступный ядру |
0xffffffff |
memory map limit address |
Конец памяти устройств |
|
0xffff0000 |
MMIO base address |
Начало памяти устройств |
|
0xfffeffff |
kernel data segment limit address |
Область данных ядра |
Конец данных ядра |
0x90000000 |
.kdata base address |
Начало данных ядра |
|
0x8ffffffc |
kernel text limit address |
Область кода ядра |
Предел кода ядра |
0x80000180 |
exception handler address |
Обработчик прерываний |
|
0x80000000 |
.ktext base address |
Начало кода ядра |
|
0x80000000 |
lowest address in kernel space |
Начало памяти ядра |
|
0x7fffffff |
highest address in user space |
Область данных |
Последняя ячейка, доступная пользователю |
data segment limit address |
Последняя ячейка области данных |
||
0x7ffffffc |
stack base address |
Адрес исчерпания стека |
|
0x7fffeffc |
stack pointer $sp |
Сюда указывает регистр стека (растёт вниз) |
|
0x10040000 |
stack limit address |
Стек может расти досюда |
|
heap base address |
Начало кучи (растёт вверх) |
||
0x10010000 |
.data base Address |
Начало статических данных |
|
0x10008000 |
Global Pointer $gp) |
Сюда указывает регистр глобальных данных |
|
0x10000000 |
.extern Base Address |
Область глобальных данных |
|
Data Segment base address |
Начало области данных |
||
0x0ffffffc |
text limit address |
Область программного кода |
Последняя ячейка области программного кода |
0x00400000 |
.text Base Address |
Начало программы |
|
0x00000000 |
|
Зарезервированная область |
|
Резервированная память (до 0x400000) может быть использована операционной системой для различных нужд. Например, в MARS директивы .text и .data приводят к заполнению памяти непосредственно по указанным адресам. На самом деле чаще всего результат трансляции записывается в исполняемый файл, который имеет довольно сложный формат, а при необходимости загружается в память в соответствии со специальными таблицами размещения, динамической компоновкой и т. п. Некоторые из этих данных нужны для работы программы под управлением ОС, они-то и размещаются в младших адресах памяти. Чтение и запись в эту область запрещены.
Text base — область для инструкций программы. Теоретически никто не мешает иметь несколько директив .text, размещающих код по различным адресам в пределах 0x400000 - 0x1000000. Обычно после загрузки программы, когда она начала работать, запись по адресам 0x400000 - 0xffffff запрещена.
- Extern — область для внешних данных (нужна для взаимодействия с ОС). Кроме того, метки в этой области оказываются «видны» при сборке программ из нескольких файлов.
Data base — область, в которую обычно раскладываются данные директивами .data. Именно там лежат переменные, объявленные массивы и прочее. Традиционно имеется зазор между началом области данных (0x10000000) и непосредственно статическими данными (0x10010000 - 0x10040000). Обычно в процессе работы программы нельзя переходить по адресам из области данных и декодировать их как инструкции.
Heap (Куча) — область данных, в которую принято помещать динамические данные. Идея в повторном использовании одних и тех же областей памяти для различных нужд. Для этого служат процедуры выделения памяти, в которых запоминается размер и адрес запрошенного фрагмента, и освобождения, в которых эти данные объявляются устаревшими (можно совсем забыть, а можно область пометить как свободную), после чего очередная процедура выделения вполне может выдать ту же самую область. Механизмы выделения/освобождения памяти (т. н. memory managment) обычно довольно непросты, и соответствующие функции предоставляет ОС. Добавление и освобождение данных в куче обычно происходит в сторону увеличения адреса.
Stack — область динамических данных особого вида, реализующая абстракцию «стек» и используемая при вызове подпрограмм и передачи им параметров. Добавление и освобождение данных в стеке обычно происходит в сторону уменьшения адреса. Бесконтрольное снятие данных со стека может привести к тому, что регистр стека начнёт указывать за пределы пользовательской памяти, поэтому (и по каким-то ещё соображениям) изначально $sp указывает не на самое «дно» стека, а существенно ниже (под 0x7ffff000). Стек и куча растут навстречу друг другу, и могут занимать друг дружкину память, лишь бы не пересекались.
KText и KData. Начиная с адреса 0x80000000 идёт область, недоступная программе пользователя. Это область кода и данных ядра. Безотносительно к тому, запущена программа под управлением ОС или «на голом железе», для исполнения кода и доступа к памяти требуется особый режим работы процессора. Чтение, запись и переход с использованием адресов ядра пользовательской программе запрещены.
- Среди прочего программного кода ядра выделяется адрес обработчика прерываний (0x80000180). По этому адресу передаётся управление при возникновении исключительной ситуации (наподобие переполнения или обращение к «не своей» памяти)
- Область MMIO служит для адресации ячеек, вообще не принадлежащих оперативной памяти. Обращение по этим адресам приведёт к взаимодействию с данными на внешних устройствах (обычно с регистрами ввода-вывода или собственной памятью устройств)
Задание: проверить, можно ли в MARS прочитать байт из раздела .text по нечётному адресу? (Почему ?)