Правда ли, что Python идеально подходит для написания драйверов устройств

Обновлено: 21.11.2024

Эта глава содержит описание драйверов устройств для Azure RTOS ThreadX. Информация, представленная в этой главе, призвана помочь разработчикам в написании драйверов для конкретных приложений.

Введение в драйвер устройства

Связь с внешней средой — важный компонент большинства встроенных приложений. Эта связь осуществляется через аппаратные устройства, доступные для встроенного прикладного программного обеспечения. Программные компоненты, отвечающие за управление такими устройствами, обычно называются драйверами устройств.

Драйверы устройств во встроенных системах реального времени по своей природе зависят от приложений. Это верно по двум основным причинам: огромное разнообразие целевого оборудования и столь же высокие требования к производительности, предъявляемые к приложениям реального времени. Из-за этого практически невозможно предоставить общий набор драйверов, отвечающий требованиям каждого приложения. По этим причинам информация в этой главе предназначена для того, чтобы помочь пользователям настроить готовые драйверы устройств ThreadX и написать свои собственные драйверы.

Функции драйвера

Драйверы устройств ThreadX состоят из восьми следующих основных функциональных областей.

За исключением инициализации, каждая функциональная область драйвера является необязательной. Кроме того, конкретная обработка в каждой области зависит от драйвера устройства.

Инициализация драйвера

Эта функциональная область отвечает за инициализацию реального аппаратного устройства и внутренних структур данных драйвера. Вызов других служб драйверов не допускается до завершения инициализации.

Компонент функции инициализации драйвера обычно вызывается из функции tx_application_define или из потока инициализации.

Управление водителем

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

Доступ к водителю

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

Ввод драйвера

Эта функциональная область отвечает за ввод всех устройств. Основные проблемы, связанные с входными данными драйвера, обычно связаны с тем, как буферизуются входные данные и как потоки ожидают таких входных данных.

Вывод драйвера

Эта функциональная область отвечает за все выходные данные устройства. Основные проблемы, связанные с выводом драйвера, обычно связаны с буферизацией вывода и ожиданием потоков перед выполнением вывода.

Прерывания драйвера

Большинство систем реального времени полагаются на аппаратные прерывания для уведомления драйвера устройства о событиях ввода, вывода, управления и ошибок. Прерывания обеспечивают гарантированное время отклика на такие внешние события. Вместо прерываний программное обеспечение драйвера может периодически проверять внешнее оборудование на наличие таких событий. Этот метод называется опрос. Это менее реальное время, чем прерывания, но опрос может иметь смысл для некоторых приложений, менее оперативных.

Статус водителя

Эта функциональная область отвечает за предоставление статуса во время выполнения и статистику, связанную с работой драйвера. Информация, которой управляет эта функциональная область, обычно включает следующее.

  • Текущее состояние устройства
  • Входные байты
  • Выходные байты
  • Счетчик ошибок устройства

Удаление драйвера

Эта функциональная область не является обязательной. Это требуется только в том случае, если необходимо отключить драйвер и/или физическое аппаратное устройство. После завершения драйвер нельзя вызывать снова, пока он не будет повторно инициализирован.

Пример простого драйвера

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

Простая инициализация драйвера

Функция tx_sdriver_initialize простого драйвера создает два счетных семафора, которые используются для управления операциями ввода и вывода драйвера. Входной семафор устанавливается входным ISR, когда символ получен последовательным аппаратным устройством. По этой причине входной семафор создается с нулевым начальным счетчиком.

Наоборот, выходной семафор указывает на доступность последовательного аппаратного регистра передачи. Он создается со значением 1, чтобы указать, что регистр передачи изначально доступен.

Функция инициализации также отвечает за установку низкоуровневых обработчиков векторов прерываний для уведомлений ввода и вывода. Как и другие подпрограммы обработки прерываний ThreadX, низкоуровневый обработчик должен вызвать _tx_thread_context_save перед вызовом простого драйвера ISR. После возврата ISR драйвера низкоуровневый обработчик должен вызвать _tx_thread_context_restore.

*Важно, чтобы инициализация вызывалась перед любой другой функцией драйвера. Обычно инициализация драйвера вызывается из tx_application_define.

РИСУНОК 9. Простая инициализация драйвера

Простой ввод драйвера

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

Существует несколько ограничений на простую обработку ввода драйвера. Наиболее важным является возможность отбрасывания вводимых символов. Это возможно, потому что нет возможности буферизовать входные символы, поступающие до того, как будет обработан предыдущий символ. С этим легко справиться, добавив буфер ввода символов.

Только потоки могут вызывать tx_sdriver_input функцию.

На рис. 10 показан исходный код, связанный с простым вводом драйвера.

РИСУНОК 10. Простой ввод драйвера

Простой вывод драйвера

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

Выходной ISR отвечает за обработку прерывания завершения передачи. Обработка выходного ISR сводится к установке выходного семафора, что позволяет выводить другой символ.

Только потоки могут вызывать tx_sdriver_output функцию.

На рис. 11 показан исходный код, связанный с выходными данными простого драйвера.

РИСУНОК 11. Простой вывод драйвера

Простые недостатки драйверов

Этот простой пример драйвера устройства иллюстрирует основную идею драйвера устройства ThreadX. Однако, поскольку простой драйвер устройства не решает проблем с буферизацией данных или какими-либо накладными расходами, он не полностью представляет реальные драйверы ThreadX. В следующем разделе описаны некоторые более сложные проблемы, связанные с драйверами устройств.

Дополнительные проблемы с драйверами

Как упоминалось ранее, к драйверам устройств предъявляются такие же уникальные требования, как и к их приложениям. Некоторым приложениям может потребоваться буферизация огромного объема данных, в то время как другим приложениям могут потребоваться оптимизированные ISR драйвера из-за высокочастотных прерываний устройств.

Буферизация ввода-вывода

Буферизация данных во встроенных приложениях реального времени требует тщательного планирования. Часть дизайна продиктована базовым аппаратным устройством. Если устройство обеспечивает базовый байтовый ввод-вывод, возможно, подойдет простой кольцевой буфер. Однако, если устройство обеспечивает блочный, прямой доступ к памяти или пакетный ввод-вывод, схема управления буфером, вероятно, оправдана.

Циклические буферы байтов

Циклические байтовые буферы обычно используются в драйверах, которые управляют простым последовательным аппаратным устройством, таким как UART. В таких ситуациях чаще всего используются два кольцевых буфера — один для ввода и один для вывода.

Каждый циклический байтовый буфер состоит из области байтовой памяти (обычно это массив символов UCHAR), указателя чтения и указателя записи. Буфер считается пустым, когда указатель чтения и указатели записи ссылаются на одну и ту же ячейку памяти в буфере. Инициализация драйвера устанавливает указатели буфера чтения и записи на начальный адрес буфера.

Циклический буфер ввода

Входной буфер используется для хранения символов, поступающих до того, как приложение будет готово к их приему. При получении входного символа (обычно в процедуре обслуживания прерывания) новый символ извлекается из аппаратного устройства и помещается во входной буфер в месте, указанном указателем записи. Затем указатель записи перемещается на следующую позицию в буфере. Если следующая позиция находится за концом буфера, указатель записи устанавливается в начало буфера. Состояние заполнения очереди обрабатывается путем отмены продвижения указателя записи, если новый указатель записи совпадает с указателем чтения.

Запросы входных байтов приложения к драйверу сначала проверяют указатели чтения и записи входного буфера.Если указатели чтения и записи совпадают, буфер пуст. В противном случае, если указатель чтения отличается, байт, на который указывает указатель чтения, копируется из входного буфера, а указатель чтения перемещается в следующую ячейку буфера. Если новый указатель чтения находится за концом буфера, он сбрасывается в начало. На рис. 12 показана логика циклического входного буфера.

РИСУНОК 12. Логика циклического буфера ввода

*Для надежной работы может потребоваться блокировка прерываний при манипулировании указателями чтения и записи как входного, так и выходного кольцевых буферов. *

Циклический буфер вывода

Буфер вывода используется для хранения символов, поступивших на вывод до того, как аппаратное устройство завершило отправку предыдущего байта. Обработка выходного буфера аналогична обработке входного буфера, за исключением того, что обработка прерывания завершения передачи манипулирует выходным указателем чтения, в то время как запрос вывода приложения использует выходной указатель записи. В противном случае обработка выходного буфера такая же. На рис. 13 показана логика кольцевого выходного буфера.

РИСУНОК 13. Логика циклического буфера вывода

Управление буфером ввода/вывода

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

Размер и расположение буферов ввода/вывода определяется приложением и/или программным драйвером. Как правило, буферы имеют фиксированный размер и управляются в пуле блочной памяти ThreadX. На рис. 14 показан типичный буфер ввода-вывода и пул памяти блоков ThreadX, который управляет их распределением.

РИСУНОК 14. Буфер ввода/вывода

TX_IO_BUFFER

Typedef TX_IO_BUFFER состоит из двух указателей. Указатель tx_next_packet используется для связывания нескольких пакетов либо во входном, либо в выходном списке. Указатель tx_next_buffer используется для связывания буферов, составляющих отдельный пакет данных с устройства. Оба этих указателя устанавливаются в NULL, когда буфер выделяется из пула. Кроме того, для некоторых устройств может потребоваться дополнительное поле, чтобы указать, какая часть области буфера фактически содержит данные.

Преимущество буферизованного ввода-вывода

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

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

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

Обязанности буферизованного драйвера

Драйверы буферизованных устройств занимаются только управлением связанными списками буферов ввода/вывода. Список входных буферов поддерживается для пакетов, полученных до того, как прикладное программное обеспечение будет готово. И наоборот, список выходного буфера поддерживается для пакетов, отправляемых быстрее, чем аппаратное устройство может их обработать. На рис. 15 показаны простые входные и выходные связанные списки пакетов данных и буферы, из которых состоит каждый пакет.

Список ввода

Выходной список

РИСУНОК 15. Списки ввода-вывода

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

В некоторых приложениях может быть полезно создать интерфейс ввода драйвера, требующий от приложения обмена свободного буфера на входной буфер из драйвера. Это может упростить обработку выделения буфера внутри драйвера.

Управление прерываниями

В некоторых приложениях частота прерывания устройства может запрещать запись ISR на языке C или взаимодействие с ThreadX при каждом прерывании. Например, если для сохранения и восстановления прерванного контекста требуется 25 мкс, было бы нецелесообразно выполнять полное сохранение контекста, если частота прерывания составляла 50 мкс. В таких случаях для обработки большинства прерываний устройства используется небольшой ISR на языке ассемблера. Этот ISR с низкими издержками будет взаимодействовать с ThreadX только при необходимости.

Похожее обсуждение можно найти в обсуждении управления прерываниями в конце главы 3.

Приостановка темы

В простом примере драйвера, представленном ранее в этой главе, вызывающая служба ввода приостанавливает работу, если символ недоступен. В некоторых приложениях это может быть неприемлемо.

Например, если у потока, ответственного за обработку входных данных драйвера, есть и другие обязанности, приостановка только на входных данных драйвера, вероятно, не сработает. Вместо этого драйвер необходимо настроить так, чтобы он запрашивал обработку аналогично тому, как другие запросы на обработку передаются потоку.

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

Мне просто интересно, почему драйвера и прошивки почти всегда пишутся на C или ассемблере, а не на C++?

Я слышал, что для этого есть техническая причина.

Кто-нибудь знает это?

С любовью, Луиза

+1 за бесполезность. Я бы добавил, что хлам влияет на скорость выполнения, а драйверы/прошивки в основном чувствительны к производительности, и вы хотите, чтобы они работали как можно быстрее.

При использовании C вы в конечном итоге вручную кодируете то же самое "бесполезное", что было в C++: полиморфизм -> дескрипторы, классы -> модули, шаблоны -> макросы, подобные функциям, пространства имен -> префиксы имен и т. д. См. мой ответ.

@Heath - мой опыт не говорит о том, что «динамическое управление памятью должно быть реализовано для запуска большей части кода C++», это далеко не так. И даже если это так, вы можете реализовать свое собственное управление памятью (заменить новое/удалить глобально или для каждого класса). По моему опыту, если вашей реализации на C не потребуется динамическая память, то и реализации на C++ не понадобится. Исключение может быть, если ваша реализация C использует связанные списки "создавайте собственные" и т. д., а версия C++ использует стандартные контейнеры lib (список, вектор и т. д.)

15 ответов 15

Потому что в большинстве случаев операционная система (или "библиотека времени выполнения") предоставляет функции stdlib, необходимые для C++.

В C и ASM вы можете создавать исполняемые файлы без внешних зависимостей.

Однако, поскольку Windows поддерживает стандартную библиотеку C++, большинство драйверов Windows написаны на (ограниченном подмножестве) C++.

Кроме того, когда прошивка написана на ASM, это обычно происходит потому, что либо (А) платформа, на которой она выполняется, не имеет компилятора C++, либо (Б) существуют экстремальные ограничения по скорости или размеру.

Обратите внимание, что (B) обычно не было проблемой с начала 2000-х годов.

@Луиза: Нет, не обязательно. Я думаю, вы могли бы статически связать stdlib, но я не уверен, что вы можете сделать таким образом голый исполняемый файл. В любом случае, в Windows драйверы написаны на C++, и все они имеют внешние зависимости.

+1 за правильный ответ. Это не из-за лишних нескольких байтов «мусора» в экземплярах класса, как говорят некоторые другие. Кроме того, следует отметить, что компиляторы C написать гораздо проще, чем компиляторы C++, и многие встраиваемые системы работают на более дешевых или редко используемых процессорах, которые просто не имеют (приличного) компилятора C++.

Я не уверен, что язык C так часто используется при начальной загрузке, как утверждают его сторонники. Например, первым резидентным программным обеспечением как для Intel 8086, так и для Apple Macintosh был Forth, еще один язык, известный своей простотой и легкостью переноса.

Код в ядре работает в совершенно другой среде, чем в пользовательском пространстве. Нет разделения процессов, поэтому исправить ошибки намного сложнее; исключения практически исключены. Существуют разные распределители памяти, поэтому может быть сложнее получить новый и удалить для правильной работы в контексте ядра. Доступной стандартной библиотеки меньше, что значительно усложняет эффективное использование таких языков, как C++.

Windows позволяет использовать очень ограниченное подмножество C++ в драйверах ядра; по сути, те вещи, которые можно было бы тривиально перевести на C, такие как объявления переменных в местах, кроме начала блоков. Они не рекомендуют использовать new и delete и не поддерживают RTTI или большую часть стандартной библиотеки C++.

В Mac OS X используется I/O Kit, фреймворк, основанный на ограниченном подмножестве C++, хотя, насколько я могу судить, более совершенный, чем разрешенный в Windows. По сути, это C++ без исключений и RTTI.

Большинство Unix-подобных операционных систем (Linux, BSD) написаны на C, и я думаю, что никто никогда не видел пользы от добавления поддержки C++ в ядро, учитывая, что C++ в ядре, как правило, настолько ограничен. .

«Объявления переменных в местах, кроме начала блоков» теперь фактически являются частью C. Так что его действительно легко перевести на C. :-)

Ну, да, вы можете сделать это в C99, но компилятор Microsoft не поддерживает C99, поэтому, чтобы получить эту функцию, вам нужно использовать C++ (я хотел написать оговорку об этом в своем ответе, но я думаю, что я забыл).

Кроме того, библиотеки и шаблоны C++ упрощают случайное создание сложного, раздутого машинного кода, а его соглашения о двоичных вызовах исторически были менее четко определены, чем C (см.: ABI). Многие специалисты по сопровождению ядра не хотят утруждать себя контролированием вкладов, чтобы избежать таких ловушек.

1) "Потому что так было всегда" — на самом деле это объясняет больше, чем вы думаете, — учитывая, что API почти всех современных систем изначально были написаны на основе модели C или ASM, а также с учетом того, что многие предыдущие код существует на C и ASM, часто проще «плыть по течению», чем выяснять, как использовать преимущества C++.

2) Среда. Чтобы использовать все функции C++, вам нужна среда выполнения, некоторые из которых довольно сложно предоставить драйверу. Это проще сделать, если вы ограничите свой набор функций, но, среди прочего, управление памятью может стать очень интересным в C++, если у вас не так много кучи. Исключения также очень интересно рассмотреть в этой среде, как и RTTI.

3) "Я не вижу, что он делает". Любой достаточно опытный программист может посмотреть на строку C и получить хорошее представление о том, что происходит на уровне машинного кода для реализации этой строки. Очевидно, что оптимизация несколько меняет это, но по большей части вы можете сказать, что происходит. В C++, учитывая перегрузку операторов, конструкторы, деструкторы, исключения и т. д., становится очень трудно понять, что произойдет в данной строке кода. При написании драйверов устройств это может быть смертельно опасным, потому что вы часто ДОЛЖНЫ знать, собираетесь ли вы взаимодействовать с диспетчером памяти, или влияет ли строка кода (или зависит от) уровней прерывания или маскировки.

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

За исключением более широкой поддержки инструментов и переносимости оборудования, я не думаю, что есть веские причины ограничивать себя языком C. Я часто вижу сложные коды, написанные вручную на C, которые более естественно можно сделать на C++:

  • Группировка в «модули» функций (не общего назначения), которые работают только с одной и той же структурой данных (часто называемой «объектом») -> Используйте классы C++.
  • Использование указателя "дескриптор", чтобы функции модуля могли работать с "экземплярами" структур данных -> Использовать классы C++.
  • Статические функции файловой области, не являющиеся частью API модуля -> частные функции-члены C++, анонимные пространства имен или пространства имен "detail".
  • Использование макросов, подобных функциям -> шаблоны C++ и встроенные/constexpr-функции
  • Различное поведение во время выполнения в зависимости от идентификатора типа либо с созданной вручную виртуальной таблицей ("дескриптор"), либо с отправкой с помощью оператора switch -> полиморфизм C++
  • Подверженная ошибкам арифметика указателей для маршалинга/демаршаллинга данных из/в коммуникационный порт или использования непереносимых структур -> концепция потока C++ (не обязательно std::iostream)
  • К черту все префиксы, чтобы избежать конфликтов имен: пространства имен C++
  • Макросы как константы времени компиляции -> константы constexpr C++11
  • Забыть закрыть ресурсы до того, как дескрипторы выйдут за пределы области действия -> C++ RAII

Ни одна из функций C++, описанных выше, не стоит больше, чем написанные от руки реализации C. Мне, наверное, чего-то еще не хватает. Я думаю, что инерция C в этой области больше связана с тем, что C используется в основном.

Конечно, вы не сможете широко (или вообще) использовать STL в ограниченной среде, но это не означает, что вы не можете использовать C++ как "лучший C".

+1 за очень беспристрастное обсуждение. Кроме того, 3-й пункт списка, я мог бы также предложить встроенные функции в качестве еще одной альтернативы макросам, подобным функциям (хотя я полагаю, что технически «встроенные» доступны и в C99, хотя большинство магазинов, с которыми я работаю, не используют его)

10 лет спустя. Сейчас я работаю над хобби-проектом Arduino с жалким микроконтроллером, имеющим 2 КБ ОЗУ и 32 КБ полезной флэш-памяти. Я начал с эскиза на основе C и преобразовал его в C++11 просто для удовольствия. Когда я выполнил рефакторинг для C++, использование памяти/флеш-памяти почти не увеличилось по сравнению с эквивалентом C. Я даже написал полноценный класс string_view и static_string, который использует библиотеку времени выполнения C для низкоуровневых операций со строками. Какой бы небольшой прирост у меня ни был с использованием ОЗУ/ПЗУ, это было связано с тем, что дизайн стал более расширяемым, а компоненты стали более пригодными для повторного использования (с меньшим количеством жестко закодированных элементов).

  1. C++ приводит к раздуванию кода
  2. Исключения C++ занимают слишком много места.
  3. Полиморфизм C++ и виртуальные таблицы требуют слишком много памяти или времени выполнения.
  4. Люди в магазине не знают языка C++.

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

Обработка исключений в C при правильной реализации занимает много места. Я бы сказал примерно так же, как C++. Преимущество исключений C++: они есть в языке, и программистам не нужно переделывать колесо.

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

Большинство причин против C++ связаны с концепцией дизайна, а не с самим языком.

Преимущество исключений C, виртуальных функций C и ООП C, которое делает их важными, особенно для драйверов и прошивок: они вообще не должны существовать в драйверах. Встроенный != Драйверы; Android — это встроенная система, но она в основном написана на Java, за исключением того, что ядро ​​и драйверы написаны на C. Ядро ОС обычно обеспечивает защиту памяти, но само ядро ​​не получает преимуществ от системы безопасности, поэтому для написания драйверов и прошивок нужны надежные гарантии. это не может быть так легко реализовано с помощью C++, если вы не используете очень ограниченное подмножество C++, что ограниченное подмножество, которое вы используете, больше не будет C++.

Размер имеет мало общего с тем, что не используется C++. Однако я соглашусь с вами, что плыть по течению — главная причина. Никто не удосужился выяснить, какое подмножество C++ можно безопасно использовать при разработке драйверов, поскольку они знали, что просто создадут что-то, что «похоже на C и C++, но ни на C, ни на C++», IOW — еще один новый язык, который никто не будет изучать. .

+1 за "Люди в магазине не знают язык C++". Это очень типично, а также веская причина избегать языка. Как и вы, я склонен использовать C++, когда могу, но часто, если я работаю с клиентом, который работает только с C++, я обычно остаюсь с чистым C. Что-то вроде ситуации с курицей и яйцом, но это то, что является. Самое интересное, когда я работаю в компании, которая хочет перейти на C++ и готова сделать это обдуманно и осторожно.

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

