閱讀246 返回首頁    go 技術社區[雲棲]


中斷子係統1_中斷子係統初始化

//	控製單元對中斷信號的處理:
//		當cpu執行一條指令後,cs和eip包含下一條要執行的指令的邏輯地址,在處理那條指令之前,
//		控製單元會檢查在運行前一條指令時是否發生了一個中斷或異常,如果發生了一個中斷或異常,
//		控製單元執行下列操作:
//			1.確定與中斷或異常關聯的向量i
//			2.讀入由idtr寄存器指向的IDT表中的第i項
//			3.從gdtr寄存器獲得GDT的基地址,並在GDT中查找,以讀取IDT表項中的選擇符所標識的段描述符。
//				這個描述符指定中斷或異常處理程序的基地址
//			4.權限和安全性檢查
//			5.檢查是否發生了特權級的變化,也就是說,CPL是否不同於所選擇的段描述符的DPL,如果是,
//			控製單元必須開始使用與新的特權級相關的棧。
//				5.1 讀入tr寄存器,以訪問運行進程的TSS段
//				5.2 用與新特權級相關的棧段和棧指針的正確值裝在ss和esp,這些值可以在tss中找到。
//				5.3 在新的棧中保存ss和esp以前的值,這些值定義了與舊特權級相關的棧的邏輯地址。
//			6.如果故障已發生,用引起異常的指令地址裝載cs和eip寄存器,從而使得這條指令能再次被執行。
//			7.在棧中保存eflags,cs,及eip的內容。
//			8.如果異常產生了一個硬件出錯碼,則將其保存在棧中。
//			9.裝載cs和eip寄存器,其值分別時IDT表中第i項門描述符的段選擇符和偏移量字段。這些值給出了
//			中斷或者異常處理程序的第一條指令地址。
//			10.由中斷處理程序,執行中斷處理。
//			11.中斷或異常處理完畢後,相應的處理程序必須產生一條iret指令,把控製權轉交給被中斷的程序,
//			迫使控製單元:
//				11.1 用保存在棧中的值裝載cs、eip或eflags寄存器。
//				11.2 檢查處理程序的CPL是否等於cs中最低兩位的值(這意味著被中斷的進程與處理程序運行在
//				同一特權級),如果是,iret終止執行;否則,轉入下一步
//				11.3 從棧中裝載ss和esp寄存器,因此返回到與舊特權級相關的棧。


//	中斷向量:
//		1.每個中斷和異常是由0~255之間的一個數來標識的,intel將這個8位的無符號整數叫做一個向量。
//		2.中斷向量是Intel從IA-32 CPU角度看到的中斷信號劃分;中斷號則是Linux係統對外部中斷的號碼分配。 
//			當外設把中斷信號遞送給PIC時,與之關聯的是一個“中斷號”(每個中斷號對應一條中斷線,從軟件的角度來看,這兩個術語可以混用);
//			當PIC把這個中斷信號發送給CPU時,與之關聯的是一個“中斷向量”。
//		3.通常,不可屏蔽中斷和異常的中斷向量是固定的,而可屏蔽中斷的中斷向量則可以對中斷控製器進行編程來改變。
//		4.i386 CPU的256個中斷向量是這樣分配的:
//			4.1 0~31這一共32個向量用於異常和不可屏蔽中斷。
//			4.2 32~47這一共16個向量用於可屏蔽中斷,分別對應於主、從8259A中斷控製器的IRQ輸入線
//			4.3 剩餘的48~255用於標識軟中斷
//		5.Linux全部使用了0~47之間的向量。但對於48-255之間的軟中斷向量,Linux隻使用了其中的一個,即用於實現係統調用的中斷向量128(0x80)。
//			當用戶態下的進程執行一條int 0x80匯編指令時,CPU切換到內核態,以服務於係統調用。

//	i386的IDT  
//		i386 CPU的IDT表一共有256項,分別對應每一個中斷向量。每一個表項就是一個中斷描述符,用以描述相對應的中斷向量,中斷向量就是該描述符
//		在IDT中的索引,每一個中斷描述符的大小都是8個字節。根據INTEL的術語,中斷描述符也稱為“門(Gate)”。  


//	IDT表
//		idt_table首地址保存在idt寄存器中
1.1 struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, };

//	CPU異常初始化
//	調用路徑:	start_kernel->trap_init
//	函數主要任務:
//		1.初始化apic控製器
//		2.注冊0~19號異常、中斷的處理函數,128號係統調用處理程序
//		3.注冊係統調用的處理函數
//		4.cpu的初始化

