Почему какая-то часть ОС должна быть написана на ассемблере?
Планировщик моей мини ОС написан на ассемблере и мне интересно почему. Я узнал, что инструкция eret
не может быть сгенерировано компилятором C, может ли это быть распространено на другие платформы, кроме Nios, а также на архитектуру x86 и / или MIPS? Поскольку я считаю, что часть os всегда пишется на ассемблере, я ищу, почему системный программист должен знать ассемблер для написания операционной системы. Дело в том, что существуют встроенные ограничения компилятора C, которые не могут генерировать определенные инструкции по сборке, такие как eret
что возвращает программу к тому, что делалось после прерывания?
4 ответа
Общий ответ по одной из трех причин:
Потому что этот конкретный тип кода не может быть написан на C. Я думаю,
eret
является инструкцией "возврата из исключения", поэтому здесь нет эквивалента C (поскольку аппаратные исключения, такие как ошибки страниц, деление на ноль и т. п., не являются исключениями в стиле C/C++). Другим примером может быть сохранение регистров в стеке при переключении задач и сохранение указателя стека в блок управления задачами. Код C не может этого сделать, потому что нет прямого доступа к указателю стека.Потому что компилятор не будет производить такой хороший код, как кто-то умный, пишущий на ассемблере. Некоторые специализированные операции могут быть трудны для написания на C - компилятор может не генерировать очень хороший код, или код становится очень сложным для достижения чего-то простого в ассемблере.
Запуск C-кода должен быть написан на ассемблере, потому что C-программе необходимо настроить некоторые вещи, прежде чем вы сможете запустить настоящий C-код. Например, настройка указателя стека и некоторых других регистров.
Да, это так. Есть инструкции, которые вы не можете сгенерировать, используя язык Си. Обычно для ОС требуется одна или несколько инструкций, поэтому требуется некоторая сборка. Это верно практически для любого набора инструкций, x86, arm, mips и так далее. Компиляторы C позволяют выполнять встроенную сборку для заклинивания инструкций, но сам язык не может реально обрабатывать нюансы каждого набора команд и пытаться их учесть. Некоторые компиляторы добавляют вещи, специфичные для компилятора, например, для возврата функции с использованием прерывания return. Гораздо проще просто написать ассемблер там, где это необходимо, чем настраивать язык или компиляторы, так что там действительно нет необходимости.
Язык C выражает то, что ему определено выражать: фундаментальные арифметические операции, присвоение значений переменным, а также ветви и вызовы функций. Объекты могут быть размещены с использованием static
автоматическое (локальное) или динамическое (malloc) время хранения. Если вы хотите что-то вне этой концептуальной области, вам нужно что-то кроме чистого C.
Язык C может быть расширен произвольно, и многие платформы определяют синтаксис для таких вещей, как определение функции или переменной по определенному адресу.
Но аппаратное обеспечение ЦП заботится о многих деталях, таких как значения регистров флагов. Часть планировщика, которая переключает потоки, должна иметь возможность сохранять все регистры в памяти, прежде чем что-либо делать, потому что перезапись любого регистра приведет к потере важных данных в прерванном потоке.
Единственный способ написать такую вещь на C - это предоставить компилятору функцию C, которая генерирует точно настроенную сборку. И тогда вы, по сути, вернулись к квадрату 1, потому что важные детали все еще находятся на уровне кода сборки.
Производители с несколькими линейками микроконтроллеров иногда делают все возможное, чтобы обеспечить совместимость с исходным кодом C даже на самых низких уровнях, чтобы позволить своим клиентам переносить код (или, наоборот, чтобы они не переходили к другому поставщику, когда им нужно переключать платформы)., Но различие между C и сборкой стирается в определенный момент, когда вы вызываете псевдофункции, которые генерируют конкретные инструкции (известные как встроенные функции).
Некоторые вещи, которые нельзя сделать в C, или которые, если они могут быть выполнены, лучше делать в сборке, потому что они более просты и / или обслуживаемы таким образом, включают:
- Выполнить инструкции возврата из исключения и возврата из прерывания.
- Чтение и запись в специальные регистры процессора (которые управляют состоянием процессора, отображением памяти, конфигурацией кэша, управлением исключениями и т. Д.).
- Выполняйте атомарные операции чтения и записи по специальным адресам, которые являются подключениями к аппаратным устройствам, а не к памяти.
- Выполняйте загрузку и сохраняйте инструкции определенных размеров или характеристик по специальным адресам, как описано выше. (Например, запись на определенные устройства может потребовать использования только 16-битной инструкции хранилища, а не 32-битной инструкции обычного хранилища.)
- Выполните инструкции для барьеров памяти или упорядочения, управления кэшем и очистки карт памяти.
Как правило, C в основном предназначен для выполнения вычислений (считывания входных данных, вычисления вещей, записи выходных данных), а не для управления машиной (взаимодействует со всеми элементами управления и устройствами в машине).