中斷解析

http://blog.csdn.net/maochengtao/article/details/30713459
本文寫的很形象,很容易理解费坊。

一贫贝、中斷是什么

中斷的漢語(yǔ)解釋是半中間發(fā)生阻隔热幔、停頓或故障而斷開。那么众雷,在計(jì)算機(jī)系統(tǒng)中灸拍,我們?yōu)槭裁葱枰白韪簟⑼nD和斷開”呢砾省?

舉個(gè)日常生活中的例子鸡岗,比如說(shuō)我正在廚房用煤氣燒一壺水,這樣就只能守在廚房里编兄,苦苦等著水開——如果水溢出來(lái)澆滅了煤氣轩性,有可能就要發(fā)生一場(chǎng)災(zāi)難了。等啊等啊翻诉,外邊突然傳來(lái)了驚奇的叫聲“怎么不關(guān)水龍頭炮姨?”于是我慚愧的發(fā)現(xiàn)捌刮,剛才接水之后只顧著抱怨這份無(wú)聊的差事,居然忘了這事舒岸,于是慌慌張張的沖向水管绅作,三下兩下關(guān)了龍頭,聲音又傳到耳邊蛾派,“怎么干什么都是這么馬虎俄认?”。伸伸舌頭洪乍,這件小事就這么過(guò)去了眯杏,我落寞的眼神又落在了水壺上。

門外忽然又傳來(lái)了鏗鏘有力的歌聲壳澳,我最喜歡的古裝劇要開演了岂贩,真想奪門而出,然而巷波,聽著水壺發(fā)出“咕嘟咕嘟”的聲音萎津,我清楚:除非等到水開,否則沒(méi)有我享受人生的時(shí)候抹镊。

這個(gè)場(chǎng)景跟中斷有什么關(guān)系呢锉屈?

如果說(shuō)我專心致志等待水開是一個(gè)過(guò)程的話,那么叫聲垮耳、電視里傳出的音樂(lè)不都讓這個(gè)過(guò)程“半中間發(fā)生阻隔颈渊、停頓或故障而斷開”了嗎?這不就是活生生的“中斷”嗎终佛?

在這個(gè)場(chǎng)景中俊嗽,我是唯一具有處理能力的主體,不管是燒水查蓉、關(guān)水龍頭還是看電視乌询,同一個(gè)時(shí)間點(diǎn)上我只能干一件事情榜贴。但是豌研,在我專心致志干一件事情時(shí),總有許多或緊迫或不緊迫的事情突然出現(xiàn)在面前唬党,都需要去關(guān)注鹃共,有些還需要我停下手頭的工作馬上去處理。只有在處理完之后驶拱,方能回頭完成先前的任務(wù)霜浴,“把一壺水徹底燒開!”

中斷機(jī)制不僅賦予了我處理意外情況的能力蓝纲,如果我能充分發(fā)揮這個(gè)機(jī)制的妙用阴孟,就可以“同時(shí)”完成多個(gè)任務(wù)了晌纫。回到燒水的例子永丝,實(shí)際上锹漱,無(wú)論我在不在廚房,煤氣灶總是會(huì)把水燒開的慕嚷,我要做的哥牍,只不過(guò)是及時(shí)關(guān)掉煤氣灶而已,為了這么一個(gè)一秒鐘就能完成的動(dòng)作喝检,卻讓我死死地守候在廚房里嗅辣,在10分鐘的時(shí)間里不停地看壺嘴是不是冒蒸氣,怎么說(shuō)都不劃算挠说。我決定安下心來(lái)看電視澡谭。當(dāng)然,在有生之年损俭,我都不希望讓廚房成為火海译暂,于是我上了鬧鐘,10分鐘以后它會(huì)發(fā)出“尖叫”撩炊,提醒我爐子上的水燒開了外永,那時(shí)我再去關(guān)煤氣也完全來(lái)得及。我用一個(gè)中斷信號(hào)——鬧鈴——換來(lái)了10分鐘的歡樂(lè)時(shí)光拧咳,心里不禁由衷地感嘆:中斷機(jī)制真是個(gè)好東西伯顶。

正是由于中斷機(jī)制,我才能有條不紊地“同時(shí)”完成多個(gè)任務(wù)骆膝,中斷機(jī)制實(shí)質(zhì)上幫助我提高了并發(fā)“處理”能力祭衩。它也能給計(jì)算機(jī)系統(tǒng)帶來(lái)同樣的好處:如果在鍵盤按下的時(shí)候會(huì)得到一個(gè)中斷信號(hào),CPU就不必死守著等待鍵盤輸入了阅签;如果硬盤讀寫完成后發(fā)送一個(gè)中斷信號(hào)掐暮,CPU就可以騰出手來(lái)集中精力“服務(wù)大眾”了——無(wú)論是人類敲打鍵盤的指尖還是來(lái)回讀寫介質(zhì)的磁頭,跟CPU的處理速度相比政钟,都太慢了路克。沒(méi)有中斷機(jī)制,就像我們苦守廚房一樣养交,計(jì)算機(jī)談不上有什么并行處理能力精算。

跟人相似,CPU也一樣要面對(duì)紛繁蕪雜的局面——現(xiàn)實(shí)中的意外是無(wú)處不在的——有可能是用戶等得不耐煩碎连,猛敲鍵盤灰羽;有可能是運(yùn)算中碰到了0除數(shù);還有可能網(wǎng)卡突然接收到了一個(gè)新的數(shù)據(jù)包。這些都需要CPU具體情況具體分析廉嚼,要么馬上處理玫镐,要么暫緩響應(yīng),要么置之不理怠噪。無(wú)論如何應(yīng)對(duì)摘悴,都需要CPU暫停“手頭”的工作舰绘,拿出一種對(duì)策蹂喻,只有在響應(yīng)之后,方能回頭完成先前的使命捂寿,“把一壺水徹底燒開口四!”

先讓我們感受一下中斷機(jī)制對(duì)并發(fā)處理帶來(lái)的幫助。

讓我們用程序來(lái)探討一下燒水問(wèn)題秦陋,如果沒(méi)有“中斷”(注意蔓彩,我們這里只是模仿中斷的場(chǎng)景,實(shí)際上是用異步事件——消息——處理機(jī)制來(lái)展示中斷產(chǎn)生的效果驳概。畢竟赤嚼,在用戶空間沒(méi)有辦法與實(shí)際中斷產(chǎn)生直接聯(lián)系,不過(guò)操作系統(tǒng)為用戶空間提供的異步事件機(jī)制顺又,可以看作是模仿中斷的產(chǎn)物)更卒,設(shè)計(jì)如下:

void StayInKitchen()
{
    bool WaterIsBoiled = false;
    while ( WaterIsBoiled != true )
{
    bool VaporGavenOff  = false;
    if  (VaporGavenOff )
        WaterIsBoiled  = true;
    else
        WaterIsBoiled  = false;
}
// 關(guān)煤氣爐
printf(“Close gas oven.\n”);
// 一切安定下來(lái),終于可以看電視了稚照,10分鐘的寶貴時(shí)間啊蹂空,逝者如斯夫…
watching_tv();
return;
}

可以看出,整個(gè)流程如同我們前面描述的一樣果录,所有工作要順序執(zhí)行上枕,沒(méi)有辦法完成并發(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í)會(huì)執(zhí)行此程序
void sig_alarm(int signo)
{
       //關(guān)煤氣爐
       printf(“Close gas oven.\n”);
}

void watching_tv()
{
       while(1)
       {
              // 呵呵琉苇,悠哉悠哉
}
}

int main()
{
    // 點(diǎn)火后設(shè)置定時(shí)中斷
    printf(“Start to 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í)行嘲玫。第二段程序跟中斷也沒(méi)有太大的關(guān)系,實(shí)際上它只用了信號(hào)機(jī)制而已并扇。但是,通過(guò)這兩個(gè)程序的對(duì)比抡诞,我們可以清楚地看到異步事件的處理機(jī)制是如何提升并發(fā)處理能力的穷蛹。

Alarm定時(shí)器:alarm相當(dāng)于系統(tǒng)中的一個(gè)定時(shí)器土陪,如果我們調(diào)用alarm(5),那么5秒鐘后就會(huì)“響起一個(gè)鬧鈴”(實(shí)際上靠信號(hào)機(jī)制實(shí)現(xiàn)的肴熏,我們這里不想深入細(xì)節(jié)鬼雀,如果你對(duì)此很感興趣,請(qǐng)參考Richard Stevens不朽著作《Unix環(huán)境高級(jí)編程》)蛙吏。在鬧鈴響起的時(shí)候會(huì)發(fā)生什么呢?系統(tǒng)會(huì)執(zhí)行一個(gè)函數(shù)源哩,至于到底是什么函數(shù),系統(tǒng)允許程序自行決定鸦做。程序員編寫一個(gè)函數(shù)励烦,并調(diào)用signal對(duì)該函數(shù)進(jìn)行注冊(cè),這樣一旦定時(shí)到來(lái)泼诱,系統(tǒng)就會(huì)調(diào)用程序員提供的函數(shù)(CallBack函數(shù)坛掠?沒(méi)錯(cuò),不過(guò)在這里如何實(shí)現(xiàn)并不關(guān)鍵治筒,我們就不引入新的概念和細(xì)節(jié)了)屉栓。上面的例子里我們提供的函數(shù)是sig_alarm,所做的工作很簡(jiǎn)單耸袜,打印“關(guān)閉煤氣灶”消息友多。

上面的兩個(gè)例子很簡(jiǎn)單,但很能說(shuō)明問(wèn)題堤框,首先夷陋,它證明采用異步的消息處理機(jī)制可以提高系統(tǒng)的并發(fā)處理能力。更重要的是胰锌,它揭示了這種處理機(jī)制的模式骗绕。用戶根據(jù)需要設(shè)計(jì)處理程序,并可以將該程序和特定的外部事件綁定起來(lái)资昧,在外部事件發(fā)生時(shí)系統(tǒng)自動(dòng)調(diào)用處理程序酬土,完成相關(guān)工作。這種模式給系統(tǒng)帶來(lái)了統(tǒng)一的管理方法格带,也帶來(lái)無(wú)盡的功能擴(kuò)展空間撤缴。

計(jì)算機(jī)系統(tǒng)實(shí)現(xiàn)中斷機(jī)制是非常復(fù)雜的一件工作,再怎么說(shuō)人都是高度智能化的生物叽唱,而計(jì)算機(jī)作為一個(gè)鐵疙瘩屈呕,沒(méi)有程序的教導(dǎo)就一事無(wú)成。而處理一個(gè)中斷過(guò)程棺亭,它受到的限制和需要學(xué)習(xí)的東西太多了虎眨。

首先,計(jì)算機(jī)能夠接收的外部信號(hào)形式非常有限。中斷是由外部的輸入引起的嗽桩,可以說(shuō)是一種刺激岳守。在燒水的場(chǎng)景中,這些輸入是叫聲和電視的音樂(lè)碌冶,我們這里只以聲音為例湿痢。其實(shí)現(xiàn)實(shí)世界中能輸入人類CPU——大腦的信號(hào)很多,圖像扑庞、氣味一樣能被我們接受譬重,人的信息接口很完善。而計(jì)算機(jī)則不然罐氨,接受外部信號(hào)的途徑越多臀规,設(shè)計(jì)實(shí)現(xiàn)就越復(fù)雜,代價(jià)就越高岂昭。因此個(gè)人計(jì)算機(jī)(PC)給所有的外部刺激只留了一種輸入方式——特定格式的電信號(hào)以现,并對(duì)這種信號(hào)的格式、接入方法约啊、響應(yīng)方法邑遏、處理步驟都做了規(guī)約(具體內(nèi)容本文后面部分會(huì)繼續(xù)詳解),這種信號(hào)就是中斷或中斷信號(hào)恰矩,而這一整套機(jī)制就是中斷機(jī)制记盒。

其次,計(jì)算機(jī)不懂得如何應(yīng)對(duì)信號(hào)外傅。人類的大腦可以自行處理外部輸入纪吮,我從來(lái)不用去擔(dān)心鬧鐘響時(shí)會(huì)手足無(wú)措——走進(jìn)廚房關(guān)煤氣,這簡(jiǎn)直是天經(jīng)地義的事情萎胰,還用大腦想啊碾盟,小腿肚子都知道——可惜計(jì)算機(jī)不行,沒(méi)有程序技竟,它就紋絲不動(dòng)冰肴。因此,必須有機(jī)制保證外部中斷信號(hào)到來(lái)后榔组,有正確的程序在正確的時(shí)候被執(zhí)行熙尉。

還有,計(jì)算機(jī)不懂得如何保持工作的持續(xù)性搓扯。我在看電視的時(shí)候如果去廚房關(guān)了煤氣检痰,回來(lái)以后能繼續(xù)將電視進(jìn)行到底,不受太大的影響锨推。而計(jì)算機(jī)則不然铅歼,如果放下手頭的工作直接去處理“意外”的中斷公壤,那么它就再也沒(méi)有辦法想起來(lái)曾經(jīng)作過(guò)什么,做到什么程度了谭贪。自然也就沒(méi)有什么“重操舊業(yè)”的機(jī)會(huì)了境钟。這樣的處理方式就不是并發(fā)執(zhí)行锦担,而是東一榔頭俭识,西一棒槌了。

那么洞渔,通用的計(jì)算機(jī)系統(tǒng)是如何解決這些問(wèn)題的呢套媚?它是靠硬件和軟件配合來(lái)協(xié)同實(shí)現(xiàn)中斷處理的全過(guò)程的。我們將通過(guò)Intel X86架構(gòu)的實(shí)現(xiàn)來(lái)介紹這一過(guò)程磁椒。

CPU執(zhí)行完一條指令后堤瘤,下一條指令的邏輯地址存放在cs和eip這對(duì)寄存器中。在執(zhí)行新指令前浆熔,控制單元會(huì)檢查在執(zhí)行前一條指令的過(guò)程中是否有中斷或異常發(fā)生本辐。如果有,控制單元就會(huì)拋下指令医增,進(jìn)入下面的流程:

  1.   確定與中斷或異常關(guān)聯(lián)的向量i (0£i£255)
    
  2.   尋找向量對(duì)應(yīng)的處理程序
    
  3.   保存當(dāng)前的“工作現(xiàn)場(chǎng)”慎皱,執(zhí)行中斷或異常的處理程序
    
  4.   處理程序執(zhí)行完畢后,把控制權(quán)交還給控制單元
    
  5.   控制單元恢復(fù)現(xiàn)場(chǎng)叶骨,返回繼續(xù)執(zhí)行原程序
    

讓我們深入這個(gè)流程茫多,看看都有什么問(wèn)題需要面對(duì)。

1忽刽、異常是什么概念天揖?

在處理器執(zhí)行到由于編程失誤而導(dǎo)致的錯(cuò)誤指令(例如除數(shù)是0)的時(shí)候,或者在執(zhí)行期間出現(xiàn)特殊情況(例如缺頁(yè))跪帝,需要靠操作系統(tǒng)來(lái)處理的時(shí)候今膊,處理器就會(huì)產(chǎn)生一個(gè)異常。對(duì)大部分處理器體系結(jié)構(gòu)來(lái)說(shuō)伞剑,處理異常和處理中斷的方式基本是相同的斑唬,x86架構(gòu)的CPU也是如此。異常與中斷還是有些區(qū)別纸泄,異常的產(chǎn)生必須考慮與處理器時(shí)鐘的同步赖钞。實(shí)際上,異常往往被稱為同步中斷聘裁。

2雪营、中斷向量是什么?

中斷向量代表的是中斷源——從某種程度上講衡便,可以看作是中斷或異常的類型献起。中斷和異常的種類很多洋访,比如說(shuō)被0除是一種異常,缺頁(yè)又是一種異常谴餐,網(wǎng)卡會(huì)產(chǎn)生中斷姻政,聲卡也會(huì)產(chǎn)生中斷,CPU如何區(qū)分它們呢岂嗓?中斷向量的概念就是由此引出的汁展,其實(shí)它就是一個(gè)被送通往CPU數(shù)據(jù)線的一個(gè)整數(shù)。CPU給每個(gè)IRQ分配了一個(gè)類型號(hào)厌殉,通過(guò)這個(gè)整數(shù)CPU來(lái)識(shí)別不同類型的中斷食绿。這里可能很多朋友會(huì)尋問(wèn)為什么還要弄個(gè)中斷向量這么麻煩的東東?為什么不直接用IRQ0~IRQ15就完了公罕?比如就讓IRQ0為0器紧,IRQ1為1……,這不是要簡(jiǎn)單得多么楼眷?其實(shí)這里體現(xiàn)了模塊化設(shè)計(jì)規(guī)則铲汪,及節(jié)約規(guī)則。

首先我們先談?wù)劰?jié)約規(guī)則罐柳,所謂節(jié)約規(guī)則就是所使用的信號(hào)線數(shù)越少越好掌腰,這樣如果每個(gè)IRQ都獨(dú)立使用一根數(shù)據(jù)線,如IRQ0用0號(hào)線硝清,IRQ1用1號(hào)線……這樣辅斟,16個(gè)IRQ就會(huì)用16根線,這顯然是一種浪費(fèi)芦拿。那么也許馬上就有朋友會(huì)說(shuō):那么只用4根線不就行了嗎士飒?(2^4=16)。

這個(gè)問(wèn)題蔗崎,體現(xiàn)了模塊設(shè)計(jì)規(guī)則酵幕。我們?cè)谇懊婢驼f(shuō)過(guò)中斷有很多類,可能是外部硬件觸發(fā)缓苛,也可能是由軟件觸發(fā)芳撒,然而對(duì)于CPU來(lái)說(shuō)中斷就是中斷,只有一種未桥,CPU不用管它到底是由外部硬件觸發(fā)的還是由運(yùn)行的軟件本身觸發(fā)的笔刹,因?yàn)閷?duì)于CPU來(lái)說(shuō),中斷處理的過(guò)程都是一樣的:中斷現(xiàn)行程序冬耿,轉(zhuǎn)到中斷服務(wù)程序處執(zhí)行舌菜,回到被中斷的程序繼續(xù)執(zhí)行。CPU總共可以處理256種中斷亦镶,而并不知道日月,也不應(yīng)當(dāng)讓CPU知道這是硬件來(lái)的中斷還是軟件來(lái)的中斷袱瓮,這樣,就可以使CPU的設(shè)計(jì)獨(dú)立于中斷控制器的設(shè)計(jì)爱咬,這樣CPU所需完成的工作就很單純了尺借。CPU對(duì)于其它的模塊只提供了一種接口,這就是256個(gè)中斷處理向量精拟,也稱為中斷號(hào)燎斩。由這些中斷控制器自行去使用這256個(gè)中斷號(hào)中的一個(gè)與CPU進(jìn)行交互,比如串前,硬件中斷可以使用前128個(gè)號(hào)瘫里,軟件中斷使用后128個(gè)號(hào)实蔽,也可以軟件中斷使用前128個(gè)號(hào)荡碾,硬件中斷使用后128個(gè)號(hào),這與CPU完全無(wú)關(guān)了局装,當(dāng)你需要處理的時(shí)候坛吁,只需告訴CPU你用的是哪個(gè)中斷號(hào)就行,而不需告訴CPU你是來(lái)自哪兒的中斷铐尚。這樣也方便了以后的擴(kuò)充拨脉,比如現(xiàn)在機(jī)器里又加了一片8259芯片,那么這個(gè)芯片就可以使用空閑的中斷號(hào)宣增,看哪一個(gè)空閑就使用哪一個(gè)玫膀,而不是必須要使用第0號(hào),或第1號(hào)中斷號(hào)了爹脾。其實(shí)這相當(dāng)于一種映射機(jī)制帖旨,把IRQ信號(hào)映射到不同的中斷號(hào)上,IRQ的排列或說(shuō)編號(hào)是固定的灵妨,但通過(guò)改變映射機(jī)制解阅,就可以讓IRQ映射到不同的中斷號(hào),也可以說(shuō)調(diào)用不同的中斷服務(wù)程序泌霍。

3货抄、什么是中斷服務(wù)程序?

在響應(yīng)一個(gè)特定中斷的時(shí)候朱转,內(nèi)核會(huì)執(zhí)行一個(gè)函數(shù)蟹地,該函數(shù)叫做中斷處理程序(interrupt handler)或中斷服務(wù)程序(interrupt service routine(ISR))。產(chǎn)生中斷的每個(gè)設(shè)備都有相應(yīng)的中斷處理程序藤为。例如怪与,由一個(gè)函數(shù)專門處理來(lái)自系統(tǒng)時(shí)鐘的中斷,而另外一個(gè)函數(shù)專門處理由鍵盤產(chǎn)生的中斷凉蜂。

一般來(lái)說(shuō)琼梆,中斷服務(wù)程序要負(fù)責(zé)與硬件進(jìn)行交互性誉,告訴該設(shè)備中斷已被接收。此外茎杂,還需要完成其他相關(guān)工作错览。比如說(shuō)網(wǎng)絡(luò)設(shè)備的中斷服務(wù)程序除了要對(duì)硬件應(yīng)答,還要把來(lái)自硬件的網(wǎng)絡(luò)數(shù)據(jù)包拷貝到內(nèi)存煌往,對(duì)其進(jìn)行處理后再交給合適的協(xié)議椙悴福或應(yīng)用程序。每個(gè)中斷服務(wù)程序根據(jù)其要完成的任務(wù)刽脖,復(fù)雜程度各不相同羞海。

一般來(lái)說(shuō),一個(gè)設(shè)備的中斷服務(wù)程序是它的設(shè)備驅(qū)動(dòng)程序(device driver)的一部分——設(shè)備驅(qū)動(dòng)程序是用于對(duì)設(shè)備進(jìn)行管理的內(nèi)核代碼曲管。

4却邓、隔離變化

