Второе издание
Скачать 3.09 Mb.
|
Таймеры ядра Еще один механизм выполнения отложенной работы — это таймеры ядра. В отличие от меха- низмов, рассмотренных в этой главе, таймеры позволяют отсрочить работу на указанный ин- тервал времени. Инструменты, описанные в этой главе, могут быть полезны для откладывания работы с текущего момента до какого-нибудь момента времени в будущем. Таймеры использу- ются для откладывания работы до того момента, пока не пройдет указанный период времени. Поэтому таймеры имеют другое назначение, чем механизмы, описанные в данной главе. Более пол- ное обсуждение таймеров ядра будет приведено в главе 10, "Таймеры и управление временем". Путаница с нижними половинами Некоторая путаница с обработчиками нижних половин имеет место, но на самом деле — это только проблема названий. Давайте снова вернемся к этому вопросу. Термин "нижняя половина" ("bottom half") — это общий термин, который касает- ся операционных систем и связан с тем, что некоторая часть процесса обработки прерывания откладывается на будущее. В операционной системе Linux сейчас этот термин означает то же самое. Все механизмы ядра, которые предназначены для от- ложенной обработки, являются обработчиками нижних половин. Обработка нижних половин и отложенные действия 135 Некоторые люди также называют обработчики нижних половин программными прерываниями иди "soflirq", но они просто пытаются досадить остальным. Термин "Bottom Half" также соответствует названию самого первого механиз- ма выполнения отложенных действий п операционной системе Linux. Этот меха- низм еще называется "ВН", поэтому далее так будем называть именно этот меха- низм, а термин "нижняя половина" ("bottom half") будет касаться общего названия. Механизм ВН был некоторое время назад выключен из употребления и полностью изъят в ядрах серии 2.5. Сейчас есть три метода для назначения отложенных операций: механизм отло- женных прерываний (softirq), механизм тасклетов и механизм очередей отложенных действий. Тасклеты построены на основе механизма softirq, а очереди отложенных действий имеют полностью отличную реализацию. В табл. 7.1 показана история об- работчиков нижних половин. Таблица 7 . 1 . Состояние обработчиков нижних половин Механизм обработчиков Состояние ВН Изъято в серии 2.5 Очереди заданий Изъято в серии 2.5 Отложенные прерывания Доступно начиная с серии 2.3 Тасклеты Доступно начиная с серии 2.3 Очереди отложенных действий Доступно начиная с серии 2.3 Давайте продолжим рассмотрение каждого из механизмов в отдельности, пользу- ясь этой устойчивой путаницей в названиях. Механизм отложенных прерываний (softirq) Обсуждение существующих методов обработки нижних половин начнем с меха- низма softirq. Обработчики на основе механизма отложенных прерываний исполь- зуются редко. Тасклеты — это более часто используемая форма обработчика нижних половин. Поскольку тасклеты построены на основе механизма softirq, с механизма softirq и стоит начать. Код, который касается обработчиков отложенных прерыва- ний, описан в файле kernel/softirq.с. Реализация отложенных прерываний Отложенные прерывания определяются статически во время компиляции. В отли- чие от тасклетов, нельзя динамически создать или освободить отложенное прерыва- ние. Отложенные прерывания представлены с помощью структур softirq_action, определенных в файле /* * структура, представляющая одно отложенное прерывание */ struct softirq_action { 136 Глава 7 void (*action)(struct softirq_action * ) ; /* функция, которая должна выполниться */ void *data; /* данные для передачи в функцию */ }; Массив из 32 экземпляров этой структуры определен в файле kernel/sof t i r q . с в следующем виде. static struct softirq_action softirq_vec[32]; Каждое зарегистрированное отложенное прерывание соответствует одному эле- менту этого массива. Следовательно, имеется возможность создать 32 обработчика softirq. Заметим, что это количество фиксированно. Максимальное число обработ- чиков softirq не может быть динамически изменено. В текущей версии ядра из 32 элементов используется только шесть 4 Обработчик softirq Прототип обработчика отложенного прерывания, поля action, выглядит следу- ющим образом. void softirq_handler(struct softirg_action *) Когда ядро выполняет обработчик отложенного прерывания, то функция action вызывается С указателем на соответствующую структуру softirq_action в качестве аргумента. Например, если переменная rny_softirq содержит указатель на элемент массива softirq_vec, то ядро вызовет функцию-обработчик соответствующего от- ложенного прерывания в следующем виде. my_softirq->action(my_softirq) Может быть, несколько удивляет, что ядро передает в обработчик указатель на всю структуру, а не только на поле data. Этот прием позволяет в будущем вводить дополнительные поля в структуру без необходимости внесения изменений в суще- ствующие обработчики. Обработчик может получить доступ к значению ноля data простым разыменованием указателя на структуру и чтением ее поля data. Обработчик одного отложенного прерывания никогда не вытесняет другой об- работчик softirq. Б действительности, единственное событие, которое может вытес- нить обработчик softirq, — это аппаратное прерывание. Однако на другом процес- соре одновременно с обработчиком отложенного прерывания может выполняться другой (и даже этот же) обработчик отложенного прерывания. Выполнение отложенных прерываний Зарегистрированное отложенное прерывание должно быть отмечено для того, чтобы его можно было выполнить. Это называется генерацией отложенного прерывания (rise softirq). Обычно обработчик аппаратного прерывания перед возвратом отмечает свои обработчики отложенных прерываний. Затем в подходящий момент времени отложенное прерывание выполняется. Ожидающие выполнения обработчики отло- женных прерываний проверяются и выполняются в следующих ситуациях. 4 Большинство драйверов использует для обработки своих нижних половин механизм тасклетов, Тасклеты построены на механизме softirq, как это будет покапано ниже. Обработка нижних половин и отложенные действия 137 • После обработки аппаратного прерывания. • В контексте потока пространства ядра ksoftirqd. • В любом коде ядра, который явно проверяет и выполняет ожидающие обработ- чики отложенных прерываний, как, например, это делает сетевая подсистема. Независимо от того, каким способом выполняется отложенное прерывание, его выполнение осуществляется в функции do_softirq (). Эта функция по-настоящему проста. Если есть ожидающие отложенные прерывания, то функция do_softirq() в цикле проверяет их все и вызывает ожидающие обработчики. Давайте рассмотрим упрощенный вариант наиболее важной части функции do_sof t i r 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_pending (). Это значение — 32-х разрядная битовая маска ожида- ющих на выполнение отложенных прерываний. Если установлен бит с номе- ром n, то отложенное прерывание с этим номером ожидает на выполнение. • Когда значение битовой маски отложенных прерываний сохранено, оригиналь- ная битовая маска очищается 5 • Переменной h присваивается указатель на первый элемент массива softirq_vec. • Если первый бит маски, которая хранится в переменной pending, установлен, то вызывается функция h->action(h). • Указатель h увеличивается на единицу, и теперь он указывает на второй эле- мент массива softirq_vec. • Осуществляется логический сдвиг битовой маски, хранящейся в переменной pending, вправо на один разряд. Эта операция отбрасывает самый младший бит и сдвигает все оставшиеся биты на одну позицию вправо. Следовательно, второй бит теперь стал первым и т.д. 5 На самом деле эта операция выполняется при всех запрещенных на локальном процессоре пре- рываниях, что не показано в упрощенной версии. Если бы прерывания не были запрещены, то в период времени между сохранением и очисткой маски могло бы быть сгенерировано новое отло- женное прерывание (которое бы ожидало на выполнение), что привело бы к неверной очистке бита соответствующего отложенного прерывания. 138 Глава 7 • Указатель h теперь указывает на второй элемент массива, а в битовой маске — второй бит стал первым. Теперь необходимо повторить все ранее проделан- ные шаги. • Последовательное повторение производится до тех пор, пока битовая маска не станет равной нулю. В этот момент больше нет ожидающих отложенных пре- рываний, и наша работа выполнена. Заметим, что такой проверки достаточно для того, чтобы гарантировать, что указатель h всегда указывает на законную запись в массиве softirq_vec, так как битовая маска pending имеет 32 бит и цикл не может выполниться больше 32 раз. Использование отложенных прерываний Отложенные прерывания зарезервированы для наиболее важных и критичных ко времени выполнения обработчиков нижних половин в системе. Сейчас только две подсистемы — подсистема SCSI и сетевая подсистема — напрямую используют ме- ханизм softirq. В дополнение к этому, таймеры ядра и тасклеты построены на базе отложенных прерываний. Если есть желание добавить новое отложенное прерыва- ние, то стоит себя спросить, почему будет недостаточно использования тасклетов. Тасклеты могут создаваться динамически, а также их легче использовать в связи с более простыми требованиями к блокировкам. Кроме того, их производительность все еще остается очень хорошей. Тем не менее для задач, критичных ко времени вы- полнения, которые способны сами обеспечивать эффективные блокировки, исполь- зование механизма softirq — будет правильным решением. Назначение индексов Отложенные прерывания должны объявляться на этапе компиляции с помощью соответствующего перечисления (enum) в файле Создание нового отложенного прерывания состоит в добавлении новой записи в этот перечень (enum). Однако нужно не просто добавить новую строчку в конец спи- ска, как в других местах. Вместо этого нужно вставить строчку в соответствии с при- оритетом, который дается этому прерыванию. Исторически, HI_SOFTIRQ— имеет наибольший приоритет, a TASKLET_SOFTIRQ — наименьший. Новая запись, скорее всего, должна быть где-то ниже записей для сетевых устройств и выше записи для TASKLET_SOFTIRQ. В табл. 7.2 показан список всех типов отложенных прерываний. Таблица 7.2. Список отложенных прерываний Отложенное прерывание Приоритет Описание HI_SOFTIRQ 0 Высокоприоритетные тасклеты TIMER_SOFTIRQ 1 Обработчик нижних половин таймеров NET_TX_S0FTIRQ 2 Отправка сетевых пакетов NET_RX_SOFTIRQ 3 Прием сетевых пакетов SCSI_SOFTIRQ 4 Обработчик нижних половин подсистемы SCSI TASKLET_SOFTIRQ 5 Тасклеты Обработка нижних половин и отложенные действия 139 Регистрация обработчика Далее во время выполнения должен быть зарегистрирован обработчик отложен- ного прерывания с помощью вызова o p e n _ s o f t i r q ( ) , который принимает три па- раметра: индекс отложенного прерывания, функция-обработчик и значение поля data. Для сетевой подсистемы это делается, например, следующим образом. open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL); Обработчик отложенного прерывания выполняется при разрешенных прерыва- ниях и не может переходить в состояние ожидания (sleep). Во время выполнения обработчика отложенные прерывания на данном процессоре запрещаются. Однако на другом процессоре обработчики отложенных прерываний могут выполняться. На самом деле, если вдруг генерируется отложенное прерывание в тот момент, когда выполняется его обработчик, то такой же обработчик может быть запущен на дру- гом процессоре одновременно с первым обработчиком. Это означает, что любые совместно используемые данные, которые используются в обработчике отложен- ного прерывания, и даже глобальные данные, которые используются только в са- мом обработчике, должны соответствующим образом блокироваться (как показано в следующих двух разделах). Это очень важный момент, и именно по этой причи- не использование тасклетов обычно предпочтительнее. Простое предотвращение конкурентного выполнения обработчиков — это далеко не идеал. Если обработчик отложенного прерывания просто захватит блокировку, которая предотвращает его выполнение параллельно самому себе, то для использования отложенных прерыпа- ний не остается почти никакого смысла. Следовательно, большинство обработчиков отложенных прерываний используют данные, уникальные для каждого процессора (и следовательно, не требующие блокировок), или какие-нибудь другие ухищрения, чтобы избежать явного использования блокировок и обеспечить отличную масшта- бируемость. Главная причина использования отложенных прерываний — масштабируемость. Если нет необходимости масштабироваться на бесконечное количество процессо- ров, то лучше использовать механизм тасклетов. Тасклеты — это отложенные преры- вания, для которых обработчик не может выполняться параллельно на нескольких процессорах. Генерация отложенных прерываний После того как обработчик добавлен в перечень и зарегистрирован с помощью вызова o p e n _ s o f t i r q () , он готов выполняться. Для того чтобы отметить его как ожидающего исполнения и, соответственно, чтобы он выполнился при следующем вызове функции d o _ s o f t i r q ( ) , необходимо вызвать функцию r a i s e _ s o f t i r q ( ) . Например, сетевая подсистема должна вызвать эту функцию в следующем виде. raise_softirq(NET_TX_SOFTIRQ); Этот вызов сгенерирует отложенное прерывание с индексом NET_TX_SOFTIRQ. Соответствующий обработчик n e t _ t x _ a c t i o n () будет вызван при следующем вы- полнении программных прерываний ядром. Эта функция запрещает аппаратные прерывания перед тем, как сгенерировать отложенное прерывание, а затем восста- навливает их в первоначальное состояние. Если аппаратные прерывания в данный 140 Глава 7 момент запрещены, то для небольшой оптимизации можно воспользоваться функци- ей raise_sof tirq_irqoff (), как показано в следующем примере. /* * прерывания должны быть запрещены! */ raise_softirq_irqoff(NET_TX_SOFTIRQ); Наиболее часто отложенные прерывания генерируются из обработчиков аппа- ратных прерываний. В этом случае обработчик аппаратного прерывания выполняет всю основную работу, которая касается аппаратного обеспечения, генерирует отло- женное прерывание и завершается. После завершения обработки аппаратных пре- рываний ядро вызывает функцию do_softirq(). Обработчик отложенного преры- вания выполняется и подхватывает работу с того места, где обработчик аппаратного прерывания ее отложил. В таком примере раскрывается смысл названий "верхняя половина" и "нижняя половина". Тасклеты Тасклеты — это механизм обработки нижних половин, построенный на основе ме- ханизма отложенных прерываний. Как уже отмечалось, они не имеют ничего обще- го с заданиями (task). Тасклеты по своей природе и принципу работы очень похожи на отложенные прерывания. Тем не менее они имеют более простой интерфейс и упрощенные правила блокировок. Решение о том, стоит ли использовать тасклеты, принять достаточно просто: в большинстве случаев необходимо использовать тасклеты. Как было показано в пред- ыдущем разделе, примеры использования отложенных прерываний можно посчитать на пальцах одной руки. Отложенные прерывания необходимо использовать только в случае, когда необходима очень большая частота выполнений и интенсивно исполь- зуется многопоточная обработка. Тасклеты используются в очень большом количе- стве случаев — они работают достаточно хорошо и их очень просто использовать. Реализация тасклетов Так как тасклеты реализованы на основе отложенных прерываний, они тоже яв- ляются отложенными прерываниями (softirq). Как уже рассказывалось, тасклеты пред- ставлены двумя типами отложенных прерываний: HI_SOFTIRQ и TASKLET_SOFTIRQ. Единственная разница между ними в том, что тасклеты типа HI_SOFTIRQ выполня- ются всегда раньше тасклетов типа TASKLET_SOFTIRQ. Структуры тасклетов Тасклеты представлены с помощью структуры t a s k l e t _ s t r u c t . Каждый экзем- пляр структуры представляет собой уникальный тасклет. Эта структура определена в заголовочном файле struct tasklet_struct { struct tasklet_struct *next; /* указатель на следующий тасклет в списке */ unsigned long state; /* состояние тасклета */ } Обработка нижних половин и отложенные действия 141 atomic_t count; /* счетчик ссылок */ void (*func) (unsigned long); /* функция-обработчик тасклета*/ unsigned long data; /* аргумент функции-обработчика тасклета */ ); Поле func — это функция-обработчик тасклета (эквивалент поля a c t i o n для структуры, представляющей отложенное прерывание), которая получает поле d a t a в качестве единственного аргумента при вызове. Поле s t a t e может принимать одно из следующих значений: нуль, TASKLET_ STATE_SCHED или TASLET_STATE_RUN. Значение TASKLET_STATE_SCHED указывает на то, что тасклет запланирован на выполнение, а значение TASLET_STATE_RUN — что тасклет выполняется. Для оптимизации значение TASLET_STATE RUN может ис- пользоваться только на многопроцессорной машине, так как на однопроцессорной машине и без этого точно известно, выполняется ли тасклет (действительно, ведь код, который выполняется, либо принадлежит тасклету, либо нет). Поле c o u n t используется как счетчик ссылок на тасклет. Если это значение не равно нулю, то тасклет запрещен и не может выполняться; если оно равно нулю, то тасклет разрешен и может выполняться в случае, когда он помечен как ожидающий выполнения. |