天天看點

2.4 linux存儲管理-越界通路

當我們通過頁式存儲管理機制将線性位址轉換成實體位址時,如果這個過程由于遇到某種阻礙而使得CPU無法通路到相應的實體單元,映射便失敗了。 映射失敗時,CPU會觸發一種異常,這種異常稱之為 缺頁異常(page fault exception),進而執行頁面異常處理程式。 失敗的原因: 1)虛拟、實體映射尚未建立 2)實體頁面被swap off 3)通路權限不對應(想要寫隻讀權限記憶體) 可能還有别的原因

CPU在發生頁面異常時,會觸發page_fault_exception處理,最後執行do_page_fault()  do_page_fault的部分代碼(開頭):

  1. 106 asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
  2. 107 {
  3. 108 struct task_struct *tsk;
  4. 109 struct mm_struct *mm;
  5. 110 struct vm_area_struct * vma;
  6. 111 unsigned long address;
  7. 112 unsigned long page;
  8. 113 unsigned long fixup;
  9. 114 int write;
  10. 115 siginfo_t info;
  11. 116
  12. 117 /* get the address */
  13. 118 __asm__("movl %%cr2,%0":"=r" (address));
  14. 119
  15. 120 tsk = current;
  16. 121
  17. 122 /*
  18. 123 * We fault-in kernel-space virtual memory on-demand. The
  19. 124 * 'reference' page table is init_mm.pgd.
  20. 58
  21. 125 *
  22. 126 * NOTE! We MUST NOT take any locks for this case. We may
  23. 127 * be in an interrupt or a critical region, and should
  24. 128 * only copy the information from the master page table,
  25. 129 * nothing more.
  26. 130 */
  27. 131 if (address >= TASK_SIZE)
  28. 132 goto vmalloc_fault;
  29. 133
  30. 134 mm = tsk->mm;
  31. 135 info.si_code = SEGV_MAPERR;
  32. 136
  33. 137 /*
  34. 138 * If we're in an interrupt or have no user
  35. 139 * context, we must not take the fault..
  36. 140 */
  37. 141 if (in_interrupt() || !mm)
  38. 142 goto no_context;
  39. 143
  40. 144 down(&mm->mmap_sem);
  41. 145
  42. 146 vma = find_vma(mm, address);
  43. 147 if (!vma)
  44. 148 goto bad_area;
  45. 149 if (vma->vm_start <= address)
  46. 150 goto good_area;
  47. 151 if (!(vma->vm_flags & VM_GROWSDOWN))
  48. 152 goto bad_area;

(參見  arch/i386/mm/fault.c) 代碼說明:

1. 擷取産生缺頁異常的虛拟位址(線性位址)。産生缺頁異常的虛拟位址被儲存在CR2寄存器中,由于c語言無法讀取CR2寄存器,需要 使用gcc内嵌 彙編實作。 2.  核心中斷/異常機制還會傳回兩個參數:struct pt_regs *regs, unsigned long error_code, regs指向寄存器的副本(儲存“現場”),error_code指明映射失敗的原因。 3.  核心中 可以使用current(宏)取得目前程序的task結構體 ,然後我們可以擷取目前程序的mm_struct(儲存虛存管理和映射相關資訊),CPU的映射過程不影響mm_struct結構,但是mm_struct描述了這種映射。 4. 程序出錯: find_vma傳回尋找到的第一個 末尾位址大于address的vma ,根據傳回的vma結果進一步判斷錯誤原因。
4.1 第一種  goto bad_area:沒有找到合适的vma,說明address過大,通路的位址屬于系統空間
4.2  goto good_area:說明找到合适vma,映射已經建立,原因需要進一步查找
除了前面兩種情況,還有可能落在vma之間的洞裡面 4.3   第一種空洞:堆棧區下面的大空洞 堆棧區的vma的vm_flags有VM_GROWSDOWN這個标志位 (這種情況的處理,也是goto good_area,裡面會進一步判斷) 4.4 第二種空洞:vma之間的小空洞

        5. 出錯特殊情況

             中斷處理程式出錯(程式能夠檢測):

            若in_interrupt()傳回非0,說明中斷處理程式出錯

             mm_struct為空(程式不能檢測出):

因為mm_struct為空,說明程序的映射尚未建立,因而不可能與目前程序有關,而 in_interrupt()又傳回0,說明中斷異常進行中出錯,不過該錯誤 in_interrupt()無法檢測出來

do_page_fault的部分代碼(bad_area處理):

  1. 220 /*
  2. 221 * Something tried to access memory that isn't in our memory map..
  3. 222 * Fix it, but check if it's kernel or user first..
  4. 223 */
  5. 224 bad_area:
  6. 225 up(&mm->mmap_sem);
  7. 226
  8. 227 bad_area_nosemaphore:
  9. 228 /* User mode accesses just cause a SIGSEGV */
  10. 229 if (error_code & 4) {
  11. 230 tsk->thread.cr2 = address;
  12. 231 tsk->thread.error_code = error_code;
  13. 232 tsk->thread.trap_no = 14;
  14. 233 info.si_signo = SIGSEGV;
  15. 234 info.si_errno = 0;
  16. 235 /* info.si_code has been set above */
  17. 236 info.si_addr = (void *)address;
  18. 237 force_sig_info(SIGSEGV, &info, tsk);
  19. 238 return;
  20. 239 }

error_code的取值和含義:

  1. 96 /*
  2. 97 * This routine handles page faults. It determines the address,
  3. 98 * and the problem, and then passes it off to one of the appropriate
  4. 99 * routines.
  5. 100 *
  6. 101 * error_code:
  7. 102 * bit 0 == 0 means no page found, 1 means protection fault
  8. 103 * bit 1 == 0 means read, 1 means write
  9. 104 * bit 2 == 0 means kernel, 1 means user-mode
  10. 105 */

繼續閱讀