Linux x86_64內核中斷初始化


中斷分類

Linux系統中,中斷分為:

  • 硬中斷:由外部設備或者執行異常產生的需要快速處理的中斷。如缺頁中斷、定時器硬件中斷。

    根據內部產生還是外部產生,分為:

    1. 異常:異常是內部產生的中斷,不可屏蔽。
    2. 外部中斷:外部中斷是由外部設備產生的,可以屏蔽。
  • 軟中斷:

    ​ 軟中斷是Linux系統中斷處理的底半處理部分,是Linux模擬的中斷。為了加快硬件中斷的處理,防止數據的丟失,Linux對中斷處理分為頂半處理和底半處理兩部分,頂半處理程序快速處理硬件事件,把不是那么緊急的邏輯放到底半處理程序中,可以簡單的認為硬終端處理程序為頂半處理程序,軟中斷處理程序為底半處理程序。軟中斷一般在硬中斷處理程序執行后才會執行。但是當硬中斷嵌套的時候,軟中斷會在所有的硬中斷處理完畢后才會處理,當軟中斷太多,會放到ksoftirqd線程中處理。

內核初始化-中斷

intel處理器有256個硬中斷號。其中前32個中斷號為異常使用,在內核初始化的時候進行初始化。內核初始化的代碼流程如下:
可以看到首先初始化異常處理,再初始化部分外部中斷,再初始化一部分軟中斷處理。


asmlinkage void __init start_kernel(void)
{
	 
	lock_kernel();
	...
     //初始化調度模塊
	sched_init();

	...
	sort_main_extable();
    // 初始化異常處理。
	trap_init();
	...
    // 初始化外部中斷
	init_IRQ();
    ...
	// 初始化定時器模塊,同時,會注冊定時器的軟中斷處理函數。
    init_timers();
    
    // 初始化軟中斷)
	softirq_init();
	time_init();
	...
	// 初始化
	acpi_early_init();
}

異常中斷初始化

異常中斷在內核中稱為trap,異常中斷初始化代碼為

//門初始化。初始化中斷向量表。系統有固定的256個硬件中斷向量。
void __init trap_init(void)
{
    
	set_intr_gate(0,&divide_error);
	set_intr_gate_ist(1,&debug,DEBUG_STACK);
	set_intr_gate_ist(2,&nmi,NMI_STACK);
	set_intr_gate(3,&int3);
	set_system_gate(4,&overflow);	/* int4-5 can be called from all */
	set_system_gate(5,&bounds);
	set_intr_gate(6,&invalid_op);
	set_intr_gate(7,&device_not_available);
	set_intr_gate_ist(8,&double_fault, DOUBLEFAULT_STACK);
	set_intr_gate(9,&coprocessor_segment_overrun);
	set_intr_gate(10,&invalid_TSS);
	set_intr_gate(11,&segment_not_present);
	set_intr_gate_ist(12,&stack_segment,STACKFAULT_STACK);
	set_intr_gate(13,&general_protection);
	set_intr_gate(14,&page_fault);
	set_intr_gate(15,&spurious_interrupt_bug);
	set_intr_gate(16,&coprocessor_error);
	set_intr_gate(17,&alignment_check);
#ifdef CONFIG_X86_MCE
	set_intr_gate_ist(18,&machine_check, MCE_STACK); 
#endif
	set_intr_gate(19,&simd_coprocessor_error);

#ifdef CONFIG_IA32_EMULATION
	set_system_gate(IA32_SYSCALL_VECTOR, ia32_syscall);
#endif
       
	set_intr_gate(KDB_VECTOR, call_debug);
       
	/*
	 * Should be a barrier for any external CPU state.
	 */
	cpu_init();
}

總結如下:

中斷向量號 異常事件 Linux的處理程序
0 除法錯誤 Divide_error
1 調試異常 Debug
2 NMI中斷 Nmi
3 單字節,int 3 Int3
4 溢出 Overflow
5 邊界監測中斷 Bounds
6 無效操作碼 Invalid_op
7 設備不可用 Device_not_available
8 雙重故障 Double_fault
9 協處理器段溢出 Coprocessor_segment_overrun
10 無效TSS Incalid_tss
11 缺段中斷 Segment_not_present
12 堆棧異常 Stack_segment
13 一般保護異常 General_protection
14 頁異常 Page_fault
15 Spurious_interrupt_bug
16 協處理器出錯 Coprocessor_error
17 對齊檢查中斷 Alignment_check
0x80 系統調用 ia32_syscall
0xf9 內核調試 call_debug

上述中斷處理函數都是匯編語言編寫。一部分匯編直接處理完畢,一部分通過調用C函數幫助處理。
匯編代碼在linux/arch/x86_64/entry.S中,大部分都是調用C函數do_中斷處理函數名處理。
整理如下:

