| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
режим работы. В том числе и в целях отладки "сырого" (свеженаписанного)
программного обеспечения — от которого следует ожидать всяческих пакостей — ибо
в любой программе найдётся хотя бы одна ошибка, причем по закону всемирного
ехидства такая, чтобы нанести максимальный ущерб... И встала проблема как-то
эти самые задачи друг от дружки отделить (А главное — от обеспечивающего их
работу системного программного обеспечения.) Дабы этот ущерб хоть как-то
локализовать и минимизировать. Ну типа рассадить их все, как психов, по
отдельным комнатам с мягкими стенами...
Вот для этой цели и был придуман "диспетчер памяти", реализующий для каждой
задачи своё собственное "виртуальное" адресное пространство. А то, в котором
находится реально существующая память и внешние устройства — стали называть
"физическим". Появилась возможность расширить физическое адресное пространство
сначала до 2^18, потом до 2^22.
Как это сделано? А вот как:
Адресное пространство (пока что всё сплошь физическое) условно делится на
восемь "страниц" или "банков" по 8 Кбайт. (Слово "банк" — это больше к
собственно памяти а не к пространству, где она размещается. Ферритовый куб вот
как раз и был такого размера.) Управляющие регистры внешних устройств адресуются
так же как память и традиционно все размещаются в последнем восьмом банке.
Диспетчер памяти это такое-же как все внешнее устройство. Но сам он "врезан" в
шину адреса сразу после процессора. И занимается тем, что преобразует выданный
процессором "виртуальный" 16-и разрядный адрес в 18-и или 22-х разрядный
"физический". Делает он это следующим образом: делит адрес на номер страницы
(три старших бита) и смещение в ней (остальные 13). Далее, имея восемь
регистров (по числу страниц) берёт содержимое одного из них и складывает со
смещением со сдвигом на шесть бит. В результате чего с одной стороны как раз и
получается 22-х разрядный физический адрес. А с другой — в пространстве адресов,
издаваемых программой, образуется как-бы окошко (размером в 8 Кбайт), через
которое программа, изменяя соответствующий регистр диспетчера, может обозревать
всё четырёх-мегабайтное физическое адресное пространство, передвигая его по
нему с шагом в 2^6=64 байта.
Программы, работающие под суровой многозадачной ОС реального времени RSX-11
так и делают.
Однако, таких наборов регистров в диспетчере два. Какой именно из них
используется — указывает битик "текущего режима работы" в слове состояния
процессора. Точнее — два битика, но оба они либо 00 либо 11. Состояние 00
считается "привилегированным" — оно предназначено для операционной системы и в
нём честно выполняются абсолютно все команды, реализованные в данном процессоре.
А состояние 11 — соответственно "непривилегированное" — в нём некоторые
"привилегированные" команды ("стоп", "сброс внешних устройств" и еще парочку)
процессор выполнять отказывается — учиняет прерывание по вектору 10, как будто
таковых команд у него нет.
Подобным образом, т.е. вводя два или даже больше режимов работы, один из
которых привилегированный и в нём можно всё, а в остальных выполнение некоторых
действий запрещено — поступают во всех вычислительных системах, ориентированных
на работу под управлением операционной системы. В нелёгкой памяти ЕС-ЭВМ (ряд
"больших" машин с архитектурой содранной с IBM-360) таковых режимов аж
шестнадцать штук. Т.е под указание режима работы (он же — код выполняющейся в
данный момент задачи) задействовано четыре бита (тоже находящихся в ССП), из
которых привилегированной комбинацией является 0000. На эти биты завязана
"ключевая" защита памяти: всё адресное пространство разделено на страницы
размером по два килобайта (т.е. 2^12, а всё адресное пространство у неё — 2^24),
каждой из которых сопоставлен "ключ защиты". (Доступ к нему — парой
привилегированных команд, каковых в этой архитектуре пруд-пруди.) При любом
обращении к странице он аппаратно сравнивается с кодом текущего режима, и буде
не совпадает (и режим не 0000) — вместо обращения учиняется прерывание: типа
задача лезет не в свою память.
Как я уже говорил, у PDP-11 для каждого из двух режимов работы — свой
комплект регистров диспетчера памяти. Т.е. одновременно реализуются два
адресных пространства — одно для операционной системы, а другое — для текущей,
выполняющейся под нею задачи. Но только одной — при переключении задач эти
регистры придётся перенастраивать.
Ну так вот: некая программа, загрузившись в память первой — когда этот самый
диспетчер еще отключен (пропускает через себя адреса без всякого изменения)
настраивает один комплект (тот, который для себя) так, чтобы и вектора
прерывания и управляющие регистры внешних устройств были по-прежнему доступны.
А второй комплект — чтобы нет. Чтобы "окошко" восьмой страницы глядело не туда
где регистры устройств, а туда, где оперативная память. (Или вообще отключить
его нафиг: — к этим восьми адресным регистрам диспетчера прилагаются еще восемь
управляющих, позволяющих задавать размер окошка и его положение (т.е. в начале
оно страницы или в конце) и вообще учинять всякие чудеса.) Далее эта программа
грузит в то место памяти, на которое указывают регистры второго
(непривилегированного) комплекта, некую другую программу. После чего передаёт
ей управление учиняя следующий финт ушами: на вершину стэка кладёт стартовый
адрес оной программы и после него — слово, которое попадёт в ССП. В нём
указывается код режима = 11. После чего она выполняет возврат из прерывания -
команду RTI. (Но перед этим, разумеется, не забывает включить диспетчер
памяти.) Всё. Теперь работает вот эта вторая невесть куда загруженная программа,
но никакие внешние устройства ей недоступны. В том числе и регистры диспетчера
памяти. А т.к. её запустили как "непривилегированную", то и отключить диспетчер,
выполнив команду RESET (сброс внешних устройств) ей не удастся. (А если бы даже
и удалось, ничего хорошего из этого не получится: она же сидит невесть по каким
адресам...) И обратиться ко второй, запустившей её программе она (если та не
дура и изолировала её от себя полностью) может только устраивая командные
прерывания. (Для чего они собственно и нужны.) А еще первая программа, время от
времени получая управление по прерыванию от таймера, может через некоторое
время совсем отобрать управление у второй программы и выгрузив её на диск (или
оставив как есть — если памяти хватает) загрузить еще одну — третью программу,
и дать поработать ей...
Вот так все многозадачные операционные системы и работают. В т.ч. и UNIX.
Обычное распределение памяти, принятое например в RT-11, таково: с нулевого
адреса находятся вектора прерывания; далее, сразу вслед за ними располагается
системный стэк. (Чтобы он не налез на вектора — у старших моделей есть
специальный регистр контроля границы стэка: как содержимое R6 становится меньше
чем в нём указано — сразу прерывание.) После этого располагается код программы.
Сама операционная система помещается в самый конец оперативной памяти. Место
между нею и программой — свободно; туда по мере надобности подгружаются драйвера
устройств, или там-же может быть запущена вторая программа "фонового режима",
скомпилированная таким образом чтобы быть "перемещаемой" (т.е. все используемы в
ней адреса — относительные; в результате ей совершенно без разницы с какого
места оперативной памяти её загрузят). Она получает управление, когда программа
переднего плана чего нибудь ожидает. Например обратилась к операционной системе
за очередным байтом, введенным с терминала, а сидящий за ним пользователь ни
мычит — ни телится...
А вот UNIX придерживается другой политики. Он, как известно, размещает каждую
выполняемую программу в персональном, исключительно для неё предназначенном
адресном пространстве, в которое ни вектора прерывания ни управляющие регистры
внешних устройств не отображаются — только исключительно оперативная память. Ну
так он грузит туда содержимое выполняемого файла прямо с нулевого адреса, а стэк
помещает в самом конце адресного пространства. Место между вершиной стэка и
концом области памяти куда загружен код из файла — свободно. Под него даже
память не выделяется. (Регистры диспетчера памяти настраиваются так, что
обращение по этим адресам вызывает ошибку — прерывание по специально для этого
предназначенному вектору 250.) При переполнении стэка (что отслеживается с
помощью регистра его верхней границы) память под стэк добавляется автоматически.
А вот нижнюю границу доступной ей памяти программа может сдвинуть как вверх так
и вниз вполне сознательно — обратившись для этого к операционной системе. В
этом месте размещаются данные, количество которых на момент написания программы
неизвестно. (Как правило организованные в виде т.н. "кучи".)
Самая-самая старшая модель (та, которая у нас Э-79) имела не два а три
комплекта регистров диспетчера и соответственно три режима работы разной
привилегированности. А еще — второй комплект регистров общего назначения
процессора. Видимо чтобы при прерывании не тратить время на сохранение их
содержимого и перед выходом — на восстановление их обратно. Какой комплект
включен — указано в ССП, а оно загружается из второго слова вектора прерывания.
(Т.е. даже и делать ничего не надо.) А еще каждый комплект регистров диспетчера
памяти в двух экземплярах: один используется когда процессор лезет за командой,
а второй — когда за данными. То есть можно было включить такой режим... но все
обычно воздерживались. Ну и еще, чтобы не шарить окошком по физическому
адресному пространству (очень уж это громоздко, особенно когда всего-то и надо,
что прочитать номер системного вызова и к нему пару параметров) — вот те самые
команды обращения в "предыдущее" адресное пространство, номер которого хранится
вместе с кодом текущего в слове состояния процессора. Но по-моему эти четыре
команды тоже были привилегированные, а то ТАКАЯ дырища в защите памяти
получается...
В общем сплошной гигантизм.
=========================================================================
Приведённые выше (и ниже) описания систм команд к сожалению не полные:
как минимум для каждой из команд надо указать как она устанавливает признаки
результата в слове состояния, а я только описал это словесно, причем довольно
поверхностно.
Ну и конешно практически нету никаких описаний внешних устройств, так — по
одной штуке, чисто для примера.
Однако, я в общем то и не ставил себе целью написать справочник — люди пишут
об этом толстые серьёзные книжки... Я же всего лишь собирался немножко
проиллюстрировать (на конкретных примерах) как вообще может быть организована
система команд...
=========================================================================
Идейный потомок PDP-11 — семейство микроконтроллеров MSP-430.
Как это выглядит.
Как микросхема о двадцати или о шестидесяти четырёх или даже о ста ногах.
(Они разные бывают — их же целое семейство.) Вся вычислительная система на одном
кристалле: и процессор и ОЗУ и ПЗУ с программой (электрически
перепрограмируемое) и некоторое количество устройств: от полутора портов
ввода/вывода и одного таймера в самых простейших, до ЦАП`ов и АЦП или даже
устройства управления Ж-К индикатором в больших и навороченных. (Но USB с
Изернетами не замечено — не для того этот контроллер предназначен.) А тактовых
генераторов у него на борту аж три штуки. Только кварц подключай!
Тактовая частота по нынешним временам невысокая — до 8 МГц максимум. (Новые
пошли — там до 16.) А минимальная — любая (до 0 герц включительно). Но команду
типа регистр-регистр выполняет за один такт, регистр-память — за 2-3 такта, а
память-память — за 5 или 6. Тоесть максимум восемь миллионов регистровых
операций в секунду. Для сравнения — у самой старшей из PDP-11 было только три
(при такой же разрядности и сопоставимой системе команд — а это куда важнее
"численных" значений).
Питается эта штучка от трёх вольт — на батарейку рассчитано. Точнее от 3.7 -
самая свежая литиевая, до 1.9 — самая самая разряженная пара обычных
угольно-цинковых. И даже при 1.5 вольтах вроде-бы даже немножко работает. К
тому-же малопотребляющая: если раскочегарить на самую большую тактовую частоту
— потребление будет милиампер 35 — 40. А в самом "дежурном" режиме, когда
только часы тикают — порядка 3 или даже 1 микроампера. (У батареек саморазряд и
то больше бывает.) Шаг между ногами мелкий (порядка пол миллиметра), впрочем как
у большинства современных буржуйских микросхем — вручную припаять впринципе
можно, но трудно, а уж плату под неё нарисовать...
Как организована.
Примерно так же как и PDP-11, только у неё всё наоборот: управляющие
регистры внешних устройств расположены с нулевых адресов, а таблица векторов
прерываний — в самых старших. Из регистров процессора, счетчик команд тоже
самый первый — тот который R0. А R1 соответственно — указатель стэка.
Итак, машинка тоже со всех сторон 16-и разрядная; адресное пространство тоже
линейное, размером 2^16=64K и тоже адресуется с точностью до байта. В самом
начале размещаются регистры устройств (какие есть); потом идёт оперативная
память, как правило очень немного (например 2 Кбайта), а в конце адресного
пространства ЭППЗУ ("флеш-память") разного размера (от 2 до 60 Кбайт), в самом
самом конце которой — таблица векторов из 16 слов. Самый последний из векторов -
стартовый адрес программы — по нему передаётся управление сразу после включения
питания. Т.е. сразу после подачи питания устраивается сброс — вот по сбросу
содержимое этого вектора и записывается в счетчик команд.
Да кстати — в отличии от PDP-11 вектор здесь — одно слово — то, что
помещается в счетчик команд. Слово состояния (которое здесь разумеется тоже
есть) при прерывании просто обнуляется.
Машинка изначально задумана как контроллер: вектора прерывания размещаются
в ПЗУ, которое далеко не во всех моделях перепрограммируемое. Тоесть
предполагается что в период эксплуатации программа изменяться не будет.
Между ОЗУ и ЭППЗУ есть еще кусочек просто ПЗУ (1 Кбайт), где еще при
изготовлении прописана программа-загрузчик, с помощью которой это самое ЭППЗУ и
программируется. Эта программа получает управление в случае учинения сброса не
обычным а неким особо хитрым образом (что должен проделать внешний загрузчик),
| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |