Роберт Лав - Разработка ядра Linux
Было бы также плохим тоном запрещать линию прерывания, которая совместно используется несколькими обработчиками. Запрещение линии прерывания запрещает доставку прерываний для всех устройств, которые используют эту линию. Поэтому в драйверах новых устройств не рекомендуется использовать эти интерфейсы[33]. Так как устройства PCI должны согласно спецификации поддерживать совместное использование линий прерываний, они вообще не должны использовать эти интерфейсы. Поэтому функция disable_irq() и дружественные ей обычно используются для устаревших устройств, таких как параллельный порт персонального компьютера.
Состояние системы обработки прерываний
Часто необходимо знать состояние системы обработки прерываний (например, прерывания запрещены или разрешены, выполняется ли текущий код в контексте прерывания или в контексте процесса).
Макрос irq_disabled(), который определен в файле <asm/system.h>, возвращает ненулевое значение, если обработка прерываний на локальном процессоре запрещена. В противном случае возвращается нуль. Два следующих макроса позволяют определить контекст, в котором в данный момент выполняется ядро.
in_interrupt()
in_irq()
Наиболее полезный из них — это первый макрос. Он возвращает ненулевое значение, если ядро выполняется в контексте прерывания. Это включает выполнение как обработчика прерывания, так и обработчика нижней половины. Макрос in_irq() возвращает ненулевое значение, только если ядро выполняет обработчик прерывания.
Наиболее часто необходимо проверять, выполняется ли код в контексте процесса, т.е. необходимо проверить, что код выполняется не в контексте прерывания. Это требуется достаточно часто, когда коду необходимо выполнять что-то, что может быть выполнено только из контекста процесса, например переход в приостановленное состояние. Если макрос in_interrupt() возвращает нулевое значение, то ядро выполняется в контексте процесса.
Таблица 6.2. Список функций управления прерываниями
Функция Описание local_irq_disable() Запретить доставку прерываний на локальном процессоре local_irq_enable() Разрешить доставку прерываний на локальном процессоре local_irq_save(unsigned long flags) Сохранить текущее состояние системы обработки прерываний на локальном процессоре и запретить прерывания local_irq_restore(unsigned long flags) Восстановить указанное состояние системы прерываний на локальном процессоре disable_irq(unsigned int irq) Запретить указанную линию прерывания с гарантией, что после возврата из этой функции не выполняется ни один обработчик данной линии disable_irq_nosync(unsigned int irq) Запретить указанную линию прерывания enable_irq(unsigned int irq) Разрешить указанную линию прерываний irqs_disabled() Возвратить ненулевое значение, если запрещена доставка прерываний на локальном процессоре, в противном случае возвращается нуль in_interrupt() Возвратить ненулевое значение, если выполнение производится в контексте прерывания, и нуль — если в контексте процесса in_irq() Возвратить ненулевое значение, если выполнение производится в контексте прерывания, и нуль — в противном случаеНе нужно прерывать, мы почти закончили!
В этой главе были рассмотрены прерывания, аппаратные ресурсы, которые используются устройствами для подачи асинхронных сигналов процессору. Прерывания используются аппаратным обеспечением, чтобы прервать работу операционной системы.
Большинство современного аппаратного обеспечения использует прерывания, чтобы взаимодействовать с операционной системой. Драйвер устройства, который управляет некоторым оборудованием, должен зарегистрировать обработчик прерывания, чтобы отвечать на эти прерывания и обрабатывать их. Работа, которая выполняется обработчиками прерываний, включает отправку подтверждения устройству о получении прерывания, инициализацию аппаратного устройства, копирование данных из памяти устройства в память системы и, наоборот, обработку аппаратных запросов и отправку ответов на них.
Ядро предоставляет интерфейсы для регистрации и освобождения обработчиков прерываний, запрещения прерываний, маскирования линий прерываний и проверки состояния системы прерываний. В табл. 6.2 приведен обзор некоторых из этих функций.
Так как прерывания прерывают выполнение другого кода (кода процессов, кода ядра и другие обработчики прерываний), то они должны выполняться быстро. Тем не менее часто приходится выполнять много работы. Для достижения компромисса между большим количеством работы и необходимостью быстрого выполнения обработка прерывания делится на две половины. Верхняя половина — собственно обработчик прерывания — рассматривается в этой главе. Теперь давайте рассмотрим нижнюю половину процесса обработки прерывания.
Глава 7
Обработка нижних половин и отложенные действия
В предыдущей главе были рассмотрены обработчики прерываний — механизм ядра, который позволяет решать задачи, связанные с аппаратными прерываниями. Конечно, обработчики прерываний очень полезны и являются необходимой частью ядра. Однако, в связи с некоторыми ограничениями, они представляют собой лишь часть процесса обработки прерываний. Эти ограничения включают следующие моменты.
• Обработчики прерываний выполняются асинхронно и потенциально могут прерывать выполнение другого важного кода (даже другие обработчики прерываний). Поэтому обработчики прерываний должны выполняться как можно быстрее.
• Обработчики прерываний выполняются в лучшем случае при запрещенной обрабатываемой линии прерывания и в худшем случае (когда установлен флаг SA_INTERRUPT) — при всех запрещенных линиях запросов на прерывания. И снова они должны выполняться как можно быстрее.
• Обработчики прерываний очень критичны ко времени выполнения, так как они имеют дело с аппаратным обеспечением.
• Обработчики прерываний не выполняются в контексте процесса, поэтому они не могут блокироваться.
Теперь должно быть очевидным, что обработчики прерываний являются только частью полного решения проблемы обработки аппаратных прерываний. Конечно, необходим быстрый, простой и асинхронный обработчик, позволяющий немедленно отвечать на запросы оборудования и выполнять критичную ко времени выполнения работу. Эту функцию обработчики прерываний выполняют хорошо, но другая, менее критичная ко времени выполнения работа должна быть отложена до того момента, когда прерывания будут разрешены.
Для этого обработка прерывания делится на две части или половины. Первая часть обработчика прерывания (top half, верхняя половина) выполняется асинхронно и немедленно в ответ на аппаратное прерывание так, как это обсуждалось в предыдущей главе. В этой главе мы рассмотрим вторую часть процесса обработки прерываний — нижние половины (bottom half).
Нижние половины
Задача обработки нижних половин — это выполнить всю связанную с прерываниями работу, которую не выполнил обработчик прерывания. В идеальной ситуации — это почти вся работа, так как необходимо, чтобы обработчик прерывания выполнил по возможности меньшую часть работы (т.е. выполнился максимально быстро) и побыстрее возвратил управление.
Тем не менее обработчик прерывания должен выполнить некоторые действия. Например, почти всегда обработчик прерывания должен отправить устройству уведомление, что прерывание получено. Он также должен произвести копирование некоторых данных из аппаратного устройства. Эта работа чувствительна ко времени выполнения, поэтому есть смысл выполнить ее в самом обработчике прерывания.
Практически все остальные действия будет правильным выполнить в обработчике нижней половины. Например, если в верхней половине было произведено копирование данных из аппаратного устройства в память, то в обработчике нижней половины, конечно, имеет смысл эти данные обработать. К сожалению, не существует твердых правил, позволяющих определить, какую работу где нужно выполнять, — право решения остается за автором драйвера. Хотя ни одно из решений этой задачи не может быть неправильным, решение легко может оказаться неоптимальным. Следует помнить, что обработчики прерываний выполняются асинхронно при запрещенной, по крайней мере, текущей линии запроса на прерывание. Минимизация этой задержки является важной. Хотя нет строгих правил по поводу того, как делить работу между обработчиками верхней и нижней половин, все же можно привести несколько полезных советов.