Главная » Ядро Linux » Механизм отложенных прерываний (softirq) – ЧАСТЬ 1

0

Обсуждение существующих методов  обработки нижних половин начнем с механизма  softirq.  Обработчики на основе  механизма отложенных прерываний используются  редко.  Тасклеты — это более часто используемая форма  обработчика нижних половин. Поскольку тасклеты построены на основе  механизма softirq, с механизма softirq  и стоит  начать.  Код, который касается обработчиков отложенных прерываний, описан в файле  kernel/softirq.с .

Реализация  отложенных прерываний

Отложенные прерывания определяются статически во время  компиляции. В отличие от тасклетов, нельзя  динамически создать  или освободить отложенное прерывание.  Отложенные прерывания представлены с помощью структур  softirq_action , определенных в файле  <linux/interrupt.h > в следующем виде.

/*

* структура,  представляющая одно отложенное  прерывание

*/

struct softirq_action

{

void (*action)(struct softirq_action *) ;

/* функция, которая должна выполниться */

void *data;     /* данные для передачи в функцию */

};

Массив из 32 экземпляров этой структуры определен  в файле  kernel/so f tirq. с в следующем виде.

static struct softirq_action softirq_vec[32];

Каждое  зарегистрированное отложенное  прерывание  соответствует одному элементу этого массива.  Следовательно,  имеется возможность  создать 32 обработчика softirq. Заметим,  что это количество  фиксированно. Максимальное  число обработчиков softirq не может быть динамически  изменено.  В текущей версии ядра из 32 элементов используется только шесть4.

Обработчик softirq

Прототип  обработчика отложенного прерывания, поля action , выглядит следующим образом.

void softirq_handler(struct softirg_action *)

Когда ядро выполняет обработчик отложенного прерывания, то функция  actio n вызывается  С указателем  на соответствующую структуру softirq_actio n в качестве аргумента. Например,  если переменная  rny_softirq содержит указатель на элемент массива  softirq_vec ,  то ядро  вызовет  функцию-обработчик соответствующего  отложенного  прерывания  в следующем виде.

my_softirq->action(my_softirq)

Может быть,  несколько  удивляет,  что ядро передает в обработчик  указатель на всю структуру, а не только на поле data . Этот прием позволяет в будущем вводить дополнительные  поля в структуру без необходимости  внесения  изменений  в существующие обработчики.  Обработчик  может получить доступ к значению  ноля dat a простым разыменованием  указателя на структуру и чтением ее поля data .

Обработчик  одного отложенного  прерывания  никогда не вытесняет другой обработчик softirq. Б действительности,  единственное  событие,  которое может вытеснить обработчик softirq, — это аппаратное  прерывание.  Однако на другом процессоре одновременно  с обработчиком  отложенного  прерывания  может выполняться другой (и даже этот же) обработчик  отложенного прерывания.

Выполнение отложенных прерываний

Зарегистрированное отложенное  прерывание  должно быть отмечено для того, чтобыегоможнобыловыполнить.Этоназываетсягенерациейотложенногопрерывания (rise softirq). Обычно обработчик аппаратного прерывания  перед возвратом отмечает свои обработчики  отложенных прерываний.  Затем в подходящий  момент времени отложенное  прерывание  выполняется.  Ожидающие выполнения  обработчики отложенных прерываний  проверяются  и выполняются  в следующих ситуациях.

4 Большинств о драйверо в  использует  для  обработк и  своих  нижни х  полови н  механизм  тасклетов, Тасклеты  построен ы  на  механизме  softirq,  как  это  будет покапано   ниже.

• После обработки аппаратного прерывания.

• В контексте  потока пространства  ядра ksoftirqd .

• В любом коде ядра, который явно проверяет и выполняет ожидающие обработчики отложенных прерываний, как, например,  это делает сетевая подсистема.

Независимо  от того, каким способом  выполняется  отложенное  прерывание,  его выполнение  осуществляется  в функции  do_softir q () . Эта функция  по-настоящему проста.  Если есть ожидающие  отложенные  прерывания, то функция  do_softirq( ) в цикле проверяет их все и вызывает ожидающие обработчики.  Давайте рассмотрим упрощенный  вариант наиболее важной части функции  do_sof tir q () .

