第四章中斷和異常_第1頁
第四章中斷和異常_第2頁
第四章中斷和異常_第3頁
第四章中斷和異常_第4頁
第四章中斷和異常_第5頁
已閱讀5頁,還剩37頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第四章中斷和異常第一頁,共四十二頁,2022年,8月28日4.1中斷信號(hào)的作用4.2中斷和異常4.3中斷和異常處理程序的嵌套執(zhí)行4.4初始化中斷描述符表4.5異常處理4.6中斷處理4.7軟中斷及tasklet4,8工作隊(duì)列4.9從中斷和異常返回第二頁,共四十二頁,2022年,8月28日中斷:中斷是一個(gè)能改變處理器執(zhí)行順序的事件。中斷通常分為同步中斷和異步中斷。同步中斷:當(dāng)指令執(zhí)行時(shí)由CPU控制單元產(chǎn)生的,只有在一條指令終止執(zhí)行后CPU才會(huì)發(fā)出中斷。異步中斷:由其他硬件設(shè)備依照CPU時(shí)鐘信號(hào)隨機(jī)產(chǎn)生的。

在Intel微處理器手冊(cè)中,把同步和異步中斷分別稱為異常和中斷。中斷是由間隔定時(shí)器和I/O設(shè)備產(chǎn)生的,而異常是由程序的錯(cuò)誤產(chǎn)生的,或者是由內(nèi)核必須處理的異常條件產(chǎn)生的。第三頁,共四十二頁,2022年,8月28日

中斷信號(hào)的作用是提供一種特殊的方式使處理器轉(zhuǎn)而去運(yùn)行正??刂屏髦獾拇a。當(dāng)一個(gè)中斷信號(hào)到達(dá)時(shí),CPU必須停止它當(dāng)前正在做的事情,并且切換到一個(gè)新的活動(dòng)。

為了做到這一點(diǎn),就要在內(nèi)核態(tài)堆棧保存程序計(jì)數(shù)器的當(dāng)前值(即eip和cs寄存器的內(nèi)容),并把與中斷類型相關(guān)的一個(gè)地址放進(jìn)程序計(jì)數(shù)器。中斷處理與進(jìn)程切換的區(qū)別:

由中斷或異常處理程序執(zhí)行的代碼不是一個(gè)進(jìn)程。中斷處理程序比一個(gè)進(jìn)程要“l(fā)ight”(中斷的上下文很少,建立或終止中斷處理需要的時(shí)間很少)。第四頁,共四十二頁,2022年,8月28日中斷:1)可屏蔽中斷 I/O設(shè)備發(fā)出的所有中斷請(qǐng)求(IRQ)都產(chǎn)生可屏蔽中斷,可屏蔽中斷可以處于兩種狀態(tài):屏蔽的或非屏蔽的,一個(gè)屏蔽的中斷只要還是屏蔽的,控制單元就忽略它。2)非屏蔽中斷

只有幾個(gè)危急事件(如硬件故障)才引起非屏蔽中斷。非屏蔽中斷總是由CPU辨認(rèn)。異常:1)處理器探測(cè)異常

當(dāng)CPU執(zhí)行指令時(shí)探測(cè)到的一個(gè)反常條件所產(chǎn)生的異常。可以分為3組:包括故障,陷阱和異常終止。這取決于CPU控制單元產(chǎn)生異常時(shí)候保存在內(nèi)核態(tài)堆棧eip寄存器中的值。2)編程異常

在編程者發(fā)出請(qǐng)求時(shí)發(fā)生。是由int或int3指令觸發(fā)的;當(dāng)into(檢查溢出)和bound(檢查地址出界)指令檢查的條件不為真時(shí),也引起編程異常。控制單元把編程異常作為陷阱來處理,編程異常通常也叫做軟中斷。編程異常有兩種用途:執(zhí)行系統(tǒng)調(diào)用和給調(diào)試程序通報(bào)一個(gè)特定的事件。第五頁,共四十二頁,2022年,8月28日每個(gè)能夠發(fā)出中斷請(qǐng)求的硬件設(shè)備控制器都有一條IRQ輸出線,所有現(xiàn)有的IRQ線都與一個(gè)可編程中斷控制器的輸入引腳相連,可編程控制器執(zhí)行以下動(dòng)作:1.監(jiān)視IRQ線,檢查產(chǎn)生的信號(hào)。如果有兩條或兩條以上的IRQ線上產(chǎn)生信號(hào),就選擇引腳編號(hào)較小的IRQ線。2.如果一個(gè)引發(fā)信號(hào)出現(xiàn)在IRQ線上: a)把接收到的引發(fā)信號(hào)轉(zhuǎn)換成對(duì)應(yīng)的向量。 b)把這個(gè)向量存放在中斷控制器的一個(gè)I/O端口,從而允許CPU通過數(shù) 據(jù)總線讀此向量。 c)把引發(fā)信號(hào)發(fā)送到處理器的INTR引腳,即產(chǎn)生一個(gè)中斷。 d)等待,直到CPU通過把這個(gè)中斷信號(hào)寫進(jìn)可編程中斷控制器的一個(gè) I/O端口來確認(rèn)它,當(dāng)這種情況發(fā)生時(shí),清INTR線。3.返回到第一步。IRQ線是從0開始編號(hào)的,第一條IRQ線通常表示成IRQ0,與IRQn關(guān)聯(lián)的Intel的缺省向量是n+32??梢杂羞x擇的禁止每條IRQ線,可以對(duì)PCI編程從而禁止IRQ。而有選擇的激活/禁止IRQ線不同于可屏蔽中斷的全局屏蔽/非屏蔽,當(dāng)eflags寄存器的IF標(biāo)志被清0時(shí),由PIC發(fā)布的每個(gè)可屏蔽中斷都由CPU暫時(shí)忽略,cli和sti匯編指令分別清除和設(shè)置該標(biāo)志。第六頁,共四十二頁,2022年,8月28日目的:為了充分發(fā)揮多處理器體系結(jié)構(gòu)的并行性,能夠把中斷傳遞給系統(tǒng)中的每個(gè)CPU。結(jié)構(gòu):每個(gè)CPU都含有一個(gè)本地APIC,每個(gè)本地APIC都有32位的寄存器,一個(gè)內(nèi)部時(shí)鐘,一個(gè)本地定時(shí)設(shè)備以及為本地APIC中斷保留的兩條額外的IRQ線LINT0和LINT1。所有本地APIC都連接到一個(gè)外部I/OAPIC,形成一個(gè)多APIC的系統(tǒng)。I/OAPIC的組成:一組24條IRQ線,一張24項(xiàng)的中斷重定向表,可編程寄存器,以及通過APIC總線發(fā)送和接收APIC信息的一個(gè)信息單元。中斷重定向表中的信息用于把每個(gè)外部IRQ信號(hào)轉(zhuǎn)換為一條消息,然后通過APIC總線把消息發(fā)送給一個(gè)或多個(gè)本地APIC單元。來自外部硬件設(shè)備的中斷請(qǐng)求以下面兩種方式在可用CPU之間分發(fā):1)靜態(tài)分發(fā) IRQ信號(hào)傳遞給重定向表相應(yīng)項(xiàng)中所列出的本地APIC。2)動(dòng)態(tài)分發(fā)

如果處理器正在執(zhí)行最低優(yōu)先級(jí)進(jìn)程,IRQ信號(hào)就傳遞給這種處理器的本地APIC。

如果兩個(gè)或多個(gè)CPU共享最低優(yōu)先級(jí),就利用仲裁技術(shù)分配CPU。第七頁,共四十二頁,2022年,8月28日仲裁技術(shù):

在本地APIC的仲裁優(yōu)先級(jí)寄存器中,給每一個(gè)CPU都分配一個(gè)0(最低)~15(最高)范圍內(nèi)的值。

每當(dāng)中斷傳遞給一個(gè)CPU時(shí),其相應(yīng)的仲裁優(yōu)先級(jí)就自動(dòng)置為0,而其他每個(gè)CPU的仲裁優(yōu)先級(jí)都增加1,當(dāng)仲裁優(yōu)先級(jí)寄存器大于15時(shí),就把它置為獲勝CPU的前一個(gè)仲裁優(yōu)先級(jí)加1。因此,中斷以輪轉(zhuǎn)的方式在CPU之間分發(fā),且具有相同的任務(wù)優(yōu)先級(jí)。處理器間中斷(IPI):

除了在處理器之間分發(fā)中斷外,多APIC系統(tǒng)還允許CPU產(chǎn)生處理器間中斷。當(dāng)一個(gè)CPU希望把中斷發(fā)送給另一個(gè)CPU時(shí),它就在自己本地APIC的中斷指令寄存器(ICR)中存放這個(gè)中斷向量和目標(biāo)本地APIC的標(biāo)識(shí)符。然后,通過APIC總線向目標(biāo)本地APIC發(fā)送一條消息,從而向自己的CPU發(fā)出一條相應(yīng)的中斷。 IPI被linux用來在CPU之間交換信息。第八頁,共四十二頁,2022年,8月28日80x86處理器發(fā)布了大約20種異常,內(nèi)核必須為每種異常提供一個(gè)專門的異常處理程序,對(duì)于某些異常,CPU控制單元在開始執(zhí)行異常處理前會(huì)產(chǎn)生一個(gè)硬件出錯(cuò)碼,并且壓入內(nèi)核態(tài)堆棧。0Divideerror(故障)當(dāng)一個(gè)程序試圖執(zhí)行整數(shù)被0除操作時(shí)產(chǎn)生1Debug(陷阱或故障)產(chǎn)生于設(shè)置eflags的TF標(biāo)志或一條指令或操作數(shù)的地址落在一個(gè)活動(dòng)debug寄存器的范圍之內(nèi)時(shí)。未用

為非屏蔽中斷保留Breakpoint(陷阱)由int3(斷點(diǎn))指令引起Overflow(陷阱)當(dāng)eflags的OF標(biāo)志被設(shè)置時(shí),into指令被執(zhí)行。Boundscheck(故障)

對(duì)于有效地址范圍之外的操作數(shù),bound指令被執(zhí)行。Invalidopcode(故障)CPU執(zhí)行單元檢測(cè)到一個(gè)無效操作碼。Devicenotavailable(故障)隨著cr0的TS標(biāo)志被設(shè)置,ESCAPE、MMX或XMM指令被執(zhí)行。Doublefault(異常中止)正常情況下當(dāng)CPU正試圖為前一個(gè)異常調(diào)用處理程序時(shí),同時(shí)又檢測(cè)到一個(gè)異常,兩個(gè)異常能被串行的處理,然而在少數(shù)情況下,處理器不能串行的處理它們,因而產(chǎn)生這種異常。Coprocessorsegmentoverrun(異常中止)因外部的數(shù)學(xué)協(xié)處理器引起的問題。InvalidTSS(故障)CPU試圖讓一個(gè)上下文切換到有無效TSS的進(jìn)程。第九頁,共四十二頁,2022年,8月28日Segmentnotpresent(故障)引用一個(gè)不存在的內(nèi)存段。Stacksegmentfault(故障)試圖超過棧段界限的指令,或者ss標(biāo)識(shí)的段不在內(nèi)存。Generalprotection(故障)違反了80x86保護(hù)模式下的保護(hù)規(guī)則之一。Pagefault(故障)尋址的頁不在內(nèi)存,相應(yīng)的頁表項(xiàng)為空,或者違反了一種分頁保護(hù)機(jī)制。由Intel保留。Floatingpointerror(故障)集成到CPU芯片中的浮點(diǎn)單元用信號(hào)通知一個(gè)錯(cuò)誤情形,如數(shù)字溢出或被0除。Alignmentcheck(故障)操作數(shù)的地址沒有被正確的對(duì)齊Machinecheck(異常中止)機(jī)器檢查機(jī)制檢測(cè)到一個(gè)CPU錯(cuò)誤或總線錯(cuò)誤。SIMDfloatingpointexception(故障)集成到CPU芯片中的SSE或SSE2單元對(duì)浮點(diǎn)操作用信號(hào)通知一個(gè)錯(cuò)誤情形。20~31這些值由Intel留作將來開發(fā)。第十頁,共四十二頁,2022年,8月28日中斷描述符表是一個(gè)系統(tǒng)表,它與每一個(gè)中斷或異常向量相聯(lián)系,每一個(gè)向量在表中有相應(yīng)的中斷或異常處理程序的入口地址,內(nèi)核在允許中斷發(fā)生前,必須用lidt匯編指令初始化IDT。idtrCPU寄存器指定了IDT的線性基地址及其限制(最大長(zhǎng)度),因此IDT可以位于內(nèi)存的任何地方。IDT包含三種類型的描述符:1)任務(wù)門:

當(dāng)中斷信號(hào)發(fā)生時(shí),必須取代當(dāng)前進(jìn)程的那個(gè)進(jìn)程的TSS選擇符存放在

任務(wù)門中。2)中斷門:

包含段選擇符和中斷或異常處理程序的段內(nèi)偏移量。當(dāng)控制權(quán)轉(zhuǎn)移到一

個(gè)適當(dāng)?shù)亩螘r(shí),處理器清IF標(biāo)志,從而關(guān)閉將來會(huì)發(fā)生的可屏蔽中斷。3)陷阱門:

與中斷門相似,只是控制權(quán)傳遞到一個(gè)適當(dāng)?shù)亩螘r(shí)處理器不修改IF標(biāo)志。第十一頁,共四十二頁,2022年,8月28日當(dāng)執(zhí)行了一條指令后,cs和eip寄存器包含下一條將要執(zhí)行指令的邏輯地址,在處理那條指令之前,控制單元會(huì)檢查在運(yùn)行前一條指令時(shí)是否已經(jīng)發(fā)生了一個(gè)中斷或異常,如果發(fā)生了一個(gè)中斷或異常,控制單元執(zhí)行以下操作:確定與中斷或異常關(guān)聯(lián)的向量i(0<=i<=255).讀由idtr寄存器指向的IDT表中的第i項(xiàng)。從gdtr寄存器獲得GDT的基地址,并在GDT中查找,以讀取IDT表項(xiàng)中的選擇符所標(biāo)識(shí)的段描述符,這個(gè)描述符指定中斷或異常處理程序所在段的基地址。確信中斷是由授權(quán)的中斷發(fā)生源發(fā)出的。首先將當(dāng)前特權(quán)級(jí)CPL與段描述符的描述符特權(quán)級(jí)DPL比較,如果CPL小于DPL,就產(chǎn)生一個(gè)“Generalprotection”異常,因?yàn)橹袛嗵幚沓绦虻奶貦?quán)不能低于引起中斷的程序的特權(quán)。對(duì)于編程異常則做進(jìn)一步的安全檢查:比較CPL與處于IDT中的門描述符的DPL,如果DPL小于CPL就產(chǎn)生一個(gè)“Generalprotection”異常。這最后一個(gè)檢查可以避免用戶應(yīng)用程序訪問特殊的陷阱門或中斷門。檢查是否發(fā)生了特權(quán)級(jí)的變化,也就是說,CPL是否不同于所選擇的段描述符的DPL,如果是,控制單元必須開始使用與新的特權(quán)級(jí)相關(guān)的棧。如果故障已經(jīng)發(fā)生,用引起異常的指令地址裝載cs和eip寄存器,從而使得這條指令能再次被執(zhí)行。第十二頁,共四十二頁,2022年,8月28日在棧中保存eflags、cs及eip的內(nèi)容。如果異常產(chǎn)生了一個(gè)硬件出錯(cuò)碼,則將它保存在棧中。裝載cs和eip寄存器,其值分別是IDT表中第i項(xiàng)門描述符的段選擇符和偏移量字段。這些值給出了中斷或者異常處理程序的第一條指令的邏輯地址。

控制單元所執(zhí)行的最后一步就是跳轉(zhuǎn)到中斷或異常的處理程序,即被選中處理程序的第一條指令。

中斷或異常處理完后,相應(yīng)的處理程序必須產(chǎn)生一條iret指令,把控制權(quán)交給被中斷的進(jìn)程,這時(shí)控制單元執(zhí)行以下操作:用保存在棧中的值裝載cs、eip或eflags寄存器。如果一個(gè)硬件出錯(cuò)碼曾被壓入棧中,并且在eip內(nèi)容的上面,那么執(zhí)行iret指令前必須先彈出這個(gè)硬件出錯(cuò)碼。檢查處理程序的CPL是否等于cs中最低兩位的值(這意味著被中斷的進(jìn)程與處理程序運(yùn)行在同一特權(quán)級(jí)),如果是,iret終止執(zhí)行,否則轉(zhuǎn)入下一步。從棧中裝載ss和esp寄存器,因此返回到與舊特權(quán)級(jí)相關(guān)的棧。檢查ds、es、fs及gs段寄存器的內(nèi)容,如果其中一個(gè)寄存器包含的選擇符是一個(gè)段描述符,并且其DPL小于CPL,那么清相應(yīng)的段寄存器。這是為了禁止用戶態(tài)的程序(CPL=3)利用內(nèi)核以前所用的段寄存器(DPL=0),如果不清這些寄存器,懷有惡意的用戶態(tài)程序就可能利用它們來訪問內(nèi)核地址空間。第十三頁,共四十二頁,2022年,8月28日

每個(gè)中斷或異常都會(huì)引起一個(gè)內(nèi)核控制路徑,或者說代表當(dāng)前進(jìn)程在內(nèi)核態(tài)執(zhí)行單獨(dú)的指令序列。內(nèi)核控制路徑可以任意嵌套,一個(gè)中斷程序可以被另一個(gè)中斷程序“中斷”,因此引起內(nèi)核控制路徑的嵌套執(zhí)行。其結(jié)果是:對(duì)中斷進(jìn)行處理的內(nèi)核控制路徑的最后一部分指令并不總能使當(dāng)前進(jìn)程返回到用戶態(tài):如果嵌套深度大于1,這些指令將執(zhí)行上次被打斷的內(nèi)核控制路徑,此時(shí)的CPU依然運(yùn)行在內(nèi)核態(tài)。允許內(nèi)核控制路徑嵌套執(zhí)行必須要求中斷處理程序永不阻塞,即中斷處理程序運(yùn)行期間不能發(fā)生進(jìn)程切換。在內(nèi)核沒有bug的情況下,大多數(shù)異常在在CPU處于用戶態(tài)時(shí)發(fā)生。異常要么是由編程錯(cuò)誤引起,要么是由調(diào)試程序觸發(fā),但是缺頁異常發(fā)生在內(nèi)核態(tài)。一個(gè)中斷處理程序既可以搶占其他的中斷處理程序,也可以搶占異常處理程序,相反,異常處理程序從不搶占中斷處理程序。在內(nèi)核態(tài)能觸發(fā)的唯一異常就是缺頁異常,但中斷處理程序從不執(zhí)行可以導(dǎo)致缺頁的操作。在多處理器系統(tǒng)上,幾個(gè)內(nèi)核控制路徑可以并發(fā)的執(zhí)行,此外與異常相關(guān)的內(nèi)核控制路徑可以開始在一個(gè)CPU上執(zhí)行,并且由于進(jìn)程切換而移往另一個(gè)CPU上執(zhí)行。第十四頁,共四十二頁,2022年,8月28日4.4初始化中斷描述符表內(nèi)核啟用中斷以前,必須把IDT表的初始地址裝到idtr寄存器,并初始化表中的每一項(xiàng)。這項(xiàng)工作是在初始化系統(tǒng)的時(shí)候完成的。Linux中中斷描述符表分類如下:中斷門:用戶態(tài)進(jìn)程不能訪問的一個(gè)Intel中斷門,門的DPL字段為0,所有的Linux中斷處理程序都通過中斷門激活,并全部限制在內(nèi)核態(tài)。系統(tǒng)門:用戶態(tài)進(jìn)程可以訪問的一個(gè)Intel陷阱門,門的DPL字段為3,通過系統(tǒng)門來激活三個(gè)Linux異常處理程序,它們的向量是4,5及128,因此在用戶態(tài)下可以發(fā)布into、bound和int$0x80三條匯編語言指令。系統(tǒng)中斷門:能夠被用戶態(tài)進(jìn)程訪問的Intel中斷門,DPL字段為3,與向量3相關(guān)的異常處理程序是由系統(tǒng)中斷門激活的,因此在用戶態(tài)可以使用匯編語言指令int3.陷阱門:用戶態(tài)的進(jìn)程不能訪問的一個(gè)Intel陷阱門,DPL字段為0,大部分Linux異常處理程序都通過陷阱門來激活。任務(wù)門:不能被用戶態(tài)進(jìn)程訪問的一個(gè)Intel任務(wù)門,DPL字段為0,Linux對(duì)“doublefault”異常的處理程序是由任務(wù)門激活的。第十五頁,共四十二頁,2022年,8月28日IDT的初步初始化當(dāng)計(jì)算機(jī)還運(yùn)行在實(shí)模式時(shí),IDT被初始化并由BIOS例程使用,一旦Linux接管,IDT就被移到RAM的另一個(gè)區(qū)域,并進(jìn)行第二次初始化,因?yàn)長(zhǎng)inux沒有利用任何BIOS例程。

