天天看點

4,中斷和異常

中斷信号的作用. 

使CPU轉而去運作正常控制流之外的代碼.為了它.就要在核心态堆棧儲存程式計數器的目前值(eip和cs寄存器).并把與中斷類型相關的一個位址放在程式計數器.

中斷處理與程序切換的差異:由中斷或異常處理程式執行的代碼不是一個程序,而是核心控制路徑.代表中斷發生時正在運作的程序執行.其比程序"輕".

中斷和異常 

中斷: 

可屏蔽中斷(maskable): I/O裝置發出的中斷請求(irq)都屬于.可處于兩種狀态:屏蔽的/非屏蔽的.

非屏蔽中斷(nonmaskable): 隻有幾個危急事件才引起.總是由CPU辨認.

異常: 

處理器探測異常:當CPU執行指令時探測到一個反常條件所産生的異常. 根據儲存在eip寄存器中的值,分為3種;

1)故障(fault):通常可以被糾正.eip中儲存的是引起故障的指令位址.糾正後會重新執行該條指令. ;

2)陷阱(trap):在陷阱指令執行後立刻報告.核心把控制器傳回給程式後可以繼續他的執行而不失連貫性.

eip儲存的是随後要執行的指令位址.隻有當沒有必要重新執行已終止的指令時(通常為了調試程式)時才觸發陷阱. ;

3)異常中止(abort):不能在eip中儲存引起異常的指令所在的确切位置.用于報告嚴重的錯誤.異常中止處理程式會強制受影響的程序終止.

程式設計異常: 在程式設計者送出請求時發生.将其作為陷阱來處理.也叫軟中斷.用途:1)執行系統調用.2)給調試程式通報一個特定的事件.

每個中斷和異常由0~255之間的一個數來标示.稱為向量(vector).隻有可屏蔽中斷的向量可以通過程式設計改變.其餘都是固定的.  

IRQ和中斷:能發出中斷的裝置都有一個IRQ的輸出線.所有IRQ線都與一個可程式設計中斷控制器(PIC)的硬體電路的輸入引腳相連.可以有選擇地禁止每條IRQ線.可對PIC程式設計進而禁止IRQ.禁止的中斷不丢失.一旦激活,PIC就把他們發送到CPU.該特性運作中斷處理程式逐次處理同一類型的IRQ.

為了發揮SMP體系的并行性,能夠把中斷傳遞給每個CPU很重要.是以引入了I/O進階可程式設計控制器(I/O

APIC)的元件.所有CPU都含有一個本地APIC,通過APIC總線(在系統總線上)連接配接到外部的I/O

APIC.還支援CPU産生處理器間中斷(IPI),可以利用它來在CPU之間交換消息.

中斷描述符表:IDT是一個系統表,它與每一個中斷或異常向量相聯系.每一個向量在表中有相應的中斷或異常處理程式的入口位址.最多需要256*8=2048位元組來存放IDT.

idtr寄存器指定IDT的線性基位址及其最大長度,進而使IDT可以位于記憶體中的任何地方.分為3種類型:

1)任務門:信号發生時,必須取代目前程序的那個程序的TSS選擇符存放在任務門中.

;2)中斷門:包含段選擇符和處理程式的段内偏移量.當CPU控制權轉移到一個适當的斷後,清除IF标志來關閉将來會發生的可屏蔽中斷

;3)陷阱門:與中斷門相似,但控制權傳遞到一個适合的CPU時不修改IF标志.  Linux利用中斷門進行中斷,利用陷阱門處理異常.注意:

"Double fault"異常是唯一由任務們處理的異常.表示一種核心錯誤.

中斷和異常處理程式的嵌套執行. 

必須保證中斷處理程式永不阻塞,即中斷處理程式運作期間不能發生程序切換.因為嵌套的核心控制路徑恢複執行時需要的資料都存放在目前線程的核心态堆棧上.

一個中斷處理程式可以搶占其他的中斷處理程式和異常處理程式.異常處理程式從不搶占中斷處理程式.

初始化中斷描述符表 

過程: 1)在初始化系統時把IDT表的初始位址裝入idtr寄存器,并初始化表中的每一項.;

2)int指令用于在使用者态程序發出一個中斷信号,為了防止模拟非法的中斷,将門描述符的DPL=0.;

3)在使用者态程序必須要能夠發出一個程式設計異常時,将門的SPL=3。

Linux中的分類: 中斷門(DPL=0,所有LInux中斷處理程式都通過中斷門激活,并限制在核心态);

系統門(DPL=3,用來激活3個linux異常處理程式); 系統中斷門(DPL=3,激活int3的異常處理程式);

陷阱門(DPL=0,激活大部分的異常處理程式); 任務門(DPL=0,"Double Fault的異常處理).

idt的初始化分為兩步:1)将256個表項用同一個中斷門(即指向ignore_int()中斷處理程式:其是一個空的處理程式)來填充.2)用有意義的陷阱和中斷處理程式來代替空處理程式.

異常處理 

大部分異常都解釋成為出錯條件.當異常發生時,核心向引起異常的程序發送一個信号向它通知一個反常條件.

特殊情況: 1)"Device not availeble" 2)"Page

Fault"該異常推遲給程序配置設定新的頁框,直到不能再推遲位置.

異常處理程式的标準結構: 1)在核心态堆棧中儲存大多數寄存器的内容; 2)用C函數處理異常;

3)通過ret_from_exception函數從異常處理程式退出.

中斷處理 

由于一個程序被挂起好久後中斷才到達,是以一個完全無關的程序可能正在運作.是以發送信号給目前程序是無用的.

中斷處理依賴于中斷類型:1)I/O中斷(查詢裝置以确定适當的操作過程);

2)時鐘中斷(該中斷告訴核心一個固定的時間間隔已經過去,作為I/O中斷處理) ;3)處理器間中斷.

I/O中斷處理:要能給多個裝置同時提供服務.實作:

1)IRQ共享(每個ISR(中斷服務例程)是一個與單獨裝置(共享IRQ線)相關的函數,因為無法預知那個特定的裝置産生IRQ,是以,中斷處理程式執行多個ISR,以驗證它的裝置是否需要關注,如果是,當裝置産生中斷時就執行所需的操作);

2)IRQ動态配置設定(一條IRQ線在可能的最後時刻才與一個裝置相關聯.這樣,即使幾個裝置并不共享IRQ線,但同一IRQ向量也可以由這幾個裝置在不同時刻使用). 

一個中斷處理程式正在執行時,相應的IRQ線上發出的信号被暫時忽略; 中斷處理程式所代表的程序必須是出于Task_Running态的;

其不能執行任何阻塞過程.

中斷要執行的操作: 1)緊急的(在禁止可屏蔽中斷下立即執行); 2)非緊急的(在開中斷下立即執行);

3)非緊急可延遲的(由獨立的函數執行).

步驟:1)在核心态堆棧中儲存IRQ的值和寄存器的内容; 2)為正在給IRQ線服務的PIC發送一個應答來允許PIC進一步發出中斷;

3)執行共享這個IRQ的所有裝置的ISR; 4)跳到ret_from_intr()的位址後終止.

IRQ資料結構:

1)意外中斷:中斷核心沒有處理的中斷.原因是與某個IRQ線相關的ISR不存在或者與某個中斷線相關的所有例程都識别不出來.當一條IRQ線上的意外中斷次數過多時,就禁用這條IRQ線.

2)

IRQ在多CPU系統上的分發:對稱多處理器模型(SMP).一般情況下,核心能夠公平地在CPU間分發中斷.但是在某些情況下,Linxu需要使用kirqd的核心線程來糾正IRQ的自動配置設定.其利用了CPU的IRQ親和力:通過修改I/O

APIC的中斷重定向表表項,可以吧中斷信号發送到某個特定的CPU上.

CPU間中斷處理:IPI不通過IRQ線傳輸,而是作為信号直接放在連接配接所有CPU本地APIC總線上. 類型:

1)Call_Function_Vector(發往不包含發送者的所有CPU,強制這些CPU運作發送者傳遞過來的函數).

2)Reschedule_Vector(從中斷傳回後,所有的重新排程都自動運作);

3)Invalidate_TLB_Vector(強制TLB無效,來重新整理CPU的TLB).

軟中斷及tasklet:

可延遲中斷可以在開中斷的情況下執行.把可延遲中斷從中斷處理程式中抽出來有助于使核心保持較短的響應時間.Linux通過兩種非緊迫,可中斷核心函數:1)可延遲函數;2)通過工作隊列來執行的函數.

tasklet是在軟中斷之上實作的.軟中斷的配置設定是靜态的,tasklet的配置設定和初始化是在運作時.軟中斷是可重入函數并使用自旋鎖來保護資料,其實可以并行在多CPU上執行的,tasklet總是串行執行,但是不同類型的tasklet可以并發執行,其不必是可重入的.四種操作:1)初始化;2)激活;3)屏蔽;4)執行.激活和執行被綁定在一個CPU上,雖然可以更好地利用CPU的Cache,但是有潛在的危險性(一個CPU很忙,但其他的很閑).

軟中斷:使用下标(共6個)來表示優先級.softirq_action[32]

 softirq_vec數組.優先級是下标,是以隻有前6元素個有效.另外,thread_info中有一個preempt_count字段來跟蹤核心搶占和核心控制路徑的嵌套.

每個CPu都有自己的ksoftirqd/n核心線程.其為了解決以下問題:軟中斷函數可以重新激活自己,軟中斷的連續高流量可能會産生問題.不然就要選擇以下兩種之一的政策:1)忽略do_softirq()運作時新出現的軟中斷,此種情況的等待是不可接受的;2)不斷地重新檢查挂起的軟中斷,這種情況下,do_softirq()函數就會一直不傳回,使用者态程式實際上停止執行.解決:do_softirq()函數确定哪些軟中斷是挂起的,并執行他們的韓素華.如果已經執行的軟中斷又被激活,則do_softirq()喚醒核心線程并終止.核心線程有較低的優先級,是以使用者程式有就會運作.但是,如果機器空閑(沒有使用者态程式需要運作時),挂起的軟中斷就很快被執行.

tasklet:是I/O驅動程式中實作可延遲函數的首選.

工作隊列

用來代替任務隊列.允許核心函數被激活,而且稍後由一種叫做工作者線程的特殊核心線程來執行.

可延遲函數運作在中斷上下文中,而工作隊列中的函數運作在程序上下文中.執行可阻塞函數的方式是在程序上下文中運作,因為在中斷上下文中不可能發生程序切換.兩者都不能通路程序的使用者态位址空間