Компьютер сноба-потребителя за 2013 год, часть первая.
реклама
Компьютер сноба-потребителя за 2013 год
Часть 1.
Как АМД и Интел улучшают процессоры.
"Джон Ланкастер в одиночку, преимущественно ночью,
Чем-то щелкал, в чем был спрятан инфракрасный обьектив.
А потом в нормальном свете представало в черном цвете
То, что ценим мы и любим, чем гордится коллектив..."
Высоцкий. Пародия на плохого сноба-потребителя.
Продолжим рассказывать суровую правду о том, как АМД и Интел создают новую технику. За пару прошедших лет мы уже рассматривали AM3+ процессоры АМД с маловходовым L1 кэшом только для чтения; AM3/AM3+ процессоры АМД с муляжом двухканальности шины памяти (вероятно что-то внутри AM3+ процессора АМД не дает разгонять "полутораканальность" имеющуюся в стоке, возможно порогом является пропускная способность шины гипертранспорт); шину USB производства Интел, которая вместо передачи данных занята защитой авторских прав в реальном времени. Всего и не упомнишь.
А сегодня мы поговорим о том, как оба производителя модернизируют схему адресации в своих процессорах. Особенно это интересно в свете режима работы процессора amd64, который также был принят Интел.
Содержание.
1. Основательность проекта процессора.
2. Сегменты.
3. MMU Интел.
Основательность проекта процессора.
Когда я был еще совсем-совсем мал, я думал о том, как здорово было бы иметь ZX-спектрум, у которого был бы огромный 1 мегабайт ОЗУ, какие бы это открыло возможности для программирования! Потом я уже думал о том, как здорово иметь MS-DOS с 4 гигабайтами памяти и прямой плоской 32-битной адресацией, сколько всего можно сделать! Когда я подрос, я обратил внимание, что плоская модель памяти это не такая уж и веселая штука, как может показаться на первый взгляд. Проблемы у плоской модели памяти начинаются тогда, когда с вас начинают требовать безотказную работу операционной системы: указатели указывающие непонятно куда (некоторые, в порыве бессильной ярости, совсем уничтожали указатели в языках программирования); полный доступ к памяти через логические границы модулей и даже приложений - все это делало ошибку приложения катастрофой для всей системы. Обратим внимание, что уже в 90-х годах прошлого века на практике ZX-спектрум с памятью расширенной до 20 или 32 бит не показывал ожидаемых от него чудес.
Другими словами, теоретически безотказную работу операционной системы может обеспечить только производитель процессора, только аппаратно. Те, кто родился много раньше меня, поняли эту мысль тоже раньше, поэтому уже в начале 80-х годов прошлого века появился 286 процессор с весьма развитой (по сравнению с 86) системой защиты памяти. Я предполагаю, что инженеры Интел в данном случае не стали изобретать велосипед и создали схему защиты памяти модифицировав для микропроцессора готовую схему защиты с более крупных вычислительных систем.
Почему мы можем говорить об адаптации, а не о первом варианте? Слишком "плотно" скомпонованы "части" проекта, каждая часть выполняет несколько функций и является необходимой составляющей для других частей, отсутствуют "пробные" независимые части, которые можно комбинировать (даже неожиданным для проекта образом), чтобы получать новую функциональность, которая не была ясна на момент проектирования. Задание на проект было явно основано на практическом опыте программирования (невозможно написать книгу "справочник по паттернам программирования" не использовав эти паттерны в нескольких вариациях на реальных программах), поскольку проект не только включает в себя специфичные механизмы, вообразить которые в готовом виде проектировщику процессора было бы крайне трудно, но и является достаточно основательным, оставляя возможности для будущих расширений и модернизаций. Механизмы защиты настолько специфичные, что если у вас есть опыт работы с процессорами, то понять их назначение "снизу вверх" (восстановить их назначение зная только сами механизмы) будет почти невозможно, т.е. эти механизмы ясны только "сверху вниз", т.е. только тогда, когда у вас есть программа, нуждающаяся в механизмах защиты и вы смотрите как их можно реализовать на данном процессоре.
Еще раз, проект защиты в 286 основательный, он не просто решает сиюминутную проблему, но и закладывает основы для будущей модернизации в рамках общей схемы проекта. Ломать схему проекта при модернизации конечно можно, но для этого должны быть весьма веские причины и как минимум нужен новый проект, обладающий как минимум прежними характеристиками, но реализованными иначе.
Я не буду сильно комментировать 64 битный режим АМД, поскольку у меня нет опыта работы с реальными приложениями, которые не смогут уместиться в 4Гигабайта, поэтому я этот режим просто не сильно использую, но на первый взгляд АМД сломала схему проекта Интел не предложив ничего основательного взамен. АМД построила режим процессора, решающий сиюминутные проблемы - "процессор для Windows 95", и когда через 5 лет "Windows 95" исчез, с процессором возникли проблемы, поскольку в общем виде задача защиты у АМД не решается, производители процессора стали затыкать дыры и т.д. Раздражающим является только тот факт, что этот результат расширения ZX-спектрума до 64 бит был известен уже в 90-х годах прошлого века во времена МС-ДОС.
Предположим, в АМД таким странным образом хотели сделать первый шаг, чтобы перейти к более гармоничной и многобитовой системе, типа без архаизмов. В архаизмы попали 8,16 битный код и конечно же сегменты (!кто-то воплотил мечту своего детства - чтобы никаких сегментов).
Если спросить "а нужно ли отказаться от совместимости со старой схемой системы при модернизации процессора", я не глядя готов ответить "нет". Этот ответ тоже результат многолетнего опыта компьютерной индустрии - процессор всегда обходится намного дешевле, чем модернизация программ. При современном развитии технологии встроить "целый AT-386 компьютер" в процессор и чип стоит несколько центов, а вот создать для произвольной системы програмно-аппаратный эмулятор реального времени, который будет работать под управлением ОС в режиме amd64, даже для эмуляции того же "целого AT-386 компьютера", пока совершенно невозможно ни за какие деньги, не хватает мощности для эмуляции, не говоря уже об энерго-эффективности выполнения программ в режиме эмуляции.
Нужны старые программы или нет это отдельный вопрос, который точно не должны решать производители процессоров, но главное, что есть готовая схема работы системы, под которую можно создавать и новые программы. Отказ от совместимости означает полный переход программ на новую схему. По большому счету я не вижу никакой нужды менять 8,16 битную команду "mov ax,0" на что-то более современное, делающее то же самое принципиально несовместимым образом.
Но самое интересное то, что режим защиты Интел, появившийся в 286, позволяет модернизировать процессор так, что у вас в одной системе одновременно и с аппаратным ускорением смогут выполняться как "устаревшие" 8,16,32 битные программы, так и новые 64 битные программы, даже с абсолютно несовместимой кодировкой команд, например аналогичной моторолле. Более того, развитие 64 битного режима могло бы быть совершенно традиционным, когда в 8,16,32 битной системе появляются 64 битные регистры и операции над ними, а также появляется 64 битная адресация. Ничего подобного в amd64 мы не наблюдаем.
Сравнив проекты 286 и amd64 перейдем к процессорам Интел, которая уже отметилась шиной USB, что нам приготовит Интел на этот раз?
Сегменты.
Встречайте, Интел приготовила нам сегменты.
Итак, начиная с 86 процессоры Интел имеют сегменты. На деле это значит, что процессоры аппаратно поддерживают модульную схему создания программ, которая к тому времени уже была сформирована. Модульная схема программы означает, что у вас не сплошное плоское месиво из кода и данных, а все разбито на отдельные изолированные друг от друга части (модули), взаимодействие модулей не хаотично, а происходит через ограниченный интерфейс. Для того, чтобы защитить память модулей друг от друга каждый модуль должен иметь свое собственное адресное пространство (как раз оно и называется сегментами), для хранения информации об адресном пространстве модуля используются сегментные регистры.
Таким образом ясно, что сегменты совершенно необходимы даже в пределах одной задачи, исключить сегменты значит разрушить аппаратную защиту памяти модулей уже внутри задачи, не дожидаясь многозадачности. Но почему же сегменты Интел вызывали так много отрицательных эмоций?
а)
Во-первых, дело было в размере сегмента. К тому времени для микропроцессоров было традиционно разделять обычные регистры (размер равен разрядности ALU) и адресные регистры (адресные регистровые пары, размер равен разрядности адресации). Над адресными регистрами допускалась лишь ограниченная адресная арифметика, но они могли использоваться для косвенной адресации и имели доступ ко всей доступной памяти. Процессор 86 был с 20 битной адресацией, с 16 битными регистрами и 16 битным ALU; 86 также имел адресные регистровые пары сегментный_регистр:смещение с ограниченным функционалом операций над ними.
Но главное отличие 86 от других в том, что пара сегмент:смещение рассматривалась не как физический адрес, а как адрес виртуальной памяти модуля. Разделяются внутримодульный адрес (смещение) и межмодульный адрес (пара сегмент:смещение). Внутримодульный адрес это обычный традиционный "указатель С", а межмодульный адрес должен программно транспортироваться между модулями специально, в общем случае средствами протокола IPC.
Как плюс такой системы, модуль мог быть загружен по любому адресу сегмента, адрес сегмента это независимая часть адреса и внутри модуля не должна использоваться; если все модули разделены в физической памяти через 64К, то это дает некоторую межмодульную защиту памяти "бесплатно", без всяких MMU. Как минус, кто-то в Интел решил, что для 16 битного модуля непрерывного участка памяти размером 64К будет вполне достаточно всегда. Это была ошибка.
Дело в том, что памяти реально 20 бит, а массивы общих межмодульных данных могут иметь размер более 64К в огромном числе "обычных" ситуаций. Например, VGA память в режиме 320х240 256 цветов имеет размер 75K; программный стек задачи, который общий для всех модулей, может легко превысить 64К при рекурсии или неожиданных событиях времени выполнения, т.е. для 16-бит модуля непрерывного участка памяти в 64К обычно действительно хватает, но есть структуры данных, которые не вписываются в 16 бит адрес.
Получается что память есть, но ее не съесть. Отсутствие внутрисегметной регистровой пары типа [AX:BX] для полной 20 бит адресации относительно сегмента вызывало много непонимания, ее эмуляция через [ES:BX] трудоемка. Эмуляция большого стека сменой стекового фрейма при рекурсивных вызовах также не внушало никакого оптимизма, дрелью забивали гвозди.
б)
Во-вторых, в 286 процессоре четырех сегментных регистров оказалось слишком мало! По сути четырех регистров мало уже в 86 процессоре, в котором есть две сущности: процесс и модуль, например, CS для кода модуля, DS для данных модуля, SS для стека модуля. Как получить доступ к межмодульным данным процесса, межмодульному стеку процесса и множеству прочих межмодульных указателей, если свободен только регистр ES?
Строковые команды также использовали сегментные регистры трудно переопределяемым образом.
В многозадачном окружении 286 наличие только четырех сегментных регистров сразу бьет программу больно, поскольку там уже три сущности: процесс, нить и модуль. В совокупности с проблемой номер три, которая будет далее, это часто приводит к тому, что 16 битная программа с поддержкой нити превращается в беспрерывный набор перегрузок регистра ES. Фактически регистр ES становится единственным рабочим регистром доступа к данным, что приводит к крайне низкой производительности, особенно учитывая что такое пререгрузка ES при наличии дескрипторной таблицы.
Нормальное число сегментных регистров в многозадачном окружении - 9 (код, константы процесса, данные процесса, константы нити, данные нити, стек, рабочий регистр, два регистра для межмодульных пересылок память-память). Учитывая склонность к "два в степени" это 16. Я затрудняюсь придумать ситуацию, когда три сущности (процесс, нить и модуль) потребуют более 16 сегментных регистров, половина из которых перегружается один раз, только при смене нити.
в)
В-третьих, дело было в том, что указание сегментного регистра 86 при сегментной адресации было только непосредственным, в виде префикса.
Интел всегда поражала воображение регистрами своих процессоров. По сути у всех первых процессоров Интел, типа 86, как таковых регистров общего назначения (как СОЗУ) нет совсем, что теоретически благотворно для последовательной многозадачности на гигантских процессорах, которая не любит большой локальный контекст, но в случае Интел регистры строго специализированны и только хранят операнды команд. Если бы были команды, которым были бы не нужны даже такие регистры, у процессоров Интел регистров не было бы совсем. Хотя эти регистры и можно использовать чтобы просто складывать в них данные, они явно не предназначены для такой цели.
В данном случае типа адресации сегментных регистров, предположительно проектировщики системы команд процессора Интел (и это не в последний раз) не смогли получить спецификации от программистов Интел, поэтому (со своей точки зрения проектировщика процессора) приравняли сегментный регистр к обычному регистру, который также кодируется непосредственно.
Это совершенно невероятное решение в свете "виртуализации" адресного пространства модуля уже в 86 процессоре. Невероятность в том, что "виртуальность" адреса модуля подразумевает при входе в модуль разовую загрузку контекста модуля (т.е. его сегментных регистров), который после не перегружается. Отсутствие перегрузки стирает отличие модульной программы от плоской.
Проблема в том, что с появлением модуля абстрактная машина программы предполагает набор независимых адресных пространств. Это несовместимо с "абстрактной машиной С", которая работает с плоской моделью памяти, появилась "абстрактная машина С" в 70-х годах (на десятилетия раньше) и применялась на процессорах с памятью размером в несколько десятков килобайт и битами "суперюзер" и "защита от выполнения". Несовместимость модульной программы с "абстрактной машиной С" проявляется в том, что в модульной программе отсутствует такая сущность как "указатель вообще". Все указатели (и расположение статических данных) типизированы сегментом (памятью), также как статический объект типизирован своей структурой.
Типизированный указатель приводит к тому, что функция a(p1,p2,p3) принимающая указатели p1,p2,p3 как параметры, согласно системе команд Интел должна будет генерировать разный код, в котором сегмент параметра указан непосредственно. Ясно, что либо комбинаторно будет расти число инстанцирований функций, которые отличаются только префиксами, либо каждый указатель будет перегружать свободный сегметный регистр (в 286 им будет скорее всего ES), т.е. все указатели станут межмодульными. И то и другое никуда не годится и вызывает ярость программистов.
Другими словами, в модульной программе ключевое значение имеет тот факт, что программист точно знает память, в которой расположен объект, это забота языка программирования; но одновременно с этим должно быть и противоположное, указатели на разные типы памяти должны эффективно обрабатываться одним кодом времени выполнения, это забота процессора.
Таким образом, шаблон кода времени выполнения в модульной программы должен получать типизированный внутримодульный указатель, т.е. "короткий" указатель, где вместе со значением адреса во время выполнения хранится значение сегментного регистра в виде его индекса.
Давайте поможем компании Интел улучшить систему адресации их процессоров для поддержки модульных программ. Для этого нам понадобится процессор Интел 286 или старше, также мы будем рассматривать защищенный режим.
1)
Посмотрим на MSW процессора 286 и возьмем первый свободный бит. Этот бит будет управлять интерпретацией загрузки 16 битных данных в/из памяти. Если бит активен, то 16 бит данные могут быть прочитаны/записаны только по границе слова, таким образом не используется младший бит адреса, который в состоянии 0 адресует DS, а в состоянии 1 адресует SS. Это даст поддержку нитей, константы из других сегментов должны будут копироваться в стек перед использованием в шаблонных функциях. Возможны вариации с расположением бита индекса для упрощения адресной арифметики. Этот вариант подходит и для реального режима 86, если задействовать неиспользуемый бит регистра флагов 86. Компилятор должен будет выравнивать данные по границе слова (на деле многие компиляторы уже так и делают по иным соображениям).
2)
Посмотрим на MSW процессора 286 и возьмем второй свободный бит. Этот бит будет управлять интерпретацией селектора, указывающего на нулевой элемент GDT/LDT. Если бит активен, то битовое поле, состоящее из трех бит {TI,RPL} селектора, указывающего на нулевой элемент GDT/LDT, будут кодировать NULL или индексировать сегментный регистр. В этом случае внутримодульный указатель представляет собой пару индекс:смещение. Возможны варианты:
2.а)
{TI,RPL}/значение
000/NULL
001/CS
010/DS
011/SS
2.б)
В качестве альтернативы, под индексы сегментных регистров можно выделить селекторы, указывающие на последние элементы GDT/LDT.
Для 32 битного процессора эти биты MSW аналогично расширяют свое действие на 32 битные операции. Биты указателя переопределяют сегменты по умолчанию. В случае конфликта явного префикса и явного индексного бита надо генерировать исключение.
При таких модификациях программа работает неотличимо от плоской модели без сегментов или реального режима работы процессора, при том что обеспечивает аппаратную защиту памяти между модулями, нитями и процессами.
MMU Интел.
Конечно, много что можно рассказать о процессорах Интел и АМД, например, можно поговорить о поддержке этими процессорами в многозадачном окружении трех сущностей: процесса, нити и модуля.
Рассмотрим последовательную многозадачность, когда одно ядро последовательно выполняет разные нити. Для поддержки многозадачности процессоры Интел описывают выполняющийся контекст с помощью TSS. Причем аппаратное преключение контекста распознает в общем две таблицы GDT и LDT, так что LDT перегружается автоматически. Попытка разделить две таблицы дескрипторов между четырьмя сущностями (глобальный контекст + наши три) вызывает много вопросов к авторам процессора. Хотелось бы их поймать и посмотреть как они это сделают на практике, нет, не то чтобы очень хотелось, просто чтобы поглазеть.
Другими словами, еще раз окажем помощь Интел в развитии системы адресации их процессоров для поддержки последовательной многозадачности: таблиц надо четыре, глобальная GDT + три локальных LDT которые перегружаются с TSS. Одна локальная это общие адреса процесса, вторая локальная это общие адреса нити, третья это адреса модуля (чтобы закрыть горизонтальные связи в переделах уровня привилегий так, что у них может быть общий стек нити). Основная рабочая таблица это адреса модуля.
Предложите ваши варианты как это все можно сделать с одной локальной таблицей обещаным Интел способом "одной команды CPU" и "прямой аппаратной адресацией". Все это дело еще может осложниться ограниченностью числа аппаратных элементов дескрипторной таблицы, элементы которой надо будет виртуализировать программно.
Продолжение следует...
Обсуждение этой заметки в форуме.
Создано: 10.10.13
Последний раз отредактировано: 10.10.13
реклама
Лента материалов
Соблюдение Правил конференции строго обязательно!
Флуд, флейм и оффтоп преследуются по всей строгости закона!
Комментарии, содержащие оскорбления, нецензурные выражения (в т.ч. замаскированный мат), экстремистские высказывания, рекламу и спам, удаляются независимо от содержимого, а к их авторам могут применяться меры вплоть до запрета написания комментариев и, в случае написания комментария через социальные сети, жалобы в администрацию данной сети.
Сейчас обсуждают