IDT存放在idt_table表中,有256個(gè)表項(xiàng),6字節(jié)的idt_descr變量指定了IDT的大小和它的地址。在內(nèi)核初始化過程中,setup_idt()匯編語言函數(shù)用同一個(gè)中斷門(即指向ignore_int()中斷處理程序)來填充所有這256個(gè)idt_table表項(xiàng)。Ignore_int()中斷處理程序執(zhí)行以下操作:在棧中保存一些寄存器的內(nèi)容。調(diào)用printk()函數(shù)打印“Unknowninterrupt”系統(tǒng)消息。從?;謴?fù)寄存器的內(nèi)容。執(zhí)行iret指令以恢復(fù)被中斷的程序。 緊接著這個(gè)預(yù)初始化,內(nèi)核將在IDT中進(jìn)行第二遍初始化,用有意義的陷阱和中斷處理程序替換這個(gè)空處理程序。第十六頁,共四十二頁,2022年,8月28日4.5異常處理異常處理程序的標(biāo)準(zhǔn)結(jié)構(gòu),由三部分組成:在內(nèi)核堆棧中保存大多數(shù)寄存器的內(nèi)容(匯編語言實(shí)現(xiàn))。用高級(jí)的C函數(shù)處理異常。通過ret_from_exception()函數(shù)從異常處理程序退出。第十七頁,共四十二頁,2022年,8月28日為異常處理程序保存寄存器的值用handler_name表示一個(gè)通用的異常處理程序的名字,則每一個(gè)異常處理程序都以下面的匯編指令開始:handler_name: pushl$0/*onlyforsomeexceptions*/ pushl$do_handler_name jmperror_code其中error_code匯編語言代碼執(zhí)行以下步驟:把高級(jí)C函數(shù)可能用到的寄存器保存在棧中。產(chǎn)生一條cld指令來清e(cuò)flags的方向標(biāo)志DF,以確保調(diào)用字符串指令時(shí)會(huì)自動(dòng)增加edi和esi寄存器的值。把棧中位于esp+36處的硬件出錯(cuò)碼拷貝到edx中,給棧中這一位置存上值-1,這個(gè)值用來把0x80異常與其他異常隔離開。把保存在棧中的esp+32位置的do_handler_name()高級(jí)C函數(shù)的地址裝入edi寄存器中,然后在棧的這個(gè)位置寫入es值。第十八頁,共四十二頁,2022年,8月28日把內(nèi)核棧的當(dāng)前棧頂拷貝到eax寄存器。這個(gè)地址表示內(nèi)存單元的地址,在這個(gè)單元中存放的是第一步所保存的最后一個(gè)寄存器的值。把用戶數(shù)據(jù)段選擇符拷貝到ds和es寄存器中。調(diào)用地址在edi中的高級(jí)C函數(shù)。進(jìn)入和離開異常處理函數(shù)執(zhí)行異常處理程序的C函數(shù)名總是由do_前綴和處理程序名組成。其中大部分函數(shù)把硬件出錯(cuò)碼和異常向量保存在當(dāng)前進(jìn)程的描述符中,然后向當(dāng)前進(jìn)程發(fā)送一個(gè)適當(dāng)?shù)男盘?hào)。異常處理程序剛一終止,當(dāng)前進(jìn)程就關(guān)注這個(gè)信號(hào),該信號(hào)要么在用戶態(tài)由進(jìn)程自己的信號(hào)處理程序來處理,要么由內(nèi)核來處理。在后一種情況下,內(nèi)核一般會(huì)殺死這個(gè)進(jìn)程。異常處理程序總是檢查異常時(shí)發(fā)生在用戶態(tài)還是在內(nèi)核態(tài),在內(nèi)核態(tài)時(shí)還要檢查是否由系統(tǒng)調(diào)用的無效參數(shù)引起。出現(xiàn)在內(nèi)核態(tài)的任何其他異常都是由于內(nèi)核的bug引起的,在這種情況下異常處理程序認(rèn)為是內(nèi)核行為失常了。為了避免硬盤上的數(shù)據(jù)崩潰,處理程序調(diào)die()函數(shù),該函數(shù)在控制臺(tái)上打印出所有CPU寄存器內(nèi)容,并調(diào)用do_exit()來終止當(dāng)前進(jìn)程。當(dāng)執(zhí)行異常處理的C函數(shù)終止時(shí),程序執(zhí)行一條jmp指令以跳轉(zhuǎn)到ret_from_exception()函數(shù)。第十九頁,共四十二頁,2022年,8月28日4.6中斷處理中斷處理依賴于中斷類型,中斷類型主要分三類:I/O中斷、時(shí)鐘中斷和處理器間中斷。I/O中斷處理:一般情況下,I/O中斷處理程序必須足夠靈活以給多個(gè)設(shè)備同時(shí)提供服務(wù)。其靈活性是以兩種不同的方式實(shí)現(xiàn)的:1)IRQ共享:中斷處理程序執(zhí)行多個(gè)中斷服務(wù)例程(ISR),每個(gè)ISR是一個(gè)與單獨(dú)設(shè)備(共享IRQ線)相關(guān)的函數(shù),因?yàn)椴豢赡茴A(yù)先知道哪個(gè)特定的設(shè)備產(chǎn)生IRQ,因此每個(gè)ISR都執(zhí)行,以驗(yàn)證它的設(shè)備是否需要關(guān)注,如果是,當(dāng)設(shè)備產(chǎn)生中斷時(shí)就執(zhí)行需要執(zhí)行的所有操作。2)IRQ動(dòng)態(tài)分配:一條IRQ線在可能的最后時(shí)刻才與一個(gè)設(shè)備驅(qū)動(dòng)程序相關(guān)聯(lián),這樣,即使幾個(gè)硬件設(shè)備并不共享IRQ線,同一個(gè)IRQ向量也可以由這幾個(gè)設(shè)備在不同時(shí)刻使用。第二十頁,共四十二頁,2022年,8月28日所有I/O中斷處理程序都執(zhí)行四個(gè)相同的基本操作:在內(nèi)核態(tài)堆棧中保存IRQ的值和寄存器的內(nèi)容。為正在給IRQ線服務(wù)的PIC發(fā)送一個(gè)應(yīng)答,這將允許PIC進(jìn)一步發(fā)出中斷。執(zhí)行共享這個(gè)IRQ的所有設(shè)備的中斷服務(wù)例程(ISR)。跳到ret_from_intr()的地址后終止。第二十一頁,共四十二頁,2022年,8月28日中斷向量第二十二頁,共四十二頁,2022年,8月28日IRQ數(shù)據(jù)結(jié)構(gòu)每個(gè)中斷向量都有它自己的irq_desc_t描述符,其字段如下表,所有的這些描述符組織在一起形成irq_desc數(shù)組。第二十三頁,共四十二頁,2022年,8月28日第二十四頁,共四十二頁,2022年,8月28日IRQ在多處理器系統(tǒng)上的分發(fā)

