Linux 2.6.36 x86 内核中断初始化过程详解

随着硬件技术的发展,中断控制芯片已经不再是传统的ISA总线连着的简单PIC了,APIC,MSI,MSIX等等的词语大家已经非常的熟悉。同时,Linux内核也在不断发展,它在中断上的实现也越来越复杂,在这里我来讨论介绍一下Linux x86 架构下的中断初始化过程。

在start_kernel()之前的中断门初始化就不多啰嗦了,在随便的内核教科书里都能看到,这里就从start_kernel以后开始。

1.8259a、LAPIC相关数据结构初始化

在Linux之中对于每一个中断有两个重要的数据结构与之对应,他们分别是中断门描述符gate_desc和中断请求描述符irq_desc。

我们所谓的中断初始化也就是对这两个数据结构进行初始化。

gate_desc

gate_desc很简单,就是一个有着高CPL的普通门描述符。关键就是有一个成员是中断门函数地址,这个地址我们可以直接保存我们的ISR(中断服务程序),也可以保存一个一般中断的入口地址,,然后通过这个入口地址指向irq_desc里面保存的ISR,的确Linux就是这样分多种情况实现的。

irq_desc

irq_desc里面成员就非常多并且复杂了,由于本文主要描述中断初始化过程,所以对触发过程顺便带过,关于中断的各种触发方式和处理过程会在其他文章中详细讲述。

struct irq_desc {unsigned int irq;unsigned int *kstat_irqs;irq_flow_handler_t handle_irq;

struct irqaction *action;

struct irq_chip *chip;struct msi_desc *msi_desc;void *handler_data;void *chip_data;const char *name;

…s}

其中中断芯片结构指针*chip,中断触发方式 handle_irq,以及中断服务程序链表*action。

中断初始化路径:start_kernel() -> init_IRQ() -> native_init_IRQ() ->

void __init native_init_IRQ(void){ int i;

/* 初始化8259a PIC中断控制器相关数据结构*/ x86_init.irqs.pre_vector_init();

/* 初始化SMP的APIC中断门描述符*/ apic_intr_init();

/*在这里进行遍历所有的中断门,将所有还没有配置得中断门进行统一配置,在这里有一个interrupt函数数组指针,当中断发生的时候将会触发这个interrupt函数,然后所有的interrupt都会从调用do_IRQ的函数,在do_IRQ()里面触发真正的中断服务程序。 */ for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) { if (!test_bit(i, used_vectors)) set_intr_gate(i, interrupt[i-FIRST_EXTERNAL_VECTOR]); }}

(1)初始化8259a相关

void __init init_ISA_irqs(void){ int i;

/*初始化PIC和local APIC芯片,通过写IO,对芯片进行初始化。*/

#if defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC) init_bsp_APIC(); #endif init_8259A(0);

/*在这里将8259a控制的16个中断进行芯片初始化。并且设置电平触发方式,这样是对上面说的irq_desc这个结构进行初始化,当然这里只是PIC芯片部分。*/

for (i = 0; i < NR_IRQS_LEGACY; i++) { struct irq_desc *desc = irq_to_desc(i);

desc->status = IRQ_DISABLED; desc->action = NULL; desc->depth = 1;

set_irq_chip_and_handler_name(i, &i8259A_chip, handle_level_irq, “XT”); }}(2)Apic默认基本中断门初始化。

static void __init apic_intr_init(void){ smp_intr_init();

#ifdef CONFIG_X86_THERMAL_VECTOR alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt); #endif #ifdef CONFIG_X86_MCE_THRESHOLD alloc_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt); #endif #if defined(CONFIG_X86_MCE) && defined(CONFIG_X86_LOCAL_APIC) alloc_intr_gate(MCE_SELF_VECTOR, mce_self_interrupt); #endif

#if defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC) /* self generated IPI for local APIC timer */ alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);

/* IPI for X86 platform specific use */ alloc_intr_gate(X86_PLATFORM_IPI_VECTOR, x86_platform_ipi);

/* IPI vectors for APIC spurious and error interrupts */ alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt);

/* Performance monitoring interrupts: */ # ifdef CONFIG_PERF_EVENTS alloc_intr_gate(LOCAL_PENDING_VECTOR, perf_pending_interrupt); # endif

#endif}

我们可以看到这些中断初始化很特别,就像我们设置的陷阱门系统调用一样,将中断处理函数直接放在中断门得指向地址,这样只要中断到来,一旦通过中断门将直接跳像中断处理函数,而忽略了irq_desc部分,不需要考虑怎么触发,不需要考虑怎么调度。我们在Linux用

cat /proc/interrupts 也可以看到这些中断与众不同,如下图。这些中断左边显示的不是中断请求号,而是一个标识,右边显示的也不是中断控制芯片的信息。

人生的失败往往是在关键时刻少了坚持。

Linux 2.6.36 x86 内核中断初始化过程详解

相关文章:

你感兴趣的文章:

标签云: