Прерываемые приложения
В этой лекции мы поговорим об эффективной обработке ввода-вывода. Сначала мы поговорим о двух методах обработки ввода-вывода, называемых опросом и прерываниями. Затем мы создадим простое, но ужасное GPIO-приложение, просто чтобы показать вам, насколько плохим может быть опрос. Для того чтобы решить эту проблему, нам придется узнать, как обычно работают прерывания, затем как они работают в микроконтроллере FE310. А после этого, мы создадим простое приложение, управляемое прерываниями GPIO, чтобы познакомить вас с процессом обработки прерываний. И после того, как мы создадим это приложение, мы сможем перейти к исправлению нашего примера опроса с помощью прерываний. Вы увидите, что эта замена приложения намного лучше, чем первая попытка. Нам еще многому предстоит научиться.
Так что давайте приступим к делу.
К концу этой лекции вы должны:
- Иметь представление о том, как работает приложение, управляемое прерываниями.
- Иметь четкое представление о том, как режимы привилегий RISC-V обрабатывают прерывания.
- Уметь работать с системой с несколькими уровнями контроллеров прерываний.
- Понимать, как настроить устройства GPIO и ШИМ в микроконтроллере FE310 для обработки прерываний.
- Уметь преобразовать приложение с опросом в приложение, управляемое прерываниями.
Техники обработки ввода-вывода
Существует несколько подходов к работе с устройствами ввода/вывода. Мы использовали прямой подход, называемый опросом, поскольку он заключается в опросе всех используемых устройств ввода-вывода в поисках того, которое требует нашего внимания (внимания процессора). Именно этим занимались наши приложения в бесконечном главном цикле.
Более эффективная техника обработки ввода-вывода называется обработкой прерываний. Она использует уникальный аппаратный элемент в ядре процессора, который выполняет опрос за вас (грубо говоря). Всякий раз, когда устройство ввода-вывода требует внимания процессора, логика этого устройства устанавливает бит, который запускает процесс прерывания
Когда происходит прерывание, центральный процессор останавливает выполнение кода, который выполнялся в данный момент, сохраняет часть состояния процессора в стеке и переходит к выполнению функции обратного вызова, известной как подпрограмма обслуживания прерывания (Interrupt Service Routine, ISR), или обработчик прерывания.
Каждое поддерживаемое прерывание имеет свой обработчик прерывания. Задача программиста - написать эти обработчики прерываний с кодом, который будет либо принимать входящие данные, либо отправлять исходящие данные. После выполнения обработчика прерывания, вместо того чтобы вернуться в виде вызванной функции, процессор возобновляет работу, которую он выполнял, когда был прерван. Обработка прерываний - это высокоэффективная техника обработки ввода-вывода, в то время как опрос влечет за собой ненужные накладные расходы, которые обычно впустую расходуют наше драгоценное процессорное время. Именно поэтому опрос обычно делает пользовательские интерфейсы менее отзывчивыми, а вычисления медленными.
Мысленный эксперимент
Давайте рассмотрим мысленный эксперимент для лучшего понимания опроса и обработки прерываний. Предположим, вы отправились в торговый центр, потому что вам нужно купить несколько вещей в разных магазинах. Прежде чем отправиться в магазины, вы заходите в фуд-корт и заказываете еду, которая займет некоторое время. Вы не знаете, сколько времени потребуется на приготовление. Это может быть от 10 до 45 минут. Как же вы собираетесь делать покупки?
Подход, основанный на опросе
Подход, основанный на опросе, можно сравнить с походом в магазин за покупками на 5 минут, а затем возвращением в фуд-корт и вопросом о вашем блюде. Если оно еще не готово, тогда вы идете за покупками на 5 минут, возвращаетесь в фуд-корт и спрашиваете снова. Так продолжается до тех пор, пока ваше блюдо не будет готово. Когда оно готово, вы садитесь и едите. Затем вы можете продолжить покупки.
Подход, основанный на прерываниях
Подход, основанный на прерываниях, похож на то, как если бы вы оставили номер своего мобильного телефона, чтобы персонал ресторана позвонил вам, когда ваше блюдо будет готово. Когда вам позвонят, вы должны будете прекратить делать то, что делали в этот момент, и пойти в фуд-корт, чтобы поесть. После еды вы можете вернуться в магазин и продолжить начатое. Таким образом, вы можете ходить за покупками без всякого беспокойства.
Какой подход лучше?
Теперь мы добавим несколько чисел, чтобы нам было легче провести сравнение. Это будет очень упрощенно: допустим, что путь от магазина до фуд-корта занимает 2 минуты (таким образом, путь туда и обратно займет 4 минуты), и что в итоге еда будет готова через 30 минут после заказа.
При подходе, основанном на опросе, ваше драгоценное время будет использовано следующим образом: через 34 минуты вы потратите на покупки 20 минут, а на вопросы о еде - 14 минут.
Эффективность
Эффективность - одна из немногих метрик, которые волнуют инженеров. Она означает, насколько эффективно используется ограниченный ресурс. Стремление к максимизации эффективности эквивалентно стремлению к минимизации отходов. Существует столько же форм измерения эффективности, сколько и ограниченных ресурсов. Эффективность часто обозначается греческой буквой eta ().
Если мы определим эффективность как процент от общего времени, которое вы потратили на полезную работу (покупки), то в данном случае эффективность будет следующей:
![\eta_{Tine}=\frac{t_{Shopping}}{t_{Total}}*100%=\frac{20minutes}{34minutes}*100%\approx59%](/sites/default/files/tex_cache/3bef248c36af7e3beafa307b8a439857.png)
Эффективность также можно рассчитать, как количество посещений фуд-корта, которые оказались полезными, из общего числа посещений:
![\eta_{Visits}=\frac{Visits_{Useful}}{Visits_{Total}}*100%=\frac{1visit}{4visits}*100%\approx25%](/sites/default/files/tex_cache/3faea592cdcafa674bcc0b53dc4876e8.png)
При подходе, основанном на прерывании, вы потратите 30 минут на покупки, плюс время, необходимое для того, чтобы ответить на звонок и забрать продукты:
Рассчитывая те же показатели эффективности для подхода с прерыванием, мы получаем следующее:
Метод опроса | Метод прерываний | |
---|---|---|
Эффективность использования времени | 59% | 94% |
Эффективность визитов на фудкорт | 25% | 100% |
Ускорение
Еще одна метрика - Ускорение, которая измеряет, насколько быстрее работает один вариант по сравнению с другим. К тому времени, когда вы получите еду, вы проведете за покупками 20 минут в случае с методом опроса, и 30 минут в случае использования прерываний. Таким образом, если сравнивать варианты по истечении 32 минут, то ускорение при использовании прерываний по сравнению с методом опроса составит:
![Speedup=\frac{Time_{Polling}}{Time_{Interrupts}}=\frac{30minutes}{20minutes}=1.5](/sites/default/files/tex_cache/632f07b24a7c3b0e839ed7898ddcdb47.png)
Другими словами, с помощью прерываний работа выполняется в 1,5 раза быстрее по сравнению с методом опросом в данном примере.
Пример встраиваемой системы
Теперь рассмотрим встроенную систему. Здесь мы имеем код опроса для автомобильного приложения, которое управляет приборной панелью в автомобиле. Предположим, что все функции являются блокирующими, и каждой из них требуется время, указанное в комментарии справа, чтобы вернуться.
void main(){ while(1){ read_touch_screen(); // 100ms read_GPS(); // 300ms read_fuel_level(); // 200ms read_velocity(); // 200ms read_temperature(); // 100ms refresh_door_light(); // 100ms refresh_display(); // 200ms } }
Очевидно, что это приложение демонстрирует чрезмерное использование кода в главном цикле. Обратите внимание, что первая функция считывает данные с сенсорного экрана, а последняя обновляет дисплей. Вся работа между ними, безусловно, сделает приложение неотзывчивым. Также обратите внимание, что нет необходимости проверять каждое устройство ввода-вывода на каждой отдельной итерации главного цикла. Вот как выглядит выполнение с течением времени: