1. /proc/interrupts
该文件存放系统中,与中断相关的统计信息。内容如下:
xxha@fast-server02:~$ cat /proc/interrupts
CPU0 CPU1
0: 202 727 IO-APIC-edge timer
1: 3 1 IO-APIC-edge i8042
4: 1 1 IO-APIC-edge
7: 0 0 IO-APIC-edge parport0
8: 1 0 IO-APIC-edge rtc0
9: 0 0 IO-APIC-fasteoi acpi
12: 5 1 IO-APIC-edge i8042
16: 259714 167 IO-APIC-fasteoi uhci_hcd:usb3, HDA Intel
17: 9 18 IO-APIC-fasteoi uhci_hcd:usb4, uhci_hcd:usb7
18: 0 0 IO-APIC-fasteoi uhci_hcd:usb8
22: 0 3 IO-APIC-fasteoi ehci_hcd:usb1, uhci_hcd:usb5
23: 0 0 IO-APIC-fasteoi ehci_hcd:usb2, uhci_hcd:usb6
24: 25457621 0 HPET_MSI-edge hpet2
25: 0 5019320 HPET_MSI-edge hpet3
32: 2849638 1889 PCI-MSI-edge ahci
33: 59 10112204 PCI-MSI-edge eth0
34: 337311 341370 PCI-MSI-edge fglrx[0]@PCI:1:0:0
NMI: 0 0 Non-maskable interrupts
LOC: 658 635 Local timer interrupts
SPU: 0 0 Spurious interrupts
PMI: 0 0 Performance monitoring interrupts
PND: 0 0 Performance pending work
RES: 506371 494539 Rescheduling interrupts
CAL: 2763 2849 Function call interrupts
TLB: 1062330 1032721 TLB shootdowns
TRM: 0 0 Thermal event interrupts
THR: 0 0 Threshold APIC interrupts
MCE: 0 0 Machine check exceptions
MCP: 830 830 Machine check polls
ERR: 1
MIS: 0
第1列是中断号。
第2,3列是各个CPU接收到的中断个数。
第4列是处理这个中断的中断控制器。如 IO-APIC-edge。
最后一列是与这个中断相关的设备的名字, 这个名字通过参数 devname 传给 request_irq().
如果中断时共享的(如中断16,17等),则这个中断号上注册的所有设备都会列出来。
2. 中断控制
Linux内核提供了一组用于控制机器上中断状态的接口。
这些接口能禁止当前处理器的中断系统,或屏蔽掉整个机器的一个中断号的能力。
锁可以防止其他处理器的并发访问,禁止中断可以防止其他中断处理程序的并发访问。
2.1 禁止和激活当前处理器上的本地中断
local_irq_disable();
//禁止中断区
local_irq_enable();
在产生中断的处理器上,他们将禁止或激活中断的传递。
下面两个函数会更加可靠:
unsigned long flags;
local_irq_save(flags);
//禁止中断区
local_irq_restore(flags);
禁止中断之前保存系统的中断状态,激活中断时恢复原来的中断状态。
local_irq_save(flags) 和 local_irq_restore(flags) 必须在同一个函数中进行。
2.2 禁止指定的中断号
在某些情况下,只需要禁止系统中某一个中断号就够了,这就是所谓的屏蔽一个中断号(masking out)。
void disable_irq(unsigned int irq); //等待该函数处理内容完成,再返回
void disable_irq_nosync(unsigned int irq); //不等待
禁止中断控制器上指定的中断号irq,即禁止中断控制器想所有处理器传递中断号。
/**
* disable_irq - disable an irq and wait for completion
* @irq: Interrupt to disable
*
* Disable the selected interrupt line. Enables and Disables are
* nested.
* This function waits for any pending IRQ handlers for this interrupt
* to complete before returning. If you use this function while
* holding a resource the IRQ handler may need you will deadlock.
*
* This function may be called - with care - from IRQ context.
*/
void disable_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
if (!desc)
return;
disable_irq_nosync(irq);
if (desc->action)
synchronize_irq(irq);
}
EXPORT_SYMBOL(disable_irq);
/**
* disable_irq_nosync - disable an irq without waiting
* @irq: Interrupt to disable
*
* Disable the selected interrupt line. Disables and Enables are
* nested.
* Unlike disable_irq(), this function does not ensure existing
* instances of the IRQ handler have completed before returning.
*
* This function may be called from IRQ context.
*/
void disable_irq_nosync(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
unsigned long flags;
if (!desc)
return;
chip_bus_lock(desc);
raw_spin_lock_irqsave(&desc->lock, flags);
__disable_irq(desc, irq, false);
raw_spin_unlock_irqrestore(&desc->lock, flags);
chip_bus_sync_unlock(desc);
}
EXPORT_SYMBOL(disable_irq_nosync);
void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)
{
if (suspend) {
if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND))
return;
desc->status |= IRQ_SUSPENDED;
}
if (!desc->depth++) {
desc->status |= IRQ_DISABLED; //声明IRQ_DISABLE
desc->irq_data.chip->irq_disable(&desc->irq_data);
}
}
具体的操作在__diable_irq()中完成。
void synchronize_irq(unsigned int irq); //等待一个特定的中断处理程序退出。
/**
* synchronize_irq - wait for pending IRQ handlers (on other CPUs)
* @irq: interrupt number to wait for
*
* This function waits for any pending IRQ handlers for this interrupt
* to complete before returning. If you use this function while
* holding a resource the IRQ handler may need you will deadlock.
*
* This function may be called - with care - from IRQ context.
*/
void synchronize_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
unsigned int status;
if (!desc)
return;
do {
unsigned long flags;
/*
* Wait until we're out of the critical section. This might
* give the wrong answer due to the lack of memory barriers.
*/
while (desc->status & IRQ_INPROGRESS)
cpu_relax();
/* Ok, that indicated we're done: double-check carefully. */
raw_spin_lock_irqsave(&desc->lock, flags);
status = desc->status;
raw_spin_unlock_irqrestore(&desc->lock, flags);
/* Oops, that failed? */
} while (status & IRQ_INPROGRESS);
/*
* We made sure that no hardirq handler is running. Now verify
* that no threaded handlers are active.
*/
wait_event(desc->wait_for_threads, !atomic_read(&desc->threads_active));
}
EXPORT_SYMBOL(synchronize_irq);
void enable_irq(unsigned int irq); //激活中断号/**
* enable_irq - enable handling of an irq
* @irq: Interrupt to enable
*
* Undoes the effect of one call to disable_irq(). If this
* matches the last disable, processing of interrupts on this
* IRQ line is re-enabled.
*
* This function may be called from IRQ context only when
* desc->irq_data.chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
*/
void enable_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
unsigned long flags;
if (!desc)
return;
if (WARN(!desc->irq_data.chip || !desc->irq_data.chip->irq_enable,
KERN_ERR "enable_irq before setup/request_irq: irq %u\n", irq))
return;
chip_bus_lock(desc);
raw_spin_lock_irqsave(&desc->lock, flags);
__enable_irq(desc, irq, false);
raw_spin_unlock_irqrestore(&desc->lock, flags);
chip_bus_sync_unlock(desc);
}
EXPORT_SYMBOL(enable_irq);
void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
{
if (resume)
desc->status &= ~IRQ_SUSPENDED;
switch (desc->depth) {
case 0:
err_out:
WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n", irq);
break;
case 1: {
unsigned int status = desc->status & ~IRQ_DISABLED;
if (desc->status & IRQ_SUSPENDED)
goto err_out;
/* Prevent probing on this irq: */
desc->status = status | IRQ_NOPROBE;
check_irq_resend(desc, irq);
/* fall-through */
}
default:
desc->depth--;
}
}
这几个函数可以在中断上下文,或进程上下文中调用,不会睡眠。
禁止多个中断处理程序共享的中断号是不合适的,禁止了该中断号就是禁止了该中断号上的所有设备的中断传递。
3. 查询中断系统状态:
irq_disabled(); --- 如果本地中断被禁止,则返回非0, 否则返回0
in_interrupt(); --- 如果CPU出在中断上下文中,则返回非0,如果在进程上下文中则返回0。
若返回非0, 则说明内核正在执行中断处理程序,或中断下半部处理程序。
in_irq(); --- 如果正在执行中断处理程序,则返回非0,否则返回0.
/*
* Are we doing bottom half or hardware interrupt processing?
* Are we in a softirq context? Interrupt context?
* in_softirq - Are we currently processing softirq or have bh disabled?
* in_serving_softirq - Are we currently processing softirq?
*/
#define in_irq() (hardirq_count())
#define in_softirq() (softirq_count())
#define in_interrupt() (irq_count())
#define in_serving_softirq() (softirq_count() & SOFTIRQ_OFFSET)
#define hardirq_count() (preempt_count() & HARDIRQ_MASK)
#define softirq_count() (preempt_count() & SOFTIRQ_MASK)
#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \
| NMI_MASK))
#define preempt_count() (current_thread_info()->preempt_count)