linux中斷機制和時鐘_第1頁
linux中斷機制和時鐘_第2頁
linux中斷機制和時鐘_第3頁
linux中斷機制和時鐘_第4頁
linux中斷機制和時鐘_第5頁
已閱讀5頁,還剩69頁未讀 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

Lecture8

LinuxInterruptandtimer中斷機制中斷基本知識異常處理外部中斷處理中斷的后半部分處理機制一、中斷基本知識1.中斷向量Intelx86系列微機共支持256種向量中斷,按0~255編號,即賦予一個中斷類型碼,linux中稱其為中斷向量。所有的256種中斷可分為中斷和異常兩大類。異常又分為故障和陷阱,它們的共同特點是既不使用中斷控制器,又不能被屏蔽。中斷又分為外部可屏蔽中斷和外部非屏蔽中斷。所有的I/O設(shè)備產(chǎn)生的中斷請求均引起可屏蔽中斷,而緊急事件引起的故障產(chǎn)生非屏蔽中斷。非屏蔽中斷的向量和異常的向量是固定的,而可屏蔽中斷的向量可以通過對中斷控制器的編程來實現(xiàn)。從0~31的向量對應(yīng)于異常和非屏蔽中斷從32~47的向量分配給屏蔽中斷從48~255的向量用來標志軟中斷(由intn指令產(chǎn)生),

linux只用了一個:128(0x80)向量異常和非屏蔽中斷0 除法出錯 SIGFPE 故障 被0除1 調(diào)試 SIGTRAP 故障/陷阱 對程序進行逐步調(diào)試2 非屏蔽中斷(NMI)3 斷點 SIGTRAP 陷阱 由Int3斷點指令引起4 溢出 SIGSEGV 陷阱 當into(checkforoverflow)指令被執(zhí)行5 邊界檢查 SIGSEGV 故障 當bound指令被執(zhí)行6 非法操作碼 SIGILL 故障 當CPU檢查到一個無效操作碼7 設(shè)備不可用 SIGSEGV 故障 隨著CR0的TS設(shè)置,ESCAPE或MMX指令被執(zhí)行8 雙重故障 SIGSEGV 故障 處理器不能串行處理異常引起9 協(xié)處理器段越界 SIGFPE 故障 因外部的數(shù)學協(xié)處理器引起的問題(僅用在80386)10 無效TSS SIGSEGV 故障 要切換到的進程具有無效的TSS11 段不存在 SIGBUS 故障 引用一個不存在的內(nèi)存段12 棧段異常 SIGSEGV 故障 試圖超越棧段界限,或由ss標志的段不在內(nèi)存13 通用保護 SIGSEGV 故障 違反了Intelx86保護模式下的一個保護規(guī)則14 頁異常 SIGSEGV 故障 尋址的頁不在內(nèi)存,或違反了一種分頁保護機制15 Intel保留16 浮點出錯 SIGFPE 故障 浮點單元用信號通知一個錯誤情況,如溢出17 對齊檢查 SIGSEGV 故障 操作數(shù)的地址沒有被正確地排列18~31Intel保留2.外設(shè)可屏蔽中斷Intelx86通過兩片中斷控制器8259A來響應(yīng)15個外部中斷源,每個8259A可管理8個中斷源。并非每個設(shè)備都可以向中斷線(與中斷控制器相連的每條線)上發(fā)中斷信號,只有擁有了某條中斷線的控制權(quán),才可以向這條中斷線上發(fā)送信號。中斷線是非常寶貴的資源,必須采取只有當設(shè)備需要中斷的時候才申請占用一個IRQ,或者是在申請IRQ時采用共享中斷的方式。外設(shè)可屏蔽中斷

32 IRQ0 時鐘

33 IRQ1 鍵盤

35 IRQ3 tty2 36 IRQ4 tty1 37 IRQ5 XTWinchester 38 IRQ6 軟驅(qū)

39 IRQ7 打印機

40 IRQ8 實時時鐘

41 IRQ9 重定向的IRQ2 45 IRQ13 FPU異常

46 IRQ14 ATWinchester8259A執(zhí)行的操作監(jiān)視中斷線,檢查產(chǎn)生的IRQ信號如果在中斷線上產(chǎn)生了一個IRQ信號把接收的IRQ信號轉(zhuǎn)換成一個對應(yīng)的向量。(IRQn的缺省向量是n+32)把這個向量存放在8259A的一個I/O端口,從而允許CPU通過數(shù)據(jù)總線讀此向量。把產(chǎn)生的信號發(fā)送到CPU的INTR引腳——發(fā)出一個中斷。等待,直到CPU確認這個中斷信號,然后把它寫進PIC的一個I/O端口,此時清INTR線返回第一步8259A對外部I/O請求的屏蔽可分為兩種情況:

a清除EFLAGS的IF位,禁止所有外部的I/O中斷請求

b中斷控制器中有一個8位的中斷屏蔽寄存器,每位對應(yīng)于8259A中的一條中斷線,如果要禁用某條中斷線,把IRM相應(yīng)位置1中斷路由的邏輯圖3.中斷描述符表IDTIntel系統(tǒng)利用中斷描述符表將中斷向量和其處理程序?qū)?yīng)起來。IDT是一個描述符數(shù)組,每一個元素由8個字節(jié)組成,叫做門描述符。每個中斷向量對應(yīng)一個門描述符。中斷描述符表

DLP 段描述符的特權(quán)級 偏移量 入口函數(shù)地址的偏移量

P 段是否在內(nèi)存中的標志 段選擇符 入口函數(shù)所處代碼段的選擇符

D 標志位,1=32位,0=16位

XXX 三位門類型碼

偏移量31~16|P|DPL|0DXXX|000|?????

段選擇符|

偏移量15~0任務(wù)門類型碼為101,門中包含一個進程的TSS段選擇符,不使用偏移量,故不包含一個函數(shù)的入口地址,Linux中一般不用任務(wù)門進行任務(wù)切換。中斷門

類型碼為110,控制權(quán)通過中斷門進入處理程序時關(guān)中斷。中斷門中的DPL為0,所有的中斷處理程序都由中斷門激活,并全部限制在內(nèi)核態(tài)。中斷時清IF標志,關(guān)中斷以防止中斷嵌套。陷阱門類型碼為111,控制權(quán)通過陷阱門進入處理程序時不關(guān)中斷,即IF標志位不變。系統(tǒng)門是Linux內(nèi)核特別設(shè)置的,用來讓用戶態(tài)的進程訪問Intel的陷阱門,DPL為3。通過系統(tǒng)門激活3,4,5,0x80中斷。4.IDT的初始化Linux內(nèi)核在系統(tǒng)的初始化階段進行大量的初始化操作初始化可編程控制器8259A8259A內(nèi)部有4個中斷命令字寄存器,8259A初始化的目的是為了寫入有關(guān)命令字,并利用8259A內(nèi)部的相應(yīng)寄存器鎖存這些命令字,以控制8259A的工作。對IDTR進行初始化把IDT表的起始地址裝入IDTR用setup_idt()函數(shù)填充IDT表中的256個表項在對IDT表進行了預(yù)初始化后,內(nèi)核將在啟用分頁功能后對IDT表進行第二遍的初始化,用實際的陷阱和中斷處理程序替換空的處理程序。注意:在Intel體系結(jié)構(gòu)中提供了四個特權(quán)級,編號從0到3,其中0是最高特權(quán)級,3級是最低特權(quán)級。系統(tǒng)中每個段都有自己的特權(quán)級,系統(tǒng)中每個任務(wù)也都有自己的特權(quán)級。特權(quán)級低的任務(wù)不能直接調(diào)用高特權(quán)級的子程序,也不能存取高特權(quán)級的數(shù)據(jù)段。DPL——描述符的特權(quán)級CPL——當前任務(wù)的特權(quán)級注意:CPU為保護模式增設(shè)了一個IDTR,用來存放中斷描述符表在內(nèi)存的起始地址。

中斷處理與當前進程有關(guān)。中斷處理程序運行在當前進程的地址空間,使用的是當前進程的系統(tǒng)堆棧。32位基地址16位界限4716150中斷描述符表寄存器IDTR5.中斷和異常的硬件處理要求在一個中斷和異常發(fā)生后,cpu將做以下事情:確定所發(fā)生的異?;蛑袛嘞蛄縤通過IDTR寄存器找到IDT表,讀取IDT表第i項該中斷處理程序所在段的DPL必須小于或等于當前代碼段的CPL對于軟中斷,要求CPL<=中斷或陷阱門的DPL硬件中斷不檢查特權(quán)級檢查是否發(fā)生了特權(quán)級的變化,即是否需要切換堆棧發(fā)生中斷時系統(tǒng)運行在核心態(tài)發(fā)生中斷時系統(tǒng)運行在用戶態(tài)二、異常

異常是cpu內(nèi)部出現(xiàn)的中斷,即在cpu在執(zhí)行特定指令時出現(xiàn)的非法情況。非屏蔽中斷是在計算機內(nèi)部硬件出錯時引起的異常情況,intel把非屏蔽中斷作為一種異常來處理。在CPU處理一個異常處理程序時,不再為其他異?;蚩善帘沃袛嗾埱蠓?wù)。Linux異常中斷向量在0~31之間,目前發(fā)布了大約20種。內(nèi)核對異常處理程序的調(diào)用分成三個部分:在內(nèi)核棧中保存寄存器的值先是順序壓入各寄存器的值,并調(diào)整棧頂滿足pt_regs結(jié)構(gòu)。(要注意當異常發(fā)生時,如果控制單元沒有自動地把一個硬件錯誤代碼插入到棧中。系統(tǒng)會自動執(zhí)行一條push$0指令,在棧中墊入一個空值。如果錯誤代碼已經(jīng)壓入堆棧,則接下來把異常處理函數(shù)的地址壓入堆棧。)最后壓入指向pt_regs的指針和錯誤代碼。用call指令調(diào)用異常處理函數(shù)(使用當前棧頂?shù)膬蓚€參數(shù))通過ret_from_exception從異常退出,并進行異常善后處理。有無激活的底半處理,有則轉(zhuǎn)入底半處理。被中斷進程運行在核心態(tài),簡單彈出棧頂各寄存器的值,恢復(fù)被中斷的進程。被中斷進程運行在用戶態(tài),如當前進程需要調(diào)度,則運行調(diào)度函數(shù),重新調(diào)度進程。如當前進程不需要調(diào)度,但如果當前進程上有待處理信號,則先處理信號再彈出寄存器值,并返回被中斷進程。如果沒有待處理信號則彈出寄存器值,返回被中斷進程。

內(nèi)核堆棧指針ESP用戶棧的SS用戶棧的ESPEFLAGS用戶空間的CSEIP錯誤碼或0函數(shù)地址DSEAXEBPEDIESIEDXECXEBX進入異常處理程序時內(nèi)核堆棧示意圖內(nèi)核堆棧指針ESP用戶棧的SS用戶棧的ESPEFLAGS用戶空間的CSEIP-1ESDSEAXEBPEDIESIEDXECXEBX錯誤碼ESP異常處理程序被調(diào)用后內(nèi)核堆棧示意圖三、外部中斷Linux把每個外部中斷處理程序分為兩個部分:固定部分:由系統(tǒng)所定義,設(shè)置在IDT中,不能隨意修改??勺儾糠郑河筛鱾€設(shè)備驅(qū)動程序定義,在設(shè)備初始化時設(shè)定,在不需要時可以將其清除或卸載。發(fā)生外部中斷時,系統(tǒng)經(jīng)過中斷處理的固定部分進入可變部分。在處理外部中斷的過程中,通常關(guān)掉中斷,所以中斷處理程序越短愈好。(一)固定部分外部中斷初始化函數(shù)init_IRQ()中利用IRQn_interrupt創(chuàng)建0~15號中斷門,并根據(jù)IRQ號填入IDT表中。每個外部中斷處理程序的固定部分所做的工作為:將IRQ號壓入堆棧將各通用寄存器的值壓入堆棧(SAVE_ALL)

將中斷返回地址ret_from_intr壓入堆棧利用jmp指令跳轉(zhuǎn)到do_IRQ()執(zhí)行(do_IRQ根據(jù)堆棧中的IRQ號完成真正的中斷處理)(二)可變部分由于外部中斷處理程序具有可變部分,而且由于硬件限制,許多外部設(shè)備不得不共享同一個IRQ,并且各IRQ對應(yīng)的處理程序可以在運行中動態(tài)改變。Linux為每一個IRQ設(shè)置了一個隊列,即中斷請求隊列。對于224個中斷向量,每一個IRQ,Linux都用一個irq_desc_t結(jié)構(gòu)來描述,稱為IRQ描述符,形成一個irq_desc[]數(shù)組。

typedefstruct{ unsignedintstatus; //中斷線狀態(tài)

hw_irq_controller*handler; //指向hw_interrupt_type描述符

structirqaction*action; //單鏈表指針

unsignedintdepth; //0表示可用,每用一次加1

spinlock_tlock; // }__cacheline_alignedirq_desc_t;1.可變部分的數(shù)據(jù)結(jié)構(gòu)Status:描述IRQ中斷線狀態(tài)的一組標志#defineIRQ_INPROGRESS1#defineIRQ_DISABLED2#defineIRQ_PENDING4#defineIRQ_REPLAY8#defineIRQ_AUTODETECT16#defineIRQ_WAITING32handler:指向hw_interrupt_type描述符,該結(jié)構(gòu)是對中斷控制器的操作。

structhw_interrupt_type{//是對一個中斷控制器的抽象,其主要內(nèi)容是對中斷控制器的操作

constchar*typename; //控制器的可讀名

unsignedint(*startup)(unsignedintirq); //等同于enable void(*shutdown)(unsignedintirq); //等同于disable

void(*handle)(unsignedintirq); void(*enable)(unsignedintirq); // void(*disable)(unsignedintirq); //

void(*ack)(unsignedintirq); // void(*end)(unsignedintirq); // void(*set_affinity)(unsignedintirq,unsignedlongmask); // }typedefstructhw_interrupt_typehw_irq_controller;action:指向一個單項鏈表的指針,此鏈表是對中斷服務(wù)例程進行描述的irqaction結(jié)構(gòu)。depth;如果啟用該中斷線,depth則為0。如果禁用不止一次,則為一個正數(shù)。注意:disable_irq()和enable_irq()

對IRQ描述符的初始化由init_ISA_irqs()完成2.中斷服務(wù)例程及描述符irqaciton中斷服務(wù)例程描述符irqaction,描述了一個外部中斷處理的可變部分。

structirqaction{ void(*handler)(int,void*,structpt_regs); //指向一個具體的中斷服務(wù)例程

unsignedlongflags; //一組標志描述中斷線與I/O設(shè)備之間的關(guān)系

unsignedlongmask; // constchar*name; //I/O設(shè)備名

void*dev_id; //指定I/O設(shè)備的主設(shè)備號和次設(shè)備號

structirqaction*next; }handler:指向一個具體I/O設(shè)備的中斷服務(wù)例程,這是允許多個設(shè)備共享同一中斷線的關(guān)鍵。

flags:用一組標志來描述中斷線與I/O設(shè)備之間的關(guān)系:

SA_INTERRUPTSA_SHIRQSA_SAMPLE_RANDOMSA_PROBEname:I/O設(shè)備名

dev_id:指定I/O設(shè)備的主設(shè)備號和次設(shè)備號。

next:指向irqaction描述表鏈表的下一個元素。3.對數(shù)組irq_decsc所作的初始化

4.中斷請求隊列的初始化在設(shè)備驅(qū)動程序的初始化階段,每個設(shè)備驅(qū)動程序都要創(chuàng)建自己的irqaction結(jié)構(gòu),通過request_irq()函數(shù)將對應(yīng)的中斷服務(wù)例程掛入中斷請求隊列。

intrequest_irq(unsignedintirq,void(*handler)(int,void*,structpt_regs*),unsignedlongirqflags,constchar*devname,void*dev_id)檢查參數(shù)的合法性向內(nèi)核內(nèi)存管理器申請一塊內(nèi)存,用于建立irqaction結(jié)構(gòu)根據(jù)參數(shù)填寫申請到的irqaction結(jié)構(gòu)調(diào)用函數(shù)setup_x86_irq,將填寫好的irqaction結(jié)構(gòu)掛到irq_desc[irq]的action隊列上。setup_x86_irq(unsignedintirq,structirqaction*new)在數(shù)組irq_desc[]中找到參數(shù)irq對應(yīng)的action隊列如果隊列不為空,說明有多個設(shè)備共享該IRQ號,檢查共享的合法性,并將新irqaction結(jié)構(gòu)掛在對應(yīng)action隊列的末尾。如果隊列為空,則將irqaction結(jié)構(gòu)掛在對應(yīng)action隊列的頭部。并填寫對應(yīng)數(shù)組項的其它域。

5.釋放中斷處理程序利用free_irq()函數(shù)來完成

voidfree_irq(unsignedintirq,void*dev_id)

根據(jù)irq找到某條中斷線,若該中斷線是非共享的,則刪除處理程序的同時禁用該中斷線。若該中斷線是共享的,則僅刪除dev_id對應(yīng)的處理程序,該中斷線只有在刪除了所有的處理程序才會被禁用。6.中斷處理程序的執(zhí)行外部中斷處理函數(shù)do_IRQ()函數(shù)handle_IRQ_event()函數(shù)voiddo_IRQ(structpt_regsregs)從棧頂取出IRQ號根據(jù)取出的IRQ號從irq_desc[]數(shù)組中找到對應(yīng)的hw_interrupt_type類型,并執(zhí)行其handle子程序。handle執(zhí)行完后,中斷的上半部分已經(jīng)完成。檢查底半處理是否需要執(zhí)行。do_IRQ執(zhí)行完畢則返回。

staticvoiddo_8259A_IRQ(unsignedintirq,structpt_regs*regs)

向8259A發(fā)送信號,暫時禁止目前處理的IRQ中斷清除irq_desc[irq].status中的IRQ_REPLAY和IRQ_WAITING位若IRQ的狀態(tài)中包含IRQ_DISABLE|IRQ_INPROGRESS,則說明該IRQ被禁止或正在處理,則不能坐任何工作,返回。否則,將irq_desc[irq].status中加入IRQ_INPROGRESS位,并找到該IRQ對應(yīng)的action隊列。如果action隊列為空,則返回調(diào)用handle_IRQ_event(),處理action隊列。從handle_IRQ_event()返回,去掉IRQ標記中的IRQ_INPROGRESS位,表示該中斷處理已完成。解除對該IRQ中斷的禁用inthandle_IRQ_event(unsignedintirq,structpt_regs*regs,

structirqaction*action)調(diào)用函數(shù)irq_enter(cpu,irq),阻止進入底半處理對于慢速中斷,在做上半部分處理時允許再次中斷,故打開中斷。進入循環(huán),順序調(diào)用action隊列上的各個處理函數(shù),完成真正的中斷處理。關(guān)掉中斷調(diào)用irq_exit(cpu,irq),允許進入底半處理。7.從中斷返回do_IRQ的返回地址是ret_from_intr在ret_from_intr處檢查被中斷程序的運行模式若被中斷程序運行在核心態(tài),則彈出棧頂各寄存器的值并恢復(fù)該進程的執(zhí)行。若被中斷進程運行在用戶態(tài),則要視其是否需要調(diào)度和該進程上是否有信號來分別處理。8.自動探測IRQ號探測IRQ號的順序P96probe_irq_on()函數(shù)probe_irq_off()函數(shù)四、中斷處理的后半部分中斷服務(wù)例程一般是在中斷請求關(guān)閉的情況下執(zhí)行的,而關(guān)中斷時間過長,會導(dǎo)致cpu不能及時響應(yīng)其他的中斷請求,從而造成中斷的丟失。故Linux在設(shè)計內(nèi)核時,希望盡可能快地處理完中斷,把更多的處理向后推遲。Linux定義了兩種中斷,快速中斷和慢速中斷。慢速中斷本身可以再被中斷,快速中斷不允許再被中斷。因此,內(nèi)核把中斷處理的可變部分分成兩個部分,前半部分和后半部分,前半部分內(nèi)核立即執(zhí)行,而后半部分留著稍后處理。補充:對于后半部分通常在中斷一返回就可以馬上執(zhí)行,且在后半部分運行時,允許響應(yīng)所有的中斷。1.bottomhalf(bh)機制與bh機制相關(guān)的數(shù)據(jù)結(jié)構(gòu)intbh_mask_count[32];unsignedlongbh_active;unsignedlongbh_mask;void(*bh_base[32])(void);bh_mask_count[]是底半處理的屏蔽計數(shù),每個底半處理對應(yīng)其中的一項。指針數(shù)組bh_base[]的每一項都指向一個底半處理程序。bh_active表示底半程序是否被激活。bh_mask表示是否安裝了底半處理程序。他們中的每一位都對應(yīng)著數(shù)組中一項。只有在安裝并激活了第N個底半處理時,才能調(diào)用第N個底半處理程序bh的初始化由init_bh()完成將相應(yīng)的底半處理程序注冊到bh_base[]中的操作。inlinevoidinit_bh(intnr,void(*routine)(void))bh卸載卸載已注冊的底半處理

inlinevoidremove_bh(intnr)將bh_mask中的相應(yīng)位清0,表示該底半處理不可再用清除在數(shù)組bh_base中注冊的處理函數(shù)禁止和恢復(fù)bhinlinevoiddisable_bh(intnr)將變量bh_mask的nr位清0,表示底半處理不可再用。將bh_mask_count[nr]加1inlinevoidenable_bh(intnr)將bh_mask_count[nr]減1如果其為0,則將變量bh_mask的nr位置1,表示底半處理可用。bh的執(zhí)行首先利用mark_bh(intnr)激活bh_active中的nr位。每次執(zhí)行完do_IRQ()中的中斷服務(wù)例程以后,每次系統(tǒng)調(diào)用結(jié)束之前,以及當被中斷進程運行在用戶態(tài)且需要調(diào)度,在作進程調(diào)度之前,在do_bottom_half()中執(zhí)行相應(yīng)的bh函數(shù),且do_bottom_half()對bh的執(zhí)行是在關(guān)中斷的情況下執(zhí)行的。在do_bottom_half()中真正的底半操作由run_bottom_half()完成調(diào)用宏get_active_bhs(),取出激活且安裝的底半處理。調(diào)用宏clear_active_bh(),將上一步取出項所對應(yīng)的變量bh_active中的激活位清0根據(jù)取出的激活位,順序執(zhí)行在bh_base[]中注冊的底半處理操作bh機制的特點:具有“串行化”的特點,不適合于多cpu結(jié)構(gòu)。2.軟中斷機制軟中斷機制也是推遲內(nèi)核函數(shù)的執(zhí)行,與bh函數(shù)嚴格的串行執(zhí)行相比,軟中斷在任何時候都不需要串行化,同一個軟中斷的兩個實例完全可能在兩個CPU上同時執(zhí)行。因此軟中斷是可重入的,這給網(wǎng)絡(luò)部分帶來了好處。軟中斷本身是一種機制,既包含了bh機制,也包含了tasklet機制。軟中斷的執(zhí)行首先用open_softirq進行初始化,然后通過softirq_init()執(zhí)行軟中斷。一個注冊的軟中斷必須在標記以后才會被執(zhí)行。通常中斷處理程序會在返回前標記它的軟中斷,使其在稍后執(zhí)行。內(nèi)核在do_IRQ()中執(zhí)行完一個中斷請求隊列中的中斷服務(wù)例程以后,都要檢查是否有軟中斷請求在等待執(zhí)行。在檢測到軟中斷請求后,在調(diào)用do_softirq()來執(zhí)行軟中斷服務(wù)例程。軟中斷機制的特點其執(zhí)行是“并行的”,可以重入的,即允許不同的cpu同時進入對軟中斷服務(wù)例程的執(zhí)行,但不能讓它們相互干擾。3.tasklet機制是建立在軟中斷機制的基礎(chǔ)上,與bh機制類似。tasklet的初始化方法很多,可以靜態(tài)創(chuàng)建,也可以動態(tài)創(chuàng)建。動態(tài)創(chuàng)建的方法之一:由tasklet_init()完成tasklet的使用通過調(diào)用tasklet_schedule(),激活它的tasklet_struct中指針所指向的服務(wù)程序。在tasklet被調(diào)用以后,只要有可能就盡可能早的運行,若某個tasklet還沒得到運行機會之前又有一個相同的tasklet又被調(diào)度了,經(jīng)過檢查重復(fù)后,他只運行一次。若它已開始運行,那么新的tasklet會被重新調(diào)度并且再次運行。tasklet的特點同一個tasklet只能運行在一個cpu上,而不同的tasklet可以同時運行在不同的cpu上,即tasklet是非重入的。IRQ鎖軟IRQ鎖 有時不希望系統(tǒng)做底半處理,如系統(tǒng)正在做底半處理時被中斷,此時不希望在中斷的上半部分處理完以后再次進入底半處理。unsignedintlocal_bh_count[NR_CPUS]start_bh_atomic(void)用來禁止底半處理

end_bh_atomic(void)恢復(fù)底半處理

softirq_trylock(cpu)判斷是否允許進入底半處理

softirq_endlock(cpu)允許底半處理以上4個函數(shù)是在關(guān)中斷情況下執(zhí)行.硬IRQ鎖 除了軟件IRQ鎖以外,Linux還提供硬IRQ鎖,一般情況下,硬件的開中斷和關(guān)中斷不受限制,但在特殊情況下必需限制硬件的開關(guān)中斷,如在做中斷的上半部分處理時的開中斷就必需限制。硬IRQ鎖通過unsignedintlocal_irq_count[NR_CPUS]來實現(xiàn)。IRQ鎖的作用 底半處理既使用了軟IRQ鎖,也使用了硬IRQ鎖。實際上,定義IRQ鎖的目的主要是為了控制底半處理的重入。Linux的時鐘管理時鐘中斷時鐘中斷處理過程底半處理系統(tǒng)時間更新進程時間片定時器中斷和時鐘中斷時鐘中斷是需要操作系統(tǒng)內(nèi)核特別處理的一個外部中斷時鐘中斷是操作系統(tǒng)的脈搏:維護系統(tǒng)時間、統(tǒng)計運行數(shù)據(jù)、設(shè)計計時時鐘、促使進程切換、監(jiān)督系統(tǒng)工作時鐘中斷時間系統(tǒng)(即時鐘)是計算機系統(tǒng)非常重要的組成部分PC機有兩個時鐘源:實時時鐘(RTC)和OS時鐘RTC也叫CMOS時鐘,是主板上的一塊靠電池供電的晶片(晶振),獨立于OS,也稱為硬件時鐘,為計算機提供計時標準,是最底層的時鐘數(shù)據(jù)OS時鐘由硬件(定時/計數(shù)器)和軟件(時鐘中斷處理程序)組成。定時/計數(shù)器從RTC接收輸入脈沖,并計數(shù),每次計數(shù)到期就產(chǎn)生一個輸出脈沖,再從頭計數(shù)

可編程定時/計數(shù)器由計數(shù)硬件和通信寄存器組成,通信寄存器負責在計數(shù)硬件和操作系統(tǒng)之間通信時鐘中斷產(chǎn)生過程IRQ08259A-1中斷控制器8253/8254定時/記數(shù)器RTC晶振1.193180MHzOut0Out1Out2DRAM刷新?lián)P聲器接CPU中斷輸入端時鐘中斷處理過程OS注冊時鐘中斷處理程序timer_interrupt1、定時/計數(shù)器產(chǎn)生輸出脈沖時,中斷處理器送出中斷信號,CPU根據(jù)IRQ號查中斷描述符表IDT,找到并執(zhí)行中斷處理程序IRQ00_interrupt2、跳到do_IRQ3、do_IRQ執(zhí)行handle程序do_8259A_IRQ4、do_8259A_IRQ執(zhí)行timer_interrupt5、timer_interrupt調(diào)用do_timer完成上半部分處理。do_timer定義如下:voiddo_timer(structpt_regs*regs){ (*(unsignedlong*)&jiffies)++;lost_ticks++; mark_bh(TIMER_BH);if(!user_mode(regs))lost_ticks_system++; if(tq_timer) mark_bh(TQUEUE_BH);}6、timer_interrupt返回后,do_8259A_IRQ返回到do_IRQ7、底半處理8、返回到ret_from_intr,這里或者返回到被中斷的進程,或者啟動調(diào)度程序,完成新一輪調(diào)度底半處理1、時鐘底半處理函數(shù)timer_bh定義2、時鐘任務(wù)隊列處理函數(shù)tqueue_bh定義3、底半處理的工作(1)更新系統(tǒng)時間(2)更新當前進程時間片(3)處理老定時器(4)處理新定時器(5)處理時鐘任務(wù)隊列系統(tǒng)時間系統(tǒng)時間結(jié)構(gòu)

structtimeval{inttv_sec;//秒

inttv_usec;//微秒

}初始化:函數(shù)time_init從CMOS時鐘取出當前時間,通過函數(shù)mktime轉(zhuǎn)化成自1970-01-0100:00:00以來的秒數(shù),存入全局變量xtime.tv_sec中,將xtime.tv_usec初始化為0累計系統(tǒng)時間

voidupdate_wall_time(unsignedlongticks)

其中ticks是從上次時鐘中斷底半處理以來已經(jīng)過去的tick數(shù),一般為1。

update_wall_time完成的工作Xtime.tv_usec+=tick+time_adjust_step;根據(jù)變量time_adj、time_phase調(diào)整xtime.tv_usec;Ticks--;如果ticks>0,則轉(zhuǎn)第一步調(diào)整xtime值,累計秒數(shù),減少微秒數(shù):if(xtime.tv_usec>=1000000){xtime.tv_usec-=1000000;xtime.tv_sec++;second_overflow();}時間的轉(zhuǎn)換和修改

LINUX提供多個系統(tǒng)調(diào)用來轉(zhuǎn)換和設(shè)置系統(tǒng)時間更新進程時間片反映進程運行情況,若時間片用完則通知當前進程放棄CPU通過函數(shù)update_process_times更新時間片(1)計算從上次底半處理后在用戶態(tài)發(fā)生的時鐘中斷次數(shù):user=ticks-system;

(2)如果當前進程不是0號進程,則a.進程時間片減ticks:current->counter-=ticks;b.若時間片已用完,則要求進程放棄CPU:

if(p->counter<0){p->counter=0;p->need_resched=1;}c.根據(jù)user調(diào)整變量kstat中的統(tǒng)計信息

(3)調(diào)用函數(shù)update_one_process修改當前進程的其它與時間相關(guān)的信息函數(shù)update_one_processa.累計進程在當前CPU消耗的用戶態(tài)時間:

p->per_cpu_utime[cpu]+=user;b.累計進程在當前CPU消耗的核心態(tài)時間:

p->per_cpu_stime[cpu]+=system;c.累計進程消耗的用戶態(tài)時間:

p->times.tms_utime+=user;d.累計進程消耗的核心態(tài)時間:

p->times.tms_stime+=system;e.若進程消耗總時間超過當前時間界限,則在總量達到整秒時向進程發(fā)送SIGXCPU信號f.若進程消耗總時間超過最大時間界限,則向進程發(fā)送SIGKILL信號,強行殺死進程

h.若進程定義有virtual定時器,則根據(jù)消耗的用戶態(tài)時間處理它

i.若進程定義有prof定時器,則根據(jù)消耗的用戶態(tài)和核心態(tài)時間處理它定時器在進程運行時,系統(tǒng)提供的virtual和prof定時器可以實現(xiàn)定時為了在任何時候都能夠定時,LINUX提供兩類根據(jù)系統(tǒng)時間定時且與進程狀態(tài)無關(guān)的定時器:老定時器和新定時器老定時器結(jié)構(gòu)

structtimer_struct{unsignedlongexpires;//終止時間

void(*fn)(void);//到期要執(zhí)行的函數(shù)

}最多有32個老定時器系統(tǒng)定義一個位圖timer_active來標識已注冊并處于活動狀態(tài)的定時器,如果某位被置1則相應(yīng)的定時器活動通過以下函數(shù)在底半處理中檢查并處理老定時器:

run_old_timers

順序搜索位圖,發(fā)現(xiàn)某位被設(shè)置就檢查是否到期。如果到期就清除位圖中該定時器的標記,表示處理完成;執(zhí)行定時器指定操作tp->fn();每處理一個定時器就開一次中斷sti()新定時器新定時器結(jié)構(gòu)

structtimer_list{structtimer_list*next;structtimer_list*prev;unsignedlongexpires;//終止時間

unsignedlongdata;void(*function)(unsignedlong);};其中data是傳給處理函數(shù)function的參數(shù)next和prev兩個指針將定時器連成雙向鏈表tvecstv5tv4tv301234indexindextimer_l

溫馨提示

  • 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論