Назначение индексов
Отложенные прерывания должны объявляться на этапе компиляции с помощью соответствующего перечисления ( enum
) в файле . Ядро использует указанный в перечислении индекс, который начинается с нуля, как значение относительного приоритета отложенных прерываний. Отложенные прерывания с меньшим номером выполняются раньше отложенных прерываний с большим номером.
Создание нового отложенного прерывания состоит в добавлении новой записи в этот перечень ( enum
). Однако нужно не просто добавить новую строчку в конец списка, как в других местах. Вместо этого нужно вставить строчку в соответствии с приоритетом, который дается этому прерыванию. Исторически, HI_SOFTIRQ
— имеет наибольший приоритет, a TASKLET_SOFTIRQ
— наименьший. Новая запись, скорее всего, должна быть где-то ниже записей для сетевых устройств и выше записи для TASKLET_SOFTIRQ
. В табл. 7.2 показан список всех типов отложенных прерываний.
Таблица 7.2. Список отложенных прерываний
Отложенное прерывание |
Приоритет |
Описание |
HI_SOFTIRQ |
0 |
Высокоприоритетные тасклеты |
TIMER_SOFTIRQ |
1 |
Обработчик нижних половин таймеров |
NET_TX_SOFTIRQ |
2 |
Отправка сетевых пакетов |
NET_RX_SOFTIRQ |
3 |
Прием сетевых пакетов |
SCSI_SOFTIRQ |
4 |
Обработчик нижних половин подсистемы SCSI |
TASKLET_SOFTIRQ |
5 |
Тасклеты |
Регистрация обработчика
Далее во время выполнения должен быть зарегистрирован обработчик отложенного прерывания с помощью вызова open_softirq()
, который принимает три параметра: индекс отложенного прерывания, функция-обработчик и значение поля data
. Для сетевой подсистемы это делается, например, следующим образом.
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
Обработчик отложенного прерывания выполняется при разрешенных прерываниях и не может переходить в состояние ожидания (sleep). Во время выполнения обработчика отложенные прерывания на данном процессоре запрещаются. Однако на другом процессоре обработчики отложенных прерываний могут выполняться. На самом деле, если вдруг генерируется отложенное прерывание в тот момент, когда выполняется его обработчик, то такой же обработчик может быть запущен на другом процессоре одновременно с первым обработчиком. Это означает, что любые совместно используемые данные, которые используются в обработчике отложенного прерывания, и даже глобальные данные, которые используются только в самом обработчике, должны соответствующим образом блокироваться (как показано в следующих двух разделах). Это очень важный момент, и именно по этой причине использование тасклетов обычно предпочтительнее. Простое предотвращение конкурентного выполнения обработчиков — это далеко не идеал. Если обработчик отложенного прерывания просто захватит блокировку, которая предотвращает его выполнение параллельно самому себе, то для использования отложенных прерываний не остается почти никакого смысла. Следовательно, большинство обработчиков отложенных прерываний используют данные, уникальные для каждого процессора (и следовательно, не требующие блокировок), или какие-нибудь другие ухищрения, чтобы избежать явного использования блокировок и обеспечить отличную масштабируемость.
Главная причина использования отложенных прерываний — масштабируемость. Если нет необходимости масштабироваться на бесконечное количество процессоров, то лучше использовать механизм тасклетов. Тасклеты — это отложенные прерывания, для которых обработчик не может выполняться параллельно на нескольких процессорах.
Генерация отложенных прерываний
После того как обработчик добавлен в перечень и зарегистрирован с помощью вызова open_softirq()
, он готов выполняться. Для того чтобы отметить его как ожидающего исполнения и, соответственно, чтобы он выполнился при следующем вызове функции do_softirq()
, необходимо вызвать функцию raise_softirq()
. Например, сетевая подсистема должна вызвать эту функцию в следующем виде.
raise_softirq(NET_TX_SOFTIRQ);
Этот вызов сгенерирует отложенное прерывание с индексом NET_TX_SOFTIRQ
. Соответствующий обработчик net_tx_action()
будет вызван при следующем выполнении программных прерываний ядром. Эта функция запрещает аппаратные прерывания перед тем, как сгенерировать отложенное прерывание, а затем восстанавливает их в первоначальное состояние. Если аппаратные прерывания в данный момент запрещены, то для небольшой оптимизации можно воспользоваться функцией raise_softirq_irqoff()
, как показано в следующем примере.
Читать дальше