版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
Lecture8
LinuxInterruptandtimer中斷機(jī)制中斷基本知識(shí)異常處理外部中斷處理中斷的后半部分處理機(jī)制一、中斷基本知識(shí)1.中斷向量Intelx86系列微機(jī)共支持256種向量中斷,按0~255編號(hào),即賦予一個(gè)中斷類型碼,linux中稱其為中斷向量。所有的256種中斷可分為中斷和異常兩大類。異常又分為故障和陷阱,它們的共同特點(diǎn)是既不使用中斷控制器,又不能被屏蔽。中斷又分為外部可屏蔽中斷和外部非屏蔽中斷。所有的I/O設(shè)備產(chǎn)生的中斷請(qǐng)求均引起可屏蔽中斷,而緊急事件引起的故障產(chǎn)生非屏蔽中斷。非屏蔽中斷的向量和異常的向量是固定的,而可屏蔽中斷的向量可以通過(guò)對(duì)中斷控制器的編程來(lái)實(shí)現(xiàn)。從0~31的向量對(duì)應(yīng)于異常和非屏蔽中斷從32~47的向量分配給屏蔽中斷從48~255的向量用來(lái)標(biāo)志軟中斷(由intn指令產(chǎn)生),
linux只用了一個(gè):128(0x80)向量異常和非屏蔽中斷0 除法出錯(cuò) SIGFPE 故障 被0除1 調(diào)試 SIGTRAP 故障/陷阱 對(duì)程序進(jìn)行逐步調(diào)試2 非屏蔽中斷(NMI)3 斷點(diǎn) SIGTRAP 陷阱 由Int3斷點(diǎn)指令引起4 溢出 SIGSEGV 陷阱 當(dāng)into(checkforoverflow)指令被執(zhí)行5 邊界檢查 SIGSEGV 故障 當(dāng)bound指令被執(zhí)行6 非法操作碼 SIGILL 故障 當(dāng)CPU檢查到一個(gè)無(wú)效操作碼7 設(shè)備不可用 SIGSEGV 故障 隨著CR0的TS設(shè)置,ESCAPE或MMX指令被執(zhí)行8 雙重故障 SIGSEGV 故障 處理器不能串行處理異常引起9 協(xié)處理器段越界 SIGFPE 故障 因外部的數(shù)學(xué)協(xié)處理器引起的問(wèn)題(僅用在80386)10 無(wú)效TSS SIGSEGV 故障 要切換到的進(jìn)程具有無(wú)效的TSS11 段不存在 SIGBUS 故障 引用一個(gè)不存在的內(nèi)存段12 棧段異常 SIGSEGV 故障 試圖超越棧段界限,或由ss標(biāo)志的段不在內(nèi)存13 通用保護(hù) SIGSEGV 故障 違反了Intelx86保護(hù)模式下的一個(gè)保護(hù)規(guī)則14 頁(yè)異常 SIGSEGV 故障 尋址的頁(yè)不在內(nèi)存,或違反了一種分頁(yè)保護(hù)機(jī)制15 Intel保留16 浮點(diǎn)出錯(cuò) SIGFPE 故障 浮點(diǎn)單元用信號(hào)通知一個(gè)錯(cuò)誤情況,如溢出17 對(duì)齊檢查 SIGSEGV 故障 操作數(shù)的地址沒有被正確地排列18~31Intel保留2.外設(shè)可屏蔽中斷Intelx86通過(guò)兩片中斷控制器8259A來(lái)響應(yīng)15個(gè)外部中斷源,每個(gè)8259A可管理8個(gè)中斷源。并非每個(gè)設(shè)備都可以向中斷線(與中斷控制器相連的每條線)上發(fā)中斷信號(hào),只有擁有了某條中斷線的控制權(quán),才可以向這條中斷線上發(fā)送信號(hào)。中斷線是非常寶貴的資源,必須采取只有當(dāng)設(shè)備需要中斷的時(shí)候才申請(qǐng)占用一個(gè)IRQ,或者是在申請(qǐng)IRQ時(shí)采用共享中斷的方式。外設(shè)可屏蔽中斷
32 IRQ0 時(shí)鐘
33 IRQ1 鍵盤
35 IRQ3 tty2 36 IRQ4 tty1 37 IRQ5 XTWinchester 38 IRQ6 軟驅(qū)
39 IRQ7 打印機(jī)
40 IRQ8 實(shí)時(shí)時(shí)鐘
41 IRQ9 重定向的IRQ2 45 IRQ13 FPU異常
46 IRQ14 ATWinchester8259A執(zhí)行的操作監(jiān)視中斷線,檢查產(chǎn)生的IRQ信號(hào)如果在中斷線上產(chǎn)生了一個(gè)IRQ信號(hào)把接收的IRQ信號(hào)轉(zhuǎn)換成一個(gè)對(duì)應(yīng)的向量。(IRQn的缺省向量是n+32)把這個(gè)向量存放在8259A的一個(gè)I/O端口,從而允許CPU通過(guò)數(shù)據(jù)總線讀此向量。把產(chǎn)生的信號(hào)發(fā)送到CPU的INTR引腳——發(fā)出一個(gè)中斷。等待,直到CPU確認(rèn)這個(gè)中斷信號(hào),然后把它寫進(jìn)PIC的一個(gè)I/O端口,此時(shí)清INTR線返回第一步8259A對(duì)外部I/O請(qǐng)求的屏蔽可分為兩種情況:
a清除EFLAGS的IF位,禁止所有外部的I/O中斷請(qǐng)求
b中斷控制器中有一個(gè)8位的中斷屏蔽寄存器,每位對(duì)應(yīng)于8259A中的一條中斷線,如果要禁用某條中斷線,把IRM相應(yīng)位置1中斷路由的邏輯圖3.中斷描述符表IDTIntel系統(tǒng)利用中斷描述符表將中斷向量和其處理程序?qū)?yīng)起來(lái)。IDT是一個(gè)描述符數(shù)組,每一個(gè)元素由8個(gè)字節(jié)組成,叫做門描述符。每個(gè)中斷向量對(duì)應(yīng)一個(gè)門描述符。中斷描述符表
DLP 段描述符的特權(quán)級(jí) 偏移量 入口函數(shù)地址的偏移量
P 段是否在內(nèi)存中的標(biāo)志 段選擇符 入口函數(shù)所處代碼段的選擇符
D 標(biāo)志位,1=32位,0=16位
XXX 三位門類型碼
偏移量31~16|P|DPL|0DXXX|000|?????
段選擇符|
偏移量15~0任務(wù)門類型碼為101,門中包含一個(gè)進(jìn)程的TSS段選擇符,不使用偏移量,故不包含一個(gè)函數(shù)的入口地址,Linux中一般不用任務(wù)門進(jìn)行任務(wù)切換。中斷門
類型碼為110,控制權(quán)通過(guò)中斷門進(jìn)入處理程序時(shí)關(guān)中斷。中斷門中的DPL為0,所有的中斷處理程序都由中斷門激活,并全部限制在內(nèi)核態(tài)。中斷時(shí)清IF標(biāo)志,關(guān)中斷以防止中斷嵌套。陷阱門類型碼為111,控制權(quán)通過(guò)陷阱門進(jìn)入處理程序時(shí)不關(guān)中斷,即IF標(biāo)志位不變。系統(tǒng)門是Linux內(nèi)核特別設(shè)置的,用來(lái)讓用戶態(tài)的進(jìn)程訪問(wèn)Intel的陷阱門,DPL為3。通過(guò)系統(tǒng)門激活3,4,5,0x80中斷。4.IDT的初始化Linux內(nèi)核在系統(tǒng)的初始化階段進(jìn)行大量的初始化操作初始化可編程控制器8259A8259A內(nèi)部有4個(gè)中斷命令字寄存器,8259A初始化的目的是為了寫入有關(guān)命令字,并利用8259A內(nèi)部的相應(yīng)寄存器鎖存這些命令字,以控制8259A的工作。對(duì)IDTR進(jìn)行初始化把IDT表的起始地址裝入IDTR用setup_idt()函數(shù)填充IDT表中的256個(gè)表項(xiàng)在對(duì)IDT表進(jìn)行了預(yù)初始化后,內(nèi)核將在啟用分頁(yè)功能后對(duì)IDT表進(jìn)行第二遍的初始化,用實(shí)際的陷阱和中斷處理程序替換空的處理程序。注意:在Intel體系結(jié)構(gòu)中提供了四個(gè)特權(quán)級(jí),編號(hào)從0到3,其中0是最高特權(quán)級(jí),3級(jí)是最低特權(quán)級(jí)。系統(tǒng)中每個(gè)段都有自己的特權(quán)級(jí),系統(tǒng)中每個(gè)任務(wù)也都有自己的特權(quán)級(jí)。特權(quán)級(jí)低的任務(wù)不能直接調(diào)用高特權(quán)級(jí)的子程序,也不能存取高特權(quán)級(jí)的數(shù)據(jù)段。DPL——描述符的特權(quán)級(jí)CPL——當(dāng)前任務(wù)的特權(quán)級(jí)注意:CPU為保護(hù)模式增設(shè)了一個(gè)IDTR,用來(lái)存放中斷描述符表在內(nèi)存的起始地址。
中斷處理與當(dāng)前進(jìn)程有關(guān)。中斷處理程序運(yùn)行在當(dāng)前進(jìn)程的地址空間,使用的是當(dāng)前進(jìn)程的系統(tǒng)堆棧。32位基地址16位界限4716150中斷描述符表寄存器IDTR5.中斷和異常的硬件處理要求在一個(gè)中斷和異常發(fā)生后,cpu將做以下事情:確定所發(fā)生的異?;蛑袛嘞蛄縤通過(guò)IDTR寄存器找到IDT表,讀取IDT表第i項(xiàng)該中斷處理程序所在段的DPL必須小于或等于當(dāng)前代碼段的CPL對(duì)于軟中斷,要求CPL<=中斷或陷阱門的DPL硬件中斷不檢查特權(quán)級(jí)檢查是否發(fā)生了特權(quán)級(jí)的變化,即是否需要切換堆棧發(fā)生中斷時(shí)系統(tǒng)運(yùn)行在核心態(tài)發(fā)生中斷時(shí)系統(tǒng)運(yùn)行在用戶態(tài)二、異常
異常是cpu內(nèi)部出現(xiàn)的中斷,即在cpu在執(zhí)行特定指令時(shí)出現(xiàn)的非法情況。非屏蔽中斷是在計(jì)算機(jī)內(nèi)部硬件出錯(cuò)時(shí)引起的異常情況,intel把非屏蔽中斷作為一種異常來(lái)處理。在CPU處理一個(gè)異常處理程序時(shí),不再為其他異常或可屏蔽中斷請(qǐng)求服務(wù)。Linux異常中斷向量在0~31之間,目前發(fā)布了大約20種。內(nèi)核對(duì)異常處理程序的調(diào)用分成三個(gè)部分:在內(nèi)核棧中保存寄存器的值先是順序壓入各寄存器的值,并調(diào)整棧頂滿足pt_regs結(jié)構(gòu)。(要注意當(dāng)異常發(fā)生時(shí),如果控制單元沒有自動(dòng)地把一個(gè)硬件錯(cuò)誤代碼插入到棧中。系統(tǒng)會(huì)自動(dòng)執(zhí)行一條push$0指令,在棧中墊入一個(gè)空值。如果錯(cuò)誤代碼已經(jīng)壓入堆棧,則接下來(lái)把異常處理函數(shù)的地址壓入堆棧。)最后壓入指向pt_regs的指針和錯(cuò)誤代碼。用call指令調(diào)用異常處理函數(shù)(使用當(dāng)前棧頂?shù)膬蓚€(gè)參數(shù))通過(guò)ret_from_exception從異常退出,并進(jìn)行異常善后處理。有無(wú)激活的底半處理,有則轉(zhuǎn)入底半處理。被中斷進(jìn)程運(yùn)行在核心態(tài),簡(jiǎn)單彈出棧頂各寄存器的值,恢復(fù)被中斷的進(jìn)程。被中斷進(jìn)程運(yùn)行在用戶態(tài),如當(dāng)前進(jìn)程需要調(diào)度,則運(yùn)行調(diào)度函數(shù),重新調(diào)度進(jìn)程。如當(dāng)前進(jìn)程不需要調(diào)度,但如果當(dāng)前進(jìn)程上有待處理信號(hào),則先處理信號(hào)再?gòu)棾黾拇嫫髦?,并返回被中斷進(jìn)程。如果沒有待處理信號(hào)則彈出寄存器值,返回被中斷進(jìn)程。
內(nèi)核堆棧指針ESP用戶棧的SS用戶棧的ESPEFLAGS用戶空間的CSEIP錯(cuò)誤碼或0函數(shù)地址DSEAXEBPEDIESIEDXECXEBX進(jìn)入異常處理程序時(shí)內(nèi)核堆棧示意圖內(nèi)核堆棧指針ESP用戶棧的SS用戶棧的ESPEFLAGS用戶空間的CSEIP-1ESDSEAXEBPEDIESIEDXECXEBX錯(cuò)誤碼ESP異常處理程序被調(diào)用后內(nèi)核堆棧示意圖三、外部中斷Linux把每個(gè)外部中斷處理程序分為兩個(gè)部分:固定部分:由系統(tǒng)所定義,設(shè)置在IDT中,不能隨意修改??勺儾糠郑河筛鱾€(gè)設(shè)備驅(qū)動(dòng)程序定義,在設(shè)備初始化時(shí)設(shè)定,在不需要時(shí)可以將其清除或卸載。發(fā)生外部中斷時(shí),系統(tǒng)經(jīng)過(guò)中斷處理的固定部分進(jìn)入可變部分。在處理外部中斷的過(guò)程中,通常關(guān)掉中斷,所以中斷處理程序越短愈好。(一)固定部分外部中斷初始化函數(shù)init_IRQ()中利用IRQn_interrupt創(chuàng)建0~15號(hào)中斷門,并根據(jù)IRQ號(hào)填入IDT表中。每個(gè)外部中斷處理程序的固定部分所做的工作為:將IRQ號(hào)壓入堆棧將各通用寄存器的值壓入堆棧(SAVE_ALL)
將中斷返回地址ret_from_intr壓入堆棧利用jmp指令跳轉(zhuǎn)到do_IRQ()執(zhí)行(do_IRQ根據(jù)堆棧中的IRQ號(hào)完成真正的中斷處理)(二)可變部分由于外部中斷處理程序具有可變部分,而且由于硬件限制,許多外部設(shè)備不得不共享同一個(gè)IRQ,并且各IRQ對(duì)應(yīng)的處理程序可以在運(yùn)行中動(dòng)態(tài)改變。Linux為每一個(gè)IRQ設(shè)置了一個(gè)隊(duì)列,即中斷請(qǐng)求隊(duì)列。對(duì)于224個(gè)中斷向量,每一個(gè)IRQ,Linux都用一個(gè)irq_desc_t結(jié)構(gòu)來(lái)描述,稱為IRQ描述符,形成一個(gè)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)的一組標(biāo)志#defineIRQ_INPROGRESS1#defineIRQ_DISABLED2#defineIRQ_PENDING4#defineIRQ_REPLAY8#defineIRQ_AUTODETECT16#defineIRQ_WAITING32handler:指向hw_interrupt_type描述符,該結(jié)構(gòu)是對(duì)中斷控制器的操作。
structhw_interrupt_type{//是對(duì)一個(gè)中斷控制器的抽象,其主要內(nèi)容是對(duì)中斷控制器的操作
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:指向一個(gè)單項(xiàng)鏈表的指針,此鏈表是對(duì)中斷服務(wù)例程進(jìn)行描述的irqaction結(jié)構(gòu)。depth;如果啟用該中斷線,depth則為0。如果禁用不止一次,則為一個(gè)正數(shù)。注意:disable_irq()和enable_irq()
對(duì)IRQ描述符的初始化由init_ISA_irqs()完成2.中斷服務(wù)例程及描述符irqaciton中斷服務(wù)例程描述符irqaction,描述了一個(gè)外部中斷處理的可變部分。
structirqaction{ void(*handler)(int,void*,structpt_regs); //指向一個(gè)具體的中斷服務(wù)例程
unsignedlongflags; //一組標(biāo)志描述中斷線與I/O設(shè)備之間的關(guān)系
unsignedlongmask; // constchar*name; //I/O設(shè)備名
void*dev_id; //指定I/O設(shè)備的主設(shè)備號(hào)和次設(shè)備號(hào)
structirqaction*next; }handler:指向一個(gè)具體I/O設(shè)備的中斷服務(wù)例程,這是允許多個(gè)設(shè)備共享同一中斷線的關(guān)鍵。
flags:用一組標(biāo)志來(lái)描述中斷線與I/O設(shè)備之間的關(guān)系:
SA_INTERRUPTSA_SHIRQSA_SAMPLE_RANDOMSA_PROBEname:I/O設(shè)備名
dev_id:指定I/O設(shè)備的主設(shè)備號(hào)和次設(shè)備號(hào)。
next:指向irqaction描述表鏈表的下一個(gè)元素。3.對(duì)數(shù)組irq_decsc所作的初始化
4.中斷請(qǐng)求隊(duì)列的初始化在設(shè)備驅(qū)動(dòng)程序的初始化階段,每個(gè)設(shè)備驅(qū)動(dòng)程序都要?jiǎng)?chuàng)建自己的irqaction結(jié)構(gòu),通過(guò)request_irq()函數(shù)將對(duì)應(yīng)的中斷服務(wù)例程掛入中斷請(qǐng)求隊(duì)列。
intrequest_irq(unsignedintirq,void(*handler)(int,void*,structpt_regs*),unsignedlongirqflags,constchar*devname,void*dev_id)檢查參數(shù)的合法性向內(nèi)核內(nèi)存管理器申請(qǐng)一塊內(nèi)存,用于建立irqaction結(jié)構(gòu)根據(jù)參數(shù)填寫申請(qǐng)到的irqaction結(jié)構(gòu)調(diào)用函數(shù)setup_x86_irq,將填寫好的irqaction結(jié)構(gòu)掛到irq_desc[irq]的action隊(duì)列上。setup_x86_irq(unsignedintirq,structirqaction*new)在數(shù)組irq_desc[]中找到參數(shù)irq對(duì)應(yīng)的action隊(duì)列如果隊(duì)列不為空,說(shuō)明有多個(gè)設(shè)備共享該IRQ號(hào),檢查共享的合法性,并將新irqaction結(jié)構(gòu)掛在對(duì)應(yīng)action隊(duì)列的末尾。如果隊(duì)列為空,則將irqaction結(jié)構(gòu)掛在對(duì)應(yīng)action隊(duì)列的頭部。并填寫對(duì)應(yīng)數(shù)組項(xiàng)的其它域。
5.釋放中斷處理程序利用free_irq()函數(shù)來(lái)完成
voidfree_irq(unsignedintirq,void*dev_id)
根據(jù)irq找到某條中斷線,若該中斷線是非共享的,則刪除處理程序的同時(shí)禁用該中斷線。若該中斷線是共享的,則僅刪除dev_id對(duì)應(yīng)的處理程序,該中斷線只有在刪除了所有的處理程序才會(huì)被禁用。6.中斷處理程序的執(zhí)行外部中斷處理函數(shù)do_IRQ()函數(shù)handle_IRQ_event()函數(shù)voiddo_IRQ(structpt_regsregs)從棧頂取出IRQ號(hào)根據(jù)取出的IRQ號(hào)從irq_desc[]數(shù)組中找到對(duì)應(yīng)的hw_interrupt_type類型,并執(zhí)行其handle子程序。handle執(zhí)行完后,中斷的上半部分已經(jīng)完成。檢查底半處理是否需要執(zhí)行。do_IRQ執(zhí)行完畢則返回。
staticvoiddo_8259A_IRQ(unsignedintirq,structpt_regs*regs)
向8259A發(fā)送信號(hào),暫時(shí)禁止目前處理的IRQ中斷清除irq_desc[irq].status中的IRQ_REPLAY和IRQ_WAITING位若IRQ的狀態(tài)中包含IRQ_DISABLE|IRQ_INPROGRESS,則說(shuō)明該IRQ被禁止或正在處理,則不能坐任何工作,返回。否則,將irq_desc[irq].status中加入IRQ_INPROGRESS位,并找到該IRQ對(duì)應(yīng)的action隊(duì)列。如果action隊(duì)列為空,則返回調(diào)用handle_IRQ_event(),處理action隊(duì)列。從handle_IRQ_event()返回,去掉IRQ標(biāo)記中的IRQ_INPROGRESS位,表示該中斷處理已完成。解除對(duì)該IRQ中斷的禁用inthandle_IRQ_event(unsignedintirq,structpt_regs*regs,
structirqaction*action)調(diào)用函數(shù)irq_enter(cpu,irq),阻止進(jìn)入底半處理對(duì)于慢速中斷,在做上半部分處理時(shí)允許再次中斷,故打開中斷。進(jìn)入循環(huán),順序調(diào)用action隊(duì)列上的各個(gè)處理函數(shù),完成真正的中斷處理。關(guān)掉中斷調(diào)用irq_exit(cpu,irq),允許進(jìn)入底半處理。7.從中斷返回do_IRQ的返回地址是ret_from_intr在ret_from_intr處檢查被中斷程序的運(yùn)行模式若被中斷程序運(yùn)行在核心態(tài),則彈出棧頂各寄存器的值并恢復(fù)該進(jìn)程的執(zhí)行。若被中斷進(jìn)程運(yùn)行在用戶態(tài),則要視其是否需要調(diào)度和該進(jìn)程上是否有信號(hào)來(lái)分別處理。8.自動(dòng)探測(cè)IRQ號(hào)探測(cè)IRQ號(hào)的順序P96probe_irq_on()函數(shù)probe_irq_off()函數(shù)四、中斷處理的后半部分中斷服務(wù)例程一般是在中斷請(qǐng)求關(guān)閉的情況下執(zhí)行的,而關(guān)中斷時(shí)間過(guò)長(zhǎng),會(huì)導(dǎo)致cpu不能及時(shí)響應(yīng)其他的中斷請(qǐng)求,從而造成中斷的丟失。故Linux在設(shè)計(jì)內(nèi)核時(shí),希望盡可能快地處理完中斷,把更多的處理向后推遲。Linux定義了兩種中斷,快速中斷和慢速中斷。慢速中斷本身可以再被中斷,快速中斷不允許再被中斷。因此,內(nèi)核把中斷處理的可變部分分成兩個(gè)部分,前半部分和后半部分,前半部分內(nèi)核立即執(zhí)行,而后半部分留著稍后處理。補(bǔ)充:對(duì)于后半部分通常在中斷一返回就可以馬上執(zhí)行,且在后半部分運(yùn)行時(shí),允許響應(yīng)所有的中斷。1.bottomhalf(bh)機(jī)制與bh機(jī)制相關(guān)的數(shù)據(jù)結(jié)構(gòu)intbh_mask_count[32];unsignedlongbh_active;unsignedlongbh_mask;void(*bh_base[32])(void);bh_mask_count[]是底半處理的屏蔽計(jì)數(shù),每個(gè)底半處理對(duì)應(yīng)其中的一項(xiàng)。指針數(shù)組bh_base[]的每一項(xiàng)都指向一個(gè)底半處理程序。bh_active表示底半程序是否被激活。bh_mask表示是否安裝了底半處理程序。他們中的每一位都對(duì)應(yīng)著數(shù)組中一項(xiàng)。只有在安裝并激活了第N個(gè)底半處理時(shí),才能調(diào)用第N個(gè)底半處理程序bh的初始化由init_bh()完成將相應(yīng)的底半處理程序注冊(cè)到bh_base[]中的操作。inlinevoidinit_bh(intnr,void(*routine)(void))bh卸載卸載已注冊(cè)的底半處理
inlinevoidremove_bh(intnr)將bh_mask中的相應(yīng)位清0,表示該底半處理不可再用清除在數(shù)組bh_base中注冊(cè)的處理函數(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é)束之前,以及當(dāng)被中斷進(jìn)程運(yùn)行在用戶態(tài)且需要調(diào)度,在作進(jìn)程調(diào)度之前,在do_bottom_half()中執(zhí)行相應(yīng)的bh函數(shù),且do_bottom_half()對(duì)bh的執(zhí)行是在關(guān)中斷的情況下執(zhí)行的。在do_bottom_half()中真正的底半操作由run_bottom_half()完成調(diào)用宏get_active_bhs(),取出激活且安裝的底半處理。調(diào)用宏clear_active_bh(),將上一步取出項(xiàng)所對(duì)應(yīng)的變量bh_active中的激活位清0根據(jù)取出的激活位,順序執(zhí)行在bh_base[]中注冊(cè)的底半處理操作bh機(jī)制的特點(diǎn):具有“串行化”的特點(diǎn),不適合于多cpu結(jié)構(gòu)。2.軟中斷機(jī)制軟中斷機(jī)制也是推遲內(nèi)核函數(shù)的執(zhí)行,與bh函數(shù)嚴(yán)格的串行執(zhí)行相比,軟中斷在任何時(shí)候都不需要串行化,同一個(gè)軟中斷的兩個(gè)實(shí)例完全可能在兩個(gè)CPU上同時(shí)執(zhí)行。因此軟中斷是可重入的,這給網(wǎng)絡(luò)部分帶來(lái)了好處。軟中斷本身是一種機(jī)制,既包含了bh機(jī)制,也包含了tasklet機(jī)制。軟中斷的執(zhí)行首先用open_softirq進(jìn)行初始化,然后通過(guò)softirq_init()執(zhí)行軟中斷。一個(gè)注冊(cè)的軟中斷必須在標(biāo)記以后才會(huì)被執(zhí)行。通常中斷處理程序會(huì)在返回前標(biāo)記它的軟中斷,使其在稍后執(zhí)行。內(nèi)核在do_IRQ()中執(zhí)行完一個(gè)中斷請(qǐng)求隊(duì)列中的中斷服務(wù)例程以后,都要檢查是否有軟中斷請(qǐng)求在等待執(zhí)行。在檢測(cè)到軟中斷請(qǐng)求后,在調(diào)用do_softirq()來(lái)執(zhí)行軟中斷服務(wù)例程。軟中斷機(jī)制的特點(diǎn)其執(zhí)行是“并行的”,可以重入的,即允許不同的cpu同時(shí)進(jìn)入對(duì)軟中斷服務(wù)例程的執(zhí)行,但不能讓它們相互干擾。3.tasklet機(jī)制是建立在軟中斷機(jī)制的基礎(chǔ)上,與bh機(jī)制類似。tasklet的初始化方法很多,可以靜態(tài)創(chuàng)建,也可以動(dòng)態(tài)創(chuàng)建。動(dòng)態(tài)創(chuàng)建的方法之一:由tasklet_init()完成tasklet的使用通過(guò)調(diào)用tasklet_schedule(),激活它的tasklet_struct中指針?biāo)赶虻姆?wù)程序。在tasklet被調(diào)用以后,只要有可能就盡可能早的運(yùn)行,若某個(gè)tasklet還沒得到運(yùn)行機(jī)會(huì)之前又有一個(gè)相同的tasklet又被調(diào)度了,經(jīng)過(guò)檢查重復(fù)后,他只運(yùn)行一次。若它已開始運(yùn)行,那么新的tasklet會(huì)被重新調(diào)度并且再次運(yùn)行。tasklet的特點(diǎn)同一個(gè)tasklet只能運(yùn)行在一個(gè)cpu上,而不同的tasklet可以同時(shí)運(yùn)行在不同的cpu上,即tasklet是非重入的。IRQ鎖軟IRQ鎖 有時(shí)不希望系統(tǒng)做底半處理,如系統(tǒng)正在做底半處理時(shí)被中斷,此時(shí)不希望在中斷的上半部分處理完以后再次進(jìn)入底半處理。unsignedintlocal_bh_count[NR_CPUS]start_bh_atomic(void)用來(lái)禁止底半處理
end_bh_atomic(void)恢復(fù)底半處理
softirq_trylock(cpu)判斷是否允許進(jìn)入底半處理
softirq_endlock(cpu)允許底半處理以上4個(gè)函數(shù)是在關(guān)中斷情況下執(zhí)行.硬IRQ鎖 除了軟件IRQ鎖以外,Linux還提供硬IRQ鎖,一般情況下,硬件的開中斷和關(guān)中斷不受限制,但在特殊情況下必需限制硬件的開關(guān)中斷,如在做中斷的上半部分處理時(shí)的開中斷就必需限制。硬IRQ鎖通過(guò)unsignedintlocal_irq_count[NR_CPUS]來(lái)實(shí)現(xiàn)。IRQ鎖的作用 底半處理既使用了軟IRQ鎖,也使用了硬IRQ鎖。實(shí)際上,定義IRQ鎖的目的主要是為了控制底半處理的重入。Linux的時(shí)鐘管理時(shí)鐘中斷時(shí)鐘中斷處理過(guò)程底半處理系統(tǒng)時(shí)間更新進(jìn)程時(shí)間片定時(shí)器中斷和時(shí)鐘中斷時(shí)鐘中斷是需要操作系統(tǒng)內(nèi)核特別處理的一個(gè)外部中斷時(shí)鐘中斷是操作系統(tǒng)的脈搏:維護(hù)系統(tǒng)時(shí)間、統(tǒng)計(jì)運(yùn)行數(shù)據(jù)、設(shè)計(jì)計(jì)時(shí)時(shí)鐘、促使進(jìn)程切換、監(jiān)督系統(tǒng)工作時(shí)鐘中斷時(shí)間系統(tǒng)(即時(shí)鐘)是計(jì)算機(jī)系統(tǒng)非常重要的組成部分PC機(jī)有兩個(gè)時(shí)鐘源:實(shí)時(shí)時(shí)鐘(RTC)和OS時(shí)鐘RTC也叫CMOS時(shí)鐘,是主板上的一塊靠電池供電的晶片(晶振),獨(dú)立于OS,也稱為硬件時(shí)鐘,為計(jì)算機(jī)提供計(jì)時(shí)標(biāo)準(zhǔn),是最底層的時(shí)鐘數(shù)據(jù)OS時(shí)鐘由硬件(定時(shí)/計(jì)數(shù)器)和軟件(時(shí)鐘中斷處理程序)組成。定時(shí)/計(jì)數(shù)器從RTC接收輸入脈沖,并計(jì)數(shù),每次計(jì)數(shù)到期就產(chǎn)生一個(gè)輸出脈沖,再?gòu)念^計(jì)數(shù)
可編程定時(shí)/計(jì)數(shù)器由計(jì)數(shù)硬件和通信寄存器組成,通信寄存器負(fù)責(zé)在計(jì)數(shù)硬件和操作系統(tǒng)之間通信時(shí)鐘中斷產(chǎn)生過(guò)程IRQ08259A-1中斷控制器8253/8254定時(shí)/記數(shù)器RTC晶振1.193180MHzOut0Out1Out2DRAM刷新?lián)P聲器接CPU中斷輸入端時(shí)鐘中斷處理過(guò)程OS注冊(cè)時(shí)鐘中斷處理程序timer_interrupt1、定時(shí)/計(jì)數(shù)器產(chǎn)生輸出脈沖時(shí),中斷處理器送出中斷信號(hào),CPU根據(jù)IRQ號(hào)查中斷描述符表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,這里或者返回到被中斷的進(jìn)程,或者啟動(dòng)調(diào)度程序,完成新一輪調(diào)度底半處理1、時(shí)鐘底半處理函數(shù)timer_bh定義2、時(shí)鐘任務(wù)隊(duì)列處理函數(shù)tqueue_bh定義3、底半處理的工作(1)更新系統(tǒng)時(shí)間(2)更新當(dāng)前進(jìn)程時(shí)間片(3)處理老定時(shí)器(4)處理新定時(shí)器(5)處理時(shí)鐘任務(wù)隊(duì)列系統(tǒng)時(shí)間系統(tǒng)時(shí)間結(jié)構(gòu)
structtimeval{inttv_sec;//秒
inttv_usec;//微秒
}初始化:函數(shù)time_init從CMOS時(shí)鐘取出當(dāng)前時(shí)間,通過(guò)函數(shù)mktime轉(zhuǎn)化成自1970-01-0100:00:00以來(lái)的秒數(shù),存入全局變量xtime.tv_sec中,將xtime.tv_usec初始化為0累計(jì)系統(tǒng)時(shí)間
voidupdate_wall_time(unsignedlongticks)
其中ticks是從上次時(shí)鐘中斷底半處理以來(lái)已經(jīng)過(guò)去的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值,累計(jì)秒數(shù),減少微秒數(shù):if(xtime.tv_usec>=1000000){xtime.tv_usec-=1000000;xtime.tv_sec++;second_overflow();}時(shí)間的轉(zhuǎn)換和修改
LINUX提供多個(gè)系統(tǒng)調(diào)用來(lái)轉(zhuǎn)換和設(shè)置系統(tǒng)時(shí)間更新進(jìn)程時(shí)間片反映進(jìn)程運(yùn)行情況,若時(shí)間片用完則通知當(dāng)前進(jìn)程放棄CPU通過(guò)函數(shù)update_process_times更新時(shí)間片(1)計(jì)算從上次底半處理后在用戶態(tài)發(fā)生的時(shí)鐘中斷次數(shù):user=ticks-system;
(2)如果當(dāng)前進(jìn)程不是0號(hào)進(jìn)程,則a.進(jìn)程時(shí)間片減ticks:current->counter-=ticks;b.若時(shí)間片已用完,則要求進(jìn)程放棄CPU:
if(p->counter<0){p->counter=0;p->need_resched=1;}c.根據(jù)user調(diào)整變量kstat中的統(tǒng)計(jì)信息
(3)調(diào)用函數(shù)update_one_process修改當(dāng)前進(jìn)程的其它與時(shí)間相關(guān)的信息函數(shù)update_one_processa.累計(jì)進(jìn)程在當(dāng)前CPU消耗的用戶態(tài)時(shí)間:
p->per_cpu_utime[cpu]+=user;b.累計(jì)進(jìn)程在當(dāng)前CPU消耗的核心態(tài)時(shí)間:
p->per_cpu_stime[cpu]+=system;c.累計(jì)進(jìn)程消耗的用戶態(tài)時(shí)間:
p->times.tms_utime+=user;d.累計(jì)進(jìn)程消耗的核心態(tài)時(shí)間:
p->times.tms_stime+=system;e.若進(jìn)程消耗總時(shí)間超過(guò)當(dāng)前時(shí)間界限,則在總量達(dá)到整秒時(shí)向進(jìn)程發(fā)送SIGXCPU信號(hào)f.若進(jìn)程消耗總時(shí)間超過(guò)最大時(shí)間界限,則向進(jìn)程發(fā)送SIGKILL信號(hào),強(qiáng)行殺死進(jìn)程
h.若進(jìn)程定義有virtual定時(shí)器,則根據(jù)消耗的用戶態(tài)時(shí)間處理它
i.若進(jìn)程定義有prof定時(shí)器,則根據(jù)消耗的用戶態(tài)和核心態(tài)時(shí)間處理它定時(shí)器在進(jìn)程運(yùn)行時(shí),系統(tǒng)提供的virtual和prof定時(shí)器可以實(shí)現(xiàn)定時(shí)為了在任何時(shí)候都能夠定時(shí),LINUX提供兩類根據(jù)系統(tǒng)時(shí)間定時(shí)且與進(jìn)程狀態(tài)無(wú)關(guān)的定時(shí)器:老定時(shí)器和新定時(shí)器老定時(shí)器結(jié)構(gòu)
structtimer_struct{unsignedlongexpires;//終止時(shí)間
void(*fn)(void);//到期要執(zhí)行的函數(shù)
}最多有32個(gè)老定時(shí)器系統(tǒng)定義一個(gè)位圖timer_active來(lái)標(biāo)識(shí)已注冊(cè)并處于活動(dòng)狀態(tài)的定時(shí)器,如果某位被置1則相應(yīng)的定時(shí)器活動(dòng)通過(guò)以下函數(shù)在底半處理中檢查并處理老定時(shí)器:
run_old_timers
順序搜索位圖,發(fā)現(xiàn)某位被設(shè)置就檢查是否到期。如果到期就清除位圖中該定時(shí)器的標(biāo)記,表示處理完成;執(zhí)行定時(shí)器指定操作tp->fn();每處理一個(gè)定時(shí)器就開一次中斷sti()新定時(shí)器新定時(shí)器結(jié)構(gòu)
structtimer_list{structtimer_list*next;structtimer_list*prev;unsignedlongexpires;//終止時(shí)間
unsignedlongdata;void(*function)(unsignedlong);};其中data是傳給處理函數(shù)function的參數(shù)next和prev兩個(gè)指針將定時(shí)器連成雙向鏈表tvecstv5tv4tv301234indexindextimer_l
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年個(gè)人舊車轉(zhuǎn)讓協(xié)議范本
- 2024高效化妝品委托加工協(xié)議范例
- 事業(yè)單位考試計(jì)算機(jī)基礎(chǔ)知識(shí)大綱和試題
- 2024年度醫(yī)療用品購(gòu)銷協(xié)議模板
- 2024年度住宅樓施工項(xiàng)目協(xié)議目錄
- 2024年股票投資合作協(xié)議模板
- 2024年重慶市區(qū)住宅租賃協(xié)議
- 2024年軟件服務(wù)行業(yè)協(xié)議樣本
- 2024專項(xiàng)彩妝產(chǎn)品代理銷售協(xié)議
- 文書模板-《臨時(shí)勞務(wù)安全免責(zé)協(xié)議書》
- 20222023學(xué)年浙江省寧波市鄞州實(shí)驗(yàn)中學(xué)八年級(jí)(上)期中語(yǔ)文試卷(解析)
- 人教版數(shù)學(xué)二年級(jí)下冊(cè)德育滲透教案《統(tǒng)計(jì)》例2教學(xué)設(shè)計(jì)
- 超越指標(biāo):存量時(shí)代降本增效的利器
- 《中小學(xué)書法教育指導(dǎo)綱要》解讀
- 住院醫(yī)師規(guī)范化培訓(xùn)臨床技能核課件
- 青島版五四制五年級(jí)上冊(cè)數(shù)學(xué)應(yīng)用題216道
- 工程造價(jià)鑒定十大要點(diǎn)與案例分析
- 2024年金融行業(yè)發(fā)展趨勢(shì)
- 印刷設(shè)計(jì)行業(yè)檔案管理制度完善
- 地?zé)豳Y源勘查與開發(fā)利用規(guī)劃編制規(guī)程
- 三年級(jí)上海市滬版英語(yǔ)第一學(xué)期上學(xué)期期中考試試卷
評(píng)論
0/150
提交評(píng)論