All streams
Search
Write a publication
Pull to refresh
69
0.9
Иван Савватеев @SIISII

Микроконтроллеры, цифровая электроника, ОС…

Send message

А ещё сопрограммы могут быть весьма сложны при отладке. Вплоть до такой степени, что нормальная отладка не только их самих, но и окружающего кода становится невозможной: у меня такое было в KEIL. Сам компилятор (ARMCLANG) генерирует нормальный код, но у отладчика наличие сопрограмм начисто срывает крышу, из-за чего отладка возможна исключительно на уровне дизассемблированного кода. Похоже, KEIL не способен корректно интерпретировать отладочную информацию -- и вряд ли будет способен в будущем; скорей, ARM полностью переведёт разработку в VS Code, фактически свалив заботу об инструментах на "сообщество".

А там не конвейерное умножение? Т.е. не может несколько умножений выполняться одновременно, но на разных стадиях конвейера? Тогда 3 умножения подряд выполнялись бы за 5 тактов...

У МК без FPU вполне может быть несколько сотен килобайт флэша (пример -- NXP LPC17xx на ядре Cortex-M3, объём флэша -- по 512 Кбайт, объём внутреннего ОЗУ -- до 96 Кбайт в зависимости от конкретной модификации).

У арифметики с фиксированной запятой свои недостатки. В частности, если точность не важна, но диапазон чисел большой, без плавающей запятой обойтись проблематично. Так что один способ не отменяет и не заменяет другой -- нужно на задачу смотреть.

Как я ответил на другой пост под этой статьёй, мир не ограничивается ПК и смартфонами, и идея, неактуальная на них, может быть очень даже востребованной в других случаях.

Аппаратная реализация, о которой Вы говорите, есть в обязательном порядке на современных ПК и смартфонах, но мир ими не исчерпывается. А временами приходится выполнять достаточно сложные вычисления на какой-нибудь, грубо говоря, Ардуине, где даже целочисленных умножения-деления нет, не то что команд для работы с вещественными числами.

Лично я впервые увидел приближённые вычисления -- правда, для квадратного корня, -- в промышленном контроллере на базе микропроцессора КР580ВМ80А, в девичестве 8080, на рубеже 1980-90-х годов.

А ещё это может потребоваться при создании своей железки на ПЛИС. Да, там можно и полноценную обработку вещественных чисел реализовать, но будет медленнее и сожрёт больше ресурсов, что не есть хорошо.

От UBT и UHT язык не изменился, и его компилятор менять не пришлось. Эти инструменты, по большому счёту, лишь генерируют необходимые макросы перед тем, как запускать собственно компилятор, и теоретически без них можно обойтись. Но на практике писать определения этих макросов вручную -- повесишься, почему и сделали инструменты для сей цели.

В порядке придирок: контроль памяти с точностью до байта -- это ассемблер и только ассемблер. Никакой компилируемый язык, в том числе, естественно, и C/C++, такой возможности не даёт. Хотя, как правило, она не требуется вообще либо требуется только в структурах данных, где этого добиться можно.

А ещё неплохо б русский язык подтянуть. В частности, чтобы часто пишется слитно, иногда пишется раздельно, но никогда не пишется через дефис.

Четвёртая, после третьего фола. А фол-4 -- пятая :) Причём это если включать фол-1 и фол-2, но не включать всякие побочные вещи, появившиеся между ними и фолом-3 (фоллаут тактикс, ещё там что-то...)

Исследовать интересно было. А вот квестов на такую большую карту, конечно, маловато -- это если очень мягко сказать. Так что тупой перенос того же на более современный уровень -- так себе идея, а вот с доработкой/расширением...

Но если мы пишем системный код и не используем стандартную библиотеку языка (а в системном коде она не нужна), то и исключениям взяться неоткуда.

Вообще, в классическом Паскале объектов не было -- были только записи (record), которые технически ничем от классических сишных структур не отличаются. Объекты же появились в объектно-ориентированном расширении Паскаля от Борланд, в версии 5, если склероз не изменяет.

Но по существу вопроса согласен: делать ООП на C, используя обычные структуры, вместо того чтобы взять C++ -- ну, это такое.

Ну, ручные аналоги виртуальных функций в коде на чистом C используют постоянно (скажем, всякие там библиотеки периферии "от производителя", будь то STM, Microchip или кто ещё, набиты структурами, содержащими указатели на функции, и ты должен правильно заполнить такие структуры и передавать их вызываемому библиотечному коду). Почему бы не использовать язык, где всё это делается автоматически?

Ну, я вот под МК пишу на C++, и от неча делать как-то обошёлся вообще без всей стандартной библиотеки (не считая заголовков, нужных для типов вроде std::uint32_t). Абсолютно весь необходимый код библиотеки времени выполнения написал сам на ассемблере, ориентируясь на ругань компоновщика -- причём этого кода оказалось очень мало (грубо говоря, memcpy и ещё несколько подобных функций). Когда добавил статические экземпляры классов, потребовалось дописать вызовы их конструкторов перед вызовом main и вызовы деструкторов после возврата (последнее, понятное дело, фикция в данном случае). В общем, ничего реально тяжеловесного компилятору не требуется. Вот библиотекам -- там да, очень много что может быть, но это уже дело программиста -- использовать стандартные библиотеки или писать своё.

