ARM Linux中斷源碼分析2——中斷處理流程.docx

上傳人:小** 文檔編號:16662665 上傳時間:2020-10-20 格式:DOCX 頁數(shù):23 大?。?06.48KB
收藏 版權(quán)申訴 舉報 下載
ARM Linux中斷源碼分析2——中斷處理流程.docx_第1頁
第1頁 / 共23頁
ARM Linux中斷源碼分析2——中斷處理流程.docx_第2頁
第2頁 / 共23頁
ARM Linux中斷源碼分析2——中斷處理流程.docx_第3頁
第3頁 / 共23頁

下載文檔到電腦,查找使用更方便

0 積分

下載資源

還剩頁未讀,繼續(xù)閱讀

資源描述:

《ARM Linux中斷源碼分析2——中斷處理流程.docx》由會員分享,可在線閱讀,更多相關(guān)《ARM Linux中斷源碼分析2——中斷處理流程.docx(23頁珍藏版)》請在裝配圖網(wǎng)上搜索。

1、ARM Linux中斷源碼分析(2)——中斷處理流程 ARM支持7類異常中斷,所以中斷向量表設(shè)8個條目,每個條目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 外部中斷請求IRQ 0x18 IRQ模式 4 快速中斷請求FIQ 0x1c FIQ模式 3 回顧第一節(jié)所講的內(nèi)容,當(dāng)一個異?;蛑袛喟l(fā)生

2、時,處理器會將PC設(shè)置為特定地址,從而跳轉(zhuǎn)到已經(jīng)初始化好的異常向量表。因此,要理清中斷處理流程,先從異常向量表開始。對于ARM Linux而言,異常向量表和異常處理程序都存在arch/arm/kernel/entry_armv.S匯編文件中。 vector異常向量表 點擊(此處)折疊或打開 1. .globl__vectors_start 2. __vectors_start: 3. swiSYS_ERROR0 4. bvector_und+stubs_offset 5. ldrpc,.LCvswi+stubs_offset 6. bvector_pabt+stubs_of

3、fset 7. bvector_dabt+stubs_offset 8. bvector_addrexcptn+stubs_offset 9. bvector_irq+stubs_offset @中斷入口,vector_irq 10. bvector_fiq+stubs_offset 11. 12. .globl__vectors_end 13. __vectors_end: vector_irq+stubs_offset為中斷的入口點,此處之所以要加上stubs_offset,是為了實現(xiàn)位置無關(guān)編程。首先分析一下stubs_offset(宏)是如何計算的: .equstub

4、s_offset, __vectors_start + 0x200 - __stubs_start 在第3節(jié)中已經(jīng)提到,內(nèi)核啟動時會將異常向量表拷貝到 0xFFFF_0000,將異常向量處理程序的 stub 拷貝到 0xFFFF_0200。圖5-1描述了異常向量表和異常處理程序搬移前后的內(nèi)存布局。 圖5-1異常向量表和異常處理程序搬移前后對比 當(dāng)匯編器看到B指令后會把要跳轉(zhuǎn)的標(biāo)簽轉(zhuǎn)化為相對于當(dāng)前PC的偏移量(32M)寫入指令碼。由于內(nèi)核啟動時中斷向量表和stubs都發(fā)生了代碼搬移,所以如果中斷向量表中仍然寫成b vector_irq,那么實際執(zhí)行的時候就無法跳轉(zhuǎn)到搬移后的vector

5、_irq處,因為指令碼里寫的是原來的偏移量,所以需要把指令碼中的偏移量寫成搬移后的。設(shè)搬移后的偏移量為offset,如圖5-1所示, offset = L1+L2 = [0x200 - (irq_PC_X - __vectors_start_X)] + (vector_irq_X - __stubs_start_X) = [0x200 - (irq_PC - __vectors_start)] + (vector_irq - __stubs_start) = 0x200 - irq_PC + __vectors_start + vector_irq - __stubs_start =

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

7、理流程如下圖所示: 中斷剛發(fā)生時,處理器處于irq模式。在__stubs_start和__stubs_end之間找到vector_irq處理函數(shù)的定義vector_stub irq, IRQ_MODE, 4,其中vector_stub是一個宏(在arch/arm/kernel/entry_armv.S中定義),為了分析更直觀,我們將vector_stub宏展開如下: 1. /* 2. *Interrupt dispatcher 3. */ 4. vector_irq: 5. .if4 6. sublr,lr,#4@在中斷發(fā)生時,lr指向最后執(zhí)行的指令

8、地址加上8。只有在當(dāng)前指令執(zhí)行完畢后,才進(jìn)入中斷處理,所以返回地址應(yīng)指向下一條指令,即(lr-4)處。 7. .endif 8. 9. @ 10. @ Save r0,lr_(parent PC)andspsr_ 11. @(parent CPSR) 12. @ 13. stmiasp,{r0,lr}@ 保存r0,lr到irq模式下的棧中 14. mrslr,spsr 15. strlr,[sp,#8]@保存spsr到irq模式下的棧中 16. 17. @ 18. @ PrepareforSVC32 mode.IRQs rem

9、ain disabled. 19. @ 20. mrsr0,cpsr 21. eorr0,r0,#(IRQ_MODE ^ SVC_MODE)@設(shè)置成SVC模式,但未切換 22. msrspsr_cxsf,r0 @保存到spsr_irq中 23. 24. @ 25. @ the branch table must immediately followthiscode 26. @ 27. andlr,lr,#0x0f @lr存儲著上一個處理器模式的cpsr值,lr=lr & 0x0f取出用于判斷發(fā)生中斷前是用戶態(tài)還是核心態(tài)的信息,該值用于下面跳轉(zhuǎn)表的索引。 28. movr0,

10、sp @將irq模式下的sp保存到r0,作為參數(shù)傳遞給即將調(diào)用的__irq_usr或__irq_svc 29. ldrlr,[pc,lr,lsl#2]@pc指向當(dāng)前執(zhí)行指令地址加8,即跳轉(zhuǎn)表的基址。lr作為索引,由于是4字節(jié)對齊,所以lr=lr<<2. 30. movspc,lr @ branch to handlerinSVC mode 31. @當(dāng)mov指令后加“s”且目標(biāo)寄存器為pc時,當(dāng)前模式下的spsr會被復(fù)制到cpsr,從而完成模式切換(從irq模式切換到svc模式)并且跳轉(zhuǎn)到pc指向的指令繼續(xù)執(zhí)行 32. ENDPROC(vector_irq) 33. 34. .lo

11、ng__irq_usr@ 0(USR_26 / USR_32) 35. .long__irq_invalid@ 1(FIQ_26 / FIQ_32) 36. .long__irq_invalid@ 2(IRQ_26 / IRQ_32) 37. .long__irq_svc@ 3(SVC_26 / SVC_32) 38. .long__irq_invalid@ 4 39. .long__irq_invalid@ 5 40. .long__irq_invalid@ 6 41. .long__irq_invalid@ 7 42. .long__irq_invalid@ 8 43.

12、.long__irq_invalid@ 9 44. .long__irq_invalid@ a 45. .long__irq_invalid@ b 46. .long__irq_invalid@ c 47. .long__irq_invalid@ d 48. .long__irq_invalid@ e 49. .long__irq_invalid@ f __irq_usr 如果發(fā)生中斷前處于用戶態(tài)則進(jìn)入__irq_usr,其定義如下(arch/arm/kernel/entry_armv.S): 1. .align5 2. __irq_usr: 3. usr_ent

13、ry @保存中斷上下文,稍后分析 4. kuser_cmpxchg_check 5. #ifdef CONFIG_TRACE_IRQFLAGS 6. bltrace_hardirqs_off 7. #endif 8. get_thread_info tsk @獲取當(dāng)前進(jìn)程的進(jìn)程描述符中的成員變量thread_info的地址,并將該地址保存到寄存器tsk(r9)(在entry-header.S中定義) 9. #ifdef CONFIG_PREEMPT @如果定義了搶占,增加搶占數(shù)值 10. ldrr8,[tsk,#TI_PREEMPT]@ 獲取preempt計數(shù)器值 11. ad

14、dr7,r8,#1@ preempt加1,標(biāo)識禁止搶占 12. strr7,[tsk,#TI_PREEMPT]@將加1后的結(jié)果寫入進(jìn)程內(nèi)核棧的變量中 13. #endif 14. irq_handler @調(diào)用中斷處理程序,稍后分析 15. #ifdef CONFIG_PREEMPT 16. ldrr0,[tsk,#TI_PREEMPT]@獲取preempt計數(shù)器值 17. strr8,[tsk,#TI_PREEMPT]@將preempt恢復(fù)到中斷前的值 18. teqr0,r7 @比較中斷前后preempt是否相等 19. strner0,[r0,-r0]@如果不等,則產(chǎn)生異

15、常(向地址0寫入數(shù)據(jù))? 20. #endif 21. #ifdef CONFIG_TRACE_IRQFLAGS 22. bltrace_hardirqs_on 23. #endif 24. movwhy,#0 @r8=0 25. bret_to_user @中斷處理完成,恢復(fù)中斷上下文并返回中斷產(chǎn)生的位置,稍后分析 26. UNWIND(.fnend) 27. ENDPROC(__irq_usr) 宏定義usr_entry(保護(hù)上下文到棧) 上面代碼中的usr_entry是一個宏定義,主要用于保護(hù)上下文到棧中: 1. .macrousr_entry 2. UNWI

16、ND(.fnstart) 3. UNWIND(.cantunwind)@ dont unwind the user space 4. subsp,sp,#S_FRAME_SIZE @ATPCS中,堆棧被定義為遞減式滿堆棧,所以首先讓sp向下移動#S_FRAME_SIZE(pt_regs結(jié)構(gòu)體size),準(zhǔn)備向棧中存放數(shù)據(jù)。此處的sp是svc模式下的棧指針。 5. stmibsp,{r1-r12} 6. 7. ldmiar0,{r1-r3} 8. addr0,sp,#S_PC@ hereforinterlock avoidance 9. movr4,#-1@"""""""" 10

17、. 11. strr1,[sp]@ save the"real"r0 copied 12. @ from the exception stack 13. 14. @ 15. @ We are now ready to fillinthe remaining blanks on the stack: 16. @ 17. @ r2-lr_,already fixed upforcorrect return/restart 18. @ r3-spsr_ 19. @ r4-orig_r0(see pt_regs definitioninpt

18、race.h) 20. @ 21. @ Also,separately save sp_usrandlr_usr 22. @ 23. stmiar0,{r2-r4} 24. stmdbr0,{sp,lr}^ @將user模式下的sp和lr保存到svc模式的棧中 25. 26. @ 27. @ Enable the alignment trapwhileinkernel mode 28. @ 29. alignment_trap r0 30. 31. @ 32. @ Clear FP to mark the first stack frame 33. @ 34. z

19、ero_fp 35. .endm 上面的這段代碼主要是在填充結(jié)構(gòu)體pt_regs ,在include/asm/ptrace.h中定義: 1. structpt_regs { 2. long uregs[18]; 3. }; 4. 5. #define ARM_cpsruregs[16] 6. #define ARM_pcuregs[15] 7. #define ARM_lruregs[14] 8. #define ARM_spuregs[13] 9. #define ARM_ipuregs[12] 10. #define ARM_fpuregs[11] 11.

20、 #define ARM_r10uregs[10] 12. #define ARM_r9uregs[9] 13. #define ARM_r8uregs[8] 14. #define ARM_r7uregs[7] 15. #define ARM_r6uregs[6] 16. #define ARM_r5uregs[5] 17. #define ARM_r4uregs[4] 18. #define ARM_r3uregs[3] 19. #define ARM_r2uregs[2] 20. #define ARM_r1uregs[1] 21. #define ARM_r0ure

21、gs[0] 22. #define ARM_ORIG_r0uregs[17] usr_entry宏填充pt_regs結(jié)構(gòu)體的過程如圖5-2所示,先將r1~r12保存到ARM_r1~ARM_ip(綠色部分),然后將產(chǎn)生中斷時的r0寄存器內(nèi)容保存到ARM_r0(藍(lán)色部分),接下來將產(chǎn)生中斷時的下一條指令地址lr_irq、spsr_irq和r4保存到ARM_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)入

22、__irq_svc,其定義如下(arch/arm/kernel/entry_armv.S): 1. .align5 2. __irq_svc: 3. svc_entry @保存中斷上下文 4. 5. #ifdef CONFIG_TRACE_IRQFLAGS 6. bltrace_hardirqs_off 7. #endif 8. #ifdef CONFIG_PREEMPT 9. get_thread_info tsk 10. ldrr8,[tsk,#TI_PREEMPT]@ 獲取preempt計數(shù)器值 11. addr7,r8,#1@ preempt加1,標(biāo)識禁止

23、搶占 12. strr7,[tsk,#TI_PREEMPT]@將加1后的結(jié)果寫入進(jìn)程內(nèi)核棧的變量中 13. #endif 14. 15. irq_handler @調(diào)用中斷處理程序,稍后分析 16. #ifdef CONFIG_PREEMPT 17. strr8,[tsk,#TI_PREEMPT]@ 恢復(fù)中斷前的preempt計數(shù)器 18. ldrr0,[tsk,#TI_FLAGS]@ 獲取flags 19. teqr8,#0@ 判斷preempt是否等于0 20. movner0,#0@ 如果preempt不等于0,r0=0 21. tstr0,#_TIF_NEED_RE

24、SCHED @將r0與#_TIF_NEED_RESCHED做“與操作” 22. blnesvc_preempt @如果不等于0,說明發(fā)生內(nèi)核搶占,需要重新調(diào)度。 23. #endif 24. 25. ldrr0,[sp,#S_PSR]@ irqs are already disabled 26. msrspsr_cxsf,r0 27. #ifdef CONFIG_TRACE_IRQFLAGS 28. tstr0,#PSR_I_BIT 29. bleqtrace_hardirqs_on 30. #endif 31. svc_exit r4 @恢復(fù)中斷上下文,稍后分析。 32

25、. UNWIND(.fnend) 33. ENDPROC(__irq_svc) 宏定義svc_entry(保護(hù)中斷上下文到棧) 其中svc_entry是一個宏定義,主要用于保護(hù)中斷上下文到棧中。svc_entry主要是在當(dāng)前堆棧上分配一個pt_regs結(jié)構(gòu),把r0-r15以及cpsr等保存到這個結(jié)構(gòu)中,在進(jìn)入irq_handler時,sp指向pt_regs底端: 1. .macrosvc_entry,stack_hole=0 2. UNWIND(.fnstart) 3. UNWIND(.save {r0-pc}) 4. subsp,sp,#(S_FRAME_SIZE+\s

26、tack_hole) 5. SPFIX(tstsp,#4) 6. SPFIX(bicnesp,sp,#4) 7. stmibsp,{r1-r12} 8. 9. ldmiar0,{r1-r3} 10. addr5,sp,#S_SP@ hereforinterlock avoidance 11. movr4,#-1@"""""""" 12. addr0,sp,#(S_FRAME_SIZE+\stack_hole) 13. SPFIX(addner0,r0,#4) 14. strr1,[sp]@ save the"real"r0 copied 15. @ from the ex

27、ception stack 16. 17. movr1,lr 18. 19. @ 20. @ We are now ready to fillinthe remaining blanks on the stack: 21. @ 22. @ r0-sp_svc 23. @ r1-lr_svc 24. @ r2-lr_,already fixed upforcorrect return/restart 25. @ r3-spsr_ 26. @ r4-orig_r0(see pt_regs definitioninptrace.h)

28、 27. @ 28. stmiar5,{r0-r4} 29. .endm svc_entry宏填充pt_regs結(jié)構(gòu)體的過程如圖5-2所示,先將r1~r12保存到ARM_r1~ARM_ip(綠色部分),然后將產(chǎn)生中斷時的r0寄存器內(nèi)容保存到ARM_r0(藍(lán)色部分),由于是在svc模式下產(chǎn)生的中斷,所以最后將sp_svc、lr_svc、lr_irq、spsr_irq和r4保存到ARM_sp、ARM_lr、ARM_pc、ARM_cpsr和ARM_ORIG_r0(紅色部分)。 圖5-3 svc_entry宏填充pt_regs結(jié)構(gòu)體 上述的中斷上下文保存過程共涉及了3種棧指針,分別是

29、:用戶空間棧指針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.irq[0]。 附錄1,arm體系下pt_regs結(jié)構(gòu) structpt_regs { long uregs[18]; }; uregs[0] - uregs[17]分別對應(yīng),r0 - r15,cpsr,ORIG_r0 附錄1,irq中斷時堆棧的變化 -------- s

30、psr -------- 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. .macroirq_handler 2. get_irqnr_preamble r5,lr 3. 1:get_irqnr_and_base r0,r6,r5

31、,lr @獲取中斷號,存到r0中,稍后分析 4. movner1,sp @如果中斷號不等于0,將r1=sp,即pt_regs結(jié)構(gòu)體首地址 5. @ 6. @ routine called with r0=irq number,r1=structpt_regs* 7. @ 8. adrnelr,1b @如果r0(中斷號)不等于0, lr(返回地址)等于標(biāo)號1處,即get_irqnr_and_base r0,r6,r5,lr的那行,即循環(huán)處理所有的中斷。 9. bneasm_do_IRQ @進(jìn)入中斷處理,稍后分析。 10. …… 11. .endm get_irqnr_and_b

32、ase用于判斷當(dāng)前發(fā)生的中斷號(與CPU緊密相關(guān)),此處不再分析。如果獲取的中斷號不等于0,則將中斷號存入r0寄存器作為第一個參數(shù),pt_regs結(jié)構(gòu)體地址存入r1寄存器作為第二個參數(shù),跳轉(zhuǎn)到c語言函數(shù)asm_do_IRQ做進(jìn)一步處理。為了不讓大家在匯編語言和C語言之間來回切換,還是先把最后一點匯編語言代碼(中斷返回匯編代碼)分析了再去分析asm_do_IRQ吧。 回看__irq_usr和__irq_svc標(biāo)號處的代碼,在完成了irq_handler中斷處理函數(shù)后,要完成從中斷異常處理程序返回到中斷點的工作。 ret_to_user 如果中斷產(chǎn)生于用戶空間,則調(diào)用ret_to_user來

33、恢復(fù)中斷現(xiàn)場并返回用戶空間繼續(xù)運行: 1. arch/arm/kernel/entry_armv.S 2. ENTRY(ret_to_user) 3. ret_slow_syscall: 4. disable_irq@ disable interrupts,此處不明白,disable_irq應(yīng)該接受irq中斷號作為參數(shù),來禁止指定的irq號中斷線。但是此處調(diào)用disable_irq之前并沒有將irq中斷號存入r0寄存器,這是為什么? 5. ldrr1,[tsk,#TI_FLAGS]@獲取thread_info->flags 6. tstr1,#_TIF_WORK_MAS

34、K @判斷是否有待處理的work 7. bnework_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. restore_user_regs fast=0,offset=0 @調(diào)用restore_user_reg

35、s 13. ENDPROC(ret_to_user) 14. 15. 以下是恢復(fù)中斷現(xiàn)場寄存器的宏,就是將發(fā)生中斷時保存在內(nèi)核空間堆棧上的寄存器還原,可以對照圖5-2所示的內(nèi)核空間堆棧保存的內(nèi)容來理解下面代碼: 16. .macrorestore_user_regs,fast=0,offset=0 17. ldrr1,[sp,#\offset+S_PSR]@ 從內(nèi)核棧中獲取發(fā)生中斷時的cpsr值 18. ldrlr,[sp,#\offset+S_PC]!@ 從內(nèi)核棧中獲取發(fā)生中斷時的下一條指令地址 19. msrspsr_cxsf,r1@ 將r1保存到spsr_svc 20.

36、#ifdefined(CONFIG_CPU_32v6K) 21. clrex@ clear the exclusive monitor 22. #elif defined(CONFIG_CPU_V6) 23. strexr1,r2,[sp]@ clear the exclusive monitor 24. #endif 25. .if\fast 26. ldmdbsp,{r1-lr}^@ get calling r1-lr 27. .else 28. ldmdbsp,{r0-lr}^ @ 存在^,所以將內(nèi)核棧保存的內(nèi)容恢復(fù)到用戶空間的r0~lr寄存器 29. .endif

37、30. addsp,sp,#S_FRAME_SIZE-S_PC 31. movspc,lr@將發(fā)生中斷時的下一條指令地址存入pc,從而返回中斷點繼續(xù)執(zhí)行,并且將發(fā)生中斷時的cpsr內(nèi)容恢復(fù)到cpsr寄存器中(開啟中斷)。 32. .endm svc_exit 如果中斷產(chǎn)生于內(nèi)核空間,則調(diào)用svc_exit來恢復(fù)中斷現(xiàn)場: 1. arch/arm/kernel/ entry-header.S 2. .macrosvc_exit,rpsr 3. msrspsr_cxsf,\rpsr 4. #ifdefined(CONFIG_CPU_32v6K) 5. clrex@ cle

38、ar the exclusive monitor 6. ldmiasp,{r0-pc}^@ load r0-pc,cpsr 7. #elif defined(CONFIG_CPU_V6) 8. ldrr0,[sp] 9. strexr1,r2,[sp]@ clear the exclusive monitor 10. ldmibsp,{r1-pc}^@ load r1-pc,cpsr 11. #else 12. ldmiasp,{r0-pc}^@ 返回內(nèi)核空間時,恢復(fù)中斷現(xiàn)場比較簡單,就是將r0-pc以及cpsr恢復(fù)即可,同時中斷也被開啟。 13. #endif 14. .e

39、ndm asm_do_IRQ函數(shù) ok,分析完所有與中斷相關(guān)的匯編語言代碼后,下面開始分析C語言代碼: 在arch/arm/kernel/irq.c文件中找到asm_do_IRQ函數(shù)定義: 1. asmlinkage void __exception asm_do_IRQ(unsignedintirq,structpt_regs*regs) 2. { 3. /*保存新的寄存器集合指針到全局cpu變量,方便后續(xù)處理程序訪問寄存器集合。*/ 4. structpt_regs*old_regs=set_irq_regs(regs); 5. 6. irq_enter();

40、7. 8. /* 9. *Some hardware gives randomly wrong interrupts.Rather 10. *than crashing,do something sensible. 11. */ 12. if(unlikely(irq>=NR_IRQS)){ //判斷中斷號 13. if(printk_ratelimit()) 14. printk(KERN_WARNING"Bad IRQ%u\n",irq); 15. ack_bad_irq(irq); 16. }else{ 17. generic_handle_irq(irq);//調(diào)用

41、中斷處理函數(shù) 18. } 19. 20. /*AT91 specific workaround*/ 21. irq_finish(irq); 22. 23. 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)體的指針保存在一個全局的CPU變量中,后續(xù)的程序可以通過該

42、變量訪問寄存器結(jié)構(gòu)體。所以在進(jìn)入中斷處理前,先將全局CPU變量中保存的舊指針保留下來,等到中斷處理結(jié)束后再將其恢復(fù)。 2、irq_enter irq_enter負(fù)責(zé)更新一些統(tǒng)計量: 1. 2. void irq_enter(void) 3. { 4. intcpu=smp_processor_id(); 5. 6. rcu_irq_enter(); 7. if(idle_cpu(cpu)&&!in_interrupt()){ 8. __irq_enter(); 9. tick_check_idle(cpu); 10. }el

43、se 11. __irq_enter(); 12. } 如果系統(tǒng)開啟動態(tài)時鐘特性且很長時間沒有產(chǎn)生時鐘中斷,則調(diào)用tick_check_idle更新全局變量jiffies(關(guān)于動態(tài)時鐘特性,在后續(xù)的總結(jié)中再進(jìn)行分析)。宏__irq_enter()定義如下: 1. #define __irq_enter()\ 2. do{\ 3. account_system_vtime(current);\ 4. add_preempt_count(HARDIRQ_OFFSET);\ 5. trace_hardirq_enter();\ 6. }while(0) add_preem

44、pt_count(HARDIRQ_OFFSET)使表示中斷處理程序嵌套層次的計數(shù)器加1。計數(shù)器保存在當(dāng)前進(jìn)程thread_info結(jié)構(gòu)的preempt_count字段中: 圖5-5 preempt_count結(jié)構(gòu) 內(nèi)核將preempt_count分成5部分:bit0~7與PREEMPT相關(guān),bit8~15用作軟中斷計數(shù)器,bit16~25用作硬中斷計數(shù)器,bit26用作不可屏蔽中斷計數(shù)器,bit28用作PREEMPT_ACTIVE標(biāo)志。 3、generic_handle_irq generic_handle_irq是體系結(jié)構(gòu)無關(guān)函數(shù),用來調(diào)用desc->handle_irq,該函數(shù)

45、指針在中斷初始化時指向了電流處理函數(shù)(handle_level_irq或handle_edge_irq),針對不同的中斷觸發(fā)類型(邊沿觸發(fā)或電平觸發(fā))做相應(yīng)的處理。然后調(diào)用handle_IRQ_event遍歷action鏈表從而調(diào)用該中斷號對應(yīng)的一個或多個中斷處理程序action->handler,而action->handler就是通過request_irq初始化的。 handle_level_irq 首先分析一下handle_level_irq函數(shù): 1. 2. void handle_level_irq(unsignedintirq

46、,struct irq_desc*desc) 3. { 4. struct irqaction*action; 5. 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同時處理同一中斷。 11. *當(dāng)desc->status包含IRQ_INPROGRESS標(biāo)志時,說明該中斷 12. *正在另一個cpu上處理,因此當(dāng)前cpu可以直接放棄處理。

47、 13. */ 14. if(unlikely(desc->status&IRQ_INPROGRESS)) 15. goto out_unlock; 16. desc->status&=~(IRQ_REPLAY|IRQ_WAITING); 17. kstat_incr_irqs_this_cpu(irq,desc); 18. 19. /* 20. *如果沒有對該中斷注冊處理程序,即desc->action為NULL。 21. *或者desc->status設(shè)置為IRQ_DISABLED,表示該中斷是被禁止的。 22. *以上兩種情況只要出現(xiàn)一種即可放棄處理。 23. */

48、 24. action=desc->action; 25. if(unlikely(!action||(desc->status&IRQ_DISABLED))) 26. goto out_unlock; 27. 28. desc->status|=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. if(!noir

49、qdebug) 34. note_interrupt(irq,desc,action_ret); 35. 36. spin_lock(&desc->lock);/*訪問desc內(nèi)容前加自旋鎖*/ 37. desc->status&=~IRQ_INPROGRESS;/*清除“正在處理”的標(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->s

50、tatus|=IRQ_MASKED; 43. /*如果中斷處理函數(shù)中未對desc->status 設(shè)置為IRQ_ DISABLED, 44. *且desc->chip->unmask不為空,則desc->chip->unmask所指向的芯片相關(guān)函數(shù), 45. *解除對該中斷的屏蔽。 46. */ 47. elseif(!(desc->status&IRQ_DISABLED)&&desc->chip->unmask) 48. desc->chip->unmask(irq); 49. out_unlock: 50. spin_unlock(&desc->lock);/*釋放自旋鎖*

51、/ 51. } handle_edge_irq 再來介紹一下handle_edge_irq函數(shù),相對于handle_level_irq要復(fù)雜一點: 1. 2. void handle_edge_irq(unsignedintirq,struct irq_desc*desc) 3. { 4. spin_lock(&desc->lock); 5. desc->status&=~(IRQ_REPLAY|IRQ_WAITING); 6. /* 7. *如果該中斷正在被其他cpu處理,或者是該中斷已被禁止, 8. *則不處理該中斷,但

52、要將其標(biāo)識為pending狀態(tài)且屏蔽該中斷以便后續(xù)處理 9. */ 10. if(unlikely((desc->status&(IRQ_INPROGRESS|IRQ_DISABLED))|| 11. !desc->action)){ 12. desc->status|=(IRQ_PENDING|IRQ_MASKED); 13. mask_ack_irq(desc,irq); 14. goto out_unlock; 15. } 16. kstat_incr_irqs_this_cpu(irq,desc); 17. 18. /*Start handling the irq*

53、/ 19. if(desc->chip->ack) 20. desc->chip->ack(irq); 21. 22. /*標(biāo)識該中斷狀態(tài)為“正在處理”*/ 23. desc->status|=IRQ_INPROGRESS; 24. 25. do{ 26. struct irqaction*action=desc->action; 27. irqreturn_t action_ret; 28. 29. if(unlikely(!action)){ 30. desc->chip->mask(irq); 31. goto out_unlock; 32. } 33.

54、34. /* 35. *如果當(dāng)處理該中斷時有另一個中斷到達(dá), 36. *那么當(dāng)時可能屏蔽了該中斷。 37. *如果該中斷沒有被禁止,則解除對該中斷的屏蔽。 38. */ 39. if(unlikely((desc->status& 40. (IRQ_PENDING|IRQ_MASKED|IRQ_DISABLED))== 41. (IRQ_PENDING|IRQ_MASKED))){ 42. desc->chip->unmask(irq); 43. desc->status&=~IRQ_MASKED; 44. } 45. 46. desc->status&=~IRQ_PE

55、NDING; 47. spin_unlock(&desc->lock); 48. /*調(diào)用由request_irq注冊的處理函數(shù),稍后分析。*/ 49. action_ret=handle_IRQ_event(irq,action); 50. if(!noirqdebug) 51. note_interrupt(irq,desc,action_ret); 52. spin_lock(&desc->lock); 53. /*如果該中斷沒有被禁止,并且有其他中斷等待處理(IRQ_PENDING), 54. *則循環(huán)處理其他中斷。 55. */ 56. }while((desc-

56、>status&(IRQ_PENDING|IRQ_DISABLED))==IRQ_PENDING); 57. 58. desc->status&=~IRQ_INPROGRESS; 59. out_unlock: 60. spin_unlock(&desc->lock); 61. } handle_IRQ_event 不管是電平觸發(fā)還是邊沿觸發(fā),最終都會通過handle_IRQ_event來調(diào)用注冊的中斷處理函數(shù)。 1. 2. irqreturn_t handle_IRQ_event(unsignedintirq,struct irqaction*action) 3. { 4. irqreturn_t ret,retval=IRQ_NONE; 5. unsignedintstatus=0; 6. 7. /*如果注冊中

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

相關(guān)資源

更多
正為您匹配相似的精品文檔
關(guān)于我們 - 網(wǎng)站聲明 - 網(wǎng)站地圖 - 資源地圖 - 友情鏈接 - 網(wǎng)站客服 - 聯(lián)系我們

copyright@ 2023-2025  zhuangpeitu.com 裝配圖網(wǎng)版權(quán)所有   聯(lián)系電話:18123376007

備案號:ICP2024067431-1 川公網(wǎng)安備51140202000466號


本站為文檔C2C交易模式,即用戶上傳的文檔直接被用戶下載,本站只是中間服務(wù)平臺,本站所有文檔下載所得的收益歸上傳人(含作者)所有。裝配圖網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對上載內(nèi)容本身不做任何修改或編輯。若文檔所含內(nèi)容侵犯了您的版權(quán)或隱私,請立即通知裝配圖網(wǎng),我們立即給予刪除!