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

0

Главная причина использования  отложенных прерываний — масштабируемость. Если  нет  необходимости  масштабироваться  на  бесконечное количество процессоров, то лучше  использовать механизм тасклетов. Тасклеты — это  отложенные прерывания, для  которых обработчик не  может  выполняться  параллельно на  нескольких процессорах.

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

После   того  как  обработчик добавлен в перечень и  зарегистрирован  с  помощью вызова   open_softir q ()  , он  готов  выполняться. Для  того  чтобы  отметить его  как ожидающего исполнения и,  соответственно, чтобы  он  выполнился при  следующем вызове   функции   do_softirq() ,  необходимо  вызвать   функцию   raise_softirq() . Например, сетевая  подсистема должна  вызвать  эту  функцию в следующем виде.

raise_softirq(NET_TX_SOFTIRQ);

Этот  вызов  сгенерирует отложенное прерывание  с  индексом NET_TX_SOFTIRQ. Соответствующий  обработчик  net_tx_actio n  ()   будет  вызван при  следующем выполнении  программных прерывани й  ядром.   Эта  функция  запрещает аппаратные прерывания перед  тем, как  сгенерировать отложенное прерывание,  а затем  восстанавливает их в первоначальное состояние. Если  аппаратные прерывания в данный

момент запрещены,  то для небольшой оптимизации  можно воспользоваться  функцией raise_so f tirq_irqof f (),  как  показано  в следующем примере.

/*

* прерывания должны быть запрещены!

*/

raise_softirq_irqoff(NET_TX_SOFTIRQ);

Наиболее  часто отложенные  прерывания  генерируются  из обработчиков  аппаратных прерываний.  В этом случае обработчик аппаратного  прерывания  выполняет всю основную работу, которая касается  аппаратного  обеспечения,  генерирует отложенное прерывание  и завершается.  После завершения  обработки аппаратных прерываний  ядро вызывает функцию  do_softirq() . Обработчик  отложенного  прерывания выполняется  и подхватывает работу с того места, где обработчик аппаратного прерывания  ее отложил.  В таком примере раскрывается  смысл названий  "верхняя половина" и "нижняя половина".

Тасклеты

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

Решение  о том, стоит ли использовать тасклеты,  принять достаточно просто: в большинстве  случаев необходимо использовать тасклеты. Как было показано  в предыдущем разделе, примеры использования отложенных прерываний  можно посчитать на пальцах одной руки. Отложенные прерывания  необходимо использовать только в случае, когда необходима очень большая частота выполнений  и интенсивно  используется многопоточная  обработка.  Тасклеты используются в очень большом количестве случаев — они работают достаточно хорошо и их очень просто использовать.

Реализация тасклетов

Так  как  тасклеты реализованы на  основе отложенных прерываний,  они  тоже  являются  отложенными прерываниями (softirq). Как  уже рассказывалось, тасклеты представлены двумя  типами отложенных прерываний: HI_SOFTIRQ  и TASKLET_SOFTIRQ. Единственная  разница   между  ними   в  том,   что  тасклеты  типа  HI_SOFTIRQ  выполняются  всегда  раньше тасклетов типа  TASKLET_SOFTIRQ.

Структуры  тасклетов

Тасклеты  представлены  с помощью  структуры tasklet_struct . Каждый  экземпляр структуры представляет собой уникальный  тасклет. Эта структура определена в заголовочном  файле  <linux/interrupt.h > в следующем виде.

struct tasklet_struct {

struct tasklet_struct *next;  /* указатель на следующий тасклет в списке */

unsigned long state;         /* состояние тасклета */

}

atomic_t count;              /* счетчик ссылок */

void (*func) (unsigned long); /* функция-обработчик тасклета*/

unsigned long data; /* аргумент функции-обработчика тасклета */

);

Поле   fun c — это  функция-обработчик тасклета   (эквивалент поля   actio n  для структуры,  представляющей  отложенное  прерывание),  которая  получает  поле   dat a в  качестве   единственного  аргумента при  вызове.

Поле   stat e  может   принимать  одно   из  следующих   значений:  нуль,  TASKLET_ STATE_SCHED или  TASLET_STATE_RUN.  Значение  TASKLET_STATE_SCHED указывает на то, что  тасклет  запланирован на  выполнение, а значение TASLET_STATE_RUN — что  тасклет  выполняется. Для  оптимизации значение TASLET_STATE RUN  может  использоваться только  на  многопроцессорной машине,  так  как  на  однопроцессорной машине и  без  этого  точно  известно,  выполняется ли  тасклет   (действительно, ведь код, который выполняется, либо  принадлежит тасклету, либо  нет).

Поле   coun t  используется как  счетчик ссылок на  тасклет.   Если  это  значение не равно  нулю, то тасклет  запрещен и  не  может  выполняться;  если  оно  равно  нулю, то тасклет  разрешен и  может  выполняться в  случае,  когда  он  помечен как  ожидающий выполнения.

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

Запланированные    (scheduled)   на   выполнени е  тасклеты   (эквивалент  сгенерированных   отложенных  прерываний) 6    хранятся  в  двух  структурах,  определенных  для каждого   процессора:  структуре   tasklet_ve c   (для   обычных  тасклетов)  и  структуре tasklet_hi_ve c  (для   высокоприоритетных  тасклетов).  Каждая  из  этих  структур  — это   связанный  список  структур   tasklet_struct .  Каждый  экземпляр  структуры tasklet_struc t    представляет собой   отдельный тасклет.

Тасклеты  могут  быт ь  запланирован ы  на  выполнени е  с  помощь ю  функци й tasklet_schedul e  ()    и  tasklet_hi_schedul e () ,  которые принимают единственный   аргумент—  указатель   на  структуру   тасклета—   tasklet_struct .  Эти   функции очень  похожи   (отличие состоит  в том, что  одна  использует отложенное прерывание с номером TASKLET_SOFTIRQ, а другая — с номером HI_SOFTIRQ). К написанию и использованию тасклетов мы  вернемся в следующем разделе.  А сейчас  рассмотрим детали  реализации функции  tasklet_hi_schedul e  () , которые состоят  в  следующем.

• Проверяется ,  не  установлено ли  поле   stat e  в  значение   TASKLET_STATE_ SCHED.  Если  установлено, то тасклет  уже запланирован на выполнение и функция  может  возвратить управление.

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

• Добавляется тасклет, который планируется на  выполнение, в начало  связанного  списка  структуры   tasklet_ve c  или   tasklet_hi_vec ,  которые  уникальны для  каждого  процессора в системе.

6 Это еще  один  приме р плохой  терминологии .  Почему  отложенные прерывани я  (softirq)  генерируются  (rise),  а тасклеты   (lasklet)  планируются (schedule)?  Кто  знает?  Оба термин а означают, что обработчики нижних  полови н помечаются как  ожидающие на выполнени е и в скором  времен и будут выполнены.

• Генерируется отложенное прерывание с номером TASKLET_SOFTIRQ или  НI_  SOFTIRQ, чтобы  в ближайшее время  данный тасклет  выполнился при  вызове  функции   do_softirq() .

• Устанавливается состояние системы прерываний в первоначальное значение  и возвращается управление.

Пр и первой же  удобной возможности  функция   do_softirq( )   выполнится, как это  обсуждалось в  предыдущем разделе.   Поскольку  большинство тасклетов помечаются  как  готовые к выполнению в обработчиках прерываний, то, скорее  всего, функция  do_softir q ()  вызывается сразу  же, как  только  возвратится последний обработчик  прерывания. Так  как  отложенные прерывания с номерами TASKLET_SOFTIRQ или  HI_SOFTIRQ к  этому  моменту уже  сгенерированы, то  функция do_softir q () выполняет  соответствующие обработчики .  Эти  обработчики , а также   функции tasklet_actio n  ()  и  tasklet_hi_actio n ()   являются сердцем механизма обработки  тасклетовДавайте рассмотрим, что  они  делают.

• Запрещаются прерывания, и получается весь список tasklet_ve c или  tasklet _

hi_ve c   для  текущего процессора.

• Список текущего процессора очищается  путем  присваивания значения  нуль указателю на  него.

• Разрешаются прерывания  (нет  необходимости восстанавливать состояние  системы  прерываний в первоначальное значение, так как  этот  код  может  выполняться только  в обработчике отложенного прерывания,  который  вызывается только  при  разрешенных прерываниях).

• Организовывается цикл  по  всем  тасклетам в полученном списке.

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

• Если  тасклет  не  выполняется, то  нужно  установить флаг  TASLET_STATE_RUN, чтобы  другой  процессор не  мог  выполнить этот  тасклет.

• Проверяется значение поля   coun t  на  равенство нулю,  чтобы  убедиться,  что тасклет  не  запрещен. Если  тасклет  запрещен (поле   coun t  не  равно  нулю),  то нужно    перейти к  следующему тасклету, который  ожидает на  выполнение.

• Теперь   можно быть  уверенным, что  тасклет  нигде  не  выполняется,  нигде  не будет  выполняться  (так  как  он  помечен как  выполняющийся на  данном  процессоре) и  что  значение поля   coun t  равно  нулю.  Необходимо выполнить  обработчик тасклета. После того  как  тасклет  выполнился, следует  очистить  флаг TASLET_STATE_RUN и  поле  state .

• Повторить описанный  алгоритм для  следующего тасклета,  пока  не  останется ни  одного  тасклета,  ожидающего выполнения.

Реализация  тасклетов проста, но  в то  же  время  очень  остроумна. Как  видно, все тасклеты реализованы  на  базе  двух  отложенных прерываний TASKLET_SOFTIRQ и HI_SOFTIRQ. Когда  тасклет  запланирован на выполнение, ядро  генерирует одно  из этих  двух отложенных прерываний.  Отложенные прерывания,  в  свою  очередь,  обрабатываются специальными функциями,  которые выполняют все запланированные на  выполнение  тасклеты. Эти  специальные функции гарантируют, что  только  один тасклет  данного типа  выполняется в  любой   момент времени (но  тасклеты разных типов  могут  выполняться одновременно). Вся  эта  сложность спрятана за простым и ясным интерфейсом.

Использование тасклетов

В большинстве случаев  тасклеты — это  самый  предпочтительный механизм,  с помощью   которого  следует  реализовать обработчики нижних половин  для  обычных аппаратных устройств. Тасклеты можно создавать динамически,  их просто  использовать, и они  сравнительно быстро  работают.

Объявление тасклетов

Тасклеты можно  создавать статически и  динамически. Какой  вариант лучше  выбрать, зависит от того, как  необходимо  (или  желательно)  пользователю обращаться к тасклету:   прямо или  через  указатель. Для  статического создания тасклета (и  соответственно, обеспечения прямого доступа  к нему)  необходимо использовать один  из двух следующих макросов,  которые определены в  файле   <linux/interrupt s ,h>;

DECLARE_TASKLET(name, func, data) DECLARE_TASKLET_DISABLED(name, func, data);

Оба  макроса статически создают  экземпляр  структуры  struct_tasklet_struc t  с указанным именем (name). Когда  тасклет  запланирован на выполнение, то вызывается  функция  func, которой передается аргумент   data .  Различие между  этими макросами  состоит в значении счетчика ссылок на тасклет   (поле  count) .  Первый макрос создает  тасклет, у которого значение  поля   coun t  равно   нулю, и,  соответственно, этот  тасклет  разрешен. Второй   макрос создает  тасклет  и устанавливает для  него  значение  поля  count , равное единице, и, соответственно, этот  тасклет  будет запрещен. Можно привести следующий пример.

DECLARE_TASKLET(my_tasklet, my_tasklet_handler, dev); Этастрокаэквивалентнаследующейдекларации.

struct tasklet_struct rny_tasklet = { NULL, 0, ATOMIC_INIT(0),

tasklet_handler, dev) ;

В данном примере создается тасклет  с  именем  my_tasklet ,  который разрешен для  выполнения.  Функция  tasklet_handle r  будет  обработчиком этого  тасклета. Значение  параметра de v  передается  в  функцию-обработчик  при   вызове   данной функции.

Для  инициализации  тасклета,  на  который  указывает заданный  указатель struc t tasklet_struct *    t  — косвенная ссылка на динамически созданную ранее  структуру, необходимо использовать следующий вызов.

tasklet_init(t ,   tasklet_handler ,  dev) ;   /*  динамически,   а  не   статически */

Написание собственной функции-обработчика тасклета

Функция-обработчик  тасклета   должна   соответствовать  правильному  прототипу.

void tasklet_handler(unsigned long data)

Так  же  как  и  в  случае  отложенных прерываний,  тасклет  не  может  переходить в состояние ожидания  (блокироваться).  Это  означает,  что  в  тасклетах  нельзя   использовать  семафоры или  другие  функции,  которые могут  блокироваться.  Тасклеты также  выполняются при  всех  разрешенных прерываниях,  поэтому  необходимо принять все  меры  предосторожности (например,  может  понадобиться запретить прерывания и  захватить  блокировку),  если  тасклет  имеет  совместно используемые данные с  обработчиком прерывания. В отличие от  отложенных прерываний,  ни  один  тасклет  не выполняется  параллельно  самому  себе, хотя  два  разных  тасклета   могут  выполняться на  разных  процессорах  параллельно.  Если  тасклет   совместно использует данные  с обработчиком прерывания  или  другим  тасклетом,  то  необходимо использовать соответствующие блокировки  (см.  глапу 8, "Введение  в синхронизацию выполнения  кода ядра"  и главу 9, "Средства  синхронизации в ядре").

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

Для  того  чтобы   запланировать  тасклет   на  выполнение,  должна   быть  вызвана функция   tasklet_schedul e  (),  которой в  качестве   аргумента  передается  указатель на  соответствующий экземпляр  структуры    tasklet_struct .

tasklet_schedule(&my_tasklet) ; /* отметить, что тасклет my_tasklet ожидает на выполнение */

После  того  как  тасклет  запланирован на  выполнение, он  выполняется один  раз в  некоторый момент  времени в ближайшем будущем.   Если  тасклет, который запланирован на  выполнение,  будет  запланирован  еще  раз  до  того,  как  он  выполнится, то  он  также  выполнится всего  один  раз.  Если  тасклет  уже  выполняется, скажем, на другом  процессоре,  то  будет  запланирован снова  и  снова  выполнится.  Для  оптимизации  тасклет  всегда  выполняется на  том  процессоре, который его  запланировал на выполнение, что  дает  надежду  на  лучшее  использование кэша  процессора.

Указанный тасклет  может  быть  запрещен с  помощью вызова   функции  tasklet _ disabl e  () .  Если  тасклет   в  данный  момент   времени выполняется,  то  эта  функция не  возвратит управление,  пока  тасклет  не  закончит выполняться.  Как  альтернативу можно   использовать  функцию   tasklet_disable_nosyn c () ,  которая запрещает указанный тасклет, но  возвращается сразу  и  не  ждет,  пока  тасклет  завершит выполнение.   Это  обычно   небезопасно, так  как  в данном случае  нельзя   гарантировать,  что тасклет  не  закончил  выполнение.  Вызов  функции   tasklet_enabl e ()   разрешает тасклет.   Эта  функция также  должна  быть  вызвана для  того,  чтобы  можно   было  использовать тасклет,  созданный  с  помощью макроса DECLARE_TASKLET_DISABLED  (), как  показано в следующем примере.

tasklet_disable(&my_tasklet); /* тасклет теперь запрещен */

/*Мы можем делать все, что угодно, зная, что тасклет не может выполняться. */

tasklet_enable(&my_tasklet); /* теперь тасклет разрешен */

Из  очереди   тасклетов,  ожидающих  на  выполнение,  тасклет   может   быть  удален с  помощью функции   tasklet_kil l   () .  Эта  функция получает  указатель  на  соответ-

ствующуго структуру tasklet_struc t в качестве единственного  аргумента.  Удаление запланированного на выполнение  тасклета из очереди очень полезно в случае, когда используются тасклеты,  которые сами себя планируют на выполнение.  Эта функция сначала ожидает, пока тасклет не закончит  выполнение, а потом удаляет его из очереди. Однако это,  конечно,  не может предотвратить возможности,  что другой код запланирует этот же тасклет на выполнение.  Так как данная функция  может переходить в состояние ожидания,  то ее нельзя вызывать из контекста прерывания.

Демон ksoftirqd

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

Как уже упоминалось,  ядро обрабатывает отложенные  прерывания  в нескольких местах, наиболее часто это происходит после возврата из обработчика прерывания. Отложенные  прерывания  могут генерироваться  с очень большими  частотами  (как, например,  в случае интенсивного  сетевого трафика).  Хуже того, функции-обработчики отложенных прерываний  могут самостоятельно  возобновлять свое выполнение (реактивизировать себя). Иными  словами,  во время выполнения  функции-обработчики отложенных прерываний  могут генерировать свое отложенное  прерывание  для того,  чтобы выполниться  снова  (на самом деле, сетевая подсистема именно  так и делает). Возможность  больших частот генерации  отложенных  прерываний  в сочетании  с их возможностью  активизировать  самих себя может привести  к тому, что программы,  работающие  в пространстве  пользователя,  будут страдать от недостатка процессорного  времени.  В свою очередь, не своевременная  обработка отложенных прерываний  также не допустима.  Возникает дилемма,  которая требует решения,  но ни одно из двух очевидных решений  не является  подходящим.  Давайте рассмотрим оба этих очевидных решения.

Первое решение — это немедленная  обработка всех отложенных прерываний, как только они приходят,  а также обработка всех ожидающих отложенных  прерываний перед возвратом из обработчика.  Это решение гарантирует,  что все отложенные прерывания  будут обрабатываться  немедленно  и в то же время,  что более важно,  что все вновь активизированные отложенные  прерывания  также будут немедленно  обработаны. Проблема возникает в системах, которые работают при большой загрузке и в которых возникает  большое количество отложенных прерываний, которые постоянно  сами себя активизируют.  Ядро может постоянно  обслуживать отложенные прерывания  без возможности  выполнять  что-либо  еще. Заданиями  пространства пользователя  пренебрегают,  а выполняются  только лишь обработчики  прерываний и отложенные  прерывания, в результате пользователи  системы начинают  нервничать. Подобный  подход может хорошо работать,  если система не находится  под очень большой нагрузкой.  Если же система испытывает хотя бы умеренную нагрузку, вызванную обработкой прерываний, то такое решение не применимо.  Пространство пользователя  не должно  продолжительно  страдать из-за  нехватки  процессорного времени.

Второе решение — это вообще не обрабатывать реактивизированные отложенные прерывания.  После  возврата из очередного обработчика  прерывания  ядро просто

просматривает список всех ожидающих на выполнение отложенных прерываний и выполняет их как обычно. Если какое-то  отложенное прерывание реактивизирует себя, то  оно не будет выполняться до  того времени, пока ядро в  следующий раз  снова  не приступит к обработке отложенных прерываний. Однако такое, скорее всего, произойдет,  только когда поступит следующее аппаратное прерывание,  что   может быть равносильно ожиданию в  течение длительного промежутка времени, пока новое (или вновь активизированное)  отложенное прерывание будет выполнено.  В  таком решении плохо то, что   на не загруженной системе выгодно обрабатывать отложенные прерывания сразу же. К сожалению, описанный подход не учитывает то, какие процессы могут выполняться, а какие нет. Следовательно, данный метод хотя и предотвращает нехватку процессорного времени для задач пространства пользователя, но создает нехватку ресурсов для отложенных прерываний,  и к тому же  такой подход не выгоден для систем, работающих при малых нагрузках.

Необходим какой-нибудь компромисс. Решение, которое реализовано в ядре, — не обрабатывать немедленно вновь активизированные отложенные прерывания. Бместо этого, если сильно возрастает количество отложенных прерываний,  ядро возвращает  к выполнению (wake up) семейство потоков пространства ядра, чтобы они справились с нагрузкой. Данные потоки ядра работают с  самым минимально возможным приоритетом (значение параметра nice равно 19). Это гарантирует, что  они не будут выполняться вместо чего-то более важного. Но они в  конце концов тоже когда-нибудь обязательно выполняются.  Это предотвращает ситуацию нехватки процессорных ресурсов для пользовательских программ. С другой стороны,  это также гарантирует, что  даже в  случае большого количества отложенных прерываний они все   в конце концов будут выполнены. И наконец, такое решение гарантирует, что  в случае незагруженной системы отложенные прерывания также обрабатываются достаточно быстро  (потому что   соответствующие потоки  пространства ядра будут запланированы на выполнение немедленно).

Для каждого процессора существует свой поток. Каждый поток имеет имя в виде ksoftirqd/n ,  где  п —  номер процессора. Так в двухпроцессорной  системе будут запущены два  потока с  именами  ksoftiqd/ 0   и ksoftirqd/1 . To, что  на каждом процессоре выполняется свой поток,  гарантирует, что   если в  системе есть свободный процессор, то  он  всегда будет в  состоянии  выполнять отложенные прерывания. После того как потоки запущены, они выполняют замкнутый цикл, похожий на следующий.

for (;;) {

set_task_state(current, TASK_INTERRUPTIBLE);

add_wait_queue(&cwq->more_work, &wait);

if (list_empty(&cwq->worklist))

schedule();

else

set_task_state(current, TASK_RUNNING);

remove_wait_queue(&cwq->more_work, &wait);

if  (!list_empty(&cwq->worklist))

run workqueue(cwq);

}

Если   есть  отложенные прерывания,  ожидающие на  обработку (что  определяет вызов   функции   softirq_pendin g ()) , т о  поток   ядра   ksoftirq d  вызывает  функцию  do_softir q ()  , которая эти  прерывания  обрабатывает. Заметим,  что  это  делается   периодически,  чтобы  обработать также  вновь   активизированные  отложенные  прерывания. После каждой итерации при  необходимости вызывается функция schedul e () , чтобы  дать  возможность выполняться более  важным процессам. После того  как  вся  обработка выполнена,  поток  ядра устанавливает свое  состояние в значение  TASK_INTERRUPTIBLE и активизирует планировцик для  выбора нового  готового к выполнению процесса.

Поток обработки отложенных прерываний вновь  возвращается в состояние готовности к выполнению, когда  функция do_softir q ()   определяет, что  отложенное прерывание реактивизировало себя.

Старый механизм ВН

Хотя  старый   интерфейс ВН,  к  счастью, уже  отсутствует   в  ядрах  серии   2.6, тем не менее  им  пользовались очень  долгое время  — с первых  версий ядра.  Учитывая, что этому  интерфейсу удалось  продержаться очень  долго, он,  конечно,  представляет собой  историческую ценность и  заслуживает большего,  чем  просто   беглого  рассмотрения.  Этот  раздел  никаким образом не  касается ядер  серии  2.6, но  значение истории переоценить трудно.

Интерфейс ВН  очень  древний, и  это  заметно. Каждый обработчик ВН  должен быть  определен статически, и количество этих  обработчиков ограничено максимальным  значением 32.  Так  как  все  обработчики ВН  должны быть  определены на  этапе компиляции,  загружаемые модули  ядра  не  могли  напрямую использовать интерфейс ВН.  Тем  не  менее  можно было  встраивать функции в уже  существующие обработчики  ВН.  Со  временем необходимость статического объявления  и максимальное количество  обработчиков нижних половин,  равное 32, стали  надоедать.

Все  обработчики ВН  выполнялись строго  последовательно — никакие два  обработчика ВН, даже  разных  типов, не  могли  выполняться параллельно. Это  позволяло обеспечить простую   синхронизацию,  но  все  же для  получения высокой производительности при  многопроцессорной обработке это  было  не  очень  хорошо. Драйверы, которые использовали интерфейс ВН, очень  плохо  масштабировались на  несколько процессоров.  Например,  страдала сетевая  подсистема.

В  остальном, за исключением указанных ограничений,  механизм ВН  был  похож на  механизм  тасклетов.  На  самом  деле, в ядрах  серии  2.4  механизм ВН  был  реализован  па  основе тасклетов. Максимальное  количество обработчиков нижних половин, равное 32, обеспечивалось значениями констант, определенных в заголовочном файле   <linux/interrupt.h> . Для  того  чтобы  отметить обработчик ВН  как  ожидающий на  выполнение,  необходимо было  вызвать  функцию mark_bh ()   с  передачей номера обработчика ВН  в качестве параметра.  В  ядрах  серии   2.4  при  этом  планировался на  выполнение тасклет  ВН, который выполнялся с  помощью обработчика bh_actio n () . До  серии  ядер  2.4  механизм ВН  существовал самостоятельно,  как  сейчас  механизм отложенных прерываний.

В связи  с недостатками этого  типа  обработчиков нижних половин,  разработчики ядра  предложили механизм очередей заданий (task  queue), чтобы  заменить механизм нижних половин. Очереди заданий так и не смогли  справиться с этой  задачей, хотя и завоевали расположение большого количества пользователей. При  разработке серии

ядер  2.3 были  предложены механизмы отложенных прерываний  (softirq)  и механизм тасклетов (lasklet), для того  чтобы  положить конец механизму ВН.  Механизм ВН при этом  был реализован на основе  механизма тасклетов. К сожалению, достаточно сложно  переносить обработчики нижних половин с использования интерфейса ВН на использование механизм тасклетов или  отложенных прерываний, в связи  с тем что у новых  интерфейсов нет свойства  строгой  последовательности выполнения7.

Однако при  разработке ядер  серии  2.5 необходимую конвертацию все же сделали, когда таймеры ядра и подсистему SCSI  (единственные оставшиеся системы, которые  использовали механизм ВН)  наконец-то перевели на использование отложенных прерываний. И в завершение, разработчики ядра совсем  убрали  интерфейс ВН. Скатертью дорога  тебе, интерфейс ВН!

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

По теме:

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