Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Модули ядра Linux
Назад Внутренние механизмы ядра Вперед

Отложенная обработка, нижняя половина

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

Термин «нижняя половина» обработчика прерываний как раз и сложился для обозначения той совокупности действий, которую можно отнести к отложенной обработке прерываний. Когда-то в ядре Linux был один из способов организации отложенной обработки, который так и именовался: обработчик нижней половины, но сейчас он неприменим. А термин так и остался как нарицательный, относящийся к всем разным способам организации отложенной обработки, которые и рассматриваются далее.

Отложенные прерывания (softirq)

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

	// структура, представляющая одно отложенное прерывание 
	struct softirq_action {
	   void (*action)(struct softirq_action *);
	};

В ядре 2.6.18 (и везде в литературе) определение (более раннее) другое:

	struct softirq_action {
	   void (*action)(struct softirq_action *);
	   void *data;
	};

Для уточнения картины с softirq нам недостаточно хэдеров, и необходимо опуститься в рассмотрение исходных кодов реализации ядра (файл <kernel/softirq.c>, если у вас не установлены исходные тексты ядра, что совершенно не есть необходимостью для всего прочего нашего рассмотрения, то здесь вы будете вынуждены это сделать, если хотите повторить наш экскурс):

	enum {          /* задействованные номера */
	   HI_SOFTIRQ=0,
	   TIMER_SOFTIRQ,
	   NET_TX_SOFTIRQ,
	   NET_RX_SOFTIRQ,
	   BLOCK_SOFTIRQ,
	   BLOCK_IOPOLL_SOFTIRQ,
	   TASKLET_SOFTIRQ,
	   SCHED_SOFTIRQ,
	   HRTIMER_SOFTIRQ,
	   RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
	   NR_SOFTIRQS  /* число задействованных номеров */
	}; 
	static struct softirq_action softirq_vec[NR_SOFTIRQS] 
	char *softirq_to_name[NR_SOFTIRQS] = {
	   "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL",
	   "TASKLET", "SCHED", "HRTIMER",<>"RCU"
	};

В 2.6.18 (то, что кочует из одного литературного источника в другой) аналогичные описания были заметно проще и статичнее:

	enum {
	   HI_SOFTIRQ=0,
	   TIMER_SOFTIRQ,
	   NET_TX_SOFTIRQ,
	   NET_RX_SOFTIRQ,
	   BLOCK_SOFTIRQ,
	   TASKLET_SOFTIRQ
	};
	static struct softirq_action softirq_vec[32] 

Следовательно, имеется возможность создать 32 обработчика softirq, и это количество фиксировано. В этой версии ядра (2.6.18) их было 32, из которых задействованных было 6. Эти определения из предыдущей версии помогают лучше понять то, что имеет место в настоящее время.

Динамическая диагностика использования softirq в работающей системе может производиться так:

# cat /proc/softirqs

	                CPU0       CPU1   
	      HI:          0          0 
	   TIMER:   16940626   16792628 
	  NET_TX:       4936          1 
	  NET_RX:      96741       1032 
	   BLOCK:     176178          2 
	BLOCK_IOPOLL:      0          0 
	 TASKLET:        570      50738 
	   SCHED:     835250    1191280 
	 HRTIMER:       6286       5457 
	     RCU:   17000398   16867989 

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

Если вы решились на перекомпиляцию ядра и создание нового уровня softirq, то для этого необходимо:

- Определить новый индекс (уровень) отложенного прерывания, вписав (файл <linux/interrupt.h>) его константу XXX_SOFT_IRQ в перечисление, где-то, очевидно, на одну позицию выше TASKLET_SOFTIRQ (иначе зачем переопределять новый уровень и не использовать тасклет?).

- Во время инициализации модуля должен быть зарегистрирован (объявлен) обработчик отложенного прерывания с помощью вызова open_softirq(), который принимает три параметра: индекс отложенного прерывания, функция-обработчик и значение поля data:

	/* The bottom half */ 
	void xxx_analyze( void *data ) { 
	  /* Analyze and do ................ */ 
	}
	void __init roller_init() { 
	   /* ... */ 
	   request_irq( irq, xxx_interrupt, 0, "xxx", NULL );
	   open_softirq( XXX_SOFT_IRQ, xxx_analyze, NULL ); 
	} 

- Функция-обработчик отложенного прерывания (в точности как и рассматриваемого ниже тасклета) должна в точности соответствовать правильному прототипу:

	void xxx_analyze( unsigned long data ); 

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

	/* The interrupt handler */ 
	static irqreturn_t xxx_interrupt( int irq, void *dev_id ) { 
	   /* ... */ 
	   /* Mark softirq as pending */ 
	   raise_softirq( XXX_SOFT_IRQ ); 
	   return IRQ_HANDLED; 
	}

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

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


Предыдущий раздел: Оглавление Следующий раздел:
Управление линиями прерывания   Тасклеты