摘要本章將向讀者依次解釋中斷概念_第1頁
摘要本章將向讀者依次解釋中斷概念_第2頁
摘要本章將向讀者依次解釋中斷概念_第3頁
摘要本章將向讀者依次解釋中斷概念_第4頁
摘要本章將向讀者依次解釋中斷概念_第5頁
已閱讀5頁,還剩13頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

1、摘要:本章將向讀者依次解釋中斷概念,解析Linux中的中斷實(shí)現(xiàn)機(jī)理以及Linux下中斷如何被使用。作為實(shí)例我們第一將向i386體系結(jié)構(gòu)一章中打造的系統(tǒng)加入一個(gè)時(shí)鐘中斷;第二將為大家注解RTC中斷,希望通過這兩個(gè)實(shí)例可以幫助讀者掌握中斷相關(guān)的概念、實(shí)現(xiàn)和編程方法。中斷是什么中斷的漢語解釋是半中間發(fā)生阻隔、停頓或故障而斷開。那么,在計(jì)算機(jī)系統(tǒng)中,我們?yōu)槭裁葱枰白韪簟⑼nD和斷開”呢?舉個(gè)日常生活中的例子,比如說我正在廚房用煤氣燒一壺水,這樣就只能守在廚房里,苦苦等著水開如果水溢出來澆滅了煤氣,有可能就要發(fā)生一場災(zāi)難了。等啊等啊,外邊突然傳來了驚奇的叫聲“怎么不關(guān)水龍頭?”于是我慚愧的發(fā)現(xiàn),剛才接水

2、之后只顧著抱怨這份無聊的差事,居然忘了這事,于是慌慌張張的沖向水管,三下兩下關(guān)了龍頭,聲音又傳到耳邊,“怎么干什么都是這么馬虎?”。伸伸舌頭,這件小事就這么過去了,我落寞的眼神又落在了水壺上。門外忽然又傳來了鏗鏘有力的歌聲,我最喜歡的古裝劇要開演了,真想奪門而出,然而,聽著水壺發(fā)出“咕嘟咕嘟”的聲音,我清楚:除非等到水開,否則沒有我享受人生的時(shí)候。這個(gè)場景跟中斷有什么關(guān)系呢?如果說我專心致志等待水開是一個(gè)過程的話,那么叫聲、電視里傳出的音樂不都讓這個(gè)過程“半中間發(fā)生阻隔、停頓或故障而斷開”了嗎?這不就是活生生的“中斷”嗎?在這個(gè)場景中,我是唯一具有處理能力的主體,不管是燒水、關(guān)龍頭還是看電視,

3、同一個(gè)時(shí)間點(diǎn)上我只能干一件事情。但是,在我專心致志干一件事情時(shí),總有許多或緊迫或不緊迫的事情突然出現(xiàn)在面前,都需要去關(guān)注,有些還需要我停下手頭的工作馬上去處理。只有在處理完之后,方能回頭完成先前的任務(wù), “把一壺水徹底燒開!”中斷機(jī)制不僅賦予了我處理意外情況的能力,如果我能充分發(fā)揮這個(gè)機(jī)制的妙用,就可以“同時(shí)”完成多個(gè)任務(wù)了?;氐綗睦?,實(shí)際上,無論我在不在廚房,煤氣灶總是會把水燒開的,我要做的,只不過是及時(shí)關(guān)掉煤氣灶而已,為了這么一個(gè)一秒鐘就能完成的動作,卻讓我死死的守候在廚房里,在10分鐘的時(shí)間里不停的看壺嘴是不是冒蒸汽,怎么說都不劃算。我決定安下心來看電視。當(dāng)然,在有生之年,我都不希

4、望讓廚房成為火海,于是我上了鬧鐘,10分鐘以后它會發(fā)出“尖叫”,提醒我爐子上的水燒開了,那時(shí)我再去關(guān)煤氣也完全來得及。我用一個(gè)中斷信號鬧鈴換來了10分鐘的歡樂時(shí)光,心里不禁由衷的感嘆:中斷機(jī)制真是個(gè)好東西。正是由于中斷機(jī)制,我才能有條不紊的“同時(shí)”完成多個(gè)任務(wù),中斷機(jī)制實(shí)質(zhì)上幫助我提高了并發(fā)“處理”能力。它也能給計(jì)算機(jī)系統(tǒng)帶來同樣的好處:如果在鍵盤按下的時(shí)候會得到一個(gè)中斷信號,CPU就不必死守著等待鍵盤輸入了;如果硬盤讀寫完成后發(fā)送一個(gè)中斷信號,CPU就可以騰出手來集中精力“服務(wù)大眾”了無論是人類敲打鍵盤的指尖還是來回讀寫介質(zhì)的磁頭,跟CPU的處理速度相比,都太慢了。沒有中斷機(jī)制,就像我們苦守

5、廚房一樣,計(jì)算機(jī)談不上有什么的并行處理能力。跟人相似,CPU也一樣要面對紛繁蕪雜的局面現(xiàn)實(shí)中的意外是無處不在的有可能是用戶等得不耐煩,猛敲鍵盤;有可能是運(yùn)算中碰到了0除數(shù);還有可能網(wǎng)卡突然接收到了一個(gè)新的數(shù)據(jù)包。這些都需要CPU具體情況具體分析,要么馬上處理,要么暫緩響應(yīng),要么置之不理。無論如何應(yīng)對,都需要 CPU暫?!笆诸^”的工作,拿出一種對策,只有在響應(yīng)之后,方能回頭完成先前的使命,“把一壺水徹底燒開!”先讓我們感受一下中斷機(jī)制對并發(fā)處理帶來的幫助。讓我們用程序來探討一下燒水問題,如果沒有“中斷”(注意,我們這里只是模仿中斷的場景,實(shí)際上是用異步事件消息處理機(jī)制來展示中斷產(chǎn)生的效果。畢竟,

6、在用戶空間沒有辦法與實(shí)際中斷產(chǎn)生直接聯(lián)系,不過操作系統(tǒng)為用戶空間提供的異步事件機(jī)制,可以看作是模仿中斷的產(chǎn)物),設(shè)計(jì)如下:void StayInKitchen()bool WaterIsBoiled = false;while ( WaterIsBoiled != true )      bool VaporGavenOff  = false;              if  (VaporGavenOff )

7、60;                    WaterIsBoiled  = true;else              WaterIsBoiled  = false;/ 關(guān)煤氣爐printf(“Close gas oven.n”);/ 一切安定下來,終于可以看電視了,10分鐘的寶貴時(shí)間啊,逝者如斯夫watching_tv(

8、);return;可以看出,整個(gè)流程如同我們前面描述的一樣,所有工作要順序執(zhí)行,沒有辦法完成并發(fā)任務(wù)。如果用“中斷”,在開始燒水的時(shí)候設(shè)定一個(gè)10分鐘的“鬧鈴”,然后讓CPU去看電視(有點(diǎn)難度,具體實(shí)現(xiàn)不在我們關(guān)心的范圍之內(nèi),留給讀者自行解決吧:>)。等鬧鐘響的時(shí)候再去廚房關(guān)爐子。#include <sys/types.h>#include <unistd.h>#include <sys/stat.h>#include <signal.h>#include <stdio.h>/ 鬧鐘到時(shí)會執(zhí)行此程序void sig_alarm(

9、int signo)       /關(guān)煤氣爐       printf(“Close gas oven.n”);void watching_tv()       while(1)                     / 呵呵,優(yōu)哉游哉 int main()/ 點(diǎn)火后設(shè)置定時(shí)中斷  printf(“Start to

10、boil water, set Alarm”);if (signal( SIGALRM, sig_alrm ) = SIG_ERR)    perror("signal(SIGALRM) error");    return -1;  / 然后就可以欣賞電視節(jié)目了  printf(“Watching TV!n”);watching_tv();return 0;這兩段程序都在用戶空間執(zhí)行。第二段程序跟中斷也沒有太大的關(guān)系,實(shí)際上它只用了信號機(jī)制而已。但是,通過這兩個(gè)程序的對比,我們可以清楚地看到異

11、步的事件處理機(jī)制是如何提升并發(fā)處理能力的。Alarm定時(shí)器:alarm相當(dāng)于系統(tǒng)中的一個(gè)定時(shí)器,如果我們調(diào)用 alarm(5),那么5秒鐘后就會“響起一個(gè)鬧鈴”(實(shí)際上靠信號機(jī)制實(shí)現(xiàn)的,我們這里不想深入細(xì)節(jié),如果你對此很感興趣,請參考Richard Stevens不朽著作Unix環(huán)境高級編程)。在鬧鈴響起的時(shí)候會發(fā)生什么呢?系統(tǒng)會執(zhí)行一個(gè)函數(shù),至于到底是什么函數(shù),系統(tǒng)允許程序自行決定。程序員編寫一個(gè)函數(shù),并調(diào)用signal對該函數(shù)進(jìn)行注冊,這樣一旦定時(shí)到來,系統(tǒng)就會調(diào)用程序員提供的函數(shù)(CallBack函數(shù)?沒錯(cuò),不過在這里如何實(shí)現(xiàn)并不關(guān)鍵,我們就不引入新的概念和細(xì)節(jié)了)。上面的例子里我們提供

12、的函數(shù)是sig_alarm,所做的工作很簡單,打印“關(guān)閉煤氣灶”消息。上面的兩個(gè)例子很簡單,但很能說明問題,首先,它證明采用異步的消息處理機(jī)制可以提高系統(tǒng)的并發(fā)處理能力。更重要的是,它揭示了這種處理機(jī)制的模式。用戶根據(jù)需要設(shè)計(jì)處理程序,并可以將該程序和特定的外部事件綁定起來,在外部事件發(fā)生時(shí)系統(tǒng)自動調(diào)用處理程序,完成相關(guān)的工作。這種模式給系統(tǒng)帶來了統(tǒng)一的管理方法,也帶來無盡的功能擴(kuò)展空間。計(jì)算機(jī)系統(tǒng)實(shí)現(xiàn)中斷機(jī)制是非常復(fù)雜的一件工作,再怎么說人都是高度智能化的生物,而計(jì)算機(jī)作為一個(gè)鐵疙瘩,沒有程序的教導(dǎo)就一事無成。而處理一個(gè)中斷過程,它受到的限制和需要學(xué)習(xí)的東西太多了。首先,計(jì)算機(jī)能夠接收的外部

13、信號形式非常有限。中斷是由外部的輸入引起的,可以說是一種刺激。在燒水的場景中,這些輸入是叫聲和電視的音樂,我們這里只以聲音為例。其實(shí)現(xiàn)實(shí)世界中能輸入人類CPU大腦的信號很多,圖像、氣味一樣能被我們接受,人的信息接口很完善。而計(jì)算機(jī)則不然,接受外部信號的途徑越多,設(shè)計(jì)實(shí)現(xiàn)就越復(fù)雜,代價(jià)就越高。因此個(gè)人計(jì)算機(jī)(PC)給所有的外部刺激只留了一種輸入方式特定格式的電信號,并對這種信號的格式、接入方法、響應(yīng)方法、處理步驟都做了規(guī)約(具體內(nèi)容本文后面部分會繼續(xù)詳解),這種信號就是中斷或中斷信號,而這一整套機(jī)制就是中斷機(jī)制。其次,計(jì)算機(jī)不懂得如何應(yīng)對信號。人類的大腦可以自行處理外部輸入,我從來不用去擔(dān)心鬧鐘

14、響時(shí)會手足無措走進(jìn)廚房關(guān)煤氣,這簡直是天經(jīng)地義的事情,還用大腦想啊,小腿肚子都知道可惜計(jì)算機(jī)不行,沒有程序,它就紋絲不動。因此,必須有機(jī)制保證外部中斷信號到來后,有正確的程序在正確的時(shí)候被執(zhí)行。還有,計(jì)算機(jī)不懂得如何保持工作的持續(xù)性。我在看電視的時(shí)候如果去廚房關(guān)了煤氣,回來以后能繼續(xù)將電視進(jìn)行到底,不受太大的影響。而計(jì)算機(jī)則不然,如果放下手頭的工作直接去處理“意外”的中斷,那么它就再也沒有辦法想起來曾經(jīng)作過什么,做到什么程度了。自然也就沒有什么“重操舊業(yè)”的機(jī)會了。這樣的處理方式就不是并發(fā)執(zhí)行,而是東一榔頭,西一棒槌了。那么,通用的計(jì)算機(jī)系統(tǒng)是如何解決這些問題的呢?它是靠硬件和軟件配合來協(xié)同實(shí)

15、現(xiàn)中斷處理的全過程的。我們將通過Intel X86架構(gòu)的實(shí)現(xiàn)來介紹這一過程。中斷流程處理CPU執(zhí)行完一條指令后,下一條指令的邏輯地址存放在cs和eip這對寄存器中。在執(zhí)行新指令前,控制單元會檢查在執(zhí)行前一條指令的過程中是否有中斷或異常發(fā)生。如果有,控制單元就會拋下指令,進(jìn)入下面的流程:1.       確定與中斷或異常關(guān)聯(lián)的向量i (0£i£255)2.       尋找向量對應(yīng)的處理程序3.       保存當(dāng)前的“工作現(xiàn)場”,執(zhí)行中斷或異常的處理程序4

16、.       處理程序執(zhí)行完畢后,把控制權(quán)交還給控制單元5.       控制單元恢復(fù)現(xiàn)場,返回繼續(xù)執(zhí)行原程序整個(gè)流程如下圖所示:讓我們深入這個(gè)流程,看看都有什么問題需要面對。1、異常是什么概念?在處理器執(zhí)行到由于編程失誤而導(dǎo)致的錯(cuò)誤指令(例如除數(shù)是0)的時(shí)候,或者在執(zhí)行期間出現(xiàn)特殊情況(例如缺頁),需要靠操作系統(tǒng)來處理的時(shí)候,處理器就會產(chǎn)生一個(gè)異常。對大部分處理器體系結(jié)構(gòu)來說,處理異常和處理中斷的方式基本是相同的,x86架構(gòu)的CPU也是如此。異常與中斷還是有些區(qū)別,異常的產(chǎn)生必須考慮與處理器時(shí)鐘的同步。實(shí)際上,

17、異常往往被稱為同步中斷。2、中斷向量是什么?中斷向量代表的是中斷源從某種程度上講,可以看作是中斷或異常的類型。中斷和異常的種類很多,比如說被0除是一種異常,缺頁又是一種異常,網(wǎng)卡會產(chǎn)生中斷,聲卡也會產(chǎn)生中斷,CPU如何區(qū)分它們呢?中斷向量的概念就是由此引出的,其實(shí)它就是一個(gè)被送通往CPU數(shù)據(jù)線的一個(gè)整數(shù)。CPU給每個(gè) IRQ分配了一個(gè)類型號,通過這個(gè)整數(shù)CPU來識別不同類型的中斷。這里可能很多朋友會尋問為什么還要弄個(gè)中斷向量這么麻煩的東西?為什么不直接用 IRQ0IRQ15就完了?比如就讓IRQ0為0,IRQ1為1,這不是要簡單的多么?其實(shí)這里體現(xiàn)了模塊化設(shè)計(jì)規(guī)則,及節(jié)約規(guī)則。首先我們先談?wù)劰?jié)

18、約規(guī)則,所謂節(jié)約規(guī)則就是所使用的信號線數(shù)越少越好,這樣如果每個(gè)IRQ都獨(dú)立使用一根數(shù)據(jù)線,如IRQ0用0號線,IRQ1 用1號線這樣,16個(gè)IRQ就會用16根線,這顯然是一種浪費(fèi)。那么也許馬上就有朋友會說:那么只用4根線不就行了嗎?(24=16)。這個(gè)問題,體現(xiàn)了模塊設(shè)計(jì)規(guī)則。我們在前面就說過中斷有很多類,可能是外部硬件觸發(fā),也可能是由軟件觸發(fā),然而對于CPU來說中斷就是中斷,只有一種,CPU不用管它到底是由外部硬件觸發(fā)的還是由運(yùn)行的軟件本身觸發(fā)的,應(yīng)為對于CPU來說,中斷處理的過程都是一樣的:中斷現(xiàn)行程序,轉(zhuǎn)到中斷服務(wù)程序處執(zhí)行,回到被中斷的程序繼續(xù)執(zhí)行。CPU總共可以處理256種中斷,而并

19、不知道,也不應(yīng)當(dāng)讓CPU知道這是硬件來的中斷還是軟件來的中斷,這樣,就可以使CPU的設(shè)計(jì)獨(dú)立于中斷控制器的設(shè)計(jì),這樣CPU所需完成的工作就很單純了。CPU對于其它的模塊只提供了一種接口,這就是256個(gè)中斷處理向量,也稱為中斷號。由這些中斷控制器自行去使用這256個(gè)中斷號中的一個(gè)與CPU進(jìn)行交互,比如,硬件中斷可以使用前128個(gè)號,軟件中斷使用后128個(gè)號,也可以軟件中斷使用前128個(gè)號,硬件中斷使用后128個(gè)號,這與CPU完全無關(guān)了,當(dāng)你需要處理的時(shí)候,只需告訴CPU你用的是哪個(gè)中斷號就行,而不需告訴CPU你是來自哪兒的中斷。這樣也方便了以后的擴(kuò)充,比如現(xiàn)在機(jī)器里又加了一片8259芯片,那么這

20、個(gè)芯片就可以使用空閑的中斷號,看哪一個(gè)空閑就使用哪一個(gè),而不是必須要使用第0號,或第1號中斷號了。其實(shí)這相當(dāng)于一種映射機(jī)制,把IRQ信號映射到不同的中斷號上,IRQ的排列或說編號是固定的,但通過改變映射機(jī)制,就可以讓IRQ映射到不同的中斷號,也可以說調(diào)用不同的中斷服務(wù)程序。3、什么是中斷服務(wù)程序?在響應(yīng)一個(gè)特定中斷的時(shí)候,內(nèi)核會執(zhí)行一個(gè)函數(shù),該函數(shù)叫做中斷處理程序(interrupt handler)或中斷服務(wù)程序(interrupt service routine(ISR))。產(chǎn)生中斷的每個(gè)設(shè)備都有相應(yīng)的中斷處理程序。例如,由一個(gè)函數(shù)專門處理來自系統(tǒng)時(shí)鐘的中斷,而另外一個(gè)函數(shù)專門處理由鍵盤產(chǎn)

21、生的中斷。一般來說,中斷服務(wù)程序要負(fù)責(zé)與硬件進(jìn)行交互,告訴該設(shè)備中斷已被接收。此外,還需要完成其他相關(guān)工作。比如說網(wǎng)絡(luò)設(shè)備的中斷服務(wù)程序除了要對硬件應(yīng)答,還要把來自硬件的網(wǎng)絡(luò)數(shù)據(jù)包拷貝到內(nèi)存,對其進(jìn)行處理后再交給合適的協(xié)議?;驊?yīng)用程序。每個(gè)中斷服務(wù)程序根據(jù)其要完成的任務(wù),復(fù)雜程度各不相同。一般來說,一個(gè)設(shè)備的中斷服務(wù)程序是它設(shè)備驅(qū)動程序(device driver)的一部分設(shè)備驅(qū)動程序是用于對設(shè)備進(jìn)行管理的內(nèi)核代碼。4、隔離變化不知道您有沒有意識到,中斷處理前面這部分的設(shè)計(jì)是何等的簡單優(yōu)美。人是高度智能化的,能夠?qū)τ龅降母鞣N意外情況做有針對性的處理,計(jì)算機(jī)相比就差距甚遠(yuǎn)了,它只能根據(jù)預(yù)定的程序

22、進(jìn)行操作。對于計(jì)算機(jī)來說,硬件支持的,只能是中斷這種電信號傳播的方式和CPU對這種信號的接收方法,而具體如何處理這個(gè)中斷,必須得靠操作系統(tǒng)實(shí)現(xiàn)。操作系統(tǒng)支持所有事先能夠預(yù)料到的中斷信號,理論上都不存在太大的挑戰(zhàn),但在操作系統(tǒng)安裝到計(jì)算機(jī)設(shè)備上以后,肯定會時(shí)常有新的外圍設(shè)備被加入系統(tǒng),這可能會帶來安裝系統(tǒng)時(shí)根本無法預(yù)料的“意外”中斷。如何支持這種擴(kuò)展,是整個(gè)系統(tǒng)必須面對的。而硬件和軟件在這里的協(xié)作,給我們帶來了完美的答案。當(dāng)新的設(shè)備引入新類型的中斷時(shí),CPU和操作系統(tǒng)不用關(guān)注如何處理它。CPU只負(fù)責(zé)接收中斷信號,并引用中斷服務(wù)程序;而操作系統(tǒng)提供默認(rèn)的中斷服務(wù)一般來說就是不理會這個(gè)信號,返回就可

23、以了并負(fù)責(zé)提供接口,讓用戶通過該接口注冊根據(jù)設(shè)備具體功能而編制的中斷服務(wù)程序。如果用戶注冊了對應(yīng)于一個(gè)中斷的服務(wù)程序,那么CPU就會在該中斷到來時(shí)調(diào)用用戶注冊的服務(wù)程序。這樣,在中斷來臨時(shí)系統(tǒng)需要如何操作硬件、如何實(shí)現(xiàn)硬件功能這部分工作就完全獨(dú)立于CPU架構(gòu)和操作系統(tǒng)的設(shè)計(jì)了。而當(dāng)你需要加入新設(shè)備的時(shí)候,只需要告訴操作系統(tǒng)該設(shè)備占用的中斷號、按照操作系統(tǒng)要求的接口格式撰寫中斷服務(wù)程序,用操作系統(tǒng)提供的函數(shù)注冊該服務(wù)程序,設(shè)備的中斷就被系統(tǒng)支持了。中斷和對中斷的處理被解除了耦合。這樣,無論是你在需要加入新的中斷時(shí),還是在你需要改變現(xiàn)有中斷的服務(wù)程序時(shí)、又或是取消對某個(gè)中斷支持的時(shí)候,CPU架構(gòu)和

24、操作系統(tǒng)都無需作改變。5、保存當(dāng)前工作“現(xiàn)場”在中斷處理完畢后,計(jì)算機(jī)一般來說還要回頭處理原先手頭正做的工作。這給中斷的概念帶來些額外的“內(nèi)涵”1?!盎仡^”不是指從頭再來重新做,而是要接著剛才的進(jìn)度繼續(xù)做。這就需要在處理中斷信號之前保留工作“現(xiàn)場”?!艾F(xiàn)場”這個(gè)詞比較晦澀,其實(shí)就是指一個(gè)信息集,它能反映某個(gè)時(shí)間點(diǎn)上任務(wù)的狀態(tài),并能保證按照這些信息就能恢復(fù)任務(wù)到該狀態(tài),繼續(xù)執(zhí)行下去。再直白一點(diǎn),現(xiàn)場不過就是一組寄存器值。而如何保護(hù)現(xiàn)場和恢復(fù)場景是中斷機(jī)制需要考慮的重點(diǎn)之一。每個(gè)中斷處理都要經(jīng)歷這個(gè)保存和恢復(fù)過程,我們可以抽象出其中的步驟:1.      &

25、#160;  保存現(xiàn)場2.         執(zhí)行具體的中斷服務(wù)程序3.         從中斷服務(wù)返回4.         恢復(fù)現(xiàn)場上面說過了,“現(xiàn)場”看似在不斷變化,沒有哪個(gè)瞬間相同。但實(shí)際上組成現(xiàn)場的要素卻不會有任何改變。也就是說,這要我們保存了相關(guān)的寄存器狀態(tài),現(xiàn)場就能保存下來。而恢復(fù)“現(xiàn)場”就是重新載入這些寄存器。換句話說,對于任何一個(gè)中斷,保護(hù)現(xiàn)場和恢復(fù)現(xiàn)場所作的都是完全相同的操作。既然操作相同,

26、實(shí)現(xiàn)操作的過程和代碼就相同。減少代碼的冗余是模塊化設(shè)計(jì)的基本準(zhǔn)則,實(shí)在沒有道理讓所有的中斷服務(wù)程序都重復(fù)的實(shí)現(xiàn)這樣的功能,應(yīng)該將它作為一種基本的結(jié)構(gòu)由底層的操作系統(tǒng)或硬件完成。而對中斷的處理過程需要迅速完成,因此,Intel CPU的控制器就承擔(dān)了這個(gè)任務(wù),非但如此,上面的所有步驟次序都被固化下來,由控制器驅(qū)動完成。保存現(xiàn)場和恢復(fù)現(xiàn)場都由硬件自動完成,大大減輕了操作系統(tǒng)和設(shè)備驅(qū)動程序的負(fù)擔(dān)。6、硬件對中斷支持的細(xì)節(jié)下面的部分,本來應(yīng)該介紹8259、中斷控制器編程、中斷描述符表等內(nèi)容,可是我看到了瀟寒寫的“保護(hù)模式下的8259A芯片編程及中斷處理探究”,前人之述備矣,讀者直接讀它好了。Linux

27、下的中斷在Linux中,中斷處理程序看起來就是普普通通的C函數(shù)。只不過這些函數(shù)必須按照特定的類型聲明,以便內(nèi)核能夠以標(biāo)準(zhǔn)的方式傳遞處理程序的信息,在其他方面,它們與一般的函數(shù)看起來別無二致。中斷處理程序與其它內(nèi)核函數(shù)的真正區(qū)別在于,中斷處理程序是被內(nèi)核調(diào)用來響應(yīng)中斷的,而它們運(yùn)行于我們稱之為中斷上下文的特殊上下文中。關(guān)于中斷上下文,我們將在后面討論。中斷可能隨時(shí)發(fā)生,因此中斷處理程序也就隨時(shí)可能執(zhí)行。所以必須保證中斷處理程序能夠快速執(zhí)行,這樣才能保證盡可能快地恢復(fù)被中斷代碼的執(zhí)行。因此,盡管對硬件而言,迅速對其中斷進(jìn)行服務(wù)非常重要。但對系統(tǒng)的其它部分而言,讓中斷處理程序在盡可能短的時(shí)間內(nèi)完成執(zhí)

28、行也同樣重要。即使最精簡版的中斷服務(wù)程序,它也要與硬件進(jìn)行交互,告訴該設(shè)備中斷已被接收。但通常我們不能像這樣給中斷服務(wù)程序隨意減負(fù),相反,我們要靠它完成大量的其它工作。作為一個(gè)例子,我們可以考慮一下網(wǎng)絡(luò)設(shè)備的中斷處理程序面臨的挑戰(zhàn)。該處理程序除了要對硬件應(yīng)答,還要把來自硬件的網(wǎng)絡(luò)數(shù)據(jù)包拷貝到內(nèi)存,對其進(jìn)行處理后再交給合適的協(xié)議?;驊?yīng)用程序。顯而易見,這種運(yùn)動量不會太小?,F(xiàn)在我們來分析一下Linux操作系統(tǒng)為了支持中斷機(jī)制,具體都需要做些什么工作。首先,操作系統(tǒng)必須保證新的中斷能夠被支持。計(jì)算機(jī)系統(tǒng)硬件留給外設(shè)的是一個(gè)統(tǒng)一的中斷信號接口。它固化了中斷信號的接入和傳遞方法,拿PC機(jī)來說,中斷機(jī)制是

29、靠兩塊8259和CPU協(xié)作實(shí)現(xiàn)的。外設(shè)要做的只是把中斷信號發(fā)送到8259的某個(gè)特定引腳上,這樣8259就會為此中斷分配一個(gè)標(biāo)識 也就是通常所說的中斷向量,通過中斷向量,CPU就能夠在以中斷向量為索引的表中斷向量表里找到中斷服務(wù)程序,由它決定具體如何處理中斷。這是硬件規(guī)定的機(jī)制,軟件只能無條件服從。因此,操作系統(tǒng)對新中斷的支持,說簡單點(diǎn),就是維護(hù)中斷向量表。新的外圍設(shè)備加入系統(tǒng),首先得明確自己的中斷向量號是多少,還得提供自身中斷的服務(wù)程序,然后利用Linux的內(nèi)核調(diào)用界面,把中斷向量號、中斷服務(wù)程序這對信息填寫到中斷向量表中去。這樣CPU在接收到中斷信號時(shí)就會自動調(diào)用中斷服務(wù)程序了。這種注冊操作

30、一般是由設(shè)備驅(qū)動程序完成的。其次,操作系統(tǒng)必須提供給程序員簡單可靠的編程界面來支持中斷。中斷的基本流程前面已經(jīng)講了,它會打斷當(dāng)前正在進(jìn)行的工作去執(zhí)行中斷服務(wù)程序,然后再回到先前的任務(wù)繼續(xù)執(zhí)行。這中間有大量需要解決問題:如何保護(hù)現(xiàn)場、嵌套中斷如何處理等等,操作系統(tǒng)要一一化解。程序員,即使是驅(qū)動程序的開發(fā)人員,在寫中斷服務(wù)程序的時(shí)候也很少需要對被打斷的進(jìn)程心存憐憫。(當(dāng)然,出于提高系統(tǒng)效率的考慮,編寫驅(qū)動程序要比編寫用戶級程序多一些條條框框,誰讓我們頂著系統(tǒng)程序員的光環(huán)呢?)操作系統(tǒng)為我們屏蔽了這些與中斷相關(guān)硬件機(jī)制打交道的細(xì)節(jié),提供了一套精簡的接口,讓我們用極為簡單的方式實(shí)現(xiàn)對實(shí)際中斷的支持,L

31、inux是怎么完美的做到這一點(diǎn)的呢?CPU對中斷處理的流程我們首先必須了解CPU在接收到中斷信號時(shí)會做什么。沒辦法,操作系統(tǒng)必須了解硬件的機(jī)制,不配合硬件就寸步難行?,F(xiàn)在我們假定內(nèi)核已被初始化,CPU在保護(hù)模式下運(yùn)行。CPU執(zhí)行完一條指令后,下一條指令的邏輯地址存放在cs和eip這對寄存器中。在執(zhí)行新指令前,控制單元會檢查在執(zhí)行前一條指令的過程中是否有中斷或異常發(fā)生。如果有,控制單元就會拋下指令,進(jìn)入下面的流程:1.確定與中斷或異常關(guān)聯(lián)的向量i (0£i£255)。2.籍由idtr寄存器從IDT表中讀取第i項(xiàng)(在下面的描述中,我們假定該IDT表項(xiàng)中包含的是一個(gè)中斷門或一個(gè)陷

32、阱門)。3.從gdtr寄存器獲得GDT的基地址,并在GDT表中查找,以讀取IDT表項(xiàng)中的選擇符所標(biāo)識的段描述符。這個(gè)描述符指定中斷或異常處理程序所在段的基地址。4.確信中斷是由授權(quán)的(中斷)發(fā)生源發(fā)出的。首先將當(dāng)前特權(quán)級CPL(存放在cs寄存器的低兩位)與段描述符(存放在GDT中)的描述符特權(quán)級DPL比較,如果CPL小于DPL,就產(chǎn)生一個(gè)“通用保護(hù)”異常,因?yàn)橹袛嗵幚沓绦虻奶貦?quán)不能低于引起中斷的程序的特權(quán)。對于編程異常,則做進(jìn)一步的安全檢查:比較CPL與處于IDT中的門描述符的DPL,如果DPL小于CPL,就產(chǎn)生一個(gè)“通用保護(hù)”異常。這最后一個(gè)檢查可以避免用戶應(yīng)用程序訪問特殊的陷阱門或中斷門。

33、5.檢查是否發(fā)生了特權(quán)級的變化,也就是說, CPL是否不同于所選擇的段描述符的DPL。如果是,控制單元必須開始使用與新的特權(quán)級相關(guān)的棧。通過執(zhí)行以下步驟來做到這點(diǎn):    a.讀tr寄存器,以訪問運(yùn)行進(jìn)程的TSS段。b.用與新特權(quán)級相關(guān)的棧段和棧指針的正確值裝載ss和esp寄存器。這些值可以在TSS中找到(參見第三章的“任務(wù)狀態(tài)段”一節(jié))。c.在新的棧中保存ss和esp以前的值,這些值定義了與舊特權(quán)級相關(guān)的棧的邏輯地址。    6.如果故障已發(fā)生,用引起異常的指令地址裝載cs和eip寄存器,從而使得這條指令能再次被執(zhí)行。    7.在

34、棧中保存eflag、cs及eip的內(nèi)容。8.如果異常產(chǎn)生了一個(gè)硬錯(cuò)誤碼,則將它保存在棧中。9.裝載cs和eip寄存器,其值分別是IDT表中第i項(xiàng)門描述符的段選擇符和偏移量域。這些值給出了中斷或者異常處理程序的第一條指令的邏輯地址??刂茊卧鶊?zhí)行的最后一步就是跳轉(zhuǎn)到中斷或者異常處理程序。換句話說,處理完中斷信號后, 控制單元所執(zhí)行的指令就是被選中處理程序的第一條指令。中斷或異常被處理完后,相應(yīng)的處理程序必須0x20/0x21/0xa0/0xa1產(chǎn)生一條iret指令,把控制權(quán)轉(zhuǎn)交給被中斷的進(jìn)程,這將迫使控制單元:1.用保存在棧中的值裝載cs、eip、或eflag寄存器。如果一個(gè)硬錯(cuò)誤碼曾被壓入棧中

35、,并且在eip內(nèi)容的上面,那么,執(zhí)行iret指令前必須先彈出這個(gè)硬錯(cuò)誤碼。2.檢查處理程序的CPL是否等于cs中最低兩位的值(這意味著被中斷的進(jìn)程與處理程序運(yùn)行在同一特權(quán)級)。如果是,iret終止執(zhí)行;否則,轉(zhuǎn)入下一步。3. 從棧中裝載ss和esp寄存器,因此,返回到與舊特權(quán)級相關(guān)的棧。4.       檢查ds、es、fs及gs段寄存器的內(nèi)容,如果其中一個(gè)寄存器包含的選擇符是一個(gè)段描述符,并且其DPL值小于CPL,那么,清相應(yīng)的段寄存器??刂茊卧@么做是為了禁止用戶態(tài)的程序(CPL=3)利用內(nèi)核以前所用的段寄存器(DPL=0)。如果不清這些寄存器,懷有

36、惡意的用戶程序就可能利用它們來訪問內(nèi)核地址空間。再次,操作系統(tǒng)必須保證中斷信息能夠高效可靠的傳遞實(shí)例一為自己的操作系統(tǒng)中加入中斷中斷機(jī)制的實(shí)現(xiàn)在這個(gè)部分,我將為大家詳細(xì)介紹SagaLinux_irq中是如何處理中斷的。為了更好的演示軟硬件交互實(shí)現(xiàn)中斷機(jī)制的過程,我將在前期實(shí)現(xiàn)的SagaLinux上加入對一個(gè)新中斷定時(shí)中斷的支持。首先,讓我介紹一下SagaLinux_irq中涉及中斷的各部分代碼。這些代碼主要包含在kernel目錄下,包括idt.c,irq.c,i8259.s,boot目錄下的setup.s也和中斷相關(guān),下面將對他們進(jìn)行討論。1、boot/setup.ssetup.s中相關(guān)于中斷

37、的部分主要集中在pic_init小結(jié),該部分完成了對中斷控制器的初始化。對8259A的編程是通過向其相應(yīng)的端口發(fā)送一系列的ICW(初始化命令字)完成的。總共需要發(fā)送四個(gè)ICW,它們都分別有自己獨(dú)特的格式,而且必須按次序發(fā)送,并且必須發(fā)送到相應(yīng)的端口,具體細(xì)節(jié)請查閱相關(guān)資料。pic_init:    cli    mov al, 0x11             initialize PICs; 給中斷寄存器編程; 發(fā)送ICW1:使用ICW4,級聯(lián)工作   

38、 out 0x20, al             8259_MASTER    out 0xA0, al             8259_SLAVE    ; 發(fā)送 ICW2,中斷起始號從 0x20 開始(第一片)及 0x28開始(第二片)    mov al,   0x20         

39、; interrupt start 32    out 0x21, al    mov al,   0x28          ; interrupt start 40    out 0xA1, al; 發(fā)送 ICW3    mov al,   0x04          ; IRQ 2 of 8259_MASTER    out 0x2

40、1, al       ; 發(fā)送 ICW4    mov al,   0x02          ; to 8259_SLAVE    out 0xA1, al; 工作在80x86架構(gòu)下    mov al,   0x01          ; 8086 Mode    out 0x21, al   

41、; out 0xA1, al; 設(shè)置中斷屏蔽位 OCW1 ,屏蔽所有中斷請求    mov al,   0xFF          ; mask all    out 0x21, al    out 0xA1, al    sti   2、kernel/irq.c    irq.c提供了三個(gè)函數(shù)enable_irq、disable_irq和request_irq,函數(shù)原型如下:void enabl

42、e_irq(int irq)void disable_irq(int irq)void request_irq(int irq, void (*handler)()      enable_irq和disable_irq用來開啟和關(guān)閉右參數(shù)irq指定的中斷,這兩個(gè)函數(shù)直接對8259的寄存器進(jìn)行操作,因此irq 對應(yīng)的是實(shí)實(shí)在在的中斷號,比如說X86下時(shí)鐘中斷一般為0號中斷,那么啟動時(shí)鐘中斷就需要調(diào)用enable_irq(1),而鍵盤一般占用2號中斷,那么關(guān)閉鍵盤中斷就需要調(diào)用disable_irq(2)。irq對應(yīng)的不是中斷向量。equest_irq用來

43、將中斷號和中斷服務(wù)程序綁定起來,綁定完成后,命令8259開始接受中斷請求。下面是request_irq的實(shí)現(xiàn)代碼:void request_irq(int irq, void (*handler)()  irq_handlerirq = handler;  enable_irq(irq);其中irq_handler是一個(gè)擁有16個(gè)元素的數(shù)組,數(shù)組項(xiàng)是指向函數(shù)的指針,每個(gè)指針可以指向一個(gè)中斷服務(wù)程序。 irq_handlerirq = handler 就是一個(gè)給數(shù)組項(xiàng)賦值的過程,其中隱藏了中斷號向中斷向量映射的過程,在初始化IDT表的部分,我會介紹相關(guān)內(nèi)容

44、。3、kernel/i8259.s2i8259.c負(fù)責(zé)對外部中斷的支持。我們已經(jīng)討論過了,8259芯片負(fù)責(zé)接收外部設(shè)備如定時(shí)器、鍵盤、聲卡等的中斷,兩塊8259共支持16個(gè)中斷。我們也曾討論過,在編寫操作系統(tǒng)的時(shí)候,我們不可能知道每個(gè)中斷到底對應(yīng)的是哪個(gè)中斷服務(wù)程序。實(shí)際上,通常在這個(gè)時(shí)候,中斷服務(wù)程序壓根還沒有被編寫出來??墒?,X86體系規(guī)定,在初始化中斷向量表的時(shí)候,必須提供每個(gè)向量對應(yīng)的服務(wù)程序的偏移地址,以便CPU在接收到中斷時(shí)調(diào)用相應(yīng)的服務(wù)程序,這該如何是好呢?巧婦難為無米之炊,此時(shí)此刻,我們只有創(chuàng)造所有中斷對應(yīng)的服務(wù)程序,才能完成初始化IDT的工作,于是我們制造出16個(gè)函數(shù)_irq

45、0到 _irq15,在注冊中斷服務(wù)程序的時(shí)候,我們就把它們填寫到IDT的描述符中去。(在SagaLinux中當(dāng)前的實(shí)現(xiàn)里,我并沒有填寫完整的IDT 表,為了讓讀者看得較為清楚,我只加入了定時(shí)器和鍵盤對應(yīng)的_irq和_irq1。但這樣一來就帶來一個(gè)惡果,讀者會發(fā)現(xiàn)在加入新的中斷支持時(shí),需要改動idt.c中的trap_init函數(shù),用set_int_gate對新中斷進(jìn)行支持。完全背離了我們強(qiáng)調(diào)的分隔變化的原則。實(shí)際上,只要我們在這里填寫完整,并提供一個(gè)缺省的中斷服務(wù)函數(shù)就可以解決這個(gè)問題。我再強(qiáng)調(diào)一遍,這不是設(shè)計(jì)問題,只是為了便于讀者觀察而做的簡化。)可是,這16個(gè)函數(shù)怎么能對未知的中斷進(jìn)行有針對

46、性的個(gè)性化服務(wù)呢?當(dāng)然不能,這16個(gè)函數(shù)只是一個(gè)接口,我們可以在其中留下后門,當(dāng)新的中斷需要被系統(tǒng)支持時(shí),它實(shí)際的中斷服務(wù)程序就能被這些函數(shù)調(diào)用。具體調(diào)用關(guān)系請參考圖2如圖2所示,_irq0到_irq15會被填充到IDT從32到47(之所以映射到這個(gè)區(qū)間是為了模仿Linux的做法,其實(shí)這部分的整個(gè)實(shí)現(xiàn)都是在模仿Linux)這16個(gè)條目的中斷描述符中去,這樣中斷到來的時(shí)候就會調(diào)用相應(yīng)的_irq函數(shù)。所有irq函數(shù)所作的工作基本相同,把中斷號壓入棧中,再調(diào)用do_irq函數(shù);它們之間唯一區(qū)別的地方就在于不同的irq函數(shù)壓入的中斷號不同。do_irq首先會從棧中取出中斷號,然后根據(jù)中斷號計(jì)算該中斷對

47、應(yīng)的中斷服務(wù)程序在irq_handler數(shù)組中的位置,并跳到該位置上去執(zhí)行相應(yīng)的服務(wù)程序。還記得irq.c中介紹的request_irq函數(shù)嗎,該函數(shù)綁定中斷號和中斷服務(wù)程序的實(shí)現(xiàn),其實(shí)就是把指向中斷服務(wù)程序的指針填寫到中斷號對應(yīng)的irq_handler數(shù)組中去?,F(xiàn)在,你應(yīng)該明白我們是怎樣把一個(gè)中斷服務(wù)程序加入到SagaLinux中的了吧通過一個(gè)中間層,我們可以做任何事情。在上圖的實(shí)現(xiàn)中,IDT表格中墨綠色的部分外部中斷對應(yīng)的部分可以浮動,也就是說,我們可以任意選擇映射的起始位置,比如說,我們讓_irq0映射到IDT的第128項(xiàng),只要后續(xù)的映射保持連續(xù)就可以了。4、kernel/idt.cid

48、t.c當(dāng)然是用來初始化IDT表的了。在i8259.s中我們介紹了操作系統(tǒng)是如何支持中斷服務(wù)程序的添加的,但是,有兩個(gè)部分的內(nèi)容沒有涉及:一是如何把_irq函數(shù)填寫到IDT表中,另外一個(gè)就是中斷支持了,那異常怎么支持呢?idt.c負(fù)責(zé)解決這兩方面的問題。idt.c提供了trap_init函數(shù)來填充IDT表。           void trap_init()  int i;  idtr_t idtr;  / 填入系統(tǒng)默認(rèn)的異常,共17個(gè) 

49、0;set_trap_gate(0, (unsigned int)&divide_error);  set_trap_gate(1, (unsigned int)&debug);  set_trap_gate(2, (unsigned int)&nmi);  set_trap_gate(3, (unsigned int)&int3);  set_trap_gate(4, (unsigned int)&overflow);  set_trap_gate(5,

50、 (unsigned int)&bounds);  set_trap_gate(6, (unsigned int)&invalid_op);  set_trap_gate(7, (unsigned int)&device_not_available);  set_trap_gate(8, (unsigned int)&double_fault);  set_trap_gate(9, (unsigned int)&coprocessor_segment_overrun);

51、60; set_trap_gate(10,(unsigned int) &invalid_TSS);  set_trap_gate(11, (unsigned int)&segment_not_present);  set_trap_gate(12, (unsigned int)&stack_segment);  set_trap_gate(13, (unsigned int)&general_protection);  set_trap_gate(14, (unsigne

52、d int)&page_fault);  set_trap_gate(15, (unsigned int)&coprocessor_error);  set_trap_gate(16, (unsigned int)&alignment_check);/ 17到31這15個(gè)異常是intel保留的,最好不要占用  for (i = 17;i<32;i+)    set_trap_gate(i, (unsigned int)&reserved);  / 我們只在I

53、DT中填入定時(shí)器和鍵盤要用到的兩個(gè)中斷  set_int_gate(32, (unsigned int)&_irq0);   set_int_gate(33, (unsigned int)&_irq1);/ 一共有34個(gè)中斷和異常需要支持  idtr.limit = 34*8;  idtr.lowerbase = 0x0000;  idtr.higherbase = 0x0000;  cli();/ 載入IDT表,新的中斷可以用了  _as

54、m_ _volatile_ ("lidt (%0)"            :"p" (&idtr);  sti();首先我們來看看set_trap_gate和set_int_gate函數(shù),下面是它們兩個(gè)的實(shí)現(xiàn)void set_trap_gate(int vector, unsigned int handler_offset)  trapgd_t* trapgd = (trapgd_t*) IDT_BASE + vector;&

55、#160; trapgd->loffset = handler_offset & 0x0000FFFF;  trapgd->segment_s = CODESEGMENT;  trapgd->reserved = 0x00;  trapgd->options =  0x0F | PRESENT | KERNEL_LEVEL;  trapgd->hoffset = (handler_offset & 0xFFFF0000) >>

56、16);void set_int_gate(int vector,  unsigned int handler_offset)  intgd_t* intgd = (intgd_t*) IDT_BASE + vector;  intgd->loffset =  handler_offset & 0x0000FFFF;  intgd->segment_s = CODESEGMENT;  intgd->reserved = 0x0;  

57、;intgd->options =  0x0E | PRESENT | KERNEL_LEVEL;  intgd->hoffset = (handler_offset & 0xFFFF0000) >> 16); 我們可以發(fā)現(xiàn),它們所作的工作就是根據(jù)中斷向量號計(jì)算出應(yīng)該把指向中斷或異常服務(wù)程序的指針放在什么IDT表中的什么位置,然后把該指針和中斷描述符設(shè)置好就行了。同樣,中斷描述符的格式請查閱有關(guān)資料?,F(xiàn)在,來關(guān)注一下set_trap_gate的參數(shù),又是指向函數(shù)的指針。在這里,我們看到每個(gè)這樣的指針指向一個(gè)異常處理函數(shù),如

58、divide_error、debug等:void divide_error(void)  sleep("divide error");void debug(void)  sleep("debug");    每個(gè)函數(shù)都調(diào)用了sleep,那么sleep是有何作用?是不是像do_irq一樣調(diào)用具體異常的中斷服務(wù)函數(shù)呢?/ Nooooo . just sleep :)void sleep(char* message)  printk("%s",message);

59、  while(1);    看樣子不是,這個(gè)函數(shù)就是休眠而已!實(shí)際上,我們這里進(jìn)行了簡化,對于Intel定義好的前17個(gè)內(nèi)部異常,目前SagaLinux還不能做有針對性的處理,因此我們直接讓系統(tǒng)無限制地進(jìn)入休眠跟死機(jī)區(qū)別不大。因此,當(dāng)然也不用擔(dān)心恢復(fù)“現(xiàn)場”的問題了,不用考慮棧的影響,所以直接用C函數(shù)實(shí)現(xiàn)。此外,由于這17個(gè)異常如何處理在這個(gè)時(shí)候我們已經(jīng)確定下來了sleep,既然沒有什么變化,我們也就不用耗盡心思的考慮去如何支持變化了,直接把函數(shù)硬編碼就可以了。Intel規(guī)定中斷描述符表的第17-31項(xiàng)保留,為硬件將來可能的擴(kuò)展用,因此我們這里將它閑置

60、起來。void reserved(void)sleep("reserved");下面的部分是對外部中斷的初始化,放在trap_init中是否有些名不正言不順呢?確實(shí)如此,這個(gè)版本暫時(shí)把它放在這里,以后重構(gòu)的時(shí)候再調(diào)整吧。注意,這個(gè)部分解釋了我們是如何把中斷服務(wù)程序放置到IDT中的。此外,可以看出,我們使用手工方式對中斷向量號進(jìn)行了映射,_irq0對應(yīng)32 號中斷,而_irq1對應(yīng)33號中斷。能不能映射成別的向量呢?當(dāng)然可以,可是別忘了修改setup.s中的pic_init部分,要知道,我們初始化 8259的時(shí)候定義好了外部中斷對應(yīng)的向量,如果你希望從8259發(fā)來的中斷信號能

61、正確的觸發(fā)相應(yīng)的中斷服務(wù)程序,當(dāng)然要把所有的接收處理鏈條上的每個(gè)映射關(guān)系都改過來。我們只填充了34個(gè)表項(xiàng),每個(gè)表項(xiàng)8字節(jié)長,因此我們把IDT表的長度上限設(shè)為34x8,把IDT表放置在邏輯地址起始的地方(如果我們沒有啟用分頁機(jī)制,那么就是在線性空間起始的地方,也就是物理地址的0位置處)。最后,調(diào)用ldtr指令啟用新的中斷處理機(jī)制,SagaLinux的初步中斷支持機(jī)制就完成了。下面,我們以定時(shí)器(timer)設(shè)備為例,展示如何通過SagaLinux目前提供的中斷服務(wù)程序接口來支持設(shè)備的中斷。IBM PC兼容機(jī)包含了一種時(shí)間測量設(shè)備,叫做可編程間隔定時(shí)器(PIT)。PIT的作用類似于鬧鐘,在設(shè)定的時(shí)

62、間點(diǎn)到來的時(shí)候發(fā)出中斷信號。這種中斷叫做定時(shí)中斷(timer interrupt)。在Linux操作系統(tǒng)中,就是它來通知內(nèi)核又一個(gè)時(shí)間片斷過去了。與鬧鐘不同,PIT以某一固定的頻率(編程控制)不停地發(fā)出中斷。每個(gè)IBM PC兼容機(jī)至少都會包含一個(gè)PIT,一般來說,它就是一個(gè)使用0x400x43 I/O端口的8254CMOS芯片。    SagaLinux目前的版本還不支持進(jìn)程調(diào)度,因此定時(shí)中斷的作用還不明顯,不過,作為一個(gè)做常見的中斷源,我們可以讓它每隔一定時(shí)間發(fā)送一個(gè)中斷信號,而我們在定時(shí)中斷的中斷服務(wù)程序中計(jì)算流逝過去的時(shí)間數(shù),然后打印出結(jié)果,充分體現(xiàn)中斷的效果。我們在

63、kernel目錄下編寫了timer.c文件,也在include目錄下加入了相應(yīng)的timer.h,下面就是具體的實(shí)現(xiàn)。/ 流逝的時(shí)間static volatile ulong_t counter;        / 中斷服務(wù)程序void timer_handler()  / 中斷每10毫秒一次  counter += 10;/ 初始化硬件和技術(shù)器,啟用中斷void timer_init()  ushort_t pit_counter = CLOCK_RATE * INTERVA

64、L / SECOND;  counter = 0;  outb (SEL_CNTR0|RW_LSB_MSB|MODE2|BINARY_STYLE, CONTROL_REG);  outb (pit_counter & 0xFF, COUNTER0_REG);  outb (pit_counter >> 8, COUNTER0_REG);  / 申請0號中斷,TIMER定義為0  request_irq(TIMER, timer_handler);/ 返回流

65、逝過去的時(shí)間ulong_t uptime()  return counter;timer_init函數(shù)是核心函數(shù),負(fù)責(zé)硬件的初始化和中斷的申請,對8254的初始化就不多做糾纏了,請查閱有關(guān)資料。我們可以看到,申請中斷確實(shí)跟預(yù)想中的一樣容易,調(diào)用request_irq,一行語句就完成了中斷的注冊。而中斷服務(wù)程序非常簡單,由于把8254設(shè)置為每10毫秒發(fā)送一次中斷,因此每次中斷到來時(shí)都在服務(wù)程序中對counter加10,所以counter表示的就是流逝的時(shí)間。在kernel.c中,我們調(diào)用timer_init進(jìn)行初始化,此時(shí)定時(shí)中斷就被激活了,如果我們的中斷機(jī)制運(yùn)轉(zhuǎn)順利,那么流

66、逝時(shí)間會不斷增加。為了顯示出這樣的結(jié)果,我們編寫一個(gè)循環(huán)不斷的調(diào)uptime函數(shù),并把返回的結(jié)果打印在屏幕上。如果打印出的數(shù)值越來越大,那就說明我們的中斷機(jī)制確確實(shí)實(shí)發(fā)揮了作用,定時(shí)中斷被驅(qū)動起來了。              在kernel.c中:              / 初始化              int i = 0;          

溫馨提示

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

評論

0/150

提交評論