С++ может использоваться, но только в том случае, если среда выполнения специально адаптирована для обращения только к внутренним таблицам в фиксированной памяти (не подкачиваемой), когда механизм времени выполнения вызывается неявно, например, с использованием виртуальной таблицы, когда вызов виртуальных функций. Эта специальная адаптация в большинстве случаев не поставляется «из коробки».

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

Редактировать: удалены ссылки на новые и удаленные вызовы (это было неправильно/вводило в заблуждение); заменена более общей фразой "машина времени выполнения".

Причина, по которой используется C, а не C++, НЕ заключается в следующем:

  • Потому что C++ медленнее
  • Или потому, что среда выполнения c уже установлена.

Это так, потому что C++ использует исключения. Большинство реализаций исключений языка C++ непригодны для использования в коде драйвера, поскольку драйверы вызываются, когда ОС отвечает на аппаратные прерывания. Во время аппаратного прерывания коду драйвера НЕ разрешается использовать исключения, поскольку это могло бы вызвать рекурсивные прерывания. Кроме того, пространство стека, доступное для кода в контексте прерывания, обычно очень мало (и не может увеличиваться из-за правила отсутствия исключений).

Конечно, вы можете использовать new(std::nothrow), но поскольку исключения в C++ теперь вездесущи, это означает, что вы не можете полагаться на код какой-либо библиотеки для использования семантики std::nothrow.

Итак, резюмируя: - Причина, по которой для разработки драйверов используется C, а не C++, заключается в том, что драйверы, написанные на C++, либо потребляют чрезмерное количество невыгружаемой памяти, либо приводят к сбою ядра ОС.

-1 Нет никаких исследований или фактов о том, что C++ работает медленнее, чем C. Когда эквивалентная функциональность включена в C, они оба работают с одинаковой скоростью.

Я понимаю, что иронию трудно обнаружить в написанном. Однако, когда я сказал, что разница в скорости НЕ является причиной того, что С++ избегают при разработке драйверов, я подумал, что очевидно, что читатель поймет, что я не имел в виду разницу в скорости.

C очень близок к машинно-независимому языку ассемблера. Большая часть программирования типа ОС находится на уровне «голого железа». С C код, который вы читаете, является фактическим кодом. C++ может скрывать то, что не может C.

Это всего лишь мое мнение, но я потратил много времени на отладку драйверов устройств и связанных с ОС вещей. Часто, глядя на язык ассемблера. Сохраняйте простоту на низком уровне и позвольте уровню приложения стать причудливее.

Драйверы Windows написаны на C++.
Драйверы Linux написаны на c, потому что ядро ​​написано на c.

Все драйверы Windows, которые я написал или видел, написаны на C. Я думаю, что вы можете сделать это на C++, но не многие делают это, и есть некоторые проблемы. Я думаю.

Причина, по которой драйверы и микропрограммы в основном написаны на C или ASM, заключается в том, что они не зависят от реальных библиотек времени выполнения. Если бы вы представили себе этот воображаемый драйвер, написанный на C здесь

Обратите внимание, что поддержка библиотеки времени выполнения должна быть подключена и подключена во время компиляции/связывания, она не будет работать, поскольку среда выполнения (то есть, когда операционная система находится на этапе загрузки/инициализации) настроена не полностью. up и, следовательно, не было бы никакой подсказки о том, как printf , и, вероятно, прозвучал бы похоронный звон операционной системы (паника ядра для Linux, синий экран для Windows), поскольку нет ссылки на то, как выполнить функцию.< /p>

Иными словами, в случае с драйвером код драйвера имеет привилегию выполнять код вместе с кодом ядра, который будет находиться в одном и том же пространстве. интерфейс операционной системы запускается (ограниченные права на выполнение), другими словами, код кольца 3 не может иметь инструкции, зарезервированной для кольца 0, ядро ​​уничтожит код, перехватив его, как будто говоря: «Эй, у вас нет привилегия для доступа к домену Ring0'.

Другая причина, по которой он написан на ассемблере, в основном связана с размером кода и исходной собственной скоростью. время, задержка, буферизация.

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

Существует один инструментарий, позволяющий создать драйвер в Visual Studio, VisualDDK. Во что бы то ни стало, сборка драйвера не для слабонервных, вы получите вызванную стрессом активность, глядя на синие экраны, паники ядра и удивляясь, почему, отлаживая драйверы и так далее.

Отладка сложнее, код ring0 не так легко доступен коду ring3, так как двери к нему закрыты, это через люк ядра (за неимением лучшего слова), и если спросить вежливо, дверь все еще остается закрыть, пока ядро ​​делегирует задачу обработчику, находящемуся в кольце 0, выполнить его, любые возвращаемые результаты передаются обратно в код ринг3, и дверь по-прежнему остается надежно закрытой. Это аналог концепции того, как пользовательский код может выполнять привилегированный код на кольце0.

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

По данным IEEE Spectrum, Python занимает первое место среди лучших языков программирования 2017 года. Последний отчет IEEE Spectrum показывает, что Python сохраняет лидирующие позиции в рейтинге лучших языков программирования 2020 года.

Python продолжил восходящую траекторию с 2016 года и поднялся на две позиции, заняв первое место в общем рейтинге в 2017 году. Хотя первая позиция подтверждена в категориях "Интернет" и "Корпоративный", в 2017 году этому удивительному языку все еще нужно было взорвать рынок. Категория Embedded, несмотря на появление платформ разработки встраиваемых систем на основе Python, таких как Zerynth и MicroPython. Сегодня Python лидирует в обеих категориях. Взгляните на результаты 2020 года:

В этом рейтинге нет ничего удивительного. Сегодня подавляющее большинство встраиваемого кода по-прежнему написано на C. Опытные и опытные программисты на C могли бы оправдать это утверждением, что «C генерирует более быстрый, более компактный и надежный код»

Если вы замените C на «ассемблер» в этом утверждении, вы получите именно то, что говорило вымершее поколение программистов около 20 лет назад!

Зачем использовать Python для встроенных приложений?

Если вы быстро погуглите что-то вроде «Python для встраиваемых систем», вы найдете множество очень интересных статей, таких как следующие:

Как очень хорошо подытожено в одной из статей, упомянутых выше, мы можем сказать, что:

Если проблему можно решить на Python, то ее можно решить и на C; обратное не всегда верно. Однако, если проблема может быть решена на Python:

— Решение (исходный код) будет проще, чем соответствующий код C.
— Будет более «читабельно».
— Возможно, что более важно, он будет более «пригоден для записи» (это качество часто упускают из виду!).

Итак, в то время как C/C++ медленная запись, подверженная ошибкам и часто нечитаемая, Python известен своей доступностью для записи, уменьшением количества ошибок и читабельностью.

По этим и другим причинам, согласно одному недавнему исследованию, Python является не только самым популярным языком для начинающих программ CS и разработчиков-любителей, но и самым быстрорастущим языком для встроенных вычислений. Существует огромное количество профессионалов, умеющих использовать Python, «потенциально» способных разработать программное обеспечение для «следующей большой вещи» в Интернете вещей и в короткие сроки выпустить новые потрясающие встроенные приложения.

Почему вы должны использовать Zerynth для встроенных приложений на основе Python

Zerynth выходит за рамки профессиональных разработчиков встраиваемых систем и предлагает программистам высокого уровня, дизайнерам продуктов и разработчикам Интернета вещей полный набор высококачественных встроенных средств разработки для создания интерактивных и подключенных решений.

Бесплатный пакет Zerynth SDK — это доступ к нашему набору инструментов, который включает в себя ОС Zerynth и диспетчер устройств Zeynth. С помощью инструментов Zerynth пользователь может разрабатывать приложения Интернета вещей с помощью Python и C и безопасно подключать их к ведущим в отрасли облачным инфраструктурам, написав всего несколько строк кода.

Простой для начинающих и мощный для экспертов

Одно слово: простота! В отличие от других реализаций Python для микроконтроллеров, с Zerynth нет необходимости копировать/вставлять жуткие скрипты из разных веб-источников. Нет необходимости перетаскивать любой файл в любую папку или через FTP. Нет необходимости устанавливать Python на свой компьютер и не нужно устанавливать Putty или специальные драйверы. Благодаря Zerynth вы можете сделать свое устройство программируемым на Python всего за несколько кликов!

Давайте посмотрим это руководство о том, как управлять промышленной лампой с помощью нашей отладочной платы EXP-RELAY. Пользователю просто нужно загрузить и установить Zerynth SDK и следовать инструкциям руководства.

Это отличный выбор для новичков в программировании.

Для опытных пользователей Zerynth позволяет смешивать код Python и C в одном проекте.

Этот тип «гибридного» программирования чрезвычайно эффективен в сценариях, когда проекту требуется низкоуровневый код для критичных по времени задач, сохраняя при этом гибкость и удобочитаемость Python для некритичных по времени разделов.

С Zerynth вам не нужно выбирать между скоростью разработки и скоростью выполнения. Вы можете иметь и то, и другое!

Напиши один раз, работай везде

Операционная система Zerynth, основной элемент стека Zerynth, обеспечивает реальный уровень аппаратной абстракции, позволяющий повторно использовать код на широком спектре 32-разрядных микроконтроллеров, сердце встраиваемых решений и решений IoT.

Независимость от оборудования имеет решающее значение для многих:

  • для дизайнеров продуктов это сокращает время выхода на рынок, поскольку им не нужно переписывать код при переходе от прототипирования к производству;
  • системным интеграторам проще сосредоточиться на части интеграции и выбрать плату, которая лучше подходит для проекта, не беспокоясь о низкоуровневых функциях и программируемости;
  • для производителей встраиваемых систем он ускоряет внедрение нового встроенного оборудования и открывает доступ к огромному сообществу программистов Python.

Что вы можете делать с IoT-платформой Zerynth

В отличие от других платформ разработки Интернета вещей, Zerynth предоставляет полную «экосистему» ​​инструментов, которые позволяют перейти от разработки прошивки к подключению к облаку.

Нужно вдохновение? Учебники Zerynth содержат список руководств, которые помогут вам разрабатывать встроенные решения и решения IoT на Python с использованием стека Zerynth. Вы узнаете:

Помимо Arduino и Raspberry Pi…

Как упоминалось выше, студенты и любители, переходящие в отрасль с опытом программирования на Python для дронов, роботов или других проектов, часто имеют опыт работы с Raspberry Pi или Arduino.

Zerynth предлагает преимущества микропроцессорных плат, таких как Raspberry Pi:

  • Язык Python (или гибрид C/Python, если необходимо)
  • Поддержка многопоточности

наряду с огромными преимуществами плат на базе микроконтроллеров:

  • более низкое энергопотребление по сравнению с микропроцессорными платами
  • более низкие затраты на оборудование на этапе прототипирования
  • чрезвычайно низкие затраты на оборудование на этапе индустриализации

Все это при относительно небольшой площади: 60–80 КБ флэш-памяти и 3–5 КБ оперативной памяти.

…для безболезненного масштабирования

Zerynth уже используется компаниями и профессиональными командами для создания потрясающих IoT и встроенных решений в различных секторах: розничная торговля, коммерческое охлаждение, утилизация отходов, интеллектуальные устройства и многое другое, как вы можете увидеть на нашей странице с примерами использования .

Скачать Zerynth SDK — это просто и бесплатно

Теперь, когда вы знаете все преимущества разработки встраиваемых систем на Python, пришло время загрузить Zerynth SDK и начать проект своей мечты. В конце концов, это бесплатно, просто и адаптировано к вашим потребностям.

Данлео (CC BY-SA 3.0)

С 1991 года язык программирования Python считался заполнителем пробелов, способом написания скриптов, которые «автоматизируют скучные вещи» (как сказано в одной популярной книге по изучению Python) или быстрого создания прототипов приложений, которые будут реализованы. на других языках.

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

Видео по теме: Как Python упрощает программирование

Python идеально подходит для ИТ и упрощает многие виды работы, от системной автоматизации до работы в таких передовых областях, как машинное обучение.

Ключевые преимущества Python

Успех Python основан на нескольких преимуществах, которые он предоставляет как новичкам, так и экспертам.

Python прост в изучении и использовании

Количество функций в самом языке невелико, поэтому для создания ваших первых программ требуется сравнительно немного времени и усилий. Синтаксис Python удобен для чтения и прост. Эта простота делает Python идеальным языком обучения и позволяет новичкам быстро освоить его. В результате разработчики тратят больше времени на размышления о проблеме, которую они пытаются решить, и меньше времени на размышления о языковых сложностях или расшифровке кода, оставленного другими.

Python широко используется и поддерживается

Python популярен и широко используется, о чем свидетельствуют высокие рейтинги в таких опросах, как Tiobe Index, и большое количество проектов GitHub, использующих Python. Python работает на всех основных операционных системах и платформах, а также на большинстве второстепенных. Многие основные библиотеки и службы на основе API имеют привязки или оболочки Python, что позволяет Python свободно взаимодействовать с этими службами или напрямую использовать эти библиотеки.

Python — это не «игрушечный» язык

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

Python продолжает двигаться вперед

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

Для чего используется Python

Самый простой вариант использования Python – это язык сценариев и автоматизации. Python — это не просто замена сценариев оболочки или пакетных файлов; он также используется для автоматизации взаимодействия с веб-браузерами или графическими интерфейсами приложений или для подготовки и настройки системы в таких инструментах, как Ansible и Salt. Но сценарии и автоматизация — это только верхушка айсберга Python.

Общее программирование приложений с помощью Python

С помощью Python можно создавать как приложения командной строки, так и кроссплатформенные приложения с графическим интерфейсом, а также развертывать их как автономные исполняемые файлы. Python не имеет встроенной возможности генерировать автономный двоичный файл из скрипта, но для этого можно использовать сторонние пакеты, такие как cx_Freeze и PyInstaller.

Наука о данных и машинное обучение с помощью Python

Сложный анализ данных стал одной из самых быстроразвивающихся областей ИТ и одним из лучших вариантов использования Python. Подавляющее большинство библиотек, используемых для обработки данных или машинного обучения, имеют интерфейсы Python, что делает этот язык самым популярным высокоуровневым командным интерфейсом для библиотек машинного обучения и других числовых алгоритмов.

Веб-сервисы и RESTful API в Python

Собственные библиотеки Python и сторонние веб-фреймворки предоставляют быстрые и удобные способы создания всего: от простых REST API с помощью нескольких строк кода до полноценных сайтов, управляемых данными. В последних версиях Python реализована мощная поддержка асинхронных операций, что позволяет сайтам обрабатывать десятки тысяч запросов в секунду с помощью правильных библиотек.

Метапрограммирование и генерация кода в Python

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

Python также можно использовать для управления системами генерации кода, такими как LLVM, для эффективного создания кода на других языках.

«Склеивающий код» в Python

Python часто называют "связующим языком", что означает, что он позволяет взаимодействовать разрозненным кодам (как правило, библиотекам с интерфейсами на языке C). Его использование в науке о данных и машинном обучении происходит именно в этом ключе, но это лишь одно из воплощений общей идеи. Если у вас есть приложения или домены программ, которые вы хотели бы связать, но не можете общаться друг с другом напрямую, вы можете использовать Python для их соединения.

Где Python терпит неудачу

Также стоит отметить, для каких задач Python не подходит.

Python — это язык высокого уровня, поэтому он не подходит для системного программирования — драйверы устройств или ядра ОС не учитываются.

Кроме того, он не идеален для ситуаций, когда требуются межплатформенные автономные двоичные файлы. Вы можете создать отдельное приложение Python для Windows, MacOS и Linux, но не элегантно и не просто.

Наконец, Python — не лучший выбор, когда скорость является абсолютным приоритетом в каждом аспекте приложения. Для этого вам лучше использовать C/C++ или другой язык такого калибра.

Как Python упрощает программирование

Синтаксис Python должен быть удобочитаемым и чистым, с минимумом претензий. Стандартный «привет, мир» в Python 3.x — это не что иное, как:

Python предоставляет множество синтаксических элементов для краткого описания многих общих программных потоков. Рассмотрим пример программы для чтения строк из текстового файла в объект-список, попутно удаляя из каждой строки завершающий символ новой строки:

Конструкция with/as представляет собой менеджер контекста, который обеспечивает эффективный способ создания экземпляра объекта для блока кода, а затем удаления его вне этого блока. В этом случае объект my_file создается с помощью функции open(). Это заменяет несколько строк стандартного шаблона, чтобы открыть файл, прочитать из него отдельные строки, а затем закрыть его.

Конструкция [x … for x in my_file] — еще одна особенность Python, понимание списка. Он позволяет повторять элемент, который содержит другие элементы (здесь my_file и содержащиеся в нем строки), и позволяет обрабатывать каждый итерируемый элемент (то есть каждый x ) и автоматически добавлять его в список.

Вы можете написать такую ​​вещь, как формальный цикл for… на Python, почти так же, как на другом языке. Дело в том, что в Python есть способ экономичного выражения таких вещей, как циклы, которые перебирают несколько объектов и выполняют простую операцию над каждым элементом в цикле, или для работы с вещами, которые требуют явного создания экземпляров и удаления.

Подобные конструкции позволяют разработчикам Python сбалансировать краткость и удобочитаемость.

Другие языковые функции Python предназначены для дополнения распространенных вариантов использования. Большинство современных типов объектов — например, строки Unicode — встроены непосредственно в язык. Структуры данных, такие как списки, словари (например, хэш-карты или хранилища ключей и значений), кортежи (для хранения неизменяемых коллекций объектов) и наборы (для хранения коллекций уникальных объектов) доступны как стандартные элементы.

Python 2 против Python 3

Python доступен в двух версиях, которые достаточно различаются, чтобы сбить с толку многих новых пользователей. Python 2.x, более старая «унаследованная» ветвь, будет по-прежнему поддерживаться (то есть получать официальные обновления) до 2020 года, и после этого она может сохраняться неофициально. Python 3.x, текущее и будущее воплощение языка, имеет много полезных и важных функций, отсутствующих в Python 2.x, таких как новые возможности синтаксиса (например, «оператор моржа»), улучшенные средства управления параллелизмом и более эффективный интерпретатор.

Внедрение Python 3 долгое время замедлялось из-за относительного отсутствия поддержки сторонних библиотек. Многие библиотеки Python поддерживали только Python 2, что затрудняло переход. Но за последние пару лет количество библиотек, поддерживающих только Python 2, сократилось; все самые популярные библиотеки теперь совместимы как с Python 2, так и с Python 3. Сегодня Python 3 — лучший выбор для новых проектов; нет причин выбирать Python 2, если у вас нет выбора. Если вы застряли на Python 2, в вашем распоряжении есть различные стратегии.

Библиотеки Python

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

Стандартная библиотека Python содержит модули для стандартных задач программирования — математики, обработки строк, доступа к файлам и каталогам, работы в сети, асинхронных операций, многопоточности, многопроцессорного управления и т. д. Но он также включает модули, которые управляют общими высокоуровневыми задачами программирования, необходимыми современным приложениям: чтение и запись форматов структурированных файлов, таких как JSON и XML, управление сжатыми файлами, работа с интернет-протоколами и форматами данных (веб-страницы, URL-адреса, электронная почта). Доступ к большинству внешнего кода, предоставляющего C-совместимый интерфейс внешней функции, можно получить с помощью модуля Python ctypes.

Стандартный дистрибутив Python также предоставляет элементарную, но полезную кросс-платформенную библиотеку графического интерфейса через Tkinter и встроенную копию базы данных SQLite 3.

Тысячи сторонних библиотек, доступных через Индекс пакетов Python (PyPI), представляют собой наглядную демонстрацию популярности и универсальности Python.

Компромиссы Python

Важным аспектом Python является его динамичность. Все в языке, включая сами функции и модули, обрабатывается как объекты. Это происходит за счет скорости (подробнее об этом позже), но значительно упрощает написание высокоуровневого кода. Разработчики могут выполнять сложные манипуляции с объектами с помощью всего нескольких инструкций и даже рассматривать части приложения как абстракции, которые при необходимости можно изменить.

Использование в Python значительных пробелов было названо как одним из лучших, так и худшим атрибутом Python. Отступ во второй строке ниже предназначен не только для удобства чтения; это часть синтаксиса Python. Интерпретаторы Python будут отвергать программы, которые не используют надлежащие отступы для обозначения потока управления.

Синтаксические пробелы могут вызвать сморщивание носа, и некоторые люди отвергают Python по этой причине. Но строгие правила отступов на практике гораздо менее навязчивы, чем могут показаться в теории, даже при использовании самых минимальных редакторов кода, и в результате код становится чище и читабельнее.

Еще одним потенциальным препятствием, особенно для тех, кто пришел из таких языков, как C или Java, является то, как Python обрабатывает типизацию переменных. По умолчанию Python использует динамическую или «утиную» типизацию — отлично подходит для быстрого написания кода, но потенциально проблематична в больших базах кода. Тем не менее, в Python недавно была добавлена ​​поддержка необязательных подсказок типа во время компиляции, так что проекты, которым может быть полезна статическая типизация, могут ее использовать.

Является ли Python медленным? Не обязательно

Один из распространенных недостатков Python заключается в том, что он медленный. Объективно, это правда. Программы на Python обычно работают намного медленнее, чем соответствующие программы на C/C++ или Java. Некоторые программы Python будут работать медленнее на порядок и более.

Читайте также: