歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux 2.6.36 x86 內核中斷初始化過程詳解

Linux 2.6.36 x86 內核中斷初始化過程詳解

日期:2017/2/28 16:14:33   编辑:Linux教程

隨著硬件技術的發展,中斷控制芯片已經不再是傳統的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 也可以看到這些中斷與眾不同,如下圖。這些中斷左邊顯示的不是中斷請求號,而是一個標識,右邊顯示的也不是中斷控制芯片的信息。

Copyright © Linux教程網 All Rights Reserved