首先,中断产生于外部硬件,通过中断控制器的判定,发往cpu(这时会被中断控制器转换成中断向量)。这里需要注意的是,系统在初始化的时候,会设置前32个中断向量的中断函数和系统调用中断的处理函数,这个对应关系存储在一个中断向量表当中(IDT tables)
点击(此处)折叠或打开
- /*
- * Linux IRQ vector layout.
- *
- * There are 256 IDT entries (per CPU - each entry is 8 bytes) which can
- * be defined by Linux. They are used as a jump table by the CPU when a
- * given vector is triggered - by a CPU-external, CPU-internal or
- * software-triggered event.
- *
- * Linux sets the kernel code address each entry jumps to early during
- * bootup, and never changes them. This is the general layout of the
- * IDT entries:
- *
- * Vectors 0 ... 31 : system traps and exceptions - hardcoded events
- * Vectors 32 ... 127 : device interrupts
- * Vector 128 : legacy int80 syscall interface
- * Vectors 129 ... INVALIDATE_TLB_VECTOR_START-1 except 204 : device interrupts
- * Vectors INVALIDATE_TLB_VECTOR_START ... 255 : special interrupts
- *
- * 64-bit x86 has per CPU IDT tables, 32-bit has one shared IDT table.
- *
- * This file enumerates the exact layout of them:
- */
点击(此处)折叠或打开
- /**
- * idt_setup_apic_and_irq_gates - Setup APIC/SMP and normal interrupt gates
- */
- void __init idt_setup_apic_and_irq_gates(void)
- {
- int i = FIRST_EXTERNAL_VECTOR;
- void *entry;
- idt_setup_from_table(idt_table, apic_idts, ARRAY_SIZE(apic_idts), true);
- for_each_clear_bit_from(i, used_vectors, FIRST_SYSTEM_VECTOR) {
- entry = irq_entries_start + 8 * (i - FIRST_EXTERNAL_VECTOR);
- set_intr_gate(i, entry);
- }
- for_each_clear_bit_from(i, used_vectors, NR_VECTORS) {
- #ifdef CONFIG_X86_LOCAL_APIC
- set_bit(i, used_vectors);
- set_intr_gate(i, spurious_interrupt);
- #else
- entry = irq_entries_start + 8 * (i - FIRST_EXTERNAL_VECTOR);
- set_intr_gate(i, entry);
- #endif
- }
- }
点击(此处)折叠或打开
- /*
- * Build the entry stubs with some assembler magic.
- * We pack 1 stub into every 8-byte block.
- */
- .align 8
- ENTRY(irq_entries_start)
- vector=FIRST_EXTERNAL_VECTOR
- .rept (FIRST_SYSTEM_VECTOR - FIRST_EXTERNAL_VECTOR)
- pushl $(~vector+0x80) /* Note: always in signed byte range */
- vector=vector+1
- jmp common_interrupt
- .align 8
- .endr
- END(irq_entries_start)
- /*
- * the CPU automatically disables interrupts when executing an IRQ vector,
- * so IRQ-flags tracing has to follow that:
- */
- .p2align CONFIG_X86_L1_CACHE_SHIFT
- common_interrupt:
- ASM_CLAC
- addl $-0x80, (%esp) /* Adjust vector into the [-256, -1] range */
- SAVE_ALL
- ENCODE_FRAME_POINTER
- TRACE_IRQS_OFF
- movl %esp, %eax
- call do_IRQ
- jmp ret_from_intr
- ENDPROC(common_interrupt)
由于不同CPU中相同的中断向量对应的虚拟中断号irq需要不同(这是irq这一虚拟中断层出现的原因),所以,需要每个CPU在系统初始化的时候,将虚拟中断号irq与其本地的中断向量进行一个映射:
点击(此处)折叠或打开
- DEFINE_PER_CPU(vector_irq_t, vector_irq) = {
- [0 ... NR_VECTORS - 1] = VECTOR_UNUSED,
- };
点击(此处)折叠或打开
- struct irq_desc {
- struct irq_common_data irq_common_data;
- struct irq_data irq_data;
- unsigned int __percpu *kstat_irqs;
- irq_flow_handler_t handle_irq;
- ...
- struct irqaction *action; /* IRQ action list */
- unsigned int status_use_accessors;
- unsigned int core_internal_state__do_not_mess_with_it;
- unsigned int depth; /* nested irq disables */
- unsigned int wake_depth; /* nested wake enables */
- unsigned int tot_count;
- unsigned int irq_count; /* For detecting broken IRQs */
- unsigned long last_unhandled; /* Aging timer for unhandled count */
- unsigned int irqs_unhandled;
- atomic_t threads_handled;
- int threads_handled_last;
- raw_spinlock_t lock;
- struct cpumask *percpu_enabled;
- const struct cpumask *percpu_affinity;
- ...
- struct mutex request_mutex;
- int parent_irq;
- struct module *owner;
- const char *name;
- } ____cacheline_internodealigned_in_smp;
中断处理一般会分成上半部分和下半部分。其中上半部分处理时间紧急的工作,在这期间,要关闭外部硬件中断的响应;下半部分是处理时间不是那么紧急的工作,基本上实现机制都是基于软中断(在硬件中断退出会调用,ksoftirqd守护进程也会执行),软中断的话实现是在内核里面写死的,基于它的有网络收发,计时器,tasklet等。另外,内核当中有关于软中断运行时间(2ms)以及运行次数的限制(最多10次循环处理pending软中断),避免软中断一直执行,占用CPU