ARM Linux中斷源碼分析(2)――中斷處理流程_第1頁
ARM Linux中斷源碼分析(2)――中斷處理流程_第2頁
ARM Linux中斷源碼分析(2)――中斷處理流程_第3頁
ARM Linux中斷源碼分析(2)――中斷處理流程_第4頁
ARM Linux中斷源碼分析(2)――中斷處理流程_第5頁
已閱讀5頁,還剩22頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

1、ARM Linux中斷源碼分析(2)中斷處理流程ARM支持7類異常中斷,所以中斷向量表設(shè)8個(gè)條目,每個(gè)條目4字節(jié),共32字節(jié)。 異常名稱中斷向量異常中斷模式優(yōu)先級復(fù)位0x0特權(quán)模式1未定義的指令0x4未定義指令中止模式6軟件中斷0x8特權(quán)模式6指令預(yù)取中止0x0c中止模式5數(shù)據(jù)訪問中止0x10中止模式2保留0x14  外部中斷請求IRQ0x18IRQ模式4快速中斷請求FIQ0x1cFIQ模式3 回顧第一節(jié)所講的內(nèi)容,當(dāng)一個(gè)異常或中斷發(fā)生時(shí),處理器會(huì)將PC設(shè)置為特定地址,從而跳轉(zhuǎn)到已經(jīng)初始化好的異常向量表。因此,要理清中斷處理流程,先從異常向量表開始。對于

2、ARM Linux而言,異常向量表和異常處理程序都存在arch/arm/kernel/entry_armv.S匯編文件中。vector異常向量表點(diǎn)擊(此處折疊或打開1. .globl    _vectors_start2. _vectors_start:3.     swi    SYS_ERROR04.     b    vector_und + stubs_offset5.

3、     ldr    pc, .LCvswi + stubs_offset6.     b    vector_pabt + stubs_offset7.     b    vector_dabt + stubs_offset8.     b

4、60;   vector_addrexcptn + stubs_offset9.     b    vector_irq + stubs_offset 中斷入口,vector_irq10.     b    vector_fiq + stubs_offset11. 12.     .globl &#

5、160;  _vectors_end13. _vectors_end:vector_irq+stubs_offset為中斷的入口點(diǎn),此處之所以要加上stubs_offset,是為了實(shí)現(xiàn)位置無關(guān)編程。首先分析一下stubs_offset(宏是如何計(jì)算的:.equ stubs_offset, _vectors_start + 0x200 - _stubs_start在第3節(jié)中已經(jīng)提到,內(nèi)核啟動(dòng)時(shí)會(huì)將異常向量表拷貝到 0xFFFF_0000,將異常向量處理程序的 stub 拷貝到 0xFFFF_0200。圖5-1描述了異常向量表和異常處理程序搬移前后的內(nèi)存布局。圖5-1

6、 異常向量表和異常處理程序搬移前后對比當(dāng)匯編器看到B指令后會(huì)把要跳轉(zhuǎn)的標(biāo)簽轉(zhuǎn)化為相對于當(dāng)前PC的偏移量(±32M)寫入指令碼。由于內(nèi)核啟動(dòng)時(shí)中斷向量表和stubs都發(fā)生了代碼搬移,所以如果中斷向量表中仍然寫成b vector_irq,那么實(shí)際執(zhí)行的時(shí)候就無法跳轉(zhuǎn)到搬移后的vector_irq處,因?yàn)橹噶畲a里寫的是原來的偏移量,所以需要把指令碼中的偏移量寫成搬移后的。設(shè)搬移后的偏移量為offset,如圖5-1所示,offset = L1+L2     = 0x200 - (irq_PC_X - _vectors_start_X

7、 + (vector_irq_X - _stubs_start_X     = 0x200 - (irq_PC - _vectors_start + (vector_irq - _stubs_start     = 0x200 - irq_PC + _vectors_start + vector_irq - _stubs_start     = vector_irq + (_vectors_start + 0x200 - _stubs_start

8、 - irq_PC令stubs_offset = _vectors_start + 0x200 - _stubs_start則offset = vector_irq + stubs_offset - irq_PC,所以中斷入口點(diǎn)為“b       vector_irq + stubs_offset”,其中減去irq_PC是由匯編器在編譯時(shí)完成的。vector_irq處理函數(shù)在分析vector_irq處理函數(shù)之前,先了解一下當(dāng)一個(gè)異常或中斷導(dǎo)致處理器模式改變時(shí),ARM處理器內(nèi)核的處理流程如下圖所示:   

9、   中斷剛發(fā)生時(shí),處理器處于irq模式。在_stubs_start和_stubs_end之間找到vector_irq處理函數(shù)的定義vector_stub irq, IRQ_MODE, 4,其中vector_stub是一個(gè)宏(在arch/arm/kernel/entry_armv.S中定義),為了分析更直觀,我們將vector_stub宏展開如下:1. /*2.  * Interrupt dispatcher3.  */4.     vector_irq:5.    

10、0;.if 46.     sub    lr, lr, #4 在中斷發(fā)生時(shí),lr指向最后執(zhí)行的指令地址加上8。只有在當(dāng)前指令執(zhí)行完畢后,才進(jìn)入中斷處理,所以返回地址應(yīng)指向下一條指令,即(lr-4)處。7.     .endif8. 9. 10.      Save r0, lr_<exception> (parent PC and spsr_&

11、lt;exception>11.      (parent CPSR12.     13.     stmia    sp, r0, lr         保存r0, lr到irq模式下的棧中14.     mrs    lr,

12、60;spsr15.     str    lr, sp, #8        保存spsr到irq模式下的棧中16. 17.     18.      Prepare for SVC32 mode. IRQs remain disabled.19.     20. &

13、#160;   mrs    r0, cpsr 21.     eor    r0, r0, #( IRQ_MODE SVC_MODE 設(shè)置成SVC模式,但未切換22.     msr    spsr_cxsf, r0 保存到spsr_irq中23. 24.    &#

14、160;25.      the branch table must immediately follow this code26.     27.     and    lr, lr, #0x0f lr存儲(chǔ)著上一個(gè)處理器模式的cpsr值,lr = lr & 0x0f取出用于判斷發(fā)生中斷前是用戶態(tài)還是核心態(tài)的信息,該值用于下面跳轉(zhuǎn)表的索引。28.   

15、;  mov    r0, sp 將irq模式下的sp保存到r0,作為參數(shù)傳遞給即將調(diào)用的_irq_usr或_irq_svc29.     ldr    lr, pc, lr, lsl #2 pc指向當(dāng)前執(zhí)行指令地址加8,即跳轉(zhuǎn)表的基址。lr作為索引,由于是4字節(jié)對齊,所以lr = lr << 2.30.    &#

16、160;movs    pc, lr branch to handler in SVC mode31.                   當(dāng)mov指令后加“s”且目標(biāo)寄存器為pc時(shí),當(dāng)前模式下的spsr會(huì)被復(fù)制到cpsr,從而完成模式切換(從irq模式切換到svc模式)并且跳轉(zhuǎn)到pc指向的指令繼續(xù)執(zhí)行32. ENDPROC(vector_irq3

17、3. 34.     .long    _irq_usr             0 (USR_26 / USR_3235.     .long    _irq_invalid           &

18、#160; 1 (FIQ_26 / FIQ_3236.     .long    _irq_invalid             2 (IRQ_26 / IRQ_3237.     .long    _irq_svc       

19、;      3 (SVC_26 / SVC_3238.     .long    _irq_invalid             439.     .long    _irq_invalid     

20、60;       540.     .long    _irq_invalid             641.     .long    _irq_invalid        

21、;     742.     .long    _irq_invalid             843.     .long    _irq_invalid          &

22、#160;  944.     .long    _irq_invalid             a45.     .long    _irq_invalid             b4

23、6.     .long    _irq_invalid             c47.     .long    _irq_invalid             d48.   

24、;  .long    _irq_invalid             e49.     .long    _irq_invalid             f_irq_usr如果發(fā)生中斷前處于用戶態(tài)則進(jìn)入_irq_u

25、sr,其定義如下(arch/arm/kernel/entry_armv.S): 1. .align    52. _irq_usr:3.     usr_entry 保存中斷上下文,稍后分析4.     kuser_cmpxchg_check5. #ifdef CONFIG_TRACE_IRQFLAGS6.     bl    trace_hardirqs_off7. #endif8.

26、    get_thread_info tsk 獲取當(dāng)前進(jìn)程的進(jìn)程描述符中的成員變量thread_info的地址,并將該地址保存到寄存器tsk(r9)(在entry-header.S中定義)9. #ifdef CONFIG_PREEMPT 如果定義了搶占,增加搶占數(shù)值10.     ldr    r8, tsk, #TI_PREEMPT         獲取preempt計(jì)數(shù)器

27、值11.     add    r7, r8, #1             preempt加1,標(biāo)識禁止搶占12.     str    r7, tsk, #TI_PREEMPT 將加1后的結(jié)果寫入進(jìn)程內(nèi)核棧的變量中13. #endif14.   

28、  irq_handler 調(diào)用中斷處理程序,稍后分析15. #ifdef CONFIG_PREEMPT16.     ldr    r0, tsk, #TI_PREEMPT 獲取preempt計(jì)數(shù)器值17.     str    r8, tsk, #TI_PREEMPT 將preempt恢復(fù)到中斷前的值18.     

29、;teq    r0, r7 比較中斷前后preempt是否相等19.     strne    r0, r0, -r0 如果不等,則產(chǎn)生異常(向地址0寫入數(shù)據(jù))?20. #endif21. #ifdef CONFIG_TRACE_IRQFLAGS22.     bl    trace_hardirqs_on23. #endif24.   

30、;  mov    why, #0 r8=025.     b    ret_to_user 中斷處理完成,恢復(fù)中斷上下文并返回中斷產(chǎn)生的位置,稍后分析26.  UNWIND(.fnend        27. ENDPROC(_irq_usr宏定義usr_entry(保護(hù)上下文到棧上面代碼中的usr_entry是一個(gè)宏定義,主要用于保護(hù)上下文到棧中:1.

31、.macro    usr_entry2.  UNWIND(.fnstart    3.  UNWIND(.cantunwind         dont unwind the user space4.     sub    sp, sp, #S_FRAME_SIZE ATPCS中,堆棧被定義為遞減式滿堆棧,所以首先讓

32、sp向下移動(dòng)#S_FRAME_SIZE(pt_regs結(jié)構(gòu)體size),準(zhǔn)備向棧中存放數(shù)據(jù)。此處的sp是svc模式下的棧指針。5.     stmib    sp, r1 - r126. 7.     ldmia    r0, r1 - r38.     add    r0, sp,

33、60;#S_PC         here for interlock avoidance9.     mov    r4, #-1             "" "" "" "&

34、quot;10. 11.     str    r1, sp         save the "real" r0 copied12.                      from t

35、he exception stack13. 14.     15.      We are now ready to fill in the remaining blanks on the stack:16.     17.      r2 - lr_<exception>, already fixed up for correct return/rest

36、art18.      r3 - spsr_<exception>19.      r4 - orig_r0 (see pt_regs definition in ptrace.h20.     21.      Also, separately save sp_usr and lr_usr22.   &#

37、160; 23.     stmia    r0, r2 - r424.     stmdb    r0, sp, lr 將user模式下的sp和lr保存到svc模式的棧中25. 26.     27.      Enable the alignment trap while i

38、n kernel mode28.     29.     alignment_trap r030. 31.     32.      Clear FP to mark the first stack frame33.     34.     zero_fp35.     .endm上面的這段代碼主要是在填充結(jié)

39、構(gòu)體pt_regs ,在include/asm/ptrace.h中定義:1. struct pt_regs 2.     long uregs18;3. ;4. 5. #define ARM_cpsr    uregs166. #define ARM_pc        uregs157. #define ARM_lr        uregs14

40、8. #define ARM_sp        uregs139. #define ARM_ip        uregs1210. #define ARM_fp        uregs1111. #define ARM_r10        uregs1012. #defi

41、ne ARM_r9        uregs913. #define ARM_r8        uregs814. #define ARM_r7        uregs715. #define ARM_r6        uregs616. #define ARM_r5

42、60;       uregs517. #define ARM_r4        uregs418. #define ARM_r3        uregs319. #define ARM_r2        uregs220. #define ARM_r1  

43、60;     uregs121. #define ARM_r0        uregs022. #define ARM_ORIG_r0    uregs17usr_entry宏填充pt_regs結(jié)構(gòu)體的過程如圖5-2所示,先將r1r12保存到ARM_r1ARM_ip(綠色部分),然后將產(chǎn)生中斷時(shí)的r0寄存器內(nèi)容保存到ARM_r0(藍(lán)色部分),接下來將產(chǎn)生中斷時(shí)的下一條指令地址lr_irq、spsr_irq和r4保存到ARM

44、_pc、ARM_cpsr和ARM_ORIG_r0(紅色部分),最后將用戶模式下的sp和lr保存到ARM_sp 和ARM_lr 中。圖5-2 usr_entry宏填充pt_regs結(jié)構(gòu)體_irq_svc如果發(fā)生中斷前處于核心態(tài)則進(jìn)入_irq_svc,其定義如下(arch/arm/kernel/entry_armv.S):1. .align    52. _irq_svc:3.     svc_entry 保存中斷上下文4. 5. #ifdef CONFIG_TRACE_IRQFLAGS6. 

45、60;   bl    trace_hardirqs_off7. #endif8. #ifdef CONFIG_PREEMPT9.     get_thread_info tsk10.     ldr    r8, tsk, #TI_PREEMPT         獲取preempt計(jì)數(shù)器值11.  

46、;   add    r7, r8, #1             preempt加1,標(biāo)識禁止搶占12.     str    r7, tsk, #TI_PREEMPT 將加1后的結(jié)果寫入進(jìn)程內(nèi)核棧的變量中13. #endif14. 15.    

47、 irq_handler 調(diào)用中斷處理程序,稍后分析16. #ifdef CONFIG_PREEMPT17.     str    r8, tsk, #TI_PREEMPT         恢復(fù)中斷前的preempt計(jì)數(shù)器18.     ldr    r0, tsk, #TI_FLAGS  

48、;       獲取flags19.     teq    r8, #0                 判斷preempt是否等于020.     movne    r0, #0 

49、0;               如果preempt不等于0,r0=021.     tst    r0, #_TIF_NEED_RESCHED 將r0與#_TIF_NEED_RESCHED做“與操作”22.     blne    svc_preempt 如果不等于0,說明發(fā)生內(nèi)核搶占,

50、需要重新調(diào)度。23. #endif24. 25.     ldr    r0, sp, #S_PSR         irqs are already disabled26.     msr    spsr_cxsf, r027. #ifdef CONFIG_TRACE_IRQFLAGS28.    

51、; tst    r0, #PSR_I_BIT29.     bleq    trace_hardirqs_on30. #endif31.     svc_exit r4     恢復(fù)中斷上下文,稍后分析。32.  UNWIND(.fnend        33. ENDPROC(_irq

52、_svc宏定義svc_entry(保護(hù)中斷上下文到棧其中svc_entry是一個(gè)宏定義,主要用于保護(hù)中斷上下文到棧中。svc_entry主要是在當(dāng)前堆棧上分配一個(gè)pt_regs結(jié)構(gòu),把r0-r15以及cpsr等保存到這個(gè)結(jié)構(gòu)中,在進(jìn)入irq_handler時(shí),sp指向pt_regs底端:1. .macro    svc_entry, stack_hole=02.  UNWIND(.fnstart        3.  UNWIND(.save r

53、0 - pc        4.     sub    sp, sp, #(S_FRAME_SIZE + stack_hole5.  SPFIX(    tst    sp, #4        6.

54、  SPFIX(    bicne    sp, sp, #4    7.     stmib    sp, r1 - r128. 9.     ldmia    r0, r1 - r310.    

55、 add    r5, sp, #S_SP         here for interlock avoidance11.     mov    r4, #-1             ""

56、 "" "" ""12.     add    r0, sp, #(S_FRAME_SIZE + stack_hole13.  SPFIX(    addne    r0, r0, #4    14.    &

57、#160;str    r1, sp         save the "real" r0 copied15.                      from the exception stack16. 17.  

58、   mov    r1, lr18. 19.     20.      We are now ready to fill in the remaining blanks on the stack:21.     22.      r0 - sp_svc23.      r1&#

59、160;- lr_svc24.      r2 - lr_<exception>, already fixed up for correct return/restart25.      r3 - spsr_<exception>26.      r4 - orig_r0 (see pt_regs definition in 

60、;ptrace.h27.     28.     stmia    r5, r0 - r429.     .endmsvc_entry宏填充pt_regs結(jié)構(gòu)體的過程如圖5-2所示,先將r1r12保存到ARM_r1ARM_ip(綠色部分),然后將產(chǎn)生中斷時(shí)的r0寄存器內(nèi)容保存到ARM_r0(藍(lán)色部分),由于是在svc模式下產(chǎn)生的中斷,所以最后將sp_svc、lr_svc、lr_irq、spsr_irq和r4

61、保存到ARM_sp、ARM_lr、ARM_pc、ARM_cpsr和ARM_ORIG_r0(紅色部分)。圖5-3 svc_entry宏填充pt_regs結(jié)構(gòu)體上述的中斷上下文保存過程共涉及了3種棧指針,分別是:用戶空間棧指針sp_usr,內(nèi)核空間棧指針sp_svc和irq模式下的棧棧指針sp_irq。sp_usr指向在setup_arg_pages函數(shù)中創(chuàng)建的用戶空間棧。sp_svc指向在alloc_thread_info函數(shù)中創(chuàng)建的內(nèi)核空間棧。sp_irq在cpu_init函數(shù)中被賦值,指向全局變量stacks.irq0。附錄1,arm體系下pt_regs結(jié)構(gòu)struct pt_re

62、gs long uregs18;uregs0 - uregs17分別對應(yīng),r0 - r15,cpsr,ORIG_r0附錄1,irq中斷時(shí)堆棧的變化-spsr-lr ,中斷返回地址,修正后的-r0- <-進(jìn)入irq_svc之前,sp的值,也是r0的值pt_regs- <-進(jìn)入svc_entry后,sp的值irq_handler(中斷處理程序保存中斷上下文后則進(jìn)入中斷處理程序irq_handler,定義在arch/arm/kernel/entry_armv.S文件中:1. .macro    irq_handler2.   &

63、#160; get_irqnr_preamble r5, lr 3. 1:    get_irqnr_and_base r0, r6, r5, lr 獲取中斷號,存到r0中,稍后分析4.     movne    r1, sp 如果中斷號不等于0,將r1=sp,即pt_regs結(jié)構(gòu)體首地址5.     6.      routi

64、ne called with r0 = irq number, r1 = struct pt_regs *7.     8.     adrne    lr, 1b 如果r0(中斷號不等于0, lr(返回地址等于標(biāo)號1處,即get_irqnr_and_base r0, r6, r5, lr的那行,即循環(huán)處理所有的中斷。9.    

65、0;bne    asm_do_IRQ 進(jìn)入中斷處理,稍后分析。10. 11.     .endmget_irqnr_and_base用于判斷當(dāng)前發(fā)生的中斷號(與CPU緊密相關(guān)),此處不再分析。如果獲取的中斷號不等于0,則將中斷號存入r0寄存器作為第一個(gè)參數(shù),pt_regs結(jié)構(gòu)體地址存入r1寄存器作為第二個(gè)參數(shù),跳轉(zhuǎn)到c語言函數(shù)asm_do_IRQ做進(jìn)一步處理。為了不讓大家在匯編語言和C語言之間來回切換,還是先把最后一點(diǎn)匯編語言代碼(中斷返回匯編代碼)分析了再去分析asm_do_IRQ吧。回看_irq_usr和_i

66、rq_svc標(biāo)號處的代碼,在完成了irq_handler中斷處理函數(shù)后,要完成從中斷異常處理程序返回到中斷點(diǎn)的工作。ret_to_user如果中斷產(chǎn)生于用戶空間,則調(diào)用ret_to_user來恢復(fù)中斷現(xiàn)場并返回用戶空間繼續(xù)運(yùn)行:1. arch/arm/kernel/entry_armv.S2. ENTRY(ret_to_user3. ret_slow_syscall:4.     disable_irq     disable interrupts,此處不明白,disable_irq應(yīng)該接受irq中斷號作為參數(shù),來

67、禁止指定的irq號中斷線。但是此處調(diào)用disable_irq之前并沒有將irq中斷號存入r0寄存器,這是為什么?5.     ldr    r1, tsk, #TI_FLAGS 獲取thread_info->flags6.     tst    r1, #_TIF_WORK_MASK 判斷是否有待處理的work7.     bne  

68、;  work_pending 如果有,則進(jìn)入work_pending進(jìn)一步處理,主要是完成用戶進(jìn)程搶占相關(guān)處理。8. no_work_pending: 如果沒有work待處理,則準(zhǔn)備恢復(fù)中斷現(xiàn)場,返回用戶空間。9.     /* perform architecture specific actions before user return */10.     arch_ret_to_user r1, lr 調(diào)用體系結(jié)構(gòu)相關(guān)的代碼11. 12. 

69、0;   restore_user_regs fast = 0, offset = 0 調(diào)用restore_user_regs13. ENDPROC(ret_to_user14. 15. 以下是恢復(fù)中斷現(xiàn)場寄存器的宏,就是將發(fā)生中斷時(shí)保存在內(nèi)核空間堆棧上的寄存器還原,可以對照圖5-2所示的內(nèi)核空間堆棧保存的內(nèi)容來理解下面代碼:16. .macro    restore_user_regs, fast = 0, offset =

70、 017.     ldr    r1, sp, #offset + S_PSR     從內(nèi)核棧中獲取發(fā)生中斷時(shí)的cpsr值18.     ldr    lr, sp, #offset + S_PC!     從內(nèi)核棧中獲取發(fā)生中斷時(shí)的下一條指令地址19.  

71、;   msr    spsr_cxsf, r1             將r1保存到spsr_svc20. #if defined(CONFIG_CPU_32v6K21.     clrex             &

72、#160;       clear the exclusive monitor22. #elif defined (CONFIG_CPU_V623.     strex    r1, r2, sp             clear the exclusive monitor24. #endif25. 

73、60;   .if    fast26.     ldmdb    sp, r1 - lr     get calling r1 - lr27.     .else28.     ldmdb    sp, r0 -

74、60;lr 存在,所以將內(nèi)核棧保存的內(nèi)容恢復(fù)到用戶空間的r0lr寄存器29.     .endif30.     add    sp, sp, #S_FRAME_SIZE - S_PC 31.     movs    pc, lr    將發(fā)生中斷時(shí)的下一條指令地址存入pc,從而返回中斷點(diǎn)繼續(xù)執(zhí)行,

75、并且將發(fā)生中斷時(shí)的cpsr內(nèi)容恢復(fù)到cpsr寄存器中(開啟中斷)。32.     .endmsvc_exit如果中斷產(chǎn)生于內(nèi)核空間,則調(diào)用svc_exit來恢復(fù)中斷現(xiàn)場:1. arch/arm/kernel/ entry-header.S2. .macro    svc_exit, rpsr3.     msr    spsr_cxsf, rpsr4. #if defined(CONFIG_CPU_32v6

76、K5.     clrex                     clear the exclusive monitor6.     ldmia    sp, r0 - pc      &

77、#160;      load r0 - pc, cpsr7. #elif defined (CONFIG_CPU_V68.     ldr    r0, sp9.     strex    r1, r2, sp         

78、    clear the exclusive monitor10.     ldmib    sp, r1 - pc             load r1 - pc, cpsr11. #else12.     ldmia    

79、;sp, r0 - pc             返回內(nèi)核空間時(shí),恢復(fù)中斷現(xiàn)場比較簡單,就是將r0-pc以及cpsr恢復(fù)即可,同時(shí)中斷也被開啟。13. #endif14.     .endmasm_do_IRQ函數(shù)ok,分析完所有與中斷相關(guān)的匯編語言代碼后,下面開始分析C語言代碼:在arch/arm/kernel/irq.c文件中找到asm_do_IRQ函數(shù)定義:1. asmlinkage void _ex

80、ception asm_do_IRQ(unsigned int irq, struct pt_regs *regs2. 3.     /*保存新的寄存器集合指針到全局cpu變量,方便后續(xù)處理程序訪問寄存器集合。*/4.     struct pt_regs *old_regs = set_irq_regs(regs; 5. 6.     irq_enter(;7. 8. 

81、60;   /*9.      * Some hardware gives randomly wrong interrupts. Rather10.      * than crashing, do something sensible.11.      */12.     if (unlikely(irq >=

82、 NR_IRQS  /判斷中斷號13.         if (printk_ratelimit(14.             printk(KERN_WARNING "Bad IRQ%un", irq;15.         ack_bad_ir

83、q(irq;16.      else 17.         generic_handle_irq(irq; /調(diào)用中斷處理函數(shù)18.     19. 20.     /* AT91 specific workaround */21.     irq_finish(irq;22. 23.  

84、60;  irq_exit(;24.     set_irq_regs(old_regs;25. asm_do_IRQ是中斷處理的C入口函數(shù),主要負(fù)責(zé)調(diào)用request_irq注冊的中斷處理函數(shù),其流程如圖5-4所示:圖5-4 asm_do_IRQ流程1、old_regs = set_irq_regs(regs其中,set_irq_regs將指向寄存器結(jié)構(gòu)體的指針保存在一個(gè)全局的CPU變量中,后續(xù)的程序可以通過該變量訪問寄存器結(jié)構(gòu)體。所以在進(jìn)入中斷處理前,先將全局CPU變量中保存的舊指針保留下來,等到中斷處理結(jié)束后

85、再將其恢復(fù)。2、irq_enterirq_enter負(fù)責(zé)更新一些統(tǒng)計(jì)量:1. <kernel/softirq.c>2. void irq_enter(void3. 4.     int cpu = smp_processor_id(;5. 6.     rcu_irq_enter(;7.     if (idle_cpu(cpu && !in_interrupt( 8. 

86、0;       _irq_enter(;9.         tick_check_idle(cpu;10.      else11.         _irq_enter(;12. 如果系統(tǒng)開啟動(dòng)態(tài)時(shí)鐘特性且很長時(shí)間沒有產(chǎn)生時(shí)鐘中斷,則調(diào)用tick_check_idle更新全局變量jiffies(關(guān)于動(dòng)態(tài)時(shí)鐘特性,

87、在后續(xù)的總結(jié)中再進(jìn)行分析)。宏_irq_enter(定義如下:1. #define _irq_enter(                    2.     do                &#

88、160;        3.         account_system_vtime(current;        4.         add_preempt_count(HARDIRQ_OFFSET;    5.  

89、60;      trace_hardirq_enter(;            6.      while (0add_preempt_count(HARDIRQ_OFFSET使表示中斷處理程序嵌套層次的計(jì)數(shù)器加1。計(jì)數(shù)器保存在當(dāng)前進(jìn)程thread_info結(jié)構(gòu)的preempt_count字段中:圖5-5 preempt_count結(jié)構(gòu)內(nèi)核將preemp

90、t_count分成5部分:bit07與PREEMPT相關(guān),bit815用作軟中斷計(jì)數(shù)器,bit1625用作硬中斷計(jì)數(shù)器,bit26用作不可屏蔽中斷計(jì)數(shù)器,bit28用作PREEMPT_ACTIVE標(biāo)志。3、generic_handle_irqgeneric_handle_irq是體系結(jié)構(gòu)無關(guān)函數(shù),用來調(diào)用desc->handle_irq,該函數(shù)指針在中斷初始化時(shí)指向了電流處理函數(shù)(handle_level_irq或handle_edge_irq),針對不同的中斷觸發(fā)類型(邊沿觸發(fā)或電平觸發(fā))做相應(yīng)的處理。然后調(diào)用handle_IRQ_event遍歷action鏈表從而調(diào)用該中斷號對應(yīng)的一

91、個(gè)或多個(gè)中斷處理程序action->handler,而action->handler就是通過request_irq初始化的。handle_level_irq首先分析一下handle_level_irq函數(shù):1. <kernel/irq/chip.c>2. void handle_level_irq(unsigned int irq, struct irq_desc *desc3. 4.     struct irqaction *action;5.   

92、0; irqreturn_t action_ret;6. 7.     spin_lock(&desc->lock; /*訪問desc內(nèi)容之前先加自旋鎖*/8.     mask_ack_irq(desc, irq; /*屏蔽與irq號對應(yīng)的中斷線 */9. 10. /* 在多處理器系統(tǒng)上,為了避免多cpu同時(shí)處理同一中斷。11. *當(dāng)desc->status包含IRQ_INPROGRESS標(biāo)志時(shí),說明該中斷12. *正在另一個(gè)cpu上

93、處理,因此當(dāng)前cpu可以直接放棄處理。13. */14.     if (unlikely(desc->status & IRQ_INPROGRESS 15.         goto out_unlock;16.     desc->status &= (IRQ_REPLAY | IRQ_WAITING;17.  &#

94、160;  kstat_incr_irqs_this_cpu(irq, desc;18. 19.     /*20.      *如果沒有對該中斷注冊處理程序,即desc->action為NULL。21.      * 或者desc->status設(shè)置為IRQ_DISABLED,表示該中斷是被禁止的。22.      * 以上兩種情況只要出現(xiàn)一種

95、即可放棄處理。23. */24.     action = desc->action;25.     if (unlikely(!action | (desc->status & IRQ_DISABLED26.         goto out_unlock;27. 28.     desc->stat

96、us |= IRQ_INPROGRESS; /*標(biāo)識中斷狀態(tài)為正在處理*/29.     spin_unlock(&desc->lock; /*釋放自旋鎖*/30. 31.     /*調(diào)用由request_irq注冊的處理函數(shù),稍后分析。*/32.     action_ret = handle_IRQ_event(irq, action; 33.   

97、0; if (!noirqdebug34.         note_interrupt(irq, desc, action_ret;35. 36.     spin_lock(&desc->lock; /*訪問desc內(nèi)容前加自旋鎖*/37.     desc->status &= IRQ_INPROGRESS; /*清除“正在

98、處理”的標(biāo)識*/38. 39. /*如果desc->status 包含IRQ_ONESHOT,40. *則將desc->status設(shè)置為IRQ_MASKED,使該中斷仍處于被屏蔽狀態(tài)。 */41.     if (unlikely(desc->status & IRQ_ONESHOT 42.         desc->status |= IRQ_MASKED;43. /*

99、如果中斷處理函數(shù)中未對desc->status 設(shè)置為IRQ_ DISABLED,44. *且desc->chip->unmask不為空,則desc->chip->unmask所指向的芯片相關(guān)函數(shù),45. *解除對該中斷的屏蔽。 46. */47.     else if (!(desc->status & IRQ_DISABLED && desc->chip->unmask48.   

100、0;     desc->chip->unmask(irq;49. out_unlock:50.     spin_unlock(&desc->lock; /*釋放自旋鎖*/51. handle_edge_irq再來介紹一下handle_edge_irq函數(shù),相對于handle_level_irq要復(fù)雜一點(diǎn):1. <kernel/irq/chip.c>2. void handle_edge_irq(unsigned int irq,

101、60;struct irq_desc *desc3. 4.     spin_lock(&desc->lock;5.     desc->status &= (IRQ_REPLAY | IRQ_WAITING;6.     /*7.      * 如果該中斷正在被其他cpu處理,或者是該中斷已被禁止,8.    

102、;  * 則不處理該中斷,但要將其標(biāo)識為pending狀態(tài)且屏蔽該中斷以便后續(xù)處理9.      */10.     if (unlikely(desc->status & (IRQ_INPROGRESS | IRQ_DISABLED |11.          !desc->action 12.

103、        desc->status |= (IRQ_PENDING | IRQ_MASKED;13.         mask_ack_irq(desc, irq;14.         goto out_unlock;15.     16.   &#

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(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

提交評論