<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

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

          ARM Linux異常處理之data abort二

          作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò) 收藏
          上文提到data abort的正常處理過(guò)程中,最終會(huì)調(diào)用do_DataAbort函數(shù),下面分析一下該函數(shù)的處理過(guò)程。

          do_DataAbort

          asmlinkage void __exception do_DataAbort(

          本文引用地址:http://www.ex-cimer.com/article/201611/317936.htm

          unsigned long addr,//導(dǎo)致異常的內(nèi)存地址

          unsigned int fsr,//異常發(fā)生時(shí)CP15中的寄存器值,見(jiàn)前文

          struct pt_regs *regs)//異常發(fā)生前的寄存器值列表

          {

          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ù)。

          arm_notify_die

          首先看一下比較簡(jiǎn)單的情形,即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代表用戶模式。

          l如果是用戶模式,那么強(qiáng)制發(fā)送一個(gè)信號(hào)給導(dǎo)致abort的任務(wù)(注意這里的任務(wù)可能是一個(gè)線程)。具體哪個(gè)信號(hào)被發(fā)送由struct fsr_info結(jié)構(gòu)體中定義的值決定,一般來(lái)說(shuō),是一個(gè)能使進(jìn)程停止的信號(hào),比如SIGSEGV等等(SIGSEGV之類(lèi)的信號(hào)即使被發(fā)給一個(gè)線程,也會(huì)停止整個(gè)進(jìn)程,具體可看get_signal_to_deliver函數(shù))。

          l如果是內(nèi)核模式,那么調(diào)用die函數(shù),這是kernel處理OOPS的標(biāo)準(zhǔn)函數(shù)。

          fsr_info

          fsr_info數(shù)組定義在fault.c中,對(duì)于每一種可能導(dǎo)致data abort的原因,都有一個(gè)fsr_info結(jié)構(gòu)與之對(duì)應(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對(duì)大多數(shù)abort都調(diào)用do_bad函數(shù)處理,do_bad函數(shù)簡(jiǎn)單返回1,這樣就可以繼續(xù)執(zhí)行上面提到的arm_notify_die。

          fsr_info對(duì)以下四種特殊abort將作單獨(dú)處理:

          l"section translation fault"do_translation_fault
          段轉(zhuǎn)換錯(cuò)誤,即找不到二級(jí)頁(yè)表

          l"page translation fault"do_page_fault
          頁(yè)表錯(cuò)誤,即線性地址無(wú)效,沒(méi)有對(duì)應(yīng)的物理地址

          l"section permission fault"do_sect_fault
          段權(quán)限錯(cuò)誤,即二級(jí)頁(yè)表權(quán)限錯(cuò)誤

          l"page permission fault"do_page_fault
          頁(yè)權(quán)限錯(cuò)誤

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

          do_sect_fault函數(shù)直接調(diào)用do_bad_area作處理,并返回0,所以不會(huì)再經(jīng)過(guò)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ù)雜:

          l調(diào)用fixup_exception進(jìn)行修復(fù)操作,fixup的具體細(xì)節(jié)可在內(nèi)核文檔exception.txt中找到,它可用于處理get_user之類(lèi)函數(shù)傳入的地址參數(shù)無(wú)效的情況。

          l如果不能修復(fù),調(diào)用die函數(shù)處理oops。

          l如果沒(méi)有進(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域。

          段表錯(cuò)誤do_translation_fault

          do_translation_fault函數(shù)中,會(huì)首先判斷引起abort的地址是否處于用戶空間。

          l如果是用戶空間地址,調(diào)用do_page_fault,轉(zhuǎn)入和頁(yè)表錯(cuò)誤、頁(yè)權(quán)限錯(cuò)誤同樣的處理流程。

          l如果是內(nèi)核空間地址,會(huì)判斷該地址對(duì)應(yīng)的二級(jí)頁(yè)表指針是否在init_mm中。如果在init_mm里面,那么該二級(jí)頁(yè)表指針到當(dāng)前進(jìn)程的一級(jí)頁(yè)表;否則,調(diào)用do_bad_area處理(可能會(huì)調(diào)用到fixup)。

          對(duì)段表錯(cuò)誤的處理邏輯的個(gè)人理解如下(不保證完全準(zhǔn)確):
          Linux產(chǎn)生段表錯(cuò)誤,除了fixup之外還有兩種原因:一個(gè)是用戶空間映射的線性地址出現(xiàn)異常,另一個(gè)是內(nèi)核中調(diào)用vmalloc分配的線性地址出現(xiàn)異常。對(duì)用戶空間地址的異常處理很容易理解。對(duì)于內(nèi)核地址,從vmalloc的實(shí)現(xiàn)代碼中可以看到,它分配的線性空間的映射關(guān)系都會(huì)保存到全局變量init_mm中,所以,任何vmalloc生成的線性空間的二級(jí)頁(yè)表都應(yīng)該在init_mm中找到。(init_mm是內(nèi)核的mm_struct,管理整個(gè)內(nèi)核的內(nèi)存映射)。

          從這里也可以看出,對(duì)vmalloc的地址訪問(wèn)可能會(huì)產(chǎn)生兩次異常:第一次是段表錯(cuò)誤,生成二級(jí)頁(yè)表;第二次是頁(yè)表錯(cuò)誤,分配真正的物理頁(yè)面到線性空間。

          關(guān)于init_mm的細(xì)節(jié)可以參考http://my.chinaunix.net/space.php?uid=25471613&do=blog&id=323374,大概意思是內(nèi)核頁(yè)表改變時(shí)只改變init進(jìn)程的內(nèi)核頁(yè)表init_mm,其它進(jìn)程通過(guò)缺頁(yè)異常從init_mm更新自己維護(hù)的內(nèi)核頁(yè)表。

          頁(yè)表錯(cuò)誤do_page_fault

          頁(yè)權(quán)限錯(cuò)誤do_page_fault

          do_page_fault完成了真正的物理頁(yè)面分配工作,另外棧擴(kuò)展、mmap的支持等也都在這里。對(duì)于物理頁(yè)面的分配,會(huì)調(diào)用到do_anonymous_page->。。。-> __rmqueue,__rmqueue中實(shí)現(xiàn)了物理頁(yè)面分配的伙伴算法。

          如果當(dāng)前沒(méi)有足夠物理頁(yè)面供內(nèi)存分配,即分配失敗:

          l內(nèi)核模式下的abort會(huì)調(diào)用__do_kernel_fault,這與段權(quán)限錯(cuò)誤中的處理一樣。

          l用戶模式下,會(huì)調(diào)用do_group_exit退出該任務(wù)所屬的進(jìn)程。

          用戶程序申請(qǐng)內(nèi)存空間時(shí),如果庫(kù)函數(shù)本身的內(nèi)存池不能滿足分配,會(huì)調(diào)用brk系統(tǒng)調(diào)用向系統(tǒng)申請(qǐng)擴(kuò)大堆空間。但此時(shí)擴(kuò)大的只是線性空間,直到真正使用到那塊線性空間時(shí),系統(tǒng)才會(huì)通過(guò)data abort分配物理頁(yè)面。用戶空間的malloc函數(shù)返回不為NULL只能說(shuō)明得到了線性空間的資源,但物理內(nèi)存可能并沒(méi)有映射上去,所以真正物理內(nèi)存分配失敗時(shí),進(jìn)程還是會(huì)以資源不足為由,直接退出。



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

          評(píng)論


          技術(shù)專(zhuān)區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();