Chapter 7 Interrupts and Interrupt Handlers
中断处理程序与其他内核函数的区别在于内核调用它们来响应中断,并且它们运行在称为中断上下文的特殊上下文中;中断上下午也称为原子上下文,中断上下文不可调度。 处理程序必须快速运行,以便尽快恢复执行中断的代码,提高系统响应。
上半部和下半部
上半部通常是执行时间非常短,需要尽快完成。下半部是处理可以延迟的工作
中断处理函数
注册中断
/* request_irq: allocate a given interrupt line */
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
typedef irqreturn_t (*irq_handler_t)(int, void *);
中断处理FLag
- IRQF_DISABLED When set, this flag instructs the kernel to disable all interrupts when executing this interrupt handler.When unset, interrupt handlers run with all interrupts except their own enabled. 大多数中断处理都不需要设置这个flag,主要用在性能敏感并且执行非常快的中断中。
- IRQF_SAMPLE_RANDOM 这个标志指定这个设备产生的中断应该贡献给内核熵池。内核熵池提供源自各种随机事件的真正随机数。
- IRQF_TIMER This flag specifies that this handler processes interrupts for the system timer.
- IRQF_SHARED This flag specifies that the interrupt line can be shared among multiple interrupt handlers
可以通过/proc/irq和/proc/interrupts查看中断信息。request_irq()可以睡眠。
释放中断
void free_irq(unsigned int irq, void *dev)
中断控制
关闭中断也就关闭了抢占。 锁能防止其他处理器并发访问,中断能防止中断处理程序并发访问临界区;锁一般和关中断同时使用。
使能和失能中断
针对当前处理器开关中断
local_irq_disable();
/* interrupts are disabled .. */
local_irq_enable();
local_irq_disable()函数是不安全的;因为随着内核的大小和复杂性的增加,导致函数的所有代码路径变得越来越困难,所以在禁用中断系统之前保存它的状态要安全得多。
unsigned long flags;
local_irq_save(flags); /* interrupts are now disabled */
/* ... */
local_irq_restore(flags); /* interrupts are restored to their previous state */
?? 上面两个函数调用必须在同一个函数中。前面所有的函数在中断上下文和进程上下文中都可以调用。
失能指定中断
void disable_irq(unsigned int irq);
void disable_irq_nosync(unsigned int irq);
void enable_irq(unsigned int irq);
void synchronize_irq(unsigned int irq);
前两个函数禁用中断控制器中的给定中断线,所有处理器都不会收到这个中断。disable_irq会等待当前正在执行的中断处理函数执行完成。因此,调用者不仅可以保证新的中断不会在给定的线路上传递,而且任何已经执行的处理程序都已经退出。disable_irq_nosync()不会等待中断执行完成。函数synchronize_irq等待特定的中断处理程序退出(如果它正在执行),然后返回。
disable_irq或者disable_irq_nosync必须和enable_irq配对使用,即调用次数一样。对于共享中断,disable_irq是不应该使用的。
中断系统状态
知道当前中断是打开还是关闭,或者是否在中断上下文中是非常有用的。
函数 irqs_disabled()如果当前处理器中断关闭返回非0,否则返回0.
有两个宏可以检查是否在中断上下文中。
in_interrupt()
in_irq()
in_interrupt通常用的比较多,如果内核在中断上下文(包括中断上下半部)返回非0,否则返回0(即进程上下文)。in_irq只是用来判断是否在中断上半部,如果是,返回非0.