u32 pending = softirq_pending(cpu);

if (pending) {

struct softirq_action *h = softirq_vec;

softirq_pending(cpu) = 0;

do {

if (pending & 1) h->action (h); h++;

pending »= 1;

} while (pending);

}

Этот фрагмент кода является сердцем обработчика отложенных прерываний.  Он проверяет и выполняет  все ожидающие отложенные прерывания.

• Присваивает  локальной  переменной  pending значение,  возвращаемое макро сом softirq_pendin g () . Это значение — 32-х разрядная  битовая маска ожидающих на выполнение  отложенных прерываний.  Если установлен бит с номером n, то отложенное прерывание  с этим номером ожидает на выполнение.

• Когда значение битовой маски отложенных прерываний  сохранено, оригиналь ная битовая маска очищается5.

• Переменной h присваивается  указатель на первый элемент массива softirq_vec.

• Если первый бит маски, которая хранится в переменной  pending, установлен, то вызывается функция  h->action(h) .

• Указатель h увеличивается  на единицу,  и теперь он указывает на второй элемент  массива  softirq_vec .

• Осуществляется логический  сдвиг битовой маски,  хранящейся  в переменной pending , вправо на один разряд.  Эта операция  отбрасывает самый  младший бит и сдвигает все оставшиеся  биты на одну позицию  вправо.  Следовательно,  второй бит теперь стал первым и т.д.

5 На самом  деле  эта  операци я  выполняетс я  при  всех  запрещенны х на  локальном   процессор е  прерываниях,   что  не  показано  в упрощенно й  версии.  Если  бы  прерывани я  не  были  запрещены ,  то  в период  времен и  между сохранением   и  очистко й  маски   могло  бы  быть  сгенерирован о  новое  отложенно е  прерывани е  (которо е  бы  ожидало  на  выполнение) ,  что  привел о  бы  к  неверно й  очистк е бита  соответствующего  отложенного   прерывания .

• Указатель 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_SOFTIR Q

TIMER_SOFTIRQ

Приоритет

1

Описание

Высокоприоритетные тасклеты

Обработчик нижних половин таймеров

NET_TX_S0FTIR Q

2

Отправка сетевых пакетов

NET_RX_SOFTIRQ

3

Прием сетевых пакетов

SCSI_SOFTIR Q

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).   Во  время  выполнения обработчика отложенные прерывания  на  данном процессоре запрещаются. Однако на другом  процессоре обработчики отложенных прерываний могут  выполняться. На самом  деле,  если  вдруг  генерируется отложенное  прерывание  в  тот  момент,  когда выполняется  его  обработчик, то  такой  же  обработчик может  быть  запущен на  другом  процессоре одновременно  с  первым обработчиком.  Это  означает,  что  любые совместно используемые данные,  которые используются в  обработчике отложенного  прерывания,  и  даже  глобальные данные,  которые используются только   в  самом  обработчике,  должны соответствующим образом блокироваться  (как  показано в  следующих двух  разделах).  Это  очень  важный момент,  и  именно по  этой  причине  использование  тасклетов обычно  предпочтительнее.  Простое  предотвращение конкурентного выполнения обработчиков — это  далеко  не  идеал.  Если   обработчик отложенного  прерывания  просто   захватит   блокировку,  которая  предотвращает его выполнение параллельно самому  себе, то для  использования  отложенных прерыпаний  не  остается почти  никакого смысла. Следовательно, большинство обработчиков отложенных прерываний  используют данные,  уникальные для  каждого   процессора (и  следовательно, не  требующие блокировок),  или  какие-нибудь другие  ухищрения, чтобы  избежать явного использования  блокировок  и  обеспечить отличную масштабируемость.

Источник: Лав,  Роберт. Разработка ядра  Linux, 2-е  издание. : Пер.  с англ.  — М.  : ООО  «И.Д.  Вильяме» 2006. — 448 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии