Библиотека сайта rus-linux.net
Цилюрик О.И. Модули ядра Linux | ||
Назад | Внутренние механизмы ядра | Вперед |
Тасклеты
Предыдущая схема достаточно тяжеловесная, и в большинстве случаем её подменяют тасклеты — механизм на базе тех же softirq с двумя фиксированными индексами HI_SOFTIRQ или TASKLET_SOFTIRQ. Тасклеты это ни что иное, как частный случай реализации softirq. Тасклеты представляются (<linux/interrupt.h>) с помощью структуры:
struct tasklet_struct { struct tasklet_struct *next; /* указатель на следующий тасклет в списке */ unsigned long state; /* текущее состояние тасклета */ atomic_t count; /* счетчик ссылок */ void (*func)(unsigned long); /* функция-обработчик тасклета*/ unsigned long data; /* аргумент функции-обработчика тасклета */ };
Поле state может принимать только одно из значений: 0, TASKLET_STATE_SCHED, TASLET_STATE_RUN. Значение TASKLET_STATE_SCHED указывает на то, что тасклет запланирован на выполнение, а значение TASLET_STATE_RUN — что тасклет выполняется.
enum { TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */ TASKLET_STATE_RUN /* Tasklet is running (SMP only) */ };
Поле count используется как счетчик ссылок на тасклет. Если это значение не равно нулю, то тасклет запрещен и не может выполняться; если оно равно нулю, то тасклет разрешен и может выполняться в случае, когда он помечен как ожидающий выполнения.
Схематически код использования тасклета полностью повторяет структуру кода softirq:
- Инициализация тасклета при инициализации модуля:
struct xxx_device_struct { /* Device-specific structure */ /* ... */ struct tasklet_struct tsklt; /* ... */ } void __init xxx_init() { struct xxx_device_struct *dev_struct; /* ... */ request_irq( irq, xxx_interrupt, 0, "xxx", NULL ); /* Initialize tasklet */ tasklet_init( &dev_struct->tsklt, xxx_analyze, dev ); }
Для статического создания тасклета (и соответственно, обеспечения прямого доступа к нему) могут использоваться один из двух макросов:
DECLARE_TASKLET( name, func, data ) DECLARE_TASKLET_DISABLED( name, func, data );
Оба макроса статически создают экземпляр структуры struct tasklet_struct с указанным именем (name). Второй макрос создает тасклет, но устанавливает для него значение поля count, равное единице, и, соответственно, этот тасклет будет запрещен для исполнения. Макрос DECLARE_TASKLET( name, func, data ) эквивалентен (можно записать и так):
struct tasklet_struct namt = { NULL, 0, ATOMIC_INIT(0), func, data ) ;
Используется, что совершенно естественно, в точности тот же прототип функции обработчика тасклета, что и в случае отложенных прерываний (в моих примерах просто использована та же функция).
Для того чтобы запланировать тасклет на выполнение (обычно в обработчике прерывания), должна быть вызвана функция tasklet_schedule(), которой в качестве аргумента передается указатель на соответствующий экземпляр структуры struct tasklet_struct :
/* The interrupt handler */ static irqreturn_t xxx_interrupt( int irq, void *dev_id ) { struct xxx_device_struct *dev_struct; /* ... */ /* Mark tasklet as pending */ tasklet_schedule( &dev_struct->tsklt ); return IRQ_HANDLED; }
После того как тасклет запланирован на выполнение, он выполняется один раз в некоторый момент времени в ближайшем будущем. Для оптимизации тасклет всегда выполняется на том процессоре, который его запланировал на выполнение, что дает надежду на лучшее использование кэша процессора.
Если вместо стандартного тасклета нужно использовать тасклет высокого приоритета (HI_SOFTIRQ), то вместо функции tasklet_schedule() вызываем функцию планирования tasklet_hi_schedule().
Уже запланированный тасклет может быть запрещен к исполнению (временно) с помощью вызова функции tasklet_disable(). Если тасклет в данный момент уже начал выполнение, то функция не возвратит управление, пока тасклет не закончит своё выполнение. Как альтернативу можно использовать функцию tasklet_disable_nosync(), которая запрещает указанный тасклет, но возвращается сразу не ожидая, пока тасклет завершит выполнение (это обычно небезопасно, так как в данном случае нельзя гарантировать, что тасклет не закончил выполнение). Вызов функции tasklet_enable() разрешает тасклет. Эта функция также должна быть вызвана для того, чтобы можно было выполнить тасклет, созданный с помощью макроса DECLARE_TASKLET_DISABLED (). Из очереди тасклетов, ожидающих выполнения, тасклет может быть удален с помощью функции tasklet_kill().
Так же как и в случае отложенных прерываний (на которых он построен), тасклет не может переходить в блокированное состояние.
Предыдущий раздел: | Оглавление | Следующий раздел: |
Отложенная обработка, нижняя половина | Демон ksoftirqd |