深入剖析Linux中斷機(jī)制_第1頁(yè)
深入剖析Linux中斷機(jī)制_第2頁(yè)
深入剖析Linux中斷機(jī)制_第3頁(yè)
深入剖析Linux中斷機(jī)制_第4頁(yè)
深入剖析Linux中斷機(jī)制_第5頁(yè)
已閱讀5頁(yè),還剩42頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

1、深入剖析Linux中斷機(jī)制中斷概述【摘要】本文詳解了Linux內(nèi)核的中斷實(shí)現(xiàn)機(jī)制。首先介紹了中斷的一些基本概念,然后分析了面向?qū)ο蟮腖inux中斷的組織形式、三種主要數(shù)據(jù)結(jié)構(gòu)及其之間的關(guān)系。隨后介紹了Linux處理異常和中斷的基本流程,在此基礎(chǔ)上分析了中斷處理的詳細(xì)流程,包括保存現(xiàn)場(chǎng)、中斷處理、中斷退出時(shí)的軟中斷執(zhí)行及中斷返回時(shí)的進(jìn)程切換等問題。最后介紹了中斷相關(guān)的API,包括中斷注冊(cè)和釋放、中斷關(guān)閉和使能、如何編寫中斷ISR、共享中斷、中斷上下文中斷狀態(tài)等?!娟P(guān)鍵字】中斷,異常,hw_interrupt_type,irq_desc_t,irqaction,asm_do_IRQ,軟中斷,進(jìn)程切

2、換,中斷注冊(cè)釋放request_irq,free_irq,共享中斷,可重入,中斷上下文1 中斷概述1.1 為什么需要中斷?處理器的速度跟外圍硬件設(shè)備的速度往往不在一個(gè)數(shù)量級(jí)上,因此,如果內(nèi)核采取讓處理器向硬件發(fā)出一個(gè)請(qǐng)求,然后專門等待回應(yīng)的辦法,顯然差強(qiáng)人意。既然硬件的響應(yīng)這么慢,那么內(nèi)核就應(yīng)該在此期間處理其他事務(wù),等到硬件真正完成了請(qǐng)求的操作之后,再回過頭來對(duì)它進(jìn)行處理。想要實(shí)現(xiàn)這種功能,輪詢(polling可能會(huì)是一種解決辦法??梢宰寖?nèi)核定期對(duì)設(shè)備的狀態(tài)進(jìn)行查詢,然后做出相應(yīng)的處理。不過這種方法很可能會(huì)讓內(nèi)核做不少無用功,因?yàn)闊o論硬件設(shè)備是正在忙碌著完成任務(wù)還是已經(jīng)大功告成,輪詢總會(huì)周期性

3、地重復(fù)執(zhí)行。更好的辦法是由我們來提供一種機(jī)制,讓硬件在需要的時(shí)候再向內(nèi)核發(fā)出信號(hào)(變內(nèi)核主動(dòng)為硬件主動(dòng)。這就是中斷機(jī)制。1.2 中斷的表示形式硬件設(shè)備生成中斷的時(shí)候并不考慮與處理器的時(shí)鐘同步換句話說就是中斷隨時(shí)可以產(chǎn)生。因此,內(nèi)核隨時(shí)可能因?yàn)樾碌絹淼闹袛喽淮驍?。從物理學(xué)的角度看,中斷是一種電信號(hào),由硬件設(shè)備生成,并直接送入中斷控制器的輸入引腳上。然后再由中斷控制器向處理器發(fā)送相應(yīng)的信號(hào)。處理器一經(jīng)檢測(cè)到此信號(hào),便中斷自己的當(dāng)前工作轉(zhuǎn)而處理中斷。此后,處理器會(huì)通知操作系統(tǒng)已經(jīng)產(chǎn)生中斷,這樣,操作系統(tǒng)就可以對(duì)這個(gè)中斷進(jìn)行適當(dāng)?shù)奶幚砹?。不同的設(shè)備對(duì)應(yīng)的中斷不同,而每個(gè)中斷都通過一個(gè)惟一的數(shù)字標(biāo)識(shí)。

4、因此,來自鍵盤的中斷就有別干來自硬盤的中斷,從而使得操作系統(tǒng)能夠?qū)χ袛噙M(jìn)行區(qū)分,并知道哪個(gè)硬件設(shè)備產(chǎn)生了哪個(gè)中斷。這樣,操作系統(tǒng)才能給不同的中斷提供不同的中斷處理程序。這些中斷值通常被為中斷請(qǐng)求(IRQ線。通常IRQ都是一些數(shù)值量。例如在PC上,IRQ0是時(shí)鐘中斷,而IRQ 1是鍵盤中斷。但并非所有的中斷號(hào)都是這樣嚴(yán)格定義的。例如,對(duì)于連接在PCI總線上的設(shè)備而言,中斷是動(dòng)態(tài)分配的。而在嵌入式系統(tǒng)中,由于中斷線有限,一般外設(shè)和中斷都是一一匹配的,很少有動(dòng)態(tài)分配中斷的。不管怎樣,重點(diǎn)在于特定的中斷總是與特定的設(shè)備相關(guān)聯(lián),并且內(nèi)核要知道這些信息。1.3 異常在操作系統(tǒng)中,討論中斷就不能不提及異常。

5、廣義的中斷可分為同步(synchronous)中斷和異步(asynchronous)中斷:同步中斷:是當(dāng)指令執(zhí)行時(shí)由 CPU 控制單元產(chǎn)生,之所以稱為同步,是因?yàn)橹挥性谝粭l指令執(zhí)行完畢后 CPU 才會(huì)發(fā)出中斷,而不是發(fā)生在代碼指令執(zhí)行期間,比如系統(tǒng)調(diào)用。異步中斷:是指由其他硬件設(shè)備依照 CPU 時(shí)鐘信號(hào)隨機(jī)產(chǎn)生,即意味著中斷能夠在指令之間發(fā)生,例如鍵盤中斷。一般由處理器本身產(chǎn)生的同步中斷稱為異常(exception),異步中斷被稱為中斷(interrupt)。中斷可分為可屏蔽中斷(Maskable interrupt)和非屏蔽中斷(Nomaskable interrupt)。異??煞譃楣收希╢

6、ault)、陷阱(trap)、終止(abort)三類。表 1:中斷類別及其行為類別原因異步/同步返回行為中斷來自I/O設(shè)備的信號(hào)異步總是返回到下一條指令陷阱有意的異常同步總是返回到下一條指令故障潛在可恢復(fù)的錯(cuò)誤同步返回到當(dāng)前指令終止不可恢復(fù)的錯(cuò)誤同步不會(huì)返回在處理器執(zhí)行到由于編程失誤而導(dǎo)致的錯(cuò)誤指令(例如被0除的時(shí)候,或者是在執(zhí)行期間出現(xiàn)特殊情況(例如缺頁(yè),必須靠?jī)?nèi)核來處理的時(shí)候,處理器就會(huì)產(chǎn)生一個(gè)異常。因?yàn)樵S多處理器體系結(jié)構(gòu)處理異常與處理中斷的方式類似,因此,內(nèi)核對(duì)它們的處理也很類似。通過軟中斷實(shí)現(xiàn)系統(tǒng)調(diào)用,那就是陷人內(nèi)核,然后引起一種特殊的異常系統(tǒng)調(diào)用處理程序異常。你將會(huì)看到,中斷的工作方

7、式與之類似,其差異只在于中斷是由硬件而不是軟件引起的。1.4 中斷處理程序在響應(yīng)一個(gè)特定中斷的時(shí)候,內(nèi)核會(huì)執(zhí)行一個(gè)函數(shù),該函數(shù)叫做中斷處理程序(interrupt handler或中斷服務(wù)例程(interrupt service routine, ISR。產(chǎn)生中斷的每個(gè)設(shè)備都有一個(gè)相應(yīng)的中斷處理程序。在Linux中,中斷處理程序看起來就是普普通通的C函數(shù)。只不過這些函數(shù)必須按照特定的類型聲明,以便內(nèi)核能夠以標(biāo)準(zhǔn)的方式傳遞處理程序的信息。中斷處理程序與其他內(nèi)核函數(shù)的真正區(qū)別在于:中斷處理程序是被內(nèi)核調(diào)用來響應(yīng)中斷的,而它們運(yùn)行于我們稱之為中斷上下文的特殊上下文中。中斷可能隨時(shí)發(fā)生,因此中斷處理程

8、序也就隨時(shí)可能執(zhí)行。所以必須保證中斷處理程序能夠快速執(zhí)行,這樣才能保證盡可能快地恢復(fù)中斷代碼的執(zhí)行。因此,盡管對(duì)硬件而言,迅速對(duì)其中斷進(jìn)行服務(wù)非常重要,但對(duì)系統(tǒng)的其他部分而言,讓中斷處理程序在盡可能短的時(shí)間內(nèi)完成運(yùn)行也同樣重要。即使是最精簡(jiǎn)版的中斷服務(wù)程序,它也要與硬件進(jìn)行交互,告訴該設(shè)備中斷已被接收。我們可以考慮一下網(wǎng)絡(luò)設(shè)備的中斷處理程序面臨的挑戰(zhàn)。該處理程序除了要對(duì)硬件應(yīng)答,還要把來自硬件的網(wǎng)絡(luò)數(shù)據(jù)包拷貝到內(nèi)存,對(duì)其進(jìn)行處理后再交給合適的協(xié)議棧或應(yīng)用程序。顯而易見,這種工作量不會(huì)太小,尤其對(duì)于如今的千兆比特和萬兆比特以太網(wǎng)卡而言。因此我們一般把中斷處理切為兩個(gè)部分或兩半。中斷處理程序是上半

9、部 (top half接收到一個(gè)中斷,它就立即開始執(zhí)行,但只做有嚴(yán)格時(shí)限的工作,例如對(duì)接收的中斷進(jìn)行應(yīng)答或復(fù)位硬件,這些工作都是在所有中斷被禁止的情況下完成的。能夠被允許稍后完成的工作會(huì)推遲到下半部(bottom half去。此后,在合適的時(shí)機(jī),下半部會(huì)被開中斷執(zhí)行。以網(wǎng)卡作為實(shí)例,當(dāng)網(wǎng)卡接收流入網(wǎng)絡(luò)的數(shù)據(jù)包時(shí),需要通知內(nèi)核數(shù)據(jù)包到了。網(wǎng)卡需要立即完成這件事,從而優(yōu)化網(wǎng)絡(luò)的吞吐量和傳輸周期,以避免超時(shí)。因此,網(wǎng)卡立即發(fā)出中斷:嘀,內(nèi)核,我這里有最新數(shù)據(jù)包了。內(nèi)核通過執(zhí)行網(wǎng)卡已注冊(cè)的中斷處理程序來做出應(yīng)答。中斷開始執(zhí)行,應(yīng)答硬件,拷貝最新的網(wǎng)絡(luò)數(shù)據(jù)包到內(nèi)存,然后讀取網(wǎng)卡更多的數(shù)據(jù)包。這些都是重要

10、、緊迫而又與硬件相關(guān)的工作。處理和操作數(shù)據(jù)包的其他工作在隨后的下半部中進(jìn)行。深入剖析Linux中斷機(jī)制之二Linux中斷的組織形式【摘要】本文詳解了Linux內(nèi)核的中斷實(shí)現(xiàn)機(jī)制。首先介紹了中斷的一些基本概念,然后分析了面向?qū)ο蟮腖inux中斷的組織形式、三種主要數(shù)據(jù)結(jié)構(gòu)及其之間的關(guān)系。隨后介紹了Linux處理異常和中斷的基本流程,在此基礎(chǔ)上分析了中斷處理的詳細(xì)流程,包括保存現(xiàn)場(chǎng)、中斷處理、中斷退出時(shí)的軟中斷執(zhí)行及中斷返回時(shí)的進(jìn)程切換等問題。最后介紹了中斷相關(guān)的API,包括中斷注冊(cè)和釋放、中斷關(guān)閉和使能、如何編寫中斷ISR、共享中斷、中斷上下文中斷狀態(tài)等?!娟P(guān)鍵字】中斷,異常,hw_interr

11、upt_type,irq_desc_t,irqaction,asm_do_IRQ,軟中斷,進(jìn)程切換,中斷注冊(cè)釋放request_irq,free_irq,共享中斷,可重入,中斷上下文1 Linux中斷的組織形式1.1 IRQ描述符irq_desc對(duì)于每個(gè)IRQ中斷線,Linux都用一個(gè)irq_desc_t數(shù)據(jù)結(jié)構(gòu)來描述,我們把它叫做IRQ描述符,NR_IRQS個(gè)IRQ形成一個(gè)全局?jǐn)?shù)組irq_desc,其定義在/include/linux/irq.h中:struct irq_desc 中斷描述符148struct irq_desc 149 irq_flow_handler_t handle_ir

12、q;150 struct irq_chip *chip;151 void *handler_data;152 void *chip_data;153 struct irqaction *action; /* IRQ action list */154 unsigned int status; /* IRQ status */155156 unsigned int depth; /* nested irq disables */157 unsigned int wake_depth; /* nested wake enables */158 unsigned int irq_count; /*

13、For detecting broken IRQs */159 unsigned int irqs_unhandled;160 spinlock_t lock;161#ifdef CONFIG_SMP162 cpumask_t affinity;163 unsigned int cpu;164#endif171 const char *name;172 _cacheline_aligned;173174extern struct irq_desc irq_descNR_IRQS;handle_irq:上層的通用中斷處理函數(shù)指針,如果未設(shè)置則默認(rèn)為_do_IRQ(。通常針對(duì)電平觸發(fā)或者邊沿觸發(fā)有

14、不同的處理函數(shù)。每個(gè)中斷線可分別設(shè)置;chip:底層中斷的各種控制訪問方法集合,各個(gè)CPU實(shí)現(xiàn)的都不同,這屬于面向?qū)ο蟮闹袛嗵幚矸绞街凶畹讓拥囊徊糠郑籬andler_data:附加參數(shù),用于handle_irq;chip_data:平臺(tái)相關(guān)的附加參數(shù),用于chip;action:指向一個(gè)單向鏈表的指針,這個(gè)鏈表就是對(duì)中斷服務(wù)例程進(jìn)行描述的irqaction結(jié)構(gòu);status:中斷當(dāng)前的狀態(tài);depth:中斷關(guān)閉打開的層數(shù)。如果啟用這條IRQ中斷線,depth則為0,如果禁用這條IRQ中斷線不止一次,則為一個(gè)正數(shù)。如果depth等于0,每當(dāng)調(diào)用一次disable_irq( ,該函數(shù)就對(duì)這個(gè)域的值

15、加1,同時(shí)該函數(shù)就禁用這條IRQ中斷線。相反,每當(dāng)調(diào)用enable_irq( 函數(shù)時(shí),該函數(shù)就對(duì)這個(gè)域的值減1;如果depth變?yōu)?,該函數(shù)就啟用這條IRQ中斷線。Lock:此中斷描述符為全局共享暑假,對(duì)于SMP需要互斥訪問Dir: /proc/irq/ 入口Name: /proc/interrupts 中顯示的中斷名稱“_cacheline_aligned”表示這個(gè)數(shù)據(jù)結(jié)構(gòu)的存放按32字節(jié)(高速緩存行的大?。┻M(jìn)行對(duì)齊,以便于將來存放在高速緩存并容易存取157void _init init_IRQ(void158159 int irq;160161 for (irq = 0; irq <

16、 NR_IRQS; irq+162 irq_descirq.status |= IRQ_NOREQUEST | IRQ_DELAYED_DISABLE |163 IRQ_NOPROBE;164165#ifdef CONFIG_SMP166 bad_irq_desc.affinity = CPU_MASK_ALL;167 bad_irq_desc.cpu = smp_processor_id(;168#endif169 init_arch_irq(;1701.2 中斷控制器描述符irq_chip 由于CPU不同,故每個(gè)處理器對(duì)于中斷的處理方式不一樣。Linux為了實(shí)現(xiàn)統(tǒng)一的中斷處理,提供了底層的

17、中斷處理抽象接口,對(duì)于每個(gè)平臺(tái)都需要實(shí)現(xiàn)底層的接口函數(shù)。這樣對(duì)于上層的中斷通用處理程序就無需任何改動(dòng)。struct irq_chip 片級(jí)的中斷描述符94struct irq_chip 95 const char *name;96 unsigned int (*startup(unsigned int irq;97 void (*shutdown(unsigned int irq;98 void (*enable(unsigned int irq;99 void (*disable(unsigned int irq;100101 void (*ack(unsigned int irq;102

18、void (*mask(unsigned int irq;103 void (*mask_ack(unsigned int irq;104 void (*unmask(unsigned int irq;105 void (*eoi(unsigned int irq;106107 void (*end(unsigned int irq;108 void (*set_affinity(unsigned int irq, cpumask_t dest;109 int (*retrigger(unsigned int irq;110 int (*set_type(unsigned int irq, u

19、nsigned int flow_type;111 int (*set_wake(unsigned int irq, unsigned int on;121 const char *typename;122;Name:用于/proc/interruptsStartup:默認(rèn)為enable if NULLShutdown:默認(rèn)為 disable if NULLEnable:允許中斷,默認(rèn)為unmask if NULLDisable:禁止中斷,默認(rèn)為mask if NULLAck:響應(yīng)一個(gè)中斷Mask:mask 一個(gè)中斷源,通常是關(guān)閉中斷mask_ack:響應(yīng)并mask中斷源unmask:unma

20、sk中斷源set_type:設(shè)置中斷觸發(fā)方式IRQ_TYPE_LEVEL大多數(shù)控制方法都是重復(fù)的,基本上只要有中斷響應(yīng)、中斷屏蔽、中斷開啟、中斷觸發(fā)類型設(shè)置等方法就可以滿足要求了。其他各種方法基本上和這些相同。提供了中斷響應(yīng)、打開、關(guān)閉、設(shè)置觸發(fā)類型等底層方法的接口static struct irq_chip at91_aic_chip = .name = "AIC",.ack = at91_aic_mask_irq,.mask = at91_aic_mask_irq,.unmask = at91_aic_unmask_irq,.set_type = at91_aic_se

21、t_type,.set_wake = at91_aic_set_wake,;124/*125 * Initialize the AIC interrupt controller.126 */127void _init at91_aic_init(unsigned int priorityNR_AIC_IRQS128129 unsigned int i;130131 /*132 * The IVR is used by macro get_irqnr_and_base to read and verify.133 * The irq number is NR_AIC_IRQS when a sp

22、urious interrupt has occurred.134 */135 for (i = 0; i < NR_AIC_IRQS; i+ 136 /* Put irq number in Source Vector Register: */137 at91_sys_write(AT91_AIC_SVR(i, i;138 /* Active Low interrupt, with the specified priority */139 at91_sys_write(AT91_AIC_SMR(i, AT91_AIC_SRCTYPE_LOW | priorityi;140141 set

23、_irq_chip(i, &at91_aic_chip;142 set_irq_handler(i, do_level_IRQ;143 set_irq_flags(i, IRQF_VALID | IRQF_PROBE;144145 /* Perform 8 End Of Interrupt Command to make sure AIC will not Lock out nIRQ */146 if (i < 8147 at91_sys_write(AT91_AIC_EOICR, 0;148 149150 /*151 * Spurious Interrupt ID in Spu

24、rious Vector Register is NR_AIC_IRQS152 * When there is no current interrupt, the IRQ Vector Register reads the value stored in AIC_SPU153 */154 at91_sys_write(AT91_AIC_SPU, NR_AIC_IRQS;155156 /* No debugging in AIC: Debug (Protect Control Register */157 at91_sys_write(AT91_AIC_DCR, 0;158159 /* Disa

25、ble and clear all interrupts initially */160 at91_sys_write(AT91_AIC_IDCR, 0xFFFFFFFF;161 at91_sys_write(AT91_AIC_ICCR, 0xFFFFFFFF;162163以下這些宏定義都是為保持兼容性而設(shè)置的,后續(xù)版本中將徹底刪除47#define do_level_IRQ handle_level_irq48#define do_edge_IRQ handle_edge_irq49#define do_simple_IRQ handle_simple_irq50#define irqdes

26、c irq_desc51#define irqchip irq_chip55#define SA_INTERRUPT IRQF_DISABLED57#define SA_SHIRQ IRQF_SHARED6061#define SA_TRIGGER_LOW IRQF_TRIGGER_LOW62#define SA_TRIGGER_HIGH IRQF_TRIGGER_HIGH63#define SA_TRIGGER_FALLING IRQF_TRIGGER_FALLING64#define SA_TRIGGER_RISING IRQF_TRIGGER_RISING65#define SA_TRI

27、GGER_MASK IRQF_TRIGGER_MASKlinux/kernel/irq/chip.chandle_level_irq1.3 中斷服務(wù)例程描述符irqaction在IRQ描述符中我們看到指針action的結(jié)構(gòu)為irqaction,它是為多個(gè)設(shè)備能共享一條中斷線而設(shè)置的一個(gè)數(shù)據(jù)結(jié)構(gòu),代表了每個(gè)注冊(cè)中斷對(duì)應(yīng)的信息。在include/linux/interrupt.h中定義如下:67typedef irqreturn_t (*irq_handler_t(int, void *;6869struct irqaction 70 irq_handler_t handler;71 unsign

28、ed long flags;72 cpumask_t mask;73 const char *name;74 void *dev_id;75 struct irqaction *next;76 int irq;77 struct proc_dir_entry *dir;78;Handler:指向一個(gè)具體I/O設(shè)備的中斷服務(wù)例程。這是允許多個(gè)設(shè)備共享同一中斷線的關(guān)鍵域,中斷線可以相同,但處理函數(shù)可以不一樣。Flags:用一組標(biāo)志描述中斷線與I/O設(shè)備之間的關(guān)系。SA_INTERRUPT 中斷處理程序必須以禁用中斷來執(zhí)行。此標(biāo)志表明給定的中斷處理程序是一個(gè)快速中斷處理程序(fast interru

29、pt handler。過去,Linux將中斷處理程序分為快速和慢速兩種。那些可以迅速執(zhí)行但調(diào)用頻率可能會(huì)很高的中斷服務(wù)程序,會(huì)被貼上這樣的標(biāo)簽。通常這樣做需要修改中斷處理程序的行為,使它們能夠盡可能快地執(zhí)行?,F(xiàn)在,加不加此標(biāo)志的區(qū)別只剩下一條了:在本地處理器上,快速中斷處理程序在禁止所有中斷的情況下運(yùn)行。這使得快速中斷處理程序能夠不受其他中斷干擾,得以迅速執(zhí)行。而默認(rèn)情況下(沒有這個(gè)標(biāo)志,除了正運(yùn)行的中斷處理程序?qū)?yīng)的那條中斷線被屏蔽外,其他所有中斷都是激活的。除了時(shí)鐘中斷外,絕大多數(shù)中斷都不使用該標(biāo)志。SA_SHIRQ 此標(biāo)志表明可以在多個(gè)中斷處理程序之間共享中斷線。在同一個(gè)給定線上注冊(cè)的每

30、個(gè)處理程序必須指定這個(gè)標(biāo)志:否則,在每條線上只能有一個(gè)處理程序。各項(xiàng)該中斷線的每一個(gè)例程都需要設(shè)置此標(biāo)志。Name:I/O設(shè)備名(通過讀取/proc/interrupts文件,可以看到,在列出中斷號(hào)時(shí)也顯示設(shè)備名。)dev_id:對(duì)于共享中斷,此特定值用來區(qū)分各中斷。當(dāng)一個(gè)中斷處理程序需要釋放時(shí),dev_id將提供惟一的標(biāo)志信息(cookie,以便從共享中斷線的諸多中斷處理程序中刪除指定的那一個(gè)。如果沒有這個(gè)參數(shù),那么內(nèi)核不可能知道在給定的中斷線上到底要?jiǎng)h除哪一個(gè)處理程序。如果無需共享中斷線,那么將該參數(shù)賦為空值(NULL就可以了,但是,如果中斷線是被共享的,那么就必須傳遞惟一的信息。另外,內(nèi)

31、核每次調(diào)用中斷處理程序時(shí),都會(huì)把這個(gè)指針傳遞給它。實(shí)踐中往往會(huì)通過它傳遞驅(qū)動(dòng)程序的設(shè)備結(jié)構(gòu):這個(gè)指針是惟一的,而且有可能在中斷處理程序內(nèi)及設(shè)備模式中被用到。Next:指向irqaction描述符鏈表的下一個(gè)元素。共享同一中斷線的每個(gè)硬件設(shè)備都有其對(duì)應(yīng)的中斷服務(wù)例程,鏈表中的每個(gè)元素就是對(duì)相應(yīng)設(shè)備及中斷服務(wù)例程的描述。Irq:對(duì)應(yīng)的中斷號(hào)dir:proc文件系統(tǒng)對(duì)應(yīng)的入口1.4 三者的關(guān)系三個(gè)主要的數(shù)據(jù)結(jié)構(gòu)包含了與 IRQ 相關(guān)的所有信息:hw_interrupt_type、irq_desc_t 和 irqaction,下圖解釋了它們之間是如何關(guān)聯(lián)的。中斷服務(wù)例程ISR是irqaction 的H

32、andler成員。中斷的處理是一種面向?qū)ο蟮臋C(jī)制,通過三個(gè)數(shù)據(jù)結(jié)果實(shí)現(xiàn)了三層結(jié)構(gòu),底層是和具體硬件相關(guān)的中斷處理響應(yīng)等,中間層是統(tǒng)一的中斷處理流程,最上層是特定的中斷處理例程。IRQ 結(jié)構(gòu)之間的關(guān)系深入剖析Linux中斷機(jī)制之三Linux對(duì)異常和中斷的處理Sailor_forever sailing_9806 轉(zhuǎn)載請(qǐng)注明【摘要】本文詳解了Linux內(nèi)核的中斷實(shí)現(xiàn)機(jī)制。首先介紹了中斷的一些基本概念,然后分析了面向?qū)ο蟮腖inux中斷的組織形式、三種主要數(shù)據(jù)結(jié)構(gòu)及其之間的關(guān)系。隨后介紹了Linux處理異常和中斷的基本流程,在此基礎(chǔ)上分析了中斷處理的詳細(xì)流程,包括保存現(xiàn)場(chǎng)、中斷處理、中斷退出時(shí)的軟中

33、斷執(zhí)行及中斷返回時(shí)的進(jìn)程切換等問題。最后介紹了中斷相關(guān)的API,包括中斷注冊(cè)和釋放、中斷關(guān)閉和使能、如何編寫中斷ISR、共享中斷、中斷上下文中斷狀態(tài)等?!娟P(guān)鍵字】中斷,異常,hw_interrupt_type,irq_desc_t,irqaction,asm_do_IRQ,軟中斷,進(jìn)程切換,中斷注冊(cè)釋放request_irq,free_irq,共享中斷,可重入,中斷上下文1 Linux對(duì)異常和中斷的處理1.1 異常處理Linux利用異常來達(dá)到兩個(gè)截然不同的目的:² 給進(jìn)程發(fā)送一個(gè)信號(hào)以通報(bào)一個(gè)反常情況² 管理硬件資源對(duì)于第一種情況,例如,如果進(jìn)程執(zhí)行了一個(gè)被0除的操作,CP

34、U則會(huì)產(chǎn)生一個(gè)“除法錯(cuò)誤”異常,并由相應(yīng)的異常處理程序向當(dāng)前進(jìn)程發(fā)送一個(gè)SIGFPE信號(hào)。當(dāng)前進(jìn)程接收到這個(gè)信號(hào)后,就要采取若干必要的步驟,或者從錯(cuò)誤中恢復(fù),或者終止執(zhí)行(如果這個(gè)信號(hào)沒有相應(yīng)的信號(hào)處理程序)。內(nèi)核對(duì)異常處理程序的調(diào)用有一個(gè)標(biāo)準(zhǔn)的結(jié)構(gòu),它由以下三部分組成:² 在內(nèi)核棧中保存大多數(shù)寄存器的內(nèi)容(由匯編語(yǔ)言實(shí)現(xiàn))² 調(diào)用C編寫的異常處理函數(shù)² 通過ret_from_exception(函數(shù)從異常退出。1.2 中斷處理當(dāng)一個(gè)中斷發(fā)生時(shí),并不是所有的操作都具有相同的急迫性。事實(shí)上,把所有的操作都放進(jìn)中斷處理程序本身并不合適。需要時(shí)間長(zhǎng)的、非重要的操作應(yīng)該推

35、后,因?yàn)楫?dāng)一個(gè)中斷處理程序正在運(yùn)行時(shí),相應(yīng)的IRQ中斷線上再發(fā)出的信號(hào)就會(huì)被忽略。另外中斷處理程序不能執(zhí)行任何阻塞過程,如I/O設(shè)備操作。因此,Linux把一個(gè)中斷要執(zhí)行的操作分為下面的三類:² 緊急的(Critical)這樣的操作諸如:中斷到來時(shí)中斷控制器做出應(yīng)答,對(duì)中斷控制器或設(shè)備控制器重新編程,或者對(duì)設(shè)備和處理器同時(shí)訪問的數(shù)據(jù)結(jié)構(gòu)進(jìn)行修改。這些操作都是緊急的,應(yīng)該被很快地執(zhí)行,也就是說,緊急操作應(yīng)該在一個(gè)中斷處理程序內(nèi)立即執(zhí)行,而且是在禁用中斷的狀態(tài)下。² 非緊急的(Noncritical)這樣的操作如修改那些只有處理器才會(huì)訪問的數(shù)據(jù)結(jié)構(gòu)(例如,按下一個(gè)鍵后,讀掃描碼

36、)。這些操作也要很快地完成,因此,它們由中斷處理程序立即執(zhí)行,但在啟用中斷的狀態(tài)下。² 非緊急可延遲的(Noncritical deferrable)這樣的操作如,把一個(gè)緩沖區(qū)的內(nèi)容拷貝到一些進(jìn)程的地址空間(例如,把鍵盤行緩沖區(qū)的內(nèi)容發(fā)送到終端處理程序的進(jìn)程)。這些操作可能被延遲較長(zhǎng)的時(shí)間間隔而不影響內(nèi)核操作,有興趣的進(jìn)程會(huì)等待需要的數(shù)據(jù)。所有的中斷處理程序都執(zhí)行四個(gè)基本的操作:² 在內(nèi)核棧中保存IRQ的值和寄存器的內(nèi)容。² 給與IRQ中斷線相連的中斷控制器發(fā)送一個(gè)應(yīng)答,這將允許在這條中斷線上進(jìn)一步發(fā)出中斷請(qǐng)求。² 執(zhí)行共享這個(gè)IRQ的所有設(shè)備的中斷服務(wù)

37、例程(ISR)。² 跳到ret_to_usr( 的地址后終止。1.3 中斷處理程序的執(zhí)行流程現(xiàn)在,我們可以從中斷請(qǐng)求的發(fā)生到CPU的響應(yīng),再到中斷處理程序的調(diào)用和返回,沿著這一思路走一遍,以體會(huì)Linux內(nèi)核對(duì)中斷的響應(yīng)及處理。假定外設(shè)的驅(qū)動(dòng)程序都已完成了初始化工作,并且已把相應(yīng)的中斷服務(wù)例程掛入到特定的中斷請(qǐng)求隊(duì)列。又假定當(dāng)前進(jìn)程正在用戶空間運(yùn)行(隨時(shí)可以接受中斷),且外設(shè)已產(chǎn)生了一次中斷請(qǐng)求,CPU就在執(zhí)行完當(dāng)前指令后來響應(yīng)該中斷。中斷處理系統(tǒng)在Linux中的實(shí)現(xiàn)是非常依賴于體系結(jié)構(gòu)的,實(shí)現(xiàn)依賴于處理器、所使用的中斷控制器的類型、體系結(jié)構(gòu)的設(shè)計(jì)及機(jī)器本身。設(shè)備產(chǎn)生中斷,通過總線把

38、電信號(hào)發(fā)送給中斷控制器。如果中斷線是激活的,那么中斷控制器就會(huì)把中斷發(fā)往處理器。在大多數(shù)體系結(jié)構(gòu)中,這個(gè)工作就是通過電信號(hào)給處理器的特定管腳發(fā)送一個(gè)信號(hào)。除非在處理器上禁止該中斷,否則,處理器會(huì)立即停止它正在做的事,關(guān)閉中斷系統(tǒng),然后跳到內(nèi)存中預(yù)定義的位置開始執(zhí)行那里的代碼。這個(gè)預(yù)定義的位置是由內(nèi)核設(shè)置的,是中斷處理程序的入口點(diǎn)。對(duì)于ARM系統(tǒng)來說,有個(gè)專用的IRQ運(yùn)行模式,有一個(gè)統(tǒng)一的入口地址。假定中斷發(fā)生時(shí)CPU運(yùn)行在用戶空間,而中斷處理程序?qū)儆趦?nèi)核空間,因此,要進(jìn)行堆棧的切換。也就是說,CPU從TSS中取出內(nèi)核棧指針,并切換到內(nèi)核棧(此時(shí)棧還為空)。若當(dāng)前處于內(nèi)核空間時(shí),對(duì)于ARM系統(tǒng)來

39、說是處于SVC模式,此時(shí)產(chǎn)生中斷,中斷處理完畢后,若是可剝奪內(nèi)核,則檢查是否需要進(jìn)行進(jìn)程調(diào)度,否則直接返回到被中斷的內(nèi)核空間;若需要進(jìn)行進(jìn)程調(diào)度,則svc_preempt,進(jìn)程切換。190 .align 5191_irq_svc:192 svc_entry197#ifdef CONFIG_PREEMPT198 get_thread_info tsk199 ldr r8, tsk, #TI_PREEMPT get preempt count200 add r7, r8, #1 increment it201 str r7, tsk, #TI_PREEMPT202#endif203204 irq_

40、handler205#ifdef CONFIG_PREEMPT206 ldr r0, tsk, #TI_FLAGS get flags207 tst r0, #_TIF_NEED_RESCHED208 blne svc_preempt209preempt_return:210 ldr r0, tsk, #TI_PREEMPT read preempt value211 str r8, tsk, #TI_PREEMPT restore preempt count212 teq r0, r7213 strne r0, r0, -r0 bug(214#endif215 ldr r0, sp, #S_

41、PSR irqs are already disabled216 msr spsr_cxsf, r0221 ldmia sp, r0 - pc load r0 - pc, cpsr222223 .ltorg當(dāng)前處于用戶空間時(shí),對(duì)于ARM系統(tǒng)來說是處于USR模式,此時(shí)產(chǎn)生中斷,中斷處理完畢后,無論是否是可剝奪內(nèi)核,都調(diào)轉(zhuǎn)到統(tǒng)一的用戶模式出口ret_to_user,其檢查是否需要進(jìn)行進(jìn)程調(diào)度,若需要進(jìn)行進(jìn)程調(diào)度,則進(jìn)程切換,否則直接返回到被中斷的用戶空間。404 .align 5405_irq_usr:406 usr_entry407411 get_thread_info tsk412#ifdef

42、 CONFIG_PREEMPT413 ldr r8, tsk, #TI_PREEMPT get preempt count414 add r7, r8, #1 increment it415 str r7, tsk, #TI_PREEMPT416#endif417418 irq_handler419#ifdef CONFIG_PREEMPT420 ldr r0, tsk, #TI_PREEMPT421 str r8, tsk, #TI_PREEMPT422 teq r0, r7423 strne r0, r0, -r0 bug(424#endif428429 mov why, #0430 b

43、ret_to_user432 .ltorg105/*106 * SVC mode handlers107 */108115 .macro svc_entry116 sub sp, sp, #S_FRAME_SIZE117 SPFIX( tst sp, #4 118 SPFIX( bicne sp, sp, #4 119 stmib sp, r1 - r12120121 ldmia r0, r1 - r3122 add r5, sp, #S_SP here for interlock avoidance123 mov r4, #-1 "" "" "

44、;" ""124 add r0, sp, #S_FRAME_SIZE "" "" "" ""125 SPFIX( addne r0, r0, #4 126 str r1, sp save the "real" r0 copied127 from the exception stack128129 mov r1, lr130131 132 We are now ready to fill in the remaining blanks on the stack:133

45、 134 r0 - sp_svc135 r1 - lr_svc136 r2 - lr_ , already fixed up for correct return/restart 137 r3 - spsr_ 138 r4 - orig_r0 (see pt_regs definition in ptrace.h139 140 stmia r5, r0 - r4141 .endm因?yàn)镃的調(diào)用慣例是要把函數(shù)參數(shù)放在棧的頂部,因此pt- regs結(jié)構(gòu)包含原始寄存器的值,這些值是以前在匯編入口例程svc_entry中保存在棧中的。18 .macro get_irqnr_and_base, irqnr

46、, irqstat, base, tmp19 ldr base, =(AT91_VA_BASE_SYS base virtual address of SYS peripherals20 ldr irqnr, base, #AT91_AIC_IVR read IRQ vector register: de-asserts nIRQ to processor (and clears interrupt21 ldr irqstat, base, #AT91_AIC_ISR read interrupt source number22 teq irqstat, #0 ISR is 0 when no

47、 current interrupt, or spurious interrupt23 streq tmp, base, #AT91_AIC_EOICR not going to be handled further, then ACK it now.24 .endm26/*27 * Interrupt handling. Preserves r7, r8, r928 */29 .macro irq_handler301: get_irqnr_and_base r0, r6, r5, lr31 movne r1, sp32 33 routine called with r0 = irq num

48、ber, r1 = struct pt_regs *34 35 adrne lr, 1b36 bne asm_do_IRQ58 .endm中斷號(hào)的值也在irq_handler初期得以保存,所以,asm_do_IRQ可以將它提取出來。這個(gè)中斷處理程序?qū)嶋H上要調(diào)用do_IRQ(,而do_IRQ(要調(diào)用handle_IRQ_event(函數(shù),最后這個(gè)函數(shù)才真正地執(zhí)行中斷服務(wù)例程(ISR)。下圖給出它們的調(diào)用關(guān)系:asm_do_IRQdo_IRQ(handle_IRQ_event(中斷服務(wù)例程1例程中斷服務(wù)例程2例程中斷處理函數(shù)的調(diào)用關(guān)系112asmlinkage void asm_do_IRQ(un

49、signed int irq, struct pt_regs *regs113114 struct pt_regs *old_regs = set_irq_regs(regs;115 struct irqdesc *desc = irq_desc + irq;116121 if (irq >= NR_IRQS122 desc = &bad_irq_desc;123124 irq_enter(; /記錄硬件中斷狀態(tài),便于跟蹤中斷情況確定是否是中斷上下文125126 desc_handle_irq(irq, desc;/desc_handle_irq33static inline v

50、oid desc_handle_irq(unsigned int irq, struct irq_desc *desc3435 desc->handle_irq(irq, desc; /通常handle_irq指向_do_IRQ36/desc_handle_irq130131 irq_exit(; /中斷退出前執(zhí)行可能的軟中斷,被中斷前是在中斷上下文中則直接退出,這保證了軟中斷不會(huì)嵌套132 set_irq_regs(old_regs;133157 * _do_IRQ - original all in one highlevel IRQ handler167fastcall unsig

51、ned int _do_IRQ(unsigned int irq168169 struct irq_desc *desc = irq_desc + irq;170 struct irqaction *action;171 unsigned int status;172173 kstat_this_cpu.irqsirq+;186187 spin_lock(&desc->lock;188 if (desc->chip->ack /首先響應(yīng)中斷,通常實(shí)現(xiàn)為關(guān)閉本中斷線189 desc->chip->ack(irq;190 194 status = desc-&

52、gt;status & (IRQ_REPLAY | IRQ_WAITING;195 status |= IRQ_PENDING; /* we _want_ to handle it */196201 action = NULL;202 if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS 203 action = desc->action;204 status &= IRQ_PENDING; /* we commit to handling */205 status |= IRQ_INPROGRESS; /* w

53、e are handling it */206 207 desc->status = status;208215 if (unlikely(!action216 goto out;217218 /*219 * Edge triggered interrupts need to remember220 * pending events.227 */228 for (; 229 irqreturn_t action_ret;230231 spin_unlock(&desc->lock;/解鎖,中斷處理期間可以響應(yīng)其他中斷,否則再次進(jìn)入_do_IRQ時(shí)會(huì)死鎖233 action_

54、ret = handle_IRQ_event(irq, action;237 spin_lock(&desc->lock;238 if (likely(!(desc->status & IRQ_PENDING239 break;240 desc->status &= IRQ_PENDING;241 242 desc->status &= IRQ_INPROGRESS;243244out:249 desc->chip->end(irq;250 spin_unlock(&desc->lock;251252 retur

55、n 1;253該函數(shù)的實(shí)現(xiàn)用到中斷線的狀態(tài),下面給予具體說明:#define IRQ_INPROGRESS 1 /* 正在執(zhí)行這個(gè)IRQ的一個(gè)處理程序*/#define IRQ_DISABLED 2 /* 由設(shè)備驅(qū)動(dòng)程序已經(jīng)禁用了這條IRQ中斷線 */#define IRQ_PENDING 4 /* 一個(gè)IRQ已經(jīng)出現(xiàn)在中斷線上,且被應(yīng)答,但還沒有為它提供服務(wù) */#define IRQ_REPLAY 8 /* 當(dāng)Linux重新發(fā)送一個(gè)已被刪除的IRQ時(shí) */#define IRQ_WAITING 32 /*當(dāng)對(duì)硬件設(shè)備進(jìn)行探測(cè)時(shí),設(shè)置這個(gè)狀態(tài)以標(biāo)記正在被測(cè)試的irq */#define IR

56、Q_LEVEL 64 /* IRQ level triggered */#define IRQ_MASKED 128 /* IRQ masked - shouldn't be seen again */#define IRQ_PER_CPU 256 /* IRQ is per CPU */這8個(gè)狀態(tài)的前5個(gè)狀態(tài)比較常用,因此我們給出了具體解釋。經(jīng)驗(yàn)表明,應(yīng)該避免在同一條中斷線上的中斷嵌套,內(nèi)核通過IRQ_PENDING標(biāo)志位的應(yīng)用保證了這一點(diǎn)。當(dāng)do_IRQ(執(zhí)行到for (;循環(huán)時(shí),desc->status 中的IRQ_PENDING的標(biāo)志位肯定為0。當(dāng)CPU執(zhí)行完handle_IRQ_event(函數(shù)返回時(shí),如果這個(gè)標(biāo)志位仍然為0,那么循環(huán)就此結(jié)束。如果這個(gè)標(biāo)志位變?yōu)?,那就說明這條中斷線上又有中斷產(chǎn)生(對(duì)單CPU而言),所以循環(huán)又執(zhí)行一次。通過這種循環(huán)方式,就把可能發(fā)生在同一中斷線上的嵌套循環(huán)化解為“串行”。在循環(huán)結(jié)束后調(diào)用desc->handler->end(函數(shù),具體來說,如果沒有設(shè)置IRQ_DISABLED標(biāo)志位,就啟用這條中斷線。當(dāng)

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論