多處理器下的中斷機(jī)制
[導(dǎo)讀]INTERRUPT中斷是硬件和軟件交互的一種機(jī)制,可以說整個(gè)操作系統(tǒng),整個(gè)架構(gòu)都是由中斷來驅(qū)動(dòng)的。中斷的機(jī)制分為兩種,中斷和異常,中斷通常為設(shè)備觸發(fā)的異步事件,而異常是執(zhí)行指令時(shí)發(fā)生的同步事件。本文主要來說明外設(shè)觸發(fā)的中斷,總的來說一個(gè)中斷的起末會(huì)經(jīng)歷設(shè)備,中斷控制器,CPU三個(gè)...
INTERRUPT
中斷是硬件和軟件交互的一種機(jī)制,可以說整個(gè)操作系統(tǒng),整個(gè)架構(gòu)都是由中斷來驅(qū)動(dòng)的。中斷的機(jī)制分為兩種,中斷和異常,中斷通常為 設(shè)備觸發(fā)的異步事件,而異常是 執(zhí)行指令時(shí)發(fā)生的同步事件。本文主要來說明 外設(shè)觸發(fā)的中斷,總的來說一個(gè)中斷的起末會(huì)經(jīng)歷設(shè)備,中斷控制器,CPU 三個(gè)階段:設(shè)備產(chǎn)生中斷信號(hào),中斷控制器翻譯信號(hào),CPU 來實(shí)際處理信號(hào)。本文用 的實(shí)例來講解多處理器下的中斷機(jī)制,從頭至尾的來看一看,中斷經(jīng)歷的三個(gè)過程。其中第一個(gè)階段設(shè)備如何產(chǎn)生信號(hào)不講,超過了操作系統(tǒng)的范圍,也超過了我的能力范圍。各種硬件外設(shè)有著自己的執(zhí)行邏輯,有各種形式的中斷觸發(fā)機(jī)制,比如邊沿觸發(fā),電平觸發(fā)等等。總的來說就是向中斷控制器發(fā)送一個(gè)中斷信號(hào),中斷控制器再作翻譯發(fā)送給 , 再執(zhí)行中斷服務(wù)程序?qū)χ袛噙M(jìn)行處理。中斷控制器
說到中斷控制器,是個(gè)什么東西?中斷控制器可以看作是中斷的代理,外設(shè)是很多的,如果沒有一個(gè)中斷代理,外設(shè)想要給 發(fā)送中斷信號(hào)來處理中斷,那只能是外設(shè)連接在 的管腳上, 的管腳是很寶貴的,不可能拿出那么多管腳去連接外設(shè)。所以就有了中斷控制器這個(gè)中斷代言人,所有的 外設(shè)連接其上,發(fā)送中斷請(qǐng)求時(shí)就向中斷控制器發(fā)送信號(hào),中斷控制器再通知 CPU,如此便解決了上述問題。中斷控制器有很多,前文講過PIC,PIC 只用于單處理器,對(duì)于如今的多核多處理器時(shí)代,PIC 無能為力,所以出現(xiàn)了更高級(jí)的中斷控制器 APIC ,APIC() 高級(jí)可編程中斷控制器,APIC 分成兩部分 LAPIC 和 IOAPIC,前者 LAPIC 位于 內(nèi)部,每個(gè) 都有一個(gè) LAPIC,后者 IOAPIC 與外設(shè)相連。外設(shè)發(fā)出的中斷信號(hào)經(jīng)過 IOAPIC 處理之后發(fā)送給 LAPIC,再由 LAPIC 決定是否交由 進(jìn)行實(shí)際的中斷處理。LAPIC,IOAPIC 是系統(tǒng)芯片組一部分,各個(gè)中斷消息通過總線發(fā)送接收。關(guān)于 APIC 的內(nèi)容很多也很復(fù)雜,詳細(xì)描述的可以參考 開發(fā)手冊(cè)卷三,本文不探討其中的細(xì)節(jié),只在上層較為抽象的層面講述,理清 APIC 模式下中斷的過程。計(jì)算機(jī)啟動(dòng)的時(shí)候要先對(duì) APIC 進(jìn)行初始化,后續(xù)才能正確使用,前面說過初始化就是設(shè)置一些寄存器,這部分我在再談中斷(APIC)有所講解,本文關(guān)于寄存器這一塊不會(huì)再詳述,可以先看一看。下面來看看 APIC 在一種較為簡(jiǎn)單的工作模式下的初始化過程:IOAPIC
初始化IOAPIC 就是設(shè)置 IOAPIC 的寄存器,IOAPIC 寄存器一覽:#define?REG_ID?????0x00??//?Register?index:?ID
#define?REG_VER????0x01??//?Register?index:?version
#define?REG_TABLE??0x10??//?Redirection?table?base??重定向表
但是這些寄存器是不能直接訪問的,需要通過另外兩個(gè)映射到內(nèi)存的寄存器來讀寫上述的寄存器。內(nèi)存映射的兩個(gè)寄存器
IOREGSEL,地址為 ;IOWIN,地址為 。IOREGSEL 用來指定要讀寫的寄存器,然后從 IOWIN 中讀寫。也就是常說的 index/data 訪問方式,或者說 ,用 index 端口指定寄存器,從 data 端口讀寫寄存器,data 端口就像是所有寄存器的窗口。而所謂內(nèi)存映射,就是把這些寄存器看作內(nèi)存的一部分,讀寫內(nèi)存,就是讀寫寄存器,可以用訪問內(nèi)存的指令比如 mov 來訪問寄存器。還有一種是 IO端口映射,這種映射方式是將外設(shè)的 IO端口(外設(shè)的一些寄存器) 看成一個(gè)獨(dú)立的地址空間,訪問這片空間不能用訪問內(nèi)存的指令,而需要專門的 in/out 指令來訪問。通過 IOREGSEL 和 IOWIN 既可以訪問到 IOAPIC 所有的寄存器,所以結(jié)構(gòu)體 如下定義:struct?ioapic?{
??uint?reg;???????//IOREGSEL
??uint?pad[3];????//填充12字節(jié)
??uint?data;??????//IOWIN
};
填充 字節(jié)是因?yàn)?IOREGSEL 在 ,長(zhǎng)度為 4 字節(jié),IOWIN 在 ,兩者中間差了 2 字節(jié),所以填充 字節(jié)補(bǔ)上空位方便操作。通過 IOREGSEL?選定寄存器,然后從IOWIN中讀寫相應(yīng)寄存器,因此也能明白下面兩個(gè)讀寫函數(shù):static?uint?ioapicread(int?reg)
{
??ioapic->reg?=?reg;????//選定寄存器reg
??return?ioapic->data;??//從窗口寄存器中讀出寄存器reg數(shù)據(jù)
}
static?void?ioapicwrite(int?reg,?uint?data)
{
??ioapic->reg?=?reg;????//選定寄存器reg
??ioapic->data?=?data;??//向窗口寄存器寫就相當(dāng)于向寄存器reg寫
}
這兩個(gè)函數(shù)就是根據(jù) 來讀寫 IOAPIC 的寄存器。下面來看看 IOAPIC 寄存器分別有些什么意義,了解了之后自然就知道為什么要這樣那樣的初始化了。下面只說 中涉及到的寄存器,其他的有興趣見文末鏈接。IOAPIC 寄存器
ID Register- 索引為 0
- :ID
- 索引為 1
- 表示版本,
- 表示重定向表項(xiàng)最多有幾個(gè),這里就是 23(從 0 開始計(jì)數(shù))
IOAPIC 有 24 個(gè)管腳,每個(gè)管腳都對(duì)應(yīng)著一個(gè) 64 位的重定向表項(xiàng)(也相當(dāng)于 64 位的寄存器),保存在 ,重定向表項(xiàng)的格式如下所示:IOAPIC 初始化
#define?IOAPIC??0xFEC00000???//?Default?physical?address?of?IO?APIC
void?ioapicinit(void)
{
??int?i,?id,?maxintr;
??ioapic?=?(volatile?struct?ioapic*)IOAPIC;??????//IOREGSEL的地址
??maxintr?=?(ioapicread(REG_VER)?>>?16)? 




