Роберт Лав - Разработка ядра Linux
Прерывания
Прерывания позволяют аппаратным устройствам взаимодействовать с процессором. Например, при наборе на клавиатуре контроллер клавиатуры (или другое устройство, которое обслуживает клавиатуру) генерирует прерывание, чтобы объявить операционной системе о том, что произошли нажатия клавиш. Прерывания — это специальные электрические сигналы, которые аппаратные устройства посылают процессору. Процессор получает прерывание и дает сигнал операционной системе о том, что ОС может обработать новые данные. Аппаратные устройства генерируют прерывания асинхронно по отношению к тактовому генератору процессора — прерывания могут возникать непредсказуемо, в любой момент времени. Следовательно, работа ядра может быть прервана в любой момент для того, чтобы обработать прерывания.
Физически прерывания производятся электрическими сигналами, которые создаются устройствами и направляются на входные контакты микросхемы контроллера прерываний. Контроллер прерываний в свою очередь отправляет сигнал процессору. Процессор выполняет детектирование сигнала и прерывает выполнение работы для того, чтобы обработать прерывание. После этого процессор извещает операционную систему о том, что произошло прерывание и операционная система может соответствующим образом это прерывание обработать.
Различные устройства связаны со своими прерываниями с помощью уникальных числовых значений, соответствующих каждому прерыванию. Отсюда следует, что прерывания, поступившие от клавиатуры, отличаются от прерываний, поступивших от жесткого диска. Это позволяет операционной системе различать прерывания и иметь информацию о том, какое аппаратное устройство произвело данное прерывание. Поэтому операционная система может обслуживать каждое прерывание с помощью своего уникального обработчика.
Идентификаторы, соответствующие прерываниям, часто называются линиями запросов на прерывание (interrupt request lines, IRQ lines). Обычно это некоторые числа. Например, для платформы PC значение IRQ, равное 0, — это прерывание таймера, a IRQ, равное 1, — прерывание клавиатуры. Однако не все номера прерываний жестко определены. Прерывания, связанные с устройствами шины PCI, например, назначаются динамически. Другие платформы, которые не поддерживают стандарт PCI, имеют аналогичные функции динамического назначения номеров прерываний. Основная идея состоит в том, что определенные прерывания связаны с определенными устройствами, и у ядра есть вся эта информация. Аппаратное обеспечение, чтобы привлечь внимание ядра, генерирует прерывание вроде «Эй! Было новое нажатие клавиши! Его необходимо обработать!».
Исключительные ситуацииИсключительные ситуации (exceptions) часто рассматриваются вместе с прерываниями. В отличие от прерываний, они возникают синхронно с тактовым генератором процессора. И действительно, их часто называют синхронными прерываниями. Исключительные ситуации генерируются процессором при выполнении машинных инструкций как реакция на ошибку программы (например, деление на нуль) или как реакция на аварийную ситуацию, которая может быть обработана ядром (например, прерывание из-за отсутствия страницы, page fault). Так как большинство аппаратных платформ обрабатывают исключительные ситуации аналогично обработке прерываний, то инфраструктуры ядра, для обоих видов обработки, также аналогичны. Большая часть материала, посвященная обработке прерываний (асинхронных, которые генерируются аппаратными устройствами), также относится и к исключительным ситуациям (синхронным, которые генерируются самим процессором).
С одним типом исключительной ситуации мы уже встречались в предыдущей главе. Там было рассказано, как для аппаратной платформы x86 реализованы системные вызовы на основе программных прерываний. При этом генерируется исключительная ситуация, которая заставляет переключиться в режим ядра и в конечном итоге приводит к выполнению определенного обработчика системного вызова. Прерывания работают аналогичным образом, за исключением того, что прерывания генерируются не программным, а аппаратным обеспечением.
Обработчики прерываний
Функция, которую выполняет ядро в ответ на определенное прерывание, называется обработчиком прерывания (interrupt handler) или подпрограммой обслуживания прерывания (interrupt service routine). Каждому устройству, которое генерирует прерывания, соответствует свой обработчик прерывания. Например, одна функция обрабатывает прерывание от системного таймера, а другая — прерывания, сгенерированные клавиатурой. Обработчик прерывания для какого-либо устройства является частью драйвера этого устройства — кода ядра, который управляет устройством.
В операционной системе Linux обработчики прерываний — это обычные функции, написанные на языке программирования С. Они должны соответствовать определенному прототипу, чтобы ядро могло стандартным образом принимать информацию об обработчике, а в остальном— это обычные функции. Единственное, что отличает обработчики прерываний от других функций ядра, — это то, что они вызываются ядром в ответ на прерывание и выполняются в специальном контексте, именуемом контекстом прерывания (interrupt context), который будет рассмотрен далее.
Так как прерывание может возникнуть в любой момент времени, то, соответственно, и обработчик прерывания может быть вызван в любой момент времени. Крайне важно, чтобы обработчик прерывания выполнялся очень быстро и возобновлял управление прерванного кода по возможности быстро. Поэтому, хотя для аппаратного обеспечения и важно, чтобы прерывание обслуживалось немедленно, для остальной системы важно, чтобы обработчик прерывания выполнялся в течение максимально короткого промежутка времени. Минимально возможная работа, которую должен сделать обработчик прерывания, — это отправить подтверждение устройству, что прерывание получено. Однако обычно обработчики прерываний должны выполнить большее количество работы. Например, рассмотрим обработчик прерывания сетевого устройства. Вместе с отправкой подтверждения аппаратному обеспечению, обработчик прерывания должен скопировать сетевые пакеты из аппаратного устройства в память системы, обработать их, отправить соответствующему стеку протоколов или соответствующей программе. Очевидно, что для этого требуется много работы.
Верхняя и нижняя половины
Ясно, что два указанных требования о том, что обработчик прерывания должен выполняться быстро и, в дополнение к этому, выполнять много работы, являются противоречивыми. В связи с конфликтными требованиями, обработчик прерываний разбивается на две части, или половины. Обработчик прерывания является верхней половиной (top half) — он выполняется сразу после приема прерывания и выполняет работу, критичную к задержкам во времени, такую как отправка подтверждения о получении прерывания или сброс аппаратного устройства. Работа, которую можно выполнить позже, откладывается до выполнения нижней (или основной) половины (bottom half). Нижняя половина обрабатывается позже, в более удобное время, когда все прерывания разрешены. Достаточно часто нижняя половина выполняется сразу же после возврата из обработчика прерывания.
Операционная система предоставляет различные механизмы для реализации обработки нижних половин, которые обсуждаются в главе 7, "Обработка нижних половин и отложенные действия".
Рассмотрим пример разделения обработчика прерывания на верхнюю и нижнюю половины на основе старой доброй сетевой платы. Когда сетевой интерфейсный адаптер получает входящие из сети пакеты, он должен уведомить ядро о том, что доступны новые данные. Это необходимо сделать немедленно, чтобы получить оптимальную пропускную способность и время задержки при передаче информации по сети. Поэтому немедленно генерируется прерывание: «Эй, ядро! Есть свежие пакеты!». Ядро отвечает выполнением зарегистрированного обработчика прерывания от сетевого адаптера.
Обработчик прерывания выполняется, аппаратному обеспечению направляется подтверждение, пакеты копируются в основную память, и после этого сетевой адаптер готов к получению новых пакетов. Эта задача является важной, критичной ко времени выполнения и специфической для каждого типа аппаратного обеспечения. Остальная часть обработки сетевых пакетов выполняется позже — нижней половиной обработчика прерывания. В этой главе мы рассмотрим обработку верхних половин, а в следующей — нижних.
Регистрация обработчика прерывания
Ответственность за обработчики прерываний лежит на драйверах устройств, которые управляют определенным типом аппаратного обеспечения. С каждым устройством связан драйвер, и если устройство использует прерывания (а большинство использует), то драйвер должен выполнить регистрацию обработчика прерывания.