不知道您有沒(méi)有意識(shí)到,中斷處理前面這部分的設(shè)計(jì)是何等的簡(jiǎn)單優(yōu)美院水。人是高度智能化的腊徙,能夠?qū)τ龅降母鞣N意外情況做有針對(duì)性的處理,計(jì)算機(jī)相比就差距甚遠(yuǎn)了檬某,它只能根據(jù)預(yù)定的程序進(jìn)行操作撬腾。對(duì)于計(jì)算機(jī)來(lái)說(shuō),硬件支持的恢恼,只能是中斷這種電信號(hào)傳播的方式和CPU對(duì)這種信號(hào)的接收方法民傻,而具體如何處理這個(gè)中斷,必須得靠操作系統(tǒng)實(shí)現(xiàn)场斑。操作系統(tǒng)支持所有事先能夠預(yù)料到的中斷信號(hào)漓踢,理論上都不存在太大的挑戰(zhàn),但在操作系統(tǒng)安裝到計(jì)算機(jī)設(shè)備上以后和簸,肯定會(huì)時(shí)常有新的外圍設(shè)備被加入系統(tǒng)彭雾,這可能會(huì)帶來(lái)安裝系統(tǒng)時(shí)根本無(wú)法預(yù)料的“意外”中斷。如何支持這種擴(kuò)展锁保,是整個(gè)系統(tǒng)必須面對(duì)的薯酝。

而硬件和軟件在這里的協(xié)作,給我們帶來(lái)了完美的答案爽柒。當(dāng)新的設(shè)備引入新類型的中斷時(shí)吴菠,CPU和操作系統(tǒng)不用關(guān)注如何處理它。CPU只負(fù)責(zé)接收中斷信號(hào)浩村,并引用中斷服務(wù)程序做葵;而操作系統(tǒng)提供默認(rèn)的中斷服務(wù)——一般來(lái)說(shuō)就是不理會(huì)這個(gè)信號(hào),返回就可以了——并負(fù)責(zé)提供接口心墅,讓用戶通過(guò)該接口注冊(cè)根據(jù)設(shè)備具體功能而編制的中斷服務(wù)程序酿矢。如果用戶注冊(cè)了對(duì)應(yīng)于一個(gè)中斷的服務(wù)程序榨乎,那么CPU就會(huì)在該中斷到來(lái)時(shí)調(diào)用用戶注冊(cè)的服務(wù)程序。這樣瘫筐,在中斷來(lái)臨時(shí)系統(tǒng)需要如何操作硬件蜜暑、如何實(shí)現(xiàn)硬件功能這部分工作就完全獨(dú)立于CPU架構(gòu)和操作系統(tǒng)的設(shè)計(jì)了。

而當(dāng)你需要加入新設(shè)備的時(shí)候策肝,只需要告訴操作系統(tǒng)該設(shè)備占用的中斷號(hào)肛捍、按照操作系統(tǒng)要求的接口格式撰寫中斷服務(wù)程序,用操作系統(tǒng)提供的函數(shù)注冊(cè)該服務(wù)程序之众,設(shè)備的中斷就被系統(tǒng)支持了拙毫。

中斷和對(duì)中斷的處理被解除了耦合。這樣棺禾,無(wú)論是你在需要加入新的中斷時(shí)缀蹄,還是在你需要改變現(xiàn)有中斷的服務(wù)程序時(shí)、又或是取消對(duì)某個(gè)中斷支持的時(shí)候帘睦,CPU架構(gòu)和操作系統(tǒng)都無(wú)需作改變袍患。

5、保存當(dāng)前工作“現(xiàn)場(chǎng)”

在中斷處理完畢后竣付,計(jì)算機(jī)一般來(lái)說(shuō)還要回頭處理原先手頭正做的工作。這給中斷的概念帶來(lái)些額外的“內(nèi)涵”滞欠。注一“回頭”不是指從頭再來(lái)重新做古胆,而是要接著剛才的進(jìn)度繼續(xù)做。這就需要在處理中斷信號(hào)之前保留工作“現(xiàn)場(chǎng)”筛璧∫菀铮“現(xiàn)場(chǎng)”這個(gè)詞比較晦澀,其實(shí)就是指一個(gè)信息集夭谤,它能反映某個(gè)時(shí)間點(diǎn)上任務(wù)的狀態(tài)棺牧,并能保證按照這些信息就能恢復(fù)任務(wù)到該狀態(tài),繼續(xù)執(zhí)行下去朗儒。再直白一點(diǎn)颊乘,現(xiàn)場(chǎng)不過(guò)就是一組寄存器值。而如何保護(hù)現(xiàn)場(chǎng)和恢復(fù)場(chǎng)景是中斷機(jī)制需要考慮的重點(diǎn)之一醉锄。

每個(gè)中斷處理都要經(jīng)歷這個(gè)保存和恢復(fù)過(guò)程乏悄,我們可以抽象出其中的步驟:

  1.     保存現(xiàn)場(chǎng)
    
  2.     執(zhí)行具體的中斷服務(wù)程序
    
  3.     從中斷服務(wù)返回
    
  4.     恢復(fù)現(xiàn)場(chǎng)
    

上面說(shuō)過(guò)了,“現(xiàn)場(chǎng)”看似在不斷變化恳不,沒(méi)有哪個(gè)瞬間相同檩小。但實(shí)際上組成現(xiàn)場(chǎng)的要素卻不會(huì)有任何改變。也就是說(shuō)烟勋,只要我們保存了相關(guān)的寄存器狀態(tài)规求,現(xiàn)場(chǎng)就能保存下來(lái)筐付。而恢復(fù)“現(xiàn)場(chǎng)”就是重新載入這些寄存器。換句話說(shuō)阻肿,對(duì)于任何一個(gè)中斷家妆,保護(hù)現(xiàn)場(chǎng)和恢復(fù)現(xiàn)場(chǎng)所做的都是完全相同的操作。

既然操作相同冕茅,實(shí)現(xiàn)操作的過(guò)程和代碼就相同伤极。減少代碼的冗余是模塊化設(shè)計(jì)的基本準(zhǔn)則,實(shí)在沒(méi)有道理讓所有的中斷服務(wù)程序都重復(fù)實(shí)現(xiàn)這樣的功能姨伤,應(yīng)該將它作為一種基本的結(jié)構(gòu)由底層的操作系統(tǒng)或硬件完成哨坪。而對(duì)中斷的處理過(guò)程需要迅速完成,因此乍楚,Intel CPU的控制器就承擔(dān)了這個(gè)任務(wù)当编,非但如此,上面的所有步驟次序都被固化下來(lái)徒溪,由控制器驅(qū)動(dòng)完成忿偷。保存現(xiàn)場(chǎng)和恢復(fù)現(xiàn)場(chǎng)都由硬件自動(dòng)完成,大大減輕了操作系統(tǒng)和設(shè)備驅(qū)動(dòng)程序的負(fù)擔(dān)臊泌。

6鲤桥、硬件對(duì)中斷支持的細(xì)節(jié)

下面的部分,本來(lái)應(yīng)該介紹8259渠概、中斷控制器編程茶凳、中斷描述符表等內(nèi)容,可是我看到了瀟寒寫的“保護(hù)模式下的8259A芯片編程及中斷處理探究”播揪,前人之述備矣贮喧,讀者直接讀它好了。

二猪狈、從外而內(nèi)箱沦,Linux對(duì)中斷的支持

在Linux中,中斷處理程序看起來(lái)就是普普通通的C函數(shù)雇庙。只不過(guò)這些函數(shù)必須按照特定的類型聲明谓形,以便內(nèi)核能夠以標(biāo)準(zhǔn)的方式傳遞處理程序的信息,在其他方面状共,它們與一般的函數(shù)看起來(lái)別無(wú)二致套耕。中斷處理程序與其它內(nèi)核函數(shù)的真正區(qū)別在于,中斷處理程序是被內(nèi)核調(diào)用來(lái)響應(yīng)中斷的峡继,而它們運(yùn)行于我們稱之為中斷上下文的特殊上下文中冯袍。關(guān)于中斷上下文,我們將在后面討論。

中斷可能隨時(shí)發(fā)生康愤,因此中斷處理程序也就隨時(shí)可能執(zhí)行儡循。所以必須保證中斷處理程序能夠快速執(zhí)行,這樣才能保證盡可能快地恢復(fù)被中斷代碼的執(zhí)行征冷。因此择膝,盡管對(duì)硬件而言,迅速對(duì)其中斷進(jìn)行服務(wù)非常重要检激。但對(duì)系統(tǒng)的其它部分而言肴捉,讓中斷處理程序在盡可能短的時(shí)間內(nèi)完成執(zhí)行也同樣重要。

即使最精簡(jiǎn)版的中斷服務(wù)程序叔收,它也要與硬件進(jìn)行交互齿穗,告訴該設(shè)備中斷已被接收。但通常我們不能像這樣給中斷服務(wù)程序隨意減負(fù)饺律,相反窃页,我們要靠它完成大量的其它工作。作為一個(gè)例子复濒,我們可以考慮一下網(wǎng)絡(luò)設(shè)備的中斷處理程序面臨的挑戰(zhàn)脖卖。該處理程序除了要對(duì)硬件應(yīng)答,還要把來(lái)自硬件的網(wǎng)絡(luò)數(shù)據(jù)包拷貝到內(nèi)存巧颈,對(duì)其進(jìn)行處理后再交給合適的協(xié)議椘枘荆或應(yīng)用程序。顯而易見洛二,這種運(yùn)動(dòng)量不會(huì)太小馋劈。

現(xiàn)在我們來(lái)分析一下Linux操作系統(tǒng)為了支持中斷機(jī)制,具體都需要做些什么工作晾嘶。

首先,操作系統(tǒng)必須保證新的中斷能夠被支持娶吞。計(jì)算機(jī)系統(tǒng)硬件留給外設(shè)的是一個(gè)統(tǒng)一的中斷信號(hào)接口垒迂。它固化了中斷信號(hào)的接入和傳遞方法监右,拿PC機(jī)來(lái)說(shuō)便斥,中斷機(jī)制是靠?jī)蓧K8259和CPU協(xié)作實(shí)現(xiàn)的肋殴。外設(shè)要做的只是把中斷信號(hào)發(fā)送到8259的某個(gè)特定引腳上暮芭,這樣8259就會(huì)為此中斷分配一個(gè)標(biāo)識(shí)——也就是通常所說(shuō)的中斷向量胞谈,通過(guò)中斷向量纽门,CPU就能夠在以中斷向量為索引的表——中斷向量表——里找到中斷服務(wù)程序没炒,由它決定具體如何處理中斷浓领。(具體細(xì)節(jié)還請(qǐng)查閱參考資料1陶耍,對(duì)于為何采用這種機(jī)制奋蔚,該資料有精彩描述)這是硬件規(guī)定的機(jī)制,軟件只能無(wú)條件服從。

因此泊碑,操作系統(tǒng)對(duì)新中斷的支持坤按,說(shuō)簡(jiǎn)單點(diǎn),就是維護(hù)中斷向量表馒过。新的外圍設(shè)備加入系統(tǒng)臭脓,首先得明確自己的中斷向量號(hào)是多少,還得提供自身中斷的服務(wù)程序腹忽,然后利用Linux的內(nèi)核調(diào)用界面来累,把〈中斷向量號(hào)、中斷服務(wù)程序〉這對(duì)信息填寫到中斷向量表中去窘奏。這樣CPU在接收到中斷信號(hào)時(shí)就會(huì)自動(dòng)調(diào)用中斷服務(wù)程序了嘹锁。這種注冊(cè)操作一般是由設(shè)備驅(qū)動(dòng)程序完成的。

其次蔼夜,操作系統(tǒng)必須提供給程序員簡(jiǎn)單可靠的編程界面來(lái)支持中斷兼耀。中斷的基本流程前面已經(jīng)講了,它會(huì)打斷當(dāng)前正在進(jìn)行的工作去執(zhí)行中斷服務(wù)程序求冷,然后再回到先前的任務(wù)繼續(xù)執(zhí)行瘤运。這中間有大量需要解決問(wèn)題:如何保護(hù)現(xiàn)場(chǎng)、嵌套中斷如何處理等等匠题,操作系統(tǒng)要一一化解拯坟。程序員,即使是驅(qū)動(dòng)程序的開發(fā)人員韭山,在寫中斷服務(wù)程序的時(shí)候也很少需要對(duì)被打斷的進(jìn)程心存憐憫郁季。(當(dāng)然,出于提高系統(tǒng)效率的考慮钱磅,編寫驅(qū)動(dòng)程序要比編寫用戶級(jí)程序多一些條條框框梦裂,誰(shuí)讓我們頂著系統(tǒng)程序員的光環(huán)呢?)

操作系統(tǒng)為我們屏蔽了這些與中斷相關(guān)硬件機(jī)制打交道的細(xì)節(jié)盖淡,提供了一套精簡(jiǎn)的接口年柠,讓我們用極為簡(jiǎn)單的方式實(shí)現(xiàn)對(duì)實(shí)際中斷的支持,Linux是怎么完美的做到這一點(diǎn)的呢褪迟?

CPU對(duì)中斷處理的流程:

我們首先必須了解CPU在接收到中斷信號(hào)時(shí)會(huì)做什么冗恨。沒(méi)辦法,操作系統(tǒng)必須了解硬件的機(jī)制味赃,不配合硬件就寸步難行∠颇ǎ現(xiàn)在我們假定內(nèi)核已被初始化,CPU在保護(hù)模式下運(yùn)行心俗。

CPU執(zhí)行完一條指令后傲武,下一條指令的邏輯地址存放在cs和eip這對(duì)寄存器中。在執(zhí)行新指令前,控制單元會(huì)檢查在執(zhí)行前一條指令的過(guò)程中是否有中斷或異常發(fā)生谱轨。如果有戒幔,控制單元就會(huì)拋下指令,進(jìn)入下面的流程:

  1. 確定與中斷或異常關(guān)聯(lián)的向量i (0£i£255)土童。
  2. 籍由idtr寄存器從IDT表中讀取第i項(xiàng)(在下面的描述中诗茎,我們假定該IDT表項(xiàng)中包含的是一個(gè)中斷門或一個(gè)陷阱門)。
  3. 從gdtr寄存器獲得GDT的基地址献汗,并在GDT表中查找敢订,以讀取IDT表項(xiàng)中的選擇符所標(biāo)識(shí)的段描述符。這個(gè)描述符指定中斷或異常處理程序所在段的基地址罢吃。
  4. 確信中斷是由授權(quán)的(中斷)發(fā)生源發(fā)出的楚午。首先將當(dāng)前特權(quán)級(jí)CPL(存放在cs寄存器的低兩位)與段描述符(即DPL,存放在GDT中)的描述符特權(quán)級(jí)比較尿招,如果CPL小于DPL矾柜,就產(chǎn)生一個(gè)“通用保護(hù)”異常,因?yàn)橹袛嗵幚沓绦虻奶貦?quán)不能低于引起中斷的程序的特權(quán)就谜。對(duì)于編程異常怪蔑,則做進(jìn)一步的安全檢查:比較CPL與處于IDT中的門描述符的DPL,如果DPL小于CPL丧荐,就產(chǎn)生一個(gè)“通用保護(hù)”異常缆瓣。這最后一個(gè)檢查可以避免用戶應(yīng)用程序訪問(wèn)特殊的陷阱門或中斷門。
  5. 檢查是否發(fā)生了特權(quán)級(jí)的變化虹统,也就是說(shuō)弓坞, CPL是否不同于所選擇的段描述符的DPL。如果是车荔,控制單元必須開始使用與新的特權(quán)級(jí)相關(guān)的棧渡冻。通過(guò)執(zhí)行以下步驟來(lái)做到這點(diǎn):
    a.讀tr寄存器,以訪問(wèn)運(yùn)行進(jìn)程的TSS段忧便。
    b.用與新特權(quán)級(jí)相關(guān)的棧段和棧指針的正確值裝載ss和esp寄存器菩帝。這些值可以在TSS中找到(參見第三章的“任務(wù)狀態(tài)段”一節(jié))。
    c.在新的棧中保存ss和esp以前的值茬腿,這些值定義了與舊特權(quán)級(jí)相關(guān)的棧的邏輯地址。
  6. 如果故障已發(fā)生宜雀,用引起異常的指令地址裝載cs和eip寄存器切平,從而使得這條指令能再次被執(zhí)行。
  7. 在棧中保存eflag辐董、cs及eip的內(nèi)容悴品。
  8. 如果異常產(chǎn)生了一個(gè)硬錯(cuò)誤碼,則將它保存在棧中。
  9. 裝載cs和eip寄存器苔严,其值分別是IDT表中第i項(xiàng)門描述符的段選擇符和偏移量域定枷。這些值給出了中斷或者異常處理程序的第一條指令的邏輯地址。

控制單元所執(zhí)行的最后一步就是跳轉(zhuǎn)到中斷或者異常處理程序届氢。換句話說(shuō)欠窒,處理完中斷信號(hào)后,控制單元所執(zhí)行的指令就是被選中的處理程序的第一條指令退子。

中斷或異常被處理完后岖妄,相應(yīng)的處理程序必須產(chǎn)生一條iret指令,把控制權(quán)轉(zhuǎn)交給被中斷的進(jìn)程寂祥,這將迫使控制單元:

  1. 用保存在棧中的值裝載cs荐虐、eip、或eflag寄存器丸凭。如果一個(gè)硬錯(cuò)誤碼曾被壓入棧中福扬,并且在eip內(nèi)容的上面,那么惜犀,執(zhí)行iret指令前必須先彈出這個(gè)硬錯(cuò)誤碼铛碑。
  2. 檢查處理程序的CPL是否等于cs中最低兩位的值(這意味著被中斷的進(jìn)程與處理程序運(yùn)行在同一特權(quán)級(jí))。如果是向拆,iret終止執(zhí)行亚茬;否則,轉(zhuǎn)入下一步浓恳。
  3. 從棧中裝載ss和esp寄存器刹缝,因此,返回到與舊特權(quán)級(jí)相關(guān)的棧颈将。
  4. 檢查ds梢夯、es、fs及gs段寄存器的內(nèi)容晴圾,如果其中一個(gè)寄存器包含的選擇符是一個(gè)段描述符颂砸,并且其DPL值小于CPL,那么死姚,清相應(yīng)的段寄存器人乓。控制單元這么做是為了禁止用戶態(tài)的程序(CPL=3)利用內(nèi)核以前所用的段寄存器(DPL=0)都毒。如果不清這些寄存器色罚,懷有惡意的用戶程序就可能利用它們來(lái)訪問(wèn)內(nèi)核地址空間。
    再次账劲,操作系統(tǒng)必須保證中斷信息能夠高效可靠的傳遞

注一:那么PowerOff(關(guān)機(jī))算不算中斷呢戳护?如果從字面上講金抡,肯定符合漢語(yǔ)對(duì)中斷的定義,但是從信號(hào)格式腌且、處理方法等方面來(lái)看梗肝,就很難符合我們的理解了。Intel怎么說(shuō)的呢铺董?該中斷沒(méi)有采用通用的中斷處理機(jī)制巫击。那么到底是不是中斷呢?我也說(shuō)不上來(lái):(
注二:更詳細(xì)的內(nèi)容和其它一些注意事項(xiàng)請(qǐng)參考內(nèi)核源代碼包中Documentations/rtc.txt
注三:之所以這里使用匯編而不是C來(lái)實(shí)現(xiàn)這些函數(shù)柄粹,是因?yàn)镃編譯器會(huì)在函數(shù)的實(shí)現(xiàn)中推入額外的棧信息喘鸟。而CPU在中斷來(lái)臨時(shí)保存和恢復(fù)現(xiàn)場(chǎng)都按照嚴(yán)格的格式進(jìn)行,一個(gè)字節(jié)的變化都不能有驻右。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末什黑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子堪夭,更是在濱河造成了極大的恐慌愕把,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件森爽,死亡現(xiàn)場(chǎng)離奇詭異恨豁,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)爬迟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門橘蜜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人付呕,你說(shuō)我怎么就攤上這事计福。” “怎么了徽职?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵象颖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我姆钉,道長(zhǎng)说订,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任潮瓶,我火速辦了婚禮陶冷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘毯辅。我一直安慰自己埃叭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布悉罕。 她就那樣靜靜地躺著赤屋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪壁袄。 梳的紋絲不亂的頭發(fā)上类早,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音嗜逻,去河邊找鬼涩僻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛栈顷,可吹牛的內(nèi)容都是我干的逆日。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼萄凤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼室抽!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起靡努,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤坪圾,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后惑朦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兽泄,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年漾月,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了病梢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梁肿,死狀恐怖蜓陌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情栈雳,我是刑警寧澤护奈,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站哥纫,受9級(jí)特大地震影響霉旗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛀骇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一厌秒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧擅憔,春花似錦鸵闪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辟灰。三九已至,卻和暖如春篡石,著一層夾襖步出監(jiān)牢的瞬間芥喇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工凰萨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留继控,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓胖眷,卻偏偏與公主長(zhǎng)得像武通,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子珊搀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 8086匯編 本筆記是筆者觀看小甲魚老師(魚C論壇)《零基礎(chǔ)入門學(xué)習(xí)匯編語(yǔ)言》系列視頻的筆記憔儿,在此感謝他和像他一樣...
    Gibbs基閱讀 37,219評(píng)論 8 114
  • 1 中斷介紹 1.1 簡(jiǎn)介 中斷控制是計(jì)算機(jī)發(fā)展中一種重要的技術(shù)忆植。最初它是為克服對(duì)I/O接口控制采用程序查詢所帶來(lái)...
    瘋狂小王子閱讀 8,081評(píng)論 0 9
  • 一,什么是中斷谒臼? 1.中斷的基本概念 中斷是指計(jì)算機(jī)在執(zhí)行期間朝刊,系統(tǒng)內(nèi)發(fā)生任何非尋常的或非預(yù)期的急需處理事件,使得...
    小東門兒閱讀 958評(píng)論 1 2
  • 我發(fā)現(xiàn)我的腦電波被竊聽了蜈缤,有人知道我腦子里都在想什么拾氓,有人要竊取它,用來(lái)?yè)Q取情報(bào)底哥。我把鐵鍋套到頭上咙鞍,每次出門的時(shí)候...
    租了五顆星閱讀 234評(píng)論 0 4
  • 幾年前。 第一次在山上寺廟閉關(guān)時(shí)趾徽,寺廟在后山正修建女眾閉關(guān)院续滋。 承包蓋房子的人有兩頭騾子,其實(shí)我也不太確定孵奶,有...
    薛濤濤閱讀 1,180評(píng)論 0 0