中斷向量號 異常事件 Linux匯編 調用c函數 處理結果
0 除法錯誤 Divide_error do_divide_error 發送SIGFPE信號
1 調試異常 Debug do_debug 發送SIGTRAP信號
2 NMI中斷 Nmi do_nmi
3 單字節,int 3 Int3 do_int3 發送SIGTRAP信號
4 溢出 Overflow do_overflow 發送SIGSEGV信號
5 邊界監測中斷 Bounds do_bounds 發送SIGSEGV信號
6 無效操作碼 Invalid_op do_invalid_op 發送SIGILL信號
7 設備不可用 Device_not_available math_state_restore 發送SIGSEGV信號
8 雙重故障 Double_fault do_double_fault
9 協處理器段溢出 Coprocessor_segment_overrun do_coprocessor_segment_overrun 發送SIGFPE信號
10 無效TSS Invalid_tss do_invalid_TSS 發送SIGSEGV信號
11 缺段中斷 Segment_not_present do_segment_not_present 發送SIGBUS信號
12 堆棧異常 Stack_segment do_stack_segment
13 一般保護異常 General_protection do_general_protection
14 頁異常 Page_fault do_page_fault 處理缺頁中斷
15 Spurious_interrupt_bug do_spurious_interrupt_bug
16 協處理器出錯 Coprocessor_error do_coprocessor_error 發送SIGFPE信號
17 對齊檢查中斷 Alignment_check do_alignment_check 發送SIGBUS信號
0x80 系統調用 ia32_syscall
0xf9 內核調試 call_debug do_call_debug

外部中斷初始化

中斷控制器硬件APIC分為兩種:本地APIC和全局APIC。本地APIC集成在CPU內部,每個CPU都有一個,用於處理本地中斷請求,CPU可以通過APIC向其他CPU發送中斷,現在主要用於CPU之間的通信(IPI)。全局APIC主要是連接外部設備,用於外部設備的中斷。在內核中斷初始化的時候,會初始化三個與IPI相關中斷。

void __init init_IRQ(void)
{
	int i;
	/**
	* 該函數主要是初始化硬件
	* 1. 初始化本地APIC控制芯片
	* 2. 初始化8259A芯片
	/
	init_ISA_irqs();
	/*
	 * 清空32以后的中斷向量表。(除了系統調用和內核調試用的中斷號)
	 */
	for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
		int vector = FIRST_EXTERNAL_VECTOR + i;
		if (i >= NR_IRQS)
			break;
		if (vector != IA32_SYSCALL_VECTOR && vector != KDB_VECTOR) { 
			set_intr_gate(vector, interrupt[i]);
	}
	}
// 多處理器通信中斷
#ifdef CONFIG_SMP

	set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]);
	set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
	set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt);
	set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
#endif	
// 本地APIC中斷
#ifdef CONFIG_X86_LOCAL_APIC
	/* self generated IPI for local APIC timer */
    set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
    set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
	set_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
#endif
	setup_timer();

	if (!acpi_ioapic)
		setup_irq(2, &irq2);
}

總結如下:

中斷向量號 中斷名 異常事件 中斷處理函數 調用c函數 處理結果
0xfc RESCHEDULE_VECTOR 處理器間中斷, 用於cpu之間同學,其他cpu要求重新調度 reschedule_interrupt smp_reschedule_interrupt 將線程調度標志置為需要重新調度。之后內核檢查標志的時候會重新調度線程
0xfd INVALIDATE_TLB_VECTOR 處理器間中斷, 用於cpu之間通信,其他cpu要求TLB緩存失效 invalidate_interrupt smp_invalidate_interrupt cpu刷新TLB
0xfa CALL_FUNCTION_VECTOR 處理器間中斷, 用於cpu之間通信,讓另外的cpu調用某個函數 call_function_interrupt smp_call_function_interrupt 函數數據通過call_data_struct傳送,cpu會調用該函數
0xef LOCAL_TIMER_VECTOR APIC定期器中斷 apic_timer_interrupt smp_apic_timer_interrupt 觸發定時器的軟中斷
0xff SPURIOUS_APIC_VECTOR 偽中斷 spurious_interrupt smp_spurious_interrupt 忽略
0xfe ERROR_APIC_VECTOR APIC錯誤 error_interrupt smp_error_interrupt 打印錯誤

0xfa中斷說明:
當cpu需要另一個cpu執行某個函數時,只需要初始化

struct call_data_struct {
	void (*func) (void *info);
	void *info;
	atomic_t started;
	atomic_t finished;
	int wait;
};

的結構體,然后發出一個0xfa中斷即可。

軟中斷初始化

軟中斷初始化分為兩部分:

  1. 初始化定時器時,會打開TIMER_SOFTIRQ的軟中斷,並設置中斷處理函數為run_timer_softirq。
  2. softirq_init函數執行,會打開TASKLET_SOFTIRQ和HI_SOFTIRQ,處理函數分別為 tasklet_action和 tasklet_hi_action。

軟中斷的線程處理機制就不說了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM