Иван Савватеев @SIISII
Микроконтроллеры, цифровая электроника, ОС…
Information
- Rating
- 1,786-th
- Location
- Солнечногорск, Москва и Московская обл., Россия
- Date of birth
- Registered
- Activity
Specialization
Embedded Software Engineer
Lead
Микроконтроллеры, цифровая электроника, ОС…
Мне кажется, автор просто что-то там недопонял. Недостаточное понимание видно, например, из "описания" MMU -- ведь принципиально и на ARM, и на IA-32, и почти в любых других архитектурах MMU работают одинаково -- точно так же, как ещё в Системе 370 (несколько уровней таблиц переадресации в памяти, базовый адрес таблицы верхнего уровня -- в некоем специальном регистре, а для ускорения преобразования -- TLB).
Кстати говоря, для реализации атомарных операций вроде И, ИЛИ шина AXI имеет соответствующие навороты, чтобы операции эти фактически выполнялись контроллером памяти, а не процессором. Откопал, когда смотрел сию шину (использую в своих мегапроектах на ПЛИС, поскольку она используется для подключения контроллера памяти).
В общем, похоже, от изначальных RISCов теперь не осталось ничего. Ну а микрокод высокой производительности ни разу не противоречит, как и я говорил, и всякие там Интелы на практике доказывали. Как по мне, бороться с ним не надо -- он даёт гибкость (медленная, но компактная реализация тоже может быть ценной -- а использование микропрограммного управления даёт возможность легко подстраиваться под потребности). В общем, исходить, смотря по задаче.
Про RISC-V не знал -- по большому счёту, я с ним не знаком, хотя всё собираюсь заняться. В известных же мне типа RISCах подобные операции (шифрование, например) выполняются железными блоками, которые расположены отдельно от процессора и, по сути, выступают в роли внешних устройств, т.е. частью соответствующей архитектуры не являются. Таковы, например, многие микроконтроллеры ARM: сама система команд никаких тебе шифраций или, скажем, преобразования UTF-8 в UTF-32 или обратно не содержит.
Ну и опять повторюсь, что от изначальной идеи RISC (а она -- именно что небольшое количество простых команд) отказались, считай, полностью -- она оказалась неконкурентоспособной с CISCами, как только совершенствование технологий дало возможность конвейерный (а позже и суперскалярный) CISC впихнуть на один кристалл. Так что лично для меня сейчас RISC -- это архитектура, не имеющая команд обработки данных прямо в памяти, ибо это, по сути, единственное, что нынешние RISCи отличает от нынешних CISCов, хотя и это различие, похоже, постепенно уходит (ведь, полагаю, RISC-V шифрует данные в памяти? Регистров у него не так уж много, и большой объём в них не впихнуть. Надо как-нибудь изучить сей вопрос...)
Если говорить про архитектуры (в первом приближении -- про системы команд), то никаким гибридом i486 не является -- самый натуральный (и плохой) CISC.
Если же говорить про внутреннее устройство, то, как я уже написал, конвейеризация появилась задолго до появления RISC, и одно с другим не связано от слова "совсем".
Ну, это именно ошибка в чистом виде, а не "фишка" (особенность микроархитектуры), которая превращается в уязвимость.
Строго говоря, неверны все три пункта.
Количество потребных транзисторов со сложностью системы команд связано отнюдь не линейным образом. В частности, сложность исполнительных блоков (условно говоря, АЛУ) для выполнения одних и тех же операций вообще никак не зависит от кодирования команд как такового; нет никакой разницы и в конструкциях кэшей, TLB, регистровых файлов и т.д. и т.п.
С декодированием тоже далеко не всё однозначно. Когда автор статьи пишет про длину команды от 1 до 15 байт, он, несомненно, имеет в виду IA-32 aka x86. Эта кодировка не самая длинная (у VAX-11, если память не изменяет, длина команды могла достигать вообще 36 байт), но одна из самых бредовых и сложных для декодирования -- в первую очередь, из-за наличия префиксов, ни количество, ни порядок которых никак архитектурой не ограничены. Однако CISC отнюдь не обязан быть столь безумным. Скажем, такой классический CISC, как IBM System/360 (и все её потомки, включая современную z/Architecture), имеет длину команды 2, 4 или 6 байт, причём длина однозначно устанавливается анализом двух старших битов первого байта -- т.е. даже проще, чем, скажем, для команд набора Thumb-2 архитектуры ARM (там всего две длины -- 2 или 4 байта, -- но анализ длины сложнее).
Насчёт памяти написана вообще полная ерунда -- CISC как раз требует меньше памяти, поскольку в одной команде может закодировать куда более сложную операцию. Сколько места займёт подпрограмма шифрования AES на ARM или RISC-V? Ну а на мэйнфреймах z/Architecture это делается одной командой. А если необходима десятичная арифметика? (а она необходима для финансового сектора -- недаром соответствующие вещи есть, например, в Жабе) На большинстве архитектур, включая IA-32, будут пляски с бубном, чтобы добиться того же результата, используя целочисленную двоичную арифметику, ну а мэйнфреймы ещё с Системы 360 имеют и команды десятичной арифметики. Да, и операция пересылки строк на них тоже используется весьма и весьма часто -- пересылать ведь не только строки в узком смысле приходится, а произвольные блоки данных.
(Замечу, что есть по крайней мере одно типа исследование, которое "доказывает", что плотность кода CISC ниже, чем RISC: но, во-первых, сами авторы там между строк замечают, что, фактически, их сравнение неправомерно, поскольку сравнивают современные 64-разрядные архитектуры ARMv8 и RISC-V с древними CISCами, где 64-разрядные расширения были добавлены много позже -- а соответственно, все короткие коды операций уже попросту были заняты; а во-вторых, они начисто игнорируют случаи, когда сложные операции выполняются на CISCах одной-двумя командами, а на RISCах требуют подпрограмм значительного размера -- т.е., попросту говоря, подбирают тесты под требуемые результаты)
Усложнение декодера -- тоже далеко не факт. В частности, декодер для системы команд Thumb-2 будет сложней, чем даже для современных мэйнфреймов z/Architecture, не говоря о классической Системе 360
Усложнение управляющей логики, о чём автор почему-то не говорит, -- чуть ли не единственный пункт, где можно было бы согласиться, но и здесь не всё просто. Многие CISCи (а с 1970-х годов -- пожалуй, все или почти все) используют не чисто схемное, а микропрограммное или смешанное микропрограммно-аппаратное управление, что многократно упрощает конструкцию управляющей части процессора (память микропрограмм многократно проще и дешевле, чем управляющая логика). Впервые микропрограммы были использованы на младших и средних моделях Системы 360; старшие модели делались со схемным управлением (и были конвейерными) -- разработчики опасались, что микропрограммное управление будет медленнее. Но, как оказалось, оно может быть столь же быстрым: это зависит от особенностей микроархитектуры; поэтому, набив руку, его стали использовать и в топовых моделях. Скажем, в младших моделях (IBM System360/30 и наша ЕС-1020) АЛУ было 8-разрядным, а соответственно, простейшая 32-разрядная операция требовала для своего выполнения четыре микрокоманды только для собственно вычислений -- не говоря о всяких других потребных действиях; понятно, что это было очень медленно -- но требовало очень мало аппаратуры, почему, собственно, и использовалось. Однако, если не экономить оборудование для выборки команд и для собственно выполнения операций, разницы в скорости с машиной с чисто схемным управлением не будет, а разница в объёме аппаратуры окажется весьма значительной. Скажем, наша ЕС-1130 при заполненном конвейере выполняла простые команды регистр-регистр и регистр-память (!) за один такт, но была микропрограммной, просто вся микропрограмма для этих простых команд состояла из единственной микрокоманды (а вот выборка команды, декодирование, вычисление адресов операндов и их подготовка в ней были сделаны чисто аппаратно; микропрограмма управляла лишь ходом выполнения собственно обработки данных -- была, как я уже сказал, элементарнейшей для простейших операций, но сложной для сложных). Примерно ту же идею, только в значительно более сложном исполнении (в первую очередь, из-за суперскалярности и внеочередного исполнения) используют и современные CISC-процессоры (что у интеловских процессоров есть микрокод, наверное, все знают).
В 1980-х RISC-микропроцессоры (подчёркиваю микро) показывали более высокую производительность над CISC-микропроцессорами за счёт того, что благодаря очень малому числу команд (первый из них насчитывал, насколько помню, всего 31 команду) и другим упрощениям его можно было сделать конвейерным, но всё равно впихнуть на один кристалл; для CISC-процессора на тот момент это было невозможно, поэтому приходилось выбирать: либо медленная микропрограммная реализация на одном кристалле, либо быстрая, но на куче микросхем (в IA-32 первым конвейерным стал 80486; в мэйнфреймах конвейерными давно уже были все модели, а некоторые -- и суперскалярными, но они достаточно долго оставались многокристальными). Однако, если рассматривать все процессоры, а не только микро, RISC по скорости первыми отнюдь не были -- но далеко не каждый может позволить себе поставить мэйнфрейм или, тем более, какой-нибудь Cray с охлаждением жидким азотом (и хорошо, если азотом). Но в 1990-х, когда количество транзисторов на кристалле значительно выросло, конвейерные CISC тоже стали однокристальными -- и догнали (а нередко и перегнали) по скорости RISC. Да и сами современные RISC, прямо скажем, зачастую мало чем отличаются от CISC: имеют сотни, а то и тысячи команд, включают ряд достаточно сложных операций и т.д. Изначальная идея RISC (напомню, у первого из них была 31 команда, и среди них не было даже умножения и деления -- они сложные) оказалась несовместима с высокой производительностью, поэтому от неё отошли; фактически единственное, что осталось -- это отсутствие команд обработки данных вида "регистр-память" или "память-память" -- вся обработка выполняется исключительно в регистрах.
Более того, и конвейер, и суперскалярность появились ещё на машинах 1960-х годов. У нас первой конвейерной машиной была БЭСМ-6 (1967 год), у американцев всё это появилось ещё раньше.
А сколько нафиг пошло систем 60-70-х годов...
Проблема в том, что высокой производительности не добиться, если игнорировать технические аспекты. Правда, требовать от художников понимания, как работает железо, -- это перебор, но в компаниях-разработчиках должны быть люди, которые это понимают и при необходимости могут оптимизировать, что там художники наваяли, грубо говоря.
Сленг годится для устного общения между коллегами, в статьях же лучше придерживаться нормального литературного языка. Впрочем, Ваше право как автора выбирать те выражения, какие Вам нравятся. Но тогда непонятно, почему в одном месте Вы пишете "вершинный буфер", а в другом -- "вертексный буффер".
Ну а ошибки в русском языке в любом случае имеют место быть, и они никакой текст не красят.
А всегда писать нормальным русским языком -- "вершинный буфер" -- никак нельзя? Вообще, грамотность речи где-то в районе двойки, да и с технической точки зрения определённые недостатки есть. Например:
Во-первых, графический API вызывает не "CPU", т. е. центральный процессор, а программа, желающая что-либо вывести посредством этого API. Вот выполняется она на центральном процессоре, это да -- но вызывает она, а не процессор, он просто выполняет команду за командой, и вызов функций API для него ничем не отличается от любой другой работы.
Драйвер (пусть будет так, хотя там целый комплекс программ, и драйвер собственно железки -- лишь одна из частей) тоже работает на центральном процессоре, но формирует не только "машинные команды" (причём графического процессора), но и кучу дополнительных вещей, необходимых для установки состояния графического конвейера и т.д. и т.п.
Наконец, копирование из системной памяти в видеопамять имеет место не всегда (иногда видеопамяти не хватает, и в неё загружаются лишь наиболее критические вещи, а остальное по мере необходимости берётся прямо из системной памяти, а иногда -- например, на ноутбуках со слабой графикой -- видеопамяти как таковой просто нет), да и к типу шины оно ни разу не привязано. В частности, графический процессор, расположенный на одном кристалле с центральными процессорами архитектуры ARM, почти наверняка будет связан шиной AXI -- но это играет роль лишь для разработчиков железа и, в какой-то мере, для разработчиков операционных систем и драйверов (какие механизмы PnP используются и т.д.), но не для собственно "графических" программистов.
Не обязательно от кластера №2, положение корневого каталога (номер его первого кластера) в FAT32 указывается полем BPB_RootClus.
Вообще, процесс загрузки сильно сложней -- в частности, она может производиться не только с дисков.
Угу, размер сектора не обязательно равен 512 байтам. А вот насчёт сигнатуры 55AA сказано неверно: она находится не в последних двух байтах первого сектора, а в байтах по смещениям 510 и 511 от начала первого сектора. Т.е. если сектор имеет размер 4 килобайта, весь загрузчик, включая эту сигнатуру, будет занимать его первые 512 байт, а остальные 3,5 килобайта игнорируются.
Вообще, в общем случае, первичный загрузчик (тот, что загружается кодом BIOS) ищет и загружает вторичный загрузчик, а уже вторичный что-то там делает для загрузки ОС.
Насчёт выравнивания стека уже сказали (хотя я, честно говоря, не помню, является ли это требованием для IA-32 aka x86). А вот SS -- это не регистр конца стека, это регистр сегмента стека. Его содержимое совместно с SP и формирует физический адрес памяти, где находится текущая вершина стека.
Вообще-то, BIOS, кроме самых ранних версий, успешно работает и с LBA, только номера функций другие. И они (эти функции), кажется, не умеют работать с дискетами -- только с жёсткими дисками.
В FAT12 и FAT16 -- да, в FAT32 -- нет.
Неверно. Нулевой элемент FAT содержит в своём первом байте значение, хранящееся в поле BPB_Media; остальные его биты всегда установлены.
Первый элемент FAT12 всегда содержит значение, отмечающее конец цепочки кластеров файла. В FAT16 и FAT32 изначально это тоже признак конца цепочки кластеров, однако биты 15:14 в FAT16 и 27:26 в FAT32 имеют специальное назначение:
15/27 — если установлен, том является «чистым»; если сброшен, том является «грязным», т. е. он не был должным образом демонтирован драйвером перед завершением его использования, а соответственно, информация на нём может быть повреждена;
14/26 — если установлен, ошибок ввода-вывода при работе с томом не возникало; если сброшен, обращение к некоторым секторам раздела вызывало ошибки ввода-вывода.
Небольшая придирка:
Новое поколение z/Architecture; того, что Вы написали, в природе не существует, ну а архитектура ESA/390 (временами называемая S/390) закончилась в 2000-м году, будучи сменённой как раз z/Architecture (последняя -- 64-разрядная, тогда как все предыдущие архитектуры, начиная с Системы 360, -- 32-разрядные, но совместимость на уровне прикладного кода сохраняется).
Управляющая логика тоже далеко не всегда требует дублирования. Скажем, на основе кода команды вырабатывается энное количество управляющих сигналов плюс 1-2 контрольных -- а дальше это где-то проверяется на чётность.
С обнаружением проблем в суперскалярном проце тоже особых проблем нет: чем занимается сдохший узел в каждый момент времени, известно. Есно, усложнение будет, но отнюдь не в 100500 раз (а по площади современных кристаллов -- так вообще мизер, ведь там бОльшая часть площади -- это разного рода кэши и буферы, которые контролируются элементарно).
Я написал про регистры вообще, а не конкретно про регистры общего назначения. Очевидно, что к числу регистров PSW тоже относится -- как относятся и регистры с плавающей запятой, и появившиеся в Системе 370 управляющие регистры, регистр префикса, регистры средств отсчёта времени, и т.д. и т.п.
Зачастую достаточно обнаружить сбой, схем для чего требуется не очень много по объёму (реальное дублирование требуется для АЛУ, но их площадь на современных кристаллах незначительна -- это не машины 1960-х, где АЛУ могло по объёму аппаратуры занимать чуть ли не половину всего процессора). При обнаружении сбоя происходит прерывание от схем контроля (имелось ещё в Системе 360 -- собственно, все механизмы уже тогда были заложены, затем лишь шло их совершенствование), при котором в память записывается, в т.ч., содержимое всех программно доступных регистров процессора вместе с индикацией достоверности их содержимого. В многопроцессорной системе (а современные, понятное дело, все такие) сигнал сбоя одного процессора может передаваться другим процессорам, что при соответствующей программной поддержке позволяет "подхватить" задачу, выполнявшуюся на вышедшем из строя процессоре.
В общем, достаточно обеспечить высокую (желательно 100%) вероятность обнаружения сбоя и сохранность исходного содержимого регистров -- тогда продолжение работы на другом процессоре обеспечивается без проблем.
Плюс, ещё в OS/360 был механизм контрольных точек, позволяющий при нужде перезапустить приложение не с самого начала, а с какого-то промежуточного момента -- помогает, если продолжить выполнение прямо с точки сбоя невозможно (например, утрачено достоверное содержимое регистров).
Замечу, что в ассемблере для Системы 360 (а её продажи начались в 1965-м) был COPY = INCLUDE и CSECT = SEGMENT, если сравнивать с интеловско/мелкомягким синтаксисом. Вот PROC не было, да -- и он не нужен для ассемблера от слова "вообще". Ассемблерный текст должен 1:1 соответствовать машинным командам, которые будут сгенерированы, а соответственно, никаких автоматических прологов-эпилогов функций быть не должно (тем более, если говорить про общий случай, то в ассемблерных подпрограммах прологи-эпилоги часто могут быть "нестандартными", у одной подпрограммы может быть несколько точек входа и т.д. и т.п.). Если нужно автоматизировать повторяющиеся последовательности кода -- для этого есть макрокоманды.
Нет, чтоб увидеть, как код на исходном языке, будь то ассемблер, С/С++ или иной традиционно компилируемый, превращается в выполнимую программу (в ехешник, грубо говоря). Чтобы пришло понимание, что просто перевести мнемоники в единички-нолики мало, нужно (в общем случае) связать кучу модулей вместе, назначив секциям и их частям адреса, какую роль играет скрипт компоновщика, ну и всё такое. Без этого нет полного понимания, как программа готовится к выполнению.
Система сборки же -- это отдельный вопрос, на первых порах она не нужна, достаточно ручками запускать транслятор и компоновщик.
Как по мне, при изучении ассемблера на первых порах надо именно что вручную вызывать и транслятор ассемблера, и компоновщик: чтоб понять реальную последовательность действий и что за что отвечает. И лишь когда поймёшь, как несколько объектников связываются в выполняемую программу (с возможным добавлением библиотечных модулей), тогда можно считать, что более-менее понял, как это происходит (а как в действительности работает -- это уже надо вникать в форматы объектных и выполняемых файлов, тонкости работы компоновщика и др., что явно не для новичка).