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

UnixForum





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

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

Управление линиями прерывания

Под управлением линиями прерываний, в этом месте описаний, мы будем понимать запрет-разрешение прерываний поодной или нескольким линиям irq. Раньше существовала возможность вообще запретить прерывания (на время, естественно). Но сейчас («заточенный» под SMP) набор API для этих целей выглядит так: либо вы запрещаете прерывания по всем линиям irq, но локального процессора, либо на всех процессорах, но только для одной линии irq.

Макросы управления линиями прерываний определены в <linux/irqflags.h>. Управление запретом и разрешением прерываний на локальном процессоре:

local_irq_disable() - запретить прерывания на локальном CPU;

local_irq_enable() - разрешить прерывания на локальном CPU;

int irqs_disabled() - возвратить ненулевое значение, если запрещены прерывания на локальном CPU, в противном случае возвращается нуль ;

Напротив, управление (запрет и разрешение) одной выбранной линией irq, но уже относительно всех процессоров в системе, делают макросы:

void disable_irq( unsigned int irq ) -

void disable_irq_nosync( unsigned int irq ) - обе эти функции запрещают прерывания с линии irq на контроллере (для всех CPU), причём, disable_irq() не возвращается до тех пор, пока все обработчики прерываний, которые в данный момент выполняются, не закончат работу;

void enable_irq( unsigned int irq ) - разрешаются прерывания с линии irq на контроллере (для всех CPU);

void synchronize_irq( unsigned int irq ) - ожидает пока завершится обработчик прерывания от линии irq (если он выполняется), в принципе, хорошая идея — всегда вызывать эту функцию перед выгрузкой модуля использующего эту линию IRQ;

Вызовы функций disable_irq*() и enable_irq() должны обязательно быть парными - каждому вызову функции запрещения линии должен соответствовать вызов функции разрешения. Только после последнего вызова функции enable_irq() линия запроса на прерывание будет снова разрешена.

Пример обработчика прерываний

Обычно затруднительно показать работающий код обработчика прерываний, потому что такой код должен был бы быть связан с реальным аппаратным расширением, и таким образом он будет перегружен специфическими деталями, скрывающими суть происходящего. Но оригинальный пример приведен в [6], откуда мы его и заимствуем (архив IRQ.tgz):

lab1_interrupt.c :

	#include <linux/module.h>
	#include <linux/init.h>     
	#include <linux/interrupt.h>
	
	#define SHARED_IRQ 17             
	
	static int irq = SHARED_IRQ, my_dev_id, irq_counter = 0;
	module_param( irq, int, S_IRUGO );
	
	static irqreturn_t my_interrupt( int irq, void *dev_id ) {                   
	   irq_counter++;
	   printk( KERN_INFO "In the ISR: counter = %d\n", irq_counter );
	   return IRQ_NONE;  /* we return IRQ_NONE because we are just observing */
	}           
	
	static int __init my_init( void ) {                                                           
	   if ( request_irq( irq, my_interrupt, IRQF_SHARED, "my_interrupt", &my_dev_id ) )
	      return -1;                  
	   printk( KERN_INFO "Successfully loading ISR handler on IRQ %d\n", irq );
	   return 0;
	}
	
	static void __exit my_exit( void ) {
	   synchronize_irq( irq );        
	   free_irq( irq, &my_dev_id );            
	   printk( KERN_INFO "Successfully unloading, irq_counter = %d\n", irq_counter );
	}                                 
	
	module_init( my_init );
	module_exit( my_exit );           
	MODULE_AUTHOR( "Jerry Cooperstein" );
	MODULE_DESCRIPTION( "LDD:1.0 s_08/lab1_interrupt.c" );
	MODULE_LICENSE( "GPL v2" );

Логика этого примера в том, что обработчик вешается в цепочку с существующим в системе, но он не нарушает работу ранее работающего обработчика, фактически ничего не выполняет, но подсчитывает число обработанных прерываний. В оригинале предлагается опробовать его с установкой на IRQ сетевой платы, но ещё показательнее — с установкой на IRQ клавиатуры (IRQ 1) или мыши (IRQ 12) на интерфейсе PS/2 (если таковой используется в компьютере):

$ cat /proc/interrupts

	           CPU0                   
	  0:   20329441          XT-PIC timer
	  1:        423          XT-PIC i8042
	...

$ sudo /sbin/insmod lab1_interrupt.ko irq=1

$ cat /proc/interrupts

	           CPU0
	  0:   20527017          XT-PIC timer
	  1:        572          XT-PIC i8042, my_interrupt
	...

$ sudo /sbin/rmmod lab1_interrupt

	$ dmesg | tail -n5
	In the ISR: counter = 33
	In the ISR: counter = 34
	In the ISR: counter = 35
	In the ISR: counter = 36
	Successfully unloading, irq_counter = 36

$ cat /proc/interrupts

	           CPU0                   
	  0:   20568216          XT-PIC timer
	  1:        622          XT-PIC i8042
	...

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


Предыдущий раздел: Оглавление Следующий раздел:
Обработчик прерываний, верхняя половина   Отложенная обработка, нижняя половина