Это, конечно, freestanding в терминологии стандарта, но и ядро ОС -- тоже не прикладная программа и, прямо скажем, принципиальных отличий от прошивки микроконтроллера не имеет (не считая объёма и сложности -- но не "взаимоотношений" со средствами разработки и с аппаратурой), и там тоже не можно, а нужно использовать именно freestanding.

Кстати говоря, если б ядро реально было "тщательно продуманным", то в каждой новой большой версии не ломали бы совместимость внутриядерных механизмов, из-за чего драйверы между версиями далеко не всегда переносимы.

dynamic_cast нужен в весьма специфичных случаях, а применительно к ядру -- реально никогда. Вместо обычного new можно использовать new (std::nothrow) -- и он будет возвращать nullptr при отсутствии памяти, а не кидаться исключениями.

Так что знать эти вещи, конечно, нужно, но не находите, что программист вообще всегда должен понимать, что он делает и как работает используемый им инструмент?

C++ ровно столь же тяжеловесен, как чистый C. Если ты сам не напишешь тонну бреда, компилятор из воздуха это бред не сгенерирует. В частности, использование обычных классов с обычным наследованием будет порождать технически тот же код, что компилятор наклепает, если ты будешь использовать таблицы с адресами функций и простые структуры, вручную имитируя ООП.

И да, написанное ручками на ассемблере почти всегда будет заметно компактнее и несколько быстрее того, что выдаст компилятор.

Противопоставление идёт на уровне "код, загружаемый в адресное пространство ядра и работающий с привилегиями ядра" и "код, выполняемый как процесс режима пользователя". Модуль ядра -- это всего лишь возможность добавлять/удалять части ядра без его полной пересборки, но с точки зрения выполнения он ничем от собственно кода ядра не отличается.

Кстати говоря, в модульности ядра ничего особо нового нет. Скажем, в RSX-11 -- бабке Винды, появившейся ещё в первой половине 1970-х -- драйверы могли (и обычно делались) загружаемыми, и можно было по мере необходимости их загружать/выгружать. Ну а в OS/360 (ещё чуть ли не на 10 лет раньше) вообще 80% ядра загружалась в память по мере необходимости, причём пользователь мог писать собственные модули (и работало сие без всякой виртуальной памяти, поддержки которой тогда на уровне железа ещё не было).

И, кстати, в RSX-11 драйверы файловых систем всегда были задачами (процессами) режима пользователя, а не драйверами режима ядра -- в ядре крутились драйверы, прямо работающие с железом (и то, строго говоря, это не было совсем уж обязательным -- можно было написать "железный" драйвер, работающий как задача). Так что, когда в Винде стали выносить драйверы в пространство пользователя, отчасти вернулись к идеям, реализованным ещё в её бабке :) Хотя не в том объёме и на другом уровне -- но история движется больше по спирали, чем по кругу.

Почти всем можно пользоваться, просто голову нужно держать включённой -- а у ЛТ она как раз напрочь отключена, поэтому дичь и несёт. В частности, никто не притащит в ядро STL, Boost и прочее, если ты не притащишь это сам -- ну так не используй те средства, которые в коде ядра неуместны, в чём проблема-то?

Что же до прикладных программистов, то написание системного кода было для них сродни чёрной магии и 40 лет назад, причём совершенно независимо от используемого языка: мало того что ты остаёшься один на один с железом, так ещё и должен соблюдать изрядное количество неочевидных правил, чтобы что-нибудь не развалить из-за нарушения порядка использования внутриядерных структур данных и т.п. (на прикладном-то уровне такие проблемы возникают редко и на 95% разруливаются через ОС).

В Винде изрядная часть драйверов тоже являются частью ядра, и долгое время 95% BSODов происходило именно из-за их кривизны. Когда мелкомягкие догадались вытащить "тихоходные" драйверы или их части в пространство пользователя, многие такие проблемы ушли.

Правда, в Винде не ломают постоянно внутриядерные интерфейсы, поэтому корректно написанный под какую-нить Вынь2000 драйвер соберётся и будет работать и в 11. Есть исключения, главное из которых -- видеодрова, поскольку при переходе от 2003 на Вислу полностью поменяли драйверную модель для видюх, но практически всё остальное вполне себе переносимо.

А вот в Линухе попробуй возьми драйвер, написанный для 2-й версии, и заставь его собраться для 5-й. (Поэтому часто во всяких там встраиваемых системах и используют жутко древние версии линуховых ядер: драйверы только под них, переписывать -- то ещё удовольствие, даже если документация на железо имеется...)

Стандарт Це++ много чего не предусматривает, зато предусматривает кучу всякого бреда, особенно в плане UB, хотя 95% этих UB вполне себе DB на любой платформе.

Information

Rating
1,769-th
Location
Солнечногорск, Москва и Московская обл., Россия
Date of birth
Registered
Activity

Specialization

Embedded Software Engineer
Lead