Linux遵循對(duì)稱多處理模型(SMP),因此內(nèi)核試圖以輪轉(zhuǎn)的方式把來自硬件設(shè)備的IRQ信號(hào)在所有的CPU之間分發(fā),所有CPU服務(wù)于I/O中斷的執(zhí)行時(shí)間片幾乎相同。當(dāng)硬件設(shè)備產(chǎn)生了一個(gè)中斷信號(hào)時(shí),多APIC系統(tǒng)就選擇其中的一個(gè)CPU,并把該信號(hào)傳遞給相應(yīng)的本地APIC,本地APIC又一次中斷它的CPU,這個(gè)事件不通報(bào)給其他所有的CPU。所有這些都由硬件完成,但在有些情況下,硬件不能以公平的方式在微處理器之間成功的分配中斷,因此在必要的時(shí)候,Linux2.6利用kirqd特殊內(nèi)核線程來糾正對(duì)CPU進(jìn)行的IRQ的自動(dòng)分配。多種類型的內(nèi)核棧每個(gè)進(jìn)程的thread_info描述符和thread_union結(jié)構(gòu)中的內(nèi)核棧緊鄰,而根據(jù)內(nèi)核編譯時(shí)的選項(xiàng)不同,thread_union結(jié)構(gòu)可能占一個(gè)頁框或兩個(gè)頁框。如果thread_union結(jié)構(gòu)的大小為8KB,那么當(dāng)前進(jìn)程的內(nèi)核棧被用于所有類型的內(nèi)核控制路徑:異常、中斷和可延遲函數(shù)。如果thread_union結(jié)構(gòu)的大小為4KB,內(nèi)核就使用三種類型的內(nèi)核棧:異常棧(處理異常)、硬中斷請(qǐng)求棧(處理中斷)和軟中斷請(qǐng)求棧(處理可延遲的函數(shù))。第二十五頁,共四十二頁,2022年,8月28日為中斷處理程序保存寄存器的值保存寄存器是中斷處理程序做的第一件事情,IRQn中斷處理程序的地址開始存在interrupt[n]中,然后復(fù)制到IDT相應(yīng)表項(xiàng)的中斷門中。通過文件arch/i386/kernel/entry.S中的幾條匯編語言指令建立interrupt數(shù)組,數(shù)組中索引為n的元素中存放下面兩條匯編語言指令的地址:pushl$n-256jmpcommon_interrupt結(jié)果是把終端號(hào)減256的結(jié)果保存在棧中,內(nèi)核用負(fù)數(shù)表示所有的中斷(正數(shù)表示系統(tǒng)調(diào)用)。當(dāng)引用這個(gè)數(shù)時(shí),可以對(duì)所有的中斷處理程序都執(zhí)行相同的代碼,這段代碼開始于標(biāo)簽common_interrupt處,包含的匯編指令和宏如下:common_interrupt:SAVE_ALLmovl%esp,%eaxcalldo_IRQjmpret_from_intr

SAVE_ALL可以在棧中保存中斷處理程序可能會(huì)使用的所有CPU寄存器,但eflags、cs、eip、ss和esp除外,因?yàn)檫@幾個(gè)寄存器已經(jīng)由控制單元自動(dòng)保存了。然后這個(gè)宏把用戶數(shù)據(jù)段的選擇符裝到ds和es寄存器。保存寄存器的值以后,棧頂?shù)牡刂繁淮娣诺絜ax寄存器中,然后中斷處理程序調(diào)用do_IRQ()函數(shù),執(zhí)行到ret指令時(shí),控制轉(zhuǎn)到ret_from_intr()。第二十六頁,共四十二頁,2022年,8月28日挽救丟失的中斷第二十七頁,共四十二頁,2022年,8月28日中斷服務(wù)例程一個(gè)中斷服務(wù)例程(ISR)實(shí)現(xiàn)一種特定設(shè)備的操作。當(dāng)中斷處理程序必須執(zhí)行ISR時(shí),它就調(diào)用handle_IRQ_event()函數(shù),這個(gè)函數(shù)執(zhí)行以下操作:第二十八頁,共四十二頁,2022年,8月28日所有的ISR都作用于相同的參數(shù)(分別通過eax、edx和ecx寄存器傳遞):irq IRQ號(hào)dev_id 設(shè)備標(biāo)識(shí)符regs 指向內(nèi)核(異常)棧的pt_regs結(jié)構(gòu)的指針,棧中含有中斷發(fā)生后隨即 保存的寄存器。第一個(gè)參數(shù)允許一個(gè)單獨(dú)的ISR處理幾條IRQ線,第二個(gè)參數(shù)允許一個(gè)單獨(dú)的ISR照顧幾個(gè)同類型的設(shè)備,第三個(gè)參數(shù)允許ISR訪問被中斷的內(nèi)核控制路徑的上下文。每個(gè)中斷服務(wù)例程在成功處理完中斷后都返回1,否則返回0.IRQ線的動(dòng)態(tài)分配

在激活一個(gè)準(zhǔn)備利用的IRQ線的設(shè)備之前,其相應(yīng)的驅(qū)動(dòng)程序調(diào)用request_irq()。這個(gè)函數(shù)建立一個(gè)新的irqaction描述符,并用參數(shù)值初始化它,然后調(diào)用setup_irq()函數(shù)把這個(gè)描述符插入到合適的IRQ鏈表,如果setup_irq()返回一個(gè)出錯(cuò)碼,設(shè)備驅(qū)動(dòng)程序中止操作,這意味著IRQ線已經(jīng)已由另一個(gè)設(shè)備所使用,而這個(gè)設(shè)備部允許中斷共享。當(dāng)設(shè)備操作結(jié)束時(shí),驅(qū)動(dòng)程序調(diào)用free_irq()函數(shù)從IRQ鏈表中刪除這個(gè)描述符,并釋放相應(yīng)的內(nèi)存區(qū)。第二十九頁,共四十二頁,2022年,8月28日處理器間中斷處理第三十頁,共四十二頁,2022年,8月28日處理器間中斷程序的匯編語言代碼是由BUILD_INTERRUPT宏產(chǎn)生的:它保存寄存器,從棧頂壓入向量號(hào)減256的值,然后調(diào)用高級(jí)C函數(shù),其名字就是低級(jí)處理程序的名字加前綴smp_。每個(gè)該機(jī)處理程序應(yīng)答本地APIC上的處理器間中斷,然后執(zhí)行由中斷觸發(fā)的特定操作。第三十一頁,共四十二頁,2022年,8月28日4.7軟中斷和tasklet軟中斷和tasklet有密切關(guān)系,tasklet是在軟中斷之上實(shí)現(xiàn)。事實(shí)上,出現(xiàn)在內(nèi)核代碼中的術(shù)語“軟中斷softirq”常常表示可延遲函數(shù)的所有種類。另外一種經(jīng)常使用的術(shù)語是“中斷上下文”;表示中斷當(dāng)前正在執(zhí)行一個(gè)中斷處理程序或者一個(gè)可延遲的函數(shù)。軟中斷的分配是靜態(tài)的(在編譯時(shí)定義),而tasklet的分配和初始化可以在運(yùn)行時(shí)進(jìn)行。軟中斷(即便是同一種類型的中斷)可以并發(fā)的運(yùn)行多個(gè)CPU上。因此,軟中斷是可重入函數(shù)而且必須明確的使用自鎖保護(hù)其數(shù)據(jù)結(jié)構(gòu)。tasklet不必?fù)?dān)心這些問題,因?yàn)閮?nèi)核對(duì)tasklet的執(zhí)行有了更加嚴(yán)格的控制。相同的tasklet總是串行執(zhí)行的。,換句話說,就是不能在cpu上同時(shí)運(yùn)行兩個(gè)相當(dāng)同類型的tasklet。但是。類型不同的tasklet可以在幾個(gè)cpu上并發(fā)執(zhí)行。tasklet的串行化使tasklet函數(shù)不必是可重入的,因此簡(jiǎn)化了設(shè)備驅(qū)動(dòng)程序開發(fā)者的工作。一般而言,在可延遲函數(shù)上可以執(zhí)行四種操作:初始化:定義一個(gè)新的可延遲函數(shù),這個(gè)操作通常在內(nèi)核自身初始化或者加載模塊時(shí)進(jìn)行。激活:標(biāo)記一個(gè)可延遲函數(shù)為“掛起”(在可延遲函數(shù)的下一輪調(diào)度中執(zhí)行。)激活可以在任何時(shí)候執(zhí)行(即使正在處理中斷)。屏蔽:有選擇的屏蔽一個(gè)可延遲函數(shù),這樣,即使他被激活,內(nèi)核也不執(zhí)行它。執(zhí)行:執(zhí)行一個(gè)掛起的可執(zhí)行函數(shù)和同類型的所有掛起的可延遲函數(shù);執(zhí)行是在特定的時(shí)間進(jìn)行的。第三十二頁,共四十二頁,2022年,8月28日軟中斷一個(gè)軟中斷的下標(biāo)決定了它的優(yōu)先級(jí):低下標(biāo)意味著高優(yōu)先級(jí),因?yàn)檐浿袛嗪瘮?shù)將從下表0開始執(zhí)行。軟中斷使用的數(shù)據(jù)結(jié)構(gòu)是softirq_vec數(shù)組,該數(shù)組包含類型為softirq_action的32個(gè)元素。一個(gè)軟中斷的優(yōu)先級(jí)是相應(yīng)的soft_action元素在數(shù)組內(nèi)的下標(biāo)。Soft_action數(shù)據(jù)結(jié)構(gòu)包括兩個(gè)字段:指向軟中斷函數(shù)的一個(gè)action指針和指向軟中斷函數(shù)需要的通用數(shù)據(jù)結(jié)構(gòu)的data指針。另外一個(gè)關(guān)鍵字段是32位的preempt_count字段,用它來跟蹤內(nèi)核搶占和內(nèi)核控制路徑的嵌套,該字段存放在每個(gè)進(jìn)程描述符的thread_info字段中。第三十三頁,共四十二頁,2022年,8月28日實(shí)現(xiàn)軟中斷的最后一個(gè)關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)是每個(gè)cpu都有的32位掩碼,他存放在irq_cpustar_t數(shù)據(jù)結(jié)構(gòu)的__softirq_pending字段中。為了獲取或設(shè)置位掩碼的值,內(nèi)核使用宏local_softirq_pending(),它選擇本地cpu的軟中斷位掩碼。處理軟中斷open_softirq()函數(shù)處理軟中斷的初始化。它使用三個(gè)參數(shù):軟中斷下標(biāo),指向要執(zhí)行的軟中斷函數(shù)指針及指向可能由軟中斷函數(shù)使用的數(shù)據(jù)結(jié)構(gòu)的指針。Open_softirq()限制自己初始化softirq_vec數(shù)組中適當(dāng)?shù)牡脑亍?/p>

raise_softirq()函數(shù)用來激活軟中斷,它接受軟中斷下標(biāo)nr作為參數(shù),執(zhí)行下面操作。1.執(zhí)行l(wèi)ocal_irq_save宏以保存eflags寄存器IF標(biāo)志的狀態(tài)值并禁用本地cpu上的中斷。2.把軟中斷標(biāo)記為掛起狀態(tài),這是通過設(shè)置本地的cpu的軟中斷掩碼中與下標(biāo)nr相關(guān)的位來實(shí)現(xiàn)。3.如果in_interrupt()產(chǎn)生為1的值,則跳轉(zhuǎn)到第5步。這種情況說明,要么已經(jīng)在中斷上下文中調(diào)用了raise_softirq(),要么當(dāng)前禁用了軟中斷。4.否則,就在需要的時(shí)候去調(diào)用wakeup_softirq()以喚醒本地cpu的ksoftirqd內(nèi)核線程。5.執(zhí)行l(wèi)ocal_irq_restore宏,恢復(fù)在第1步保存的IF標(biāo)志的狀態(tài)值。第三十四頁,共四十二頁,2022年,8月28日應(yīng)該周期性的檢查活動(dòng)的軟中斷,檢查是在內(nèi)核代碼的幾個(gè)點(diǎn)上進(jìn)行的。這在下列幾種情況下進(jìn)行,:1.當(dāng)內(nèi)核調(diào)用local_bh-enable()函數(shù)激活本地cpu的軟中斷時(shí)。2.當(dāng)do_IRQ()完成了I/O中斷的處理時(shí)或調(diào)用irq_exit()宏時(shí)。3.如果系統(tǒng)使用I/OAPIC,則當(dāng)smp_apic_timer_interrupt()函數(shù)處理完本地的定時(shí)器中斷時(shí)。4.在多處理器系統(tǒng)中,當(dāng)cpu處理完被CALL_FUNCTION_VECTOR處理器間中斷所觸發(fā)的函數(shù)時(shí)。5.當(dāng)一個(gè)特殊的ksoftirqd/n內(nèi)核線程被喚醒時(shí)。tasklettasklet是I/O驅(qū)動(dòng)程序中實(shí)現(xiàn)可延遲函數(shù)的首選方法。如前所述,tasklet建立在兩個(gè)叫做HI_SOFTIRQ的軟中斷之上。幾個(gè)tasklet可以與同一個(gè)軟中斷相關(guān)聯(lián),每個(gè)tasklet執(zhí)行自己的函數(shù)。兩個(gè)軟中斷之間沒有真正的區(qū)別,只不過do-softirq()先執(zhí)行HI_SOFTIRQ的tasklet,后執(zhí)行TASKLET_SOFTIRQ的tasklet。tasklet和高優(yōu)先級(jí)的tasklet分別存放在tasklet_vec和tasklet_hi_vec數(shù)組中,二者都包含類型為tasklet_head的NR_CPUS個(gè)元素,每個(gè)元素都有一個(gè)指向tasklet描述符鏈表的指針組成。tasklet描述符是一個(gè)tasklet_struct類型的數(shù)據(jù)結(jié)構(gòu),其字段如下表:第三十五頁,共四十二頁,2022年,8月28日第三十六頁,共四十二頁,2022年,8月28日調(diào)用tasklet_disable_nosync()或tasklet_disable()可以選擇性的禁止tasklet。這兩個(gè)函數(shù)都增加tasklet描述符的count字段,但是最后一個(gè)函數(shù)只有在tasklet函數(shù)已經(jīng)運(yùn)行的實(shí)例結(jié)束后才返回。為了重新激活tasklet,調(diào)用tasklet_enable()函數(shù)。為了激活tasklet,應(yīng)該根據(jù)tasklet需要的優(yōu)先級(jí),調(diào)用tasklet_schedule()函數(shù)或tasklet_hi_schedule()函數(shù),這兩個(gè)函數(shù)非常相似,其中每個(gè)都執(zhí)行下列操作:1.檢查TASKLET_STATE_SCHED()標(biāo)志;如果設(shè)置則返回。2.調(diào)用local_irq_save保存IF標(biāo)志的狀態(tài)并禁用本地中斷。3.在tasklet_vec[n]指向的鏈表的起始處增加tasklet描述符。4.調(diào)用raise_softirq_irqoff()激活TASKLET_SOFTIRQ或HI_SOFTIRQ類型的軟中斷。5.調(diào)用local_irq_restore恢復(fù)IF標(biāo)志的狀態(tài)。軟中斷函數(shù)一旦被激活,do_softirq()函數(shù)執(zhí)行。與HI_SOFTIRQ軟中斷相關(guān)的軟中斷函數(shù)叫做tasklet_hi_action()。這兩個(gè)函數(shù)非常相似,他們都執(zhí)行下列操作:1.禁用本地中斷。2.獲得本地cpu的邏輯號(hào)n。3.把tasklet_vec[n]或tasklet_hi_vec[n]指向的鏈表的地址存入局部變量list。第三十七頁,共四十二頁,2022年,8月28日4.把tasklet_vec[n]或asklet_hi_vec[n]的值賦為null,因此,已調(diào)度的tasklet描述符的鏈表被清空。5.打開本地中斷。6.對(duì)于list指向的鏈表中的每個(gè)tasklet描述符:a.在多處理系統(tǒng)上,檢查tasklet的tasklet_STATE_RUN標(biāo)志。 如果該標(biāo)志被設(shè)置,說明同類型的一個(gè)tasklet正在cpu上運(yùn)行,因 此,把任務(wù)描述符重新插入到由tasklet_vec[n]或tasklet_hi_vec[n]指 向的鏈表中,并再次激活tasklet_softirq或HI_SOFTIRQ軟中斷。 如果未被設(shè)置,tasklet就沒有在其他cpu

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論