Разработка ядра Linux - Роберт Лав
Шрифт:
Интервал:
Закладка:
• Когда значение битовой маски отложенных прерываний сохранено, оригинальная битовая маска очищается[38].
• Переменной h присваивается указатель на первый элемент массива softirq_vec.
• Если первый бит маски, которая хранится в переменной pending, установлен, то вызывается функция h->action(h).
• Указатель h увеличивается на единицу, и теперь он указывает на второй элемент массива softirq_vec.
• Осуществляется логический сдвиг битовой маски, хранящейся в переменной pending, вправо на один разряд. Эта операция отбрасывает самый младший бит и сдвигает все оставшиеся биты на одну позицию вправо. Следовательно, второй бит теперь стал первым и т.д.
• Указатель h теперь указывает на второй элемент массива, а в битовой маске — второй бит стал первым. Теперь необходимо повторить все ранее проделанные шаги.
• Последовательное повторение производится до тех пор, пока битовая маска не станет равной нулю. В этот момент больше нет ожидающих отложенных прерываний, и наша работа выполнена. Заметим, что такой проверки достаточно для того, чтобы гарантировать, что указатель h всегда указывает на законную запись в массиве softirq_vec, так как битовая маска pending имеет 32 бит и цикл не может выполниться больше 32 раз.
Использование отложенных прерываний
Отложенные прерывания зарезервированы для наиболее важных и критичных ко времени выполнения обработчиков нижних половин в системе. Сейчас только две подсистемы — подсистема SCSI и сетевая подсистема — напрямую используют механизм softirq. В дополнение к этому, таймеры ядра и тасклеты построены на базе отложенных прерываний. Если есть желание добавить новое отложенное прерывание, то стоит себя спросить, почему будет недостаточно использования тасклетов. Тасклеты могут создаваться динамически, а также их легче использовать в связи с более простыми требованиями к блокировкам. Кроме того, их производительность все еще остается очень хорошей. Тем не менее для задач, критичных ко времени выполнения, которые способны сами обеспечивать эффективные блокировки, использование механизма softirq — будет правильным решением.
Назначение индексовОтложенные прерывания должны объявляться на этапе компиляции с помощью соответствующего перечисления (enum) в файле <linux/interrupt.h>. Ядро использует указанный в перечислении индекс, который начинается с нуля, как значение относительного приоритета отложенных прерываний. Отложенные прерывания с меньшим номером выполняются раньше отложенных прерываний с большим номером.
Создание нового отложенного прерывания состоит в добавлении новой записи в этот перечень (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(), как показано в следующем примере.
/*
* прерывания должны быть запрещены!
*/
raise_softirq_irqoff(NET_TX_SOFTIRQ);
Наиболее часто отложенные прерывания генерируются из обработчиков аппаратных прерываний. В этом случае обработчик аппаратного прерывания выполняет всю основную работу, которая касается аппаратного обеспечения, генерирует отложенное прерывание и завершается. После завершения обработки аппаратных прерываний ядро вызывает функцию do_softirq(). Обработчик отложенного прерывания выполняется и подхватывает работу с того места, где обработчик аппаратного прерывания ее отложил. В таком примере раскрывается смысл названий "верхняя половина" и "нижняя половина".
Тасклеты
Тасклеты — это механизм обработки нижних половин, построенный на основе механизма отложенных прерываний. Как уже отмечалось, они не имеют ничего общего с заданиями (task). Тасклеты по своей природе и принципу работы очень похожи на отложенные прерывания. Тем не менее они имеют более простой интерфейс и упрощенные правила блокировок.
Решение о том, стоит ли использовать тасклеты, принять достаточно просто: в большинстве случаев необходимо использовать тасклеты. Как было показано в предыдущем разделе, примеры использования отложенных прерываний можно посчитать на пальцах одной руки. Отложенные прерывания необходимо использовать только в случае, когда необходима очень большая частота выполнений и интенсивно используется многопоточная обработка. Тасклеты используются в очень большом количестве случаев — они работают достаточно хорошо и их очень просто использовать.