在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

            EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM Linux異常處理之data abort

            ARM Linux異常處理之data abort

            作者: 時(shí)間:2016-11-09 來源:網(wǎng)絡(luò) 收藏

            1 異常向量與程序跳轉(zhuǎn)

            data abortARM體系定義的異常之一。異常發(fā)生時(shí),ARM會(huì)自動(dòng)跳轉(zhuǎn)到異常向量表中,通過向量表中的跳轉(zhuǎn)命令跳轉(zhuǎn)到相應(yīng)的異常處理中去。
            ARM的異常處理向量表在entry-armv.S文件中:
            .globl      __vectors_start__vectors_start:swi   SYS_ERROR0b     vector_und + stubs_offsetldr   pc, .LCvswi + stubs_offsetb     vector_pabt + stubs_offsetb     vector_dabt + stubs_offsetb     vector_addrexcptn + stubs_offsetb     vector_irq + stubs_offsetb     vector_fiq + stubs_offset
            對于data abort,對應(yīng)的跳轉(zhuǎn)地址是vector_dabt + stubs_offset。這個(gè)地址的指令定義也在entry-armv.S:
            vector_stub     dabt, ABT_MODE, 8.long       __dabt_usr                       @  0  (USR_26 / USR_32).long       __dabt_invalid                     @  1  (FIQ_26 / FIQ_32).long       __dabt_invalid                     @  2  (IRQ_26 / IRQ_32).long       __dabt_svc                       @  3  (SVC_26 / SVC_32).long       __dabt_invalid                     @  4.long       __dabt_invalid                     @  5.long       __dabt_invalid                     @  6.long       __dabt_invalid                     @  7.long       __dabt_invalid                     @  8.long       __dabt_invalid                     @  9.long       __dabt_invalid                     @  a.long       __dabt_invalid                     @  b.long       __dabt_invalid                     @  c.long       __dabt_invalid                     @  d.long       __dabt_invalid                     @  e.long       __dabt_invalid                     @  f
            vector_stub是一個(gè)宏定義:
            .macro     vector_stub, name, mode, correction=0.align      5vector_name:.if correctionsub  lr, lr, #correction.endif@@ Save r0, lr_ (parent PC) and spsr_@ (parent CPSR)@stmia       sp, {r0, lr}             @ save r0, lrmrs  lr, spsr                          @ 保存跳轉(zhuǎn)之前的CPSR到lr寄存器str    lr, [sp, #8]                    @ save spsr@@ Prepare for SVC32 mode.  IRQs remain disabled.@mrs  r0, cpsreor   r0, r0, #(mode ^ SVC_MODE)msr  spsr_cxsf, r0                 @ 準(zhǔn)備進(jìn)入svc模式@@ the branch table must immediately follow this code@and  lr, lr, #0x0f                    @ 得到跳轉(zhuǎn)前所處的模式(usr、svr等)mov r0, spldr   lr, [pc, lr, lsl #2]            @ 根據(jù)模式跳轉(zhuǎn)到相應(yīng)的data abort指令,并進(jìn)入svc模式movs       pc, lr                     @ branch to handler in SVC modeENDPROC(vector_name).endm
            由代碼中紅色標(biāo)注部分可看出,對于同一個(gè)異常,根據(jù)進(jìn)入異常之前所處的模式,會(huì)跳轉(zhuǎn)到不同的指令分支,www.linuxidc.com這些指令分支緊跟在vector_stub宏定義的后面。如果進(jìn)入data abort之前處于usr模式,那么跳轉(zhuǎn)到__dabt_usr;如果處于svc模式,那么跳轉(zhuǎn)到__dabt_svc;否則跳轉(zhuǎn)到__dabt_invalid。
            實(shí)際上,進(jìn)入異常向量前Linux只能處于usr或者svc兩種模式之一。這時(shí)因?yàn)閕rq等異常在跳轉(zhuǎn)表中都要經(jīng)過vector_stub宏,而不管之前是哪種狀態(tài),這個(gè)宏都會(huì)將CPU狀態(tài)改為svc模式。
            usr模式即Linux中的用戶態(tài)模式,svc即內(nèi)核模式。
            下面看一下在不同模式下進(jìn)入data abort時(shí)的處理過程。

            2 svc模式進(jìn)入data abort

            svc模式進(jìn)入data abort,也就是Linux的內(nèi)核模式進(jìn)入data aboart時(shí),會(huì)跳轉(zhuǎn)到__dabt_svc。
            __dabt_svc:svc_entry               @ 保護(hù)寄存器現(xiàn)場mrs  r9, cpsrtst    r3, #PSR_I_BIT            @ 檢查是否要開中斷biceq       r9, r9, #PSR_I_BITbl    CPU_DABORT_HANDLER  @ 處理異常之前的準(zhǔn)備工作msr  cpsr_c, r9mov r2, spbl    do_DataAbort        @ 主要操作都在這里,本文暫不研究disable_irqldr   r0, [sp, #S_PSR]msr  spsr_cxsf, r0ldmia      sp, {r0 - pc}^                @ load r0 - pc, cpsrENDPROC(__dabt_svc)
            CPU_DABORT_HANDLER的定義在glue.h:
            #define CPU_DABORT_HANDLER v6_early_abort
            對于s3c6410,v6_early_abort的定義在abort-ev6.S中,里面涉及到很多ARM的細(xì)節(jié)操作,但對我們來說,只需要了解下面這兩句即可:
            mrc  p15, 0, r1, c5, c0, 0              @ get FSRmrc  p15, 0, r0, c6, c0, 0              @ get FAR
            這兩句用于讀取協(xié)處理器CP15的C5、C6寄存器。當(dāng)data abort異常發(fā)生時(shí),C5寄存器中保存的值指明了是哪種原因?qū)е碌漠惓#唧w原因可在介紹arm的資料中找到。C6寄存器中保存的是導(dǎo)致data abort的存儲(chǔ)地址。

            3 usr模式進(jìn)入data abort

            usr模式進(jìn)入data abort,也就是Linux的用戶模式進(jìn)入data bort時(shí),會(huì)跳轉(zhuǎn)到__dabt_usr。

            本文引用地址:http://www.biyoush.com/article/201611/317937.htm

            __dabt_usr:usr_entry                                    @ 保護(hù)寄存器現(xiàn)場kuser_cmpxchg_checkbl    CPU_DABORT_HANDLER  @ 與svc模式時(shí)處理過程一樣enable_irq                                  @ 開中斷mov r2, spadr  lr, ret_from_exception            @ 重設(shè)返回地址b     do_DataAbort                      @ 與svc模式時(shí)處理過程一樣ENDPROC(__dabt_usr)
            由代碼可知,用戶模式和內(nèi)核模式的data abort處理過程類似,區(qū)別在于:

            • 用戶模式下data abort處理一定是開中斷的;內(nèi)核模式下則由具體情況決定。
            • 用戶模式下異常處理返回地址被設(shè)為ret_from_exception (entry-armv.S文件);內(nèi)核模式下則返回到出現(xiàn)異常的那條語句。
            下面看一下ret_from_exception:
            ENTRY(ret_from_exception)get_thread_info tskmov why, #0b     ret_to_userENDPROC(__pabt_usr)
            ret_to_user會(huì)判斷是否需要進(jìn)行進(jìn)程調(diào)度,并最終返回到用戶空間。用戶空間data abort時(shí)可能產(chǎn)生進(jìn)程調(diào)度的原因就在這里。

            4 未定義狀態(tài)的data abort

            除了usr和svc模式之外,其它模式下發(fā)生data abort時(shí),都會(huì)調(diào)用__dabt_invalid函數(shù)。這里所說的其它模式在linux正常運(yùn)行過程中是不應(yīng)該存在的,所以如果進(jìn)入__dabt_invalid函數(shù),那就代表Linux內(nèi)核應(yīng)該崩潰了。
            __dabt_invalid:inv_entry BAD_DATAb     common_invalidENDPROC(__dabt_invalid)
            inv_entry宏做的主要工作是保存寄存器現(xiàn)場(壓棧)。
            common_invalid做一些必要的設(shè)置,最終調(diào)用C函數(shù)bad_mode (traps.c)。
            asmlinkage void bad_mode(struct pt_regs *regs, int reason){console_verbose();printk(KERN_CRIT "Bad mode in %s handler detectedn", handler[reason]);die("Oops - bad mode", regs, 0);local_irq_disable();panic("bad mode");}
            由代碼可知,bad_mode主要是輸出一些必要的信息,然后調(diào)用panic函數(shù),進(jìn)入死循環(huán)。
            上文提到data abort的正常處理過程中,最終會(huì)調(diào)用do_DataAbort函數(shù),下面分析一下該函數(shù)的處理過程。

            5 do_DataAbort

            asmlinkage void __exception do_DataAbort(unsigned long addr,                     // 導(dǎo)致異常的內(nèi)存地址unsigned int fsr,                          // 異常發(fā)生時(shí)CP15中的寄存器值,見前文struct pt_regs *regs)                     // 異常發(fā)生時(shí)的寄存器值列表{     const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6);if (!inf->fn(addr, fsr, regs))return;info.si_signo = inf->sig;info.si_errno = 0;info.si_code  = inf->code;info.si_addr  = (void __user *)addr;arm_notify_die("", regs, &info, fsr, 0);}
            處理data abort時(shí),首先根據(jù)fsr的值得到產(chǎn)生abort的原因,然后根據(jù)此原因從一個(gè)全局?jǐn)?shù)組fsr_info中得到處理此種abort的struct fsr_info結(jié)構(gòu),然后調(diào)用結(jié)構(gòu)中的fn函數(shù)處理。如果fn函數(shù)為空,或者函數(shù)返回不為0,則調(diào)用arm_notify_die函數(shù)。

            5.1 arm_notify_die

            首先看一下比較簡單的情形,即fsr_info中fn未定義,此時(shí)調(diào)用arm_notify_die處理:
            void arm_notify_die(const char *str, struct pt_regs *regs,struct siginfo *info, unsigned long err, unsigned long trap){if (user_mode(regs)) {// 。。。force_sig_info(info->si_signo, info, current);} else {die(str, regs, err);}}
            該函數(shù)首先使用user_mode判斷abort時(shí)是屬于用戶模式還是內(nèi)核模式,判斷方法是看cpsr寄存器中的模式位。按照arm的定義,模式位為0代表用戶模式。
            • 如果是用戶模式,那么強(qiáng)制發(fā)送一個(gè)信號(hào)給導(dǎo)致abort的任務(wù)(注意這里的任務(wù)可能是一個(gè)線程)。具體哪個(gè)信號(hào)被發(fā)送由struct fsr_info結(jié)構(gòu)體中定義的值決定,一般來說,是一個(gè)能使進(jìn)程停止的信號(hào),比如SIGSEGV等等(SIGSEGV之類的信號(hào)即使被發(fā)給一個(gè)線程,也會(huì)停止整個(gè)進(jìn)程,具體可看get_signal_to_deliver函數(shù))。
            • 如果是內(nèi)核模式,那么調(diào)用die函數(shù),這是kernel處理OOPS的標(biāo)準(zhǔn)函數(shù)。

            5.2 fsr_info

            fsr_info數(shù)組定義在fault.c中,對于每一種可能導(dǎo)致data abort的原因,都有一個(gè)fsr_info結(jié)構(gòu)與之對應(yīng)。
            static struct fsr_info fsr_info[] = {{ do_bad,              SIGSEGV, 0,         "vector exception"            },// 。。。{ do_translation_fault,    SIGSEGV, SEGV_MAPERR, "section translation fault"},{ do_bad,              SIGBUS, 0,          "external abort on linefetch"},{ do_page_fault,     SIGSEGV, SEGV_MAPERR, "page translation fault"      },{ do_bad,              SIGBUS, 0,          "external abort on non-linefetch"  },{ do_bad,              SIGSEGV, SEGV_ACCERR, "section domain fault"              },{ do_bad,              SIGBUS, 0,          "external abort on non-linefetch"  },{ do_bad,              SIGSEGV, SEGV_ACCERR, "page domain fault"           },{ do_bad,              SIGBUS, 0,          "external abort on translation"          },{ do_sect_fault,     SIGSEGV, SEGV_ACCERR, "section permission fault"         },{ do_bad,              SIGBUS, 0,          "external abort on translation"          },{ do_page_fault,     SIGSEGV, SEGV_ACCERR, "page permission fault"             },{ do_bad,              SIGBUS,  0,         "unknown 16"                  },// 。。。{ do_bad,              SIGBUS,  0,         "unknown 30"                  },{ do_bad,              SIGBUS,  0,         "unknown 31"                  }};
            fsr_info對大多數(shù)abort都調(diào)用do_bad函數(shù)處理,do_bad函數(shù)簡單返回1,這樣就可以繼續(xù)執(zhí)行上面提到的arm_notify_die。
            fsr_info對以下四種特殊abort將作單獨(dú)處理:
            • "section translation fault" do_translation_fault
            段轉(zhuǎn)換錯(cuò)誤,即找不到二級(jí)頁表
            • "page translation fault" do_page_fault
            頁表錯(cuò)誤,即線性地址無效,沒有對應(yīng)的物理地址
            • "section permission fault" do_sect_fault
            段權(quán)限錯(cuò)誤,即二級(jí)頁表權(quán)限錯(cuò)誤
            • "page permission fault" do_page_fault
            頁權(quán)限錯(cuò)誤

            5.3 段權(quán)限錯(cuò)誤 do_sect_fault

            do_sect_fault函數(shù)直接調(diào)用do_bad_area作處理,并返回0,所以不會(huì)再經(jīng)過arm_notify_die。do_bad_area中,判斷是否屬于用戶模式。如果是用戶模式,調(diào)用__do_user_fault函數(shù);否則調(diào)用__do_kernel_fault函數(shù)。
            void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)if (user_mode(regs))__do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs);else__do_kernel_fault(mm, addr, fsr, regs);
            __do_user_fault中,會(huì)發(fā)送信號(hào)給當(dāng)前線程。
            __do_kernel_fault則比較復(fù)雜:
            • 調(diào)用fixup_exception進(jìn)行修復(fù)操作,fixup的具體細(xì)節(jié)可在內(nèi)核文檔exception.txt中找到,它可用于處理get_user之類函數(shù)傳入的地址參數(shù)無效的情況。
            • 如果不能修復(fù),調(diào)用die函數(shù)處理oops。
            • 如果沒有進(jìn)程上下文,內(nèi)核會(huì)在上一步的oops中panic。所以到這里肯定有一個(gè)進(jìn)程與之關(guān)聯(lián),于是調(diào)用do_exit(SIGKILL)函數(shù)退出進(jìn)程,SIGKILL會(huì)被設(shè)置在task_struct的exit_code域。

            5.4 段表錯(cuò)誤 do_translation_fault

            do_translation_fault函數(shù)中,會(huì)首先判斷引起abort的地址是否處于用戶空間。
            • 如果是用戶空間地址,調(diào)用do_page_fault,轉(zhuǎn)入和頁表錯(cuò)誤、頁權(quán)限錯(cuò)誤同樣的處理流程。
            • 如果是內(nèi)核空間地址,會(huì)判斷該地址對應(yīng)的二級(jí)頁表指針是否在init_mm中。如果在init_mm里面,www.linuxidc.com那么該二級(jí)頁表指針到當(dāng)前進(jìn)程的一級(jí)頁表;否則,調(diào)用do_bad_area處理(可能會(huì)調(diào)用到fixup)。
            對段表錯(cuò)誤的處理邏輯的個(gè)人理解如下(不保證完全準(zhǔn)確):
            Linux產(chǎn)生段表錯(cuò)誤,除了fixup之外還有兩種原因:一個(gè)是用戶空間映射的線性地址出現(xiàn)異常,另一個(gè)是內(nèi)核中調(diào)用vmalloc分配的線性地址出現(xiàn)異常。對用戶空間地址的異常處理很容易理解。對于內(nèi)核地址,從vmalloc的實(shí)現(xiàn)代碼中可以看到,它分配的線性空間的映射關(guān)系都會(huì)保存到全局變量init_mm中,所以,任何vmalloc生成的線性空間的二級(jí)頁表都應(yīng)該在init_mm中找到。(init_mm是內(nèi)核的mm_struct,管理整個(gè)內(nèi)核的內(nèi)存映射)。
            從這里也可以看出,對vmalloc的地址訪問可能會(huì)產(chǎn)生兩次異常:第一次是段表錯(cuò)誤,生成二級(jí)頁表;第二次是頁表錯(cuò)誤,分配真正的物理頁面到線性空間。

            5.5 頁表錯(cuò)誤 do_page_fault
            5.6 頁權(quán)限錯(cuò)誤 do_page_fault

            do_page_fault完成了真正的物理頁面分配工作,另外棧擴(kuò)展、mmap的支持等也都在這里。對于物理頁面的分配,會(huì)調(diào)用到do_anonymous_page->。。。-> __rmqueue,__rmqueue中實(shí)現(xiàn)了物理頁面分配的伙伴算法。
            如果當(dāng)前沒有足夠物理頁面供內(nèi)存分配,即分配失?。?br />
            • 內(nèi)核模式下的abort會(huì)調(diào)用__do_kernel_fault,這與段權(quán)限錯(cuò)誤中的處理一樣。
            • 用戶模式下,會(huì)調(diào)用do_group_exit退出該任務(wù)所屬的進(jìn)程。
            用戶程序申請內(nèi)存空間時(shí),如果庫函數(shù)本身的內(nèi)存池不能滿足分配,會(huì)調(diào)用brk系統(tǒng)調(diào)用向系統(tǒng)申請擴(kuò)大堆空間。但此時(shí)擴(kuò)大的只是線性空間,直到真正使用到那塊線性空間時(shí),系統(tǒng)才會(huì)通過data abort分配物理頁面。所以,malloc返回不為NULL只能說明得到了線性空間的資源,真正物理內(nèi)存分配失敗時(shí),進(jìn)程還是會(huì)以資源不足為由,直接退出。


            關(guān)鍵詞: ARMLinux異常處理dataabor

            評論


            技術(shù)專區(qū)

            關(guān)閉