//	內核源碼:arch/x86/kernel/traps.c
//			  arch/x86/kernel/entry_32.S
1.2 void __init trap_init(void)
{
	//cpu內部apic控製器
	init_apic_mappings();
	//初始化中斷、異常、陷阱門描述符
	set_trap_gate(0,÷_error);
	set_intr_gate(1,&debug);
	//不可屏蔽中斷
	set_intr_gate(2,&nmi);
	//係統中斷門
	set_system_intr_gate(3, &int3); 
	//係統門
	set_system_gate(4,&overflow);
	set_system_gate(5,&bounds);
	//陷阱門
	set_trap_gate(6,&invalid_op);
	set_trap_gate(7,&device_not_available);
	set_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS);
	set_trap_gate(9,&coprocessor_segment_overrun);
	set_trap_gate(10,&invalid_TSS);
	set_trap_gate(11,&segment_not_present);
	set_trap_gate(12,&stack_segment);
	set_trap_gate(13,&general_protection);
	//缺頁中斷
	set_intr_gate(14,&page_fault);
	set_trap_gate(15,&spurious_interrupt_bug);
	set_trap_gate(16,&coprocessor_error);
	set_trap_gate(17,&alignment_check);
	set_trap_gate(19,&simd_coprocessor_error);
	//係統調用描述符
	set_system_gate(SYSCALL_VECTOR,&system_call);
	//cpu相關的初始化
	cpu_init();
}


//	set_XXX_gate的設置異常處理函數地址
//		intr_gate、trap_gate區別在於DPL不同

//	設置中斷門描述符
//	函數主要任務:
//		在IDT表的第n個表項插入一個中斷門
//			1.1 門中的段選擇子設置為內核代碼段的段選擇子
//			1.2 偏移量設置為中斷處理程序的地址addr
//			1.3 DPL字段設置為0
1.3 void set_intr_gate(unsigned int n, void *addr)
{
	_set_gate(idt_table+n,14,0,addr,__KERNEL_CS);
}

  
//	第一個外設中斷(即8259A的IRQ0)所對應的中斷向量
#define FIRST_EXTERNAL_VECTOR 0x20  
//	係統調用的中斷向量
#define SYSCALL_VECTOR 0x80

//	中斷初始化
//	調用路徑:start_kernel->init_IRQ
//	函數主要任務:
//		1.初始化中斷描述符的中斷控製器
//		2.初始化20~255號中斷的入口處理函數
//		3.添加體係結構特定的門
//		4.初始化時鍾中斷
//		5.初始化當前cpu的中斷棧
2.1 void __init init_IRQ(void)
{
	int i;

	//建立中斷描述符與中斷控製器的聯係
	pre_intr_init_hook();

	//設置20~255號中斷的入口函數
	for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
		//從第一個外部中斷開始
		int vector = FIRST_EXTERNAL_VECTOR + i;
		if (i >= NR_IRQS)
			break;
		//中斷門描述符
		if (vector != SYSCALL_VECTOR) 
			set_intr_gate(vector, interrupt[i]);
	}
	//添加體係結構特定的門
	intr_init_hook();
	//初始化時鍾中斷
	setup_pit_timer();
	//初始化當前cpu的中斷棧
	irq_ctx_init(smp_processor_id());
}
//	中斷描述符(IRQn描述符)
//		i8259a使用0~15號IRQ
2.2 struct irqdesc irq_desc[NR_IRQS];

//	中斷描述符初始化
//	函數主要任務:
//		1.禁用所有IRQ
//		2.0~15號IRQ關聯i8259a中斷控製器
//	注:
//		中斷關閉嵌套,當depth=0時,中斷開啟。
2.3 void __init init_ISA_irqs (void)
{
	int i;
	//初始化cpu的apic
	init_bsp_APIC();
	//初始化8259a中斷控製器
	init_8259A(0);
	//8259使用的中斷號(IRQn)
	for (i = 0; i < NR_IRQS; i++) {
		//初始化時,所有irq關閉
		irq_desc[i].status = IRQ_DISABLED;
		//沒有驅動注冊中斷處理例程
		irq_desc[i].action = NULL;
		//嵌套深度為1的關閉狀態
		irq_desc[i].depth = 1;
		//0~16號irq,關聯i8259a中斷控製器
		if (i < 16) {
			irq_desc[i].handler = &i8259A_irq_type;
		} else {
			// >16的pci中斷,按需分配
			irq_desc[i].handler = &no_irq_type;
		}
	}
}

最後更新:2017-04-03 14:53:58

  上一篇:go C#[編程實例]-編程入門試題
  下一篇:go 用SDL庫播放yuy2 Packed mode