亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

Chinaunix

標(biāo)題: do_page_fault函數(shù)處理流程 [打印本頁(yè)]

作者: cluter    時(shí)間: 2011-05-28 00:58
標(biāo)題: do_page_fault函數(shù)處理流程
本帖最后由 cluter 于 2011-05-28 01:01 編輯

只解釋下關(guān)鍵代碼的流程,附件里是關(guān)于缺頁(yè)處理思路更清晰的一個(gè)整理,無(wú)代碼的。
  1. //*********************************缺頁(yè)異常處理函數(shù)*******************************************
  2. do_page_fault(struct pt_regs *regs, unsigned long error_code)
  3. {

  4.        
  5.         //獲取當(dāng)前cpu正在運(yùn)行的進(jìn)程的進(jìn)程描述符
  6.         //然后獲取該進(jìn)程的內(nèi)存描述符
  7.         tsk = current;
  8.         mm = tsk->mm;

  9.         /* Get the faulting address: */
  10.         //獲取出錯(cuò)的地址
  11.         address = read_cr2();


  12.         /*
  13.          * We fault-in kernel-space virtual memory on-demand. The
  14.          * 'reference' page table is init_mm.pgd.
  15.          *
  16.          * NOTE! We MUST NOT take any locks for this case. We may
  17.          * be in an interrupt or a critical region, and should
  18.          * only copy the information from the master page table,
  19.          * nothing more.
  20.          *
  21.          * This verifies that the fault happens in kernel space
  22.          * (error_code & 4) == 0, and that the fault was not a
  23.          * protection error (error_code & 9) == 0.
  24.          */
  25.          //頁(yè)訪(fǎng)問(wèn)出錯(cuò)地址address在內(nèi)核空間
  26.         if (unlikely(fault_in_kernel_space(address))) {
  27.                 //檢查標(biāo)志位確定訪(fǎng)問(wèn)發(fā)生在"內(nèi)核態(tài)"
  28.                 if (!(error_code & (PF_RSVD | PF_USER | PF_PROT))) {
  29.                         //如果是內(nèi)核空間"非連續(xù)內(nèi)存"的訪(fǎng)問(wèn),
  30.                         //則直接拷貝"內(nèi)核頁(yè)表項(xiàng)"到"用戶(hù)頁(yè)表項(xiàng)"
  31.                         //如果"內(nèi)核頁(yè)表項(xiàng)"為null,說(shuō)明內(nèi)核有BUG,返回-1
  32.                         if (vmalloc_fault(address) >= 0)
  33.                                 return;
  34.                 }

  35.                 //如果在"用戶(hù)態(tài)"則直接進(jìn)入"非法訪(fǎng)問(wèn)"處理函數(shù)
  36.                 //如果vmalloc_fault返回-1,則表示內(nèi)核BUG
  37.                 bad_area_nosemaphore(regs, error_code, address);
  38.                 //錯(cuò)誤處理函數(shù)
  39.                 // 1 "用戶(hù)態(tài)"錯(cuò)誤-->直接終止進(jìn)程
  40.                 // 2 "內(nèi)核態(tài)"錯(cuò)誤
  41.                 //                       系統(tǒng)調(diào)用參數(shù)錯(cuò)誤 ---->終止進(jìn)程/返回系統(tǒng)調(diào)用錯(cuò)誤碼
  42.                 //                       內(nèi)核BUG                            ---->內(nèi)核panic
  43.                 return;
  44.         }


  45.         /*
  46.          * If we're in an interrupt, have no user context or are running
  47.          * in an atomic region then we must not take the fault:
  48.          */
  49.          // 1 在中斷中,此時(shí)沒(méi)有進(jìn)程上下文
  50.          // 2 在原子操作流程中
  51.          // 都不允許處理缺頁(yè)異常
  52.         if (unlikely(in_atomic() || !mm)) {
  53.                 bad_area_nosemaphore(regs, error_code, address);
  54.                 return;
  55.         }

  56.         /*
  57.          * When running in the kernel we expect faults to occur only to
  58.          * addresses in user space.  All other faults represent errors in
  59.          * the kernel and should generate an OOPS.  Unfortunately, in the
  60.          * case of an erroneous fault occurring in a code path which already
  61.          * holds mmap_sem we will deadlock attempting to validate the fault
  62.          * against the address space.  Luckily the kernel only validly
  63.          * references user space from well defined areas of code, which are
  64.          * listed in the exceptions table.
  65.          *
  66.          * As the vast majority of faults will be valid we will only perform
  67.          * the source reference check when there is a possibility of a
  68.          * deadlock. Attempt to lock the address space, if we cannot we then
  69.          * validate the source. If this is invalid we can skip the address
  70.          * space check, thus avoiding the deadlock:
  71.          */
  72.          //此時(shí)可以確定缺頁(yè)地址address在"用戶(hù)空間"了
  73.         if (unlikely(!down_read_trylock(&mm->mmap_sem))) {
  74.                 //錯(cuò)誤發(fā)生在"內(nèi)核態(tài)",查看異常表
  75.                 //如果在內(nèi)核態(tài)引起缺頁(yè),則引起缺頁(yè)的"指令地址"一定在"異常表"中
  76.                 //如果"異常表"中返回指令地址,則說(shuō)明可能是"請(qǐng)求調(diào)頁(yè)",也可能是"非法訪(fǎng)問(wèn)"
  77.                 //如果"異常表"中無(wú)地址,則肯定是內(nèi)核錯(cuò)誤
  78.                 if ((error_code & PF_USER) == 0 &&
  79.                     !search_exception_tables(regs->ip)) {
  80.                        //內(nèi)核panic
  81.                         bad_area_nosemaphore(regs, error_code, address);
  82.                         return;
  83.                 }
  84.                 down_read(&mm->mmap_sem);
  85.         } else {
  86.                 /*
  87.                  * The above down_read_trylock() might have succeeded in
  88.                  * which case we'll have missed the might_sleep() from
  89.                  * down_read():
  90.                  */
  91.                 might_sleep();
  92.         }
  93.         //尋找address所在的vma
  94.         vma = find_vma(mm, address);
  95.         //如果address之后無(wú)vma,則肯定是非法訪(fǎng)問(wèn)
  96.         if (unlikely(!vma)) {
  97.                 bad_area(regs, error_code, address);
  98.                 return;
  99.         }
  100.         // 1 如果vma->start_address<=address,則直接跳到 "合法訪(fǎng)問(wèn)"階段
  101.         // 2 如果vma->start_address>address,則也有可能是用戶(hù)的"入棧行為"導(dǎo)致缺頁(yè)
  102.         if (likely(vma->vm_start <= address))
  103.                 goto good_area;
  104.         // "入棧"操作,則該vma的標(biāo)志為 "向下增長(zhǎng)"
  105.         if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
  106.                 bad_area(regs, error_code, address);
  107.                 return;
  108.         }
  109.         // 確定缺頁(yè)發(fā)生在"用戶(hù)態(tài)"
  110.         if (error_code & PF_USER) {
  111.                 /*
  112.                  * Accessing the stack below %sp is always a bug.
  113.                  * The large cushion allows instructions like enter
  114.                  * and pusha to work. ("enter $65535, $31" pushes
  115.                  * 32 pointers and then decrements %sp by 65535.)
  116.                  */
  117.                  //驗(yàn)證缺頁(yè)address和棧頂sp的關(guān)系
  118.                 if (unlikely(address + 65536 + 32 * sizeof(unsigned long) < regs->sp)) {
  119.                         bad_area(regs, error_code, address);
  120.                         return;
  121.                 }
  122.         }
  123.         //擴(kuò)展棧
  124.         if (unlikely(expand_stack(vma, address))) {
  125.                 bad_area(regs, error_code, address);
  126.                 return;
  127.         }

  128.         /*
  129.          * Ok, we have a good vm_area for this memory access, so
  130.          * we can handle it..
  131.          */
  132. good_area:
  133.         write = error_code & PF_WRITE;
  134.         // 再次驗(yàn)證"權(quán)限"
  135.         if (unlikely(access_error(error_code, write, vma))) {
  136.                 bad_area_access_error(regs, error_code, address);
  137.                 return;
  138.         }

  139.         /*
  140.          * If for any reason at all we couldn't handle the fault,
  141.          * make sure we exit gracefully rather than endlessly redo
  142.          * the fault:
  143.          */
  144.          //分配新"頁(yè)框"
  145.         fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);

  146.         up_read(&mm->mmap_sem);
  147. }


  148. //*******************************訪(fǎng)問(wèn)權(quán)限驗(yàn)證函數(shù)********************************************
  149. access_error(unsigned long error_code, int write, struct vm_area_struct *vma)
  150. {       
  151.         //如果是"寫(xiě)操作"引起的缺頁(yè),則該vma必須可寫(xiě)
  152.         if (write) {
  153.                 /* write, present and write, not present: */
  154.                 if (unlikely(!(vma->vm_flags & VM_WRITE)))
  155.                         return 1;
  156.                 return 0;
  157.         }

  158.         /* read, present: */
  159.         //檢查該頁(yè)是否已經(jīng)在RAM中,如果"特權(quán)位"置位表示頁(yè)框在RAM中
  160.         //表示進(jìn)程訪(fǎng)問(wèn)"有特權(quán)" 頁(yè)框
  161.         if (unlikely(error_code & PF_PROT))
  162.                 return 1;

  163.         /* read, not present: */
  164.         //如果該頁(yè)不在內(nèi)存中,該線(xiàn)性區(qū)必須可"讀"
  165.         if (unlikely(!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))))
  166.                 return 1;

  167.         return 0;
  168. }
復(fù)制代碼


順便提出幾個(gè)問(wèn)題:
                                在"內(nèi)核態(tài)"訪(fǎng)問(wèn)非連續(xù)內(nèi)存時(shí),為何要拷貝“內(nèi)核頁(yè)表項(xiàng)”到用“戶(hù)頁(yè)表項(xiàng)”,為何不直接使用“內(nèi)核頁(yè)表”?
                                對(duì)于一個(gè)4G的機(jī)器,用戶(hù)進(jìn)程的頁(yè)框大部分分布在物理內(nèi)存的哪個(gè)范圍?
                                為什么當(dāng)物理內(nèi)存大于1G的時(shí)候,就開(kāi)始劃分出”高端內(nèi)存“?
                                64bit的機(jī)器上不需要”高端內(nèi)存“,這是為什么?
作者: amarant    時(shí)間: 2011-05-28 07:24
高端內(nèi)存的作用是訪(fǎng)問(wèn)內(nèi)核空間1g不能直接尋址之外的內(nèi)存
折衷的辦法就是
把1G劃為直接映射的一部分和高端內(nèi)存

64位機(jī)的內(nèi)核空間能夠足夠大到尋址所有內(nèi)存(虛擬地址空間不止4G)、
作者: cluter    時(shí)間: 2011-05-28 07:54
回復(fù) 2# amarant


    LS起這么早。。。關(guān)于為何使用高端內(nèi)存,閣下理解的很透徹。。,第一個(gè)問(wèn)題LS怎么看?
作者: amarant    時(shí)間: 2011-05-28 09:27
回復(fù) 3# cluter


    這個(gè)問(wèn)題我說(shuō)不準(zhǔn),就不敢胡說(shuō)了。。
作者: tempname2    時(shí)間: 2011-05-28 12:51
第一個(gè)問(wèn)題我還真沒(méi)看懂是什么意思

第二個(gè)問(wèn)題,我沒(méi)有仔細(xì)研究過(guò)代碼,這應(yīng)該取決于內(nèi)核向Buddy System索要內(nèi)存時(shí)傳入的flag參數(shù)。新版的代碼已經(jīng)改的亂七八糟了,我在ULK上看過(guò)的代碼片斷里參數(shù)好像包括了GFP_HIGHUSER,也就是說(shuō),優(yōu)先在大于896M的物理內(nèi)存里拿。

第三第四個(gè)問(wèn)題,內(nèi)核使用高端內(nèi)存是因?yàn)閮?nèi)核擁有的虛擬地址大于物理內(nèi)存。訪(fǎng)問(wèn)物理內(nèi)存必需先將其映射于某一塊虛擬地址段之上,而大多體系結(jié)構(gòu)中,內(nèi)核是與進(jìn)程共用地址空間的。Linux下內(nèi)核一般只占頂1G,資源有些緊張,能映射到的物理也就緊張。因此在其1G的虛擬地址空間之中,開(kāi)辟一個(gè)小窗口動(dòng)態(tài)映射“夠不著”的那些物理內(nèi)存。所以說(shuō),高端內(nèi)存的出現(xiàn)是因?yàn)椤皟?nèi)核虛擬地址空間 <  物理內(nèi)存大小”。

如果“內(nèi)核虛擬地址空間 >  物理內(nèi)存大小”,就不會(huì)出現(xiàn)高端內(nèi)存了。這時(shí)還可以分為兩種情況,一為“內(nèi)核虛擬地址空間太大”,比如64位操作系統(tǒng);一為“物理內(nèi)存太小”,比如32位操作系統(tǒng)時(shí)物理內(nèi)存小于896M。
作者: cluter    時(shí)間: 2011-05-28 14:44
回復(fù) 5# tempname2


    呵呵,ls 2 3 問(wèn)題都理解的很深刻。給進(jìn)程分配的頁(yè)框和進(jìn)程的頁(yè)表都優(yōu)先在高端內(nèi)存索。。

   第一個(gè)問(wèn)題其實(shí)就是:當(dāng)進(jìn)程系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài),為何不使用內(nèi)核頁(yè)表,而是把內(nèi)核頁(yè)表項(xiàng)拷貝到進(jìn)程頁(yè)表,然后繼續(xù)訪(fǎng)問(wèn)。
作者: cluter    時(shí)間: 2011-05-29 21:27
回復(fù) 4# amarant


    其實(shí),只需要逆向思考下就可以了,如果切換頁(yè)表,系統(tǒng)必須做其他什么事情?
作者: hauto    時(shí)間: 2011-05-29 22:29
對(duì)第一個(gè)問(wèn)題我的理解是:
每個(gè)進(jìn)程都有一個(gè)頁(yè)表,頁(yè)表既有內(nèi)核地址空間的,也有用戶(hù)地址空間的。
但是,有些內(nèi)核空間的只在 init_mm的頁(yè)表中有,而當(dāng)前進(jìn)程沒(méi)有,當(dāng)訪(fǎng)問(wèn)這些地址的時(shí)候,就出現(xiàn)頁(yè)中斷,
這里把 init_mm的頁(yè)表中的相關(guān)項(xiàng)拷貝到當(dāng)前進(jìn)程的頁(yè)表中。這樣再訪(fǎng)問(wèn)的時(shí)候,就不會(huì)頁(yè)中斷了。
作者: amarant    時(shí)間: 2011-05-30 10:53
我不了解X86頁(yè)表在內(nèi)存中的儲(chǔ)存結(jié)構(gòu),如果X86中的頁(yè)全局表項(xiàng)必須是以數(shù)組的方式順序存儲(chǔ)。那么就很好理解為什么要多此一舉復(fù)制內(nèi)核頁(yè)表過(guò)來(lái)了。
作者: futex    時(shí)間: 2011-05-30 13:52
看代碼是從init_mm.pgd上拷貝過(guò)去的,如果不拷貝而直接切換到init_mm_pgd上,那至少存在這個(gè)問(wèn)題,內(nèi)核中使用copy_from_user等訪(fǎng)問(wèn)進(jìn)程用戶(hù)地址空間的時(shí)候必然出錯(cuò)。另外執(zhí)行signal處理函數(shù)時(shí)要利用程序在用戶(hù)空間的棧來(lái)保存被沖掉的內(nèi)核棧保存的內(nèi)容,也必然出錯(cuò)。其原因在于init_mm.gpd上沒(méi)有當(dāng)前進(jìn)程在用戶(hù)地址空間的映射表。呵呵,不知俺說(shuō)的對(duì)不對(duì)。
作者: futex    時(shí)間: 2011-05-30 14:44
"操作"是指執(zhí)行內(nèi)存映射嗎? 如果是的話(huà)這個(gè)過(guò)程應(yīng)該由mmu來(lái)進(jìn)行吧? 不切換pgd怎么操作呢?
作者: futex    時(shí)間: 2011-05-30 15:58
另外,樓主的問(wèn)題好像不是很正確,似乎只是復(fù)制到pmd_t中的內(nèi)容,而沒(méi)有繼續(xù)復(fù)制pte_t, 也就是說(shuō)頁(yè)表其實(shí)是和init_mm.pgd共用的。每復(fù)制一次有4M地址空間的map,不算少了吧?
作者: cluter    時(shí)間: 2011-06-04 10:06
回復(fù) 8# hauto


    頁(yè)表不管在物理內(nèi)存的什么地址,都應(yīng)該由內(nèi)核管理,所以我們認(rèn)為這種是內(nèi)核態(tài)。所以不會(huì)出現(xiàn)切換時(shí)造成“缺頁(yè)”。
    就是 肯定COPY了pte_t。要不然進(jìn)程進(jìn)入“內(nèi)核態(tài)”,訪(fǎng)問(wèn)不存在的地址,肯定出錯(cuò)。
作者: cluter    時(shí)間: 2011-06-04 10:22
回復(fù) 10# futex


    閣下認(rèn)識(shí)的還是滿(mǎn)深刻的,如果不拷貝的話(huà),使用內(nèi)核頁(yè)表,當(dāng)再次訪(fǎng)問(wèn)進(jìn)程空間時(shí),映射就會(huì)丟失,因?yàn)閮?nèi)核沒(méi)有進(jìn)程空間的地址“映射表”。而系統(tǒng)調(diào)用經(jīng)常需要用到進(jìn)程空間的數(shù)據(jù)。
作者: unbutun    時(shí)間: 2011-08-18 00:31
只解釋下關(guān)鍵代碼的流程,附件里是關(guān)于缺頁(yè)處理思路更清晰的一個(gè)整理,無(wú)代碼的。

順便提出幾個(gè)問(wèn)題:
...
cluter 發(fā)表于 2011-05-28 00:58



    hi, 這個(gè)附件失效了,能否再傳一個(gè),多謝了
作者: allen303allen    時(shí)間: 2011-08-21 17:10
對(duì)第一個(gè)問(wèn)題,我的理解是:
內(nèi)核維護(hù)的是一個(gè)“主內(nèi)核頁(yè)表”,所有的用戶(hù)進(jìn)程都是在這個(gè)主內(nèi)核頁(yè)表的基礎(chǔ)上通過(guò)修改用戶(hù)地址空間部分的頁(yè)表來(lái)為自己所用,所以用戶(hù)進(jìn)程陷入內(nèi)核時(shí)不需要切換頁(yè)表,因?yàn)樵趦?nèi)核態(tài)使用的頁(yè)表其內(nèi)容跟主內(nèi)核頁(yè)表是一樣的(非缺頁(yè)的情況)。
而內(nèi)核在使用非連續(xù)物理內(nèi)存時(shí),就需要修改內(nèi)核頁(yè)表,此時(shí)內(nèi)核修改的只是主內(nèi)核頁(yè)表,而不會(huì)修改其他用戶(hù)進(jìn)程的頁(yè)表。此時(shí)當(dāng)其他用戶(hù)進(jìn)程陷入內(nèi)核后訪(fǎng)問(wèn)內(nèi)核地址空間發(fā)生缺頁(yè)中斷時(shí),就會(huì)判斷如果不是其他的異常情況,就把主內(nèi)核頁(yè)表的內(nèi)容拷貝到自己的頁(yè)表中來(lái)(使自己頁(yè)表的內(nèi)核頁(yè)表部分與主內(nèi)核頁(yè)表保持一致)。
這樣,所有的用戶(hù)進(jìn)程在陷入內(nèi)核態(tài)訪(fǎng)問(wèn)內(nèi)核地址空間發(fā)生缺頁(yè)時(shí),都要這么做,因?yàn)樗麄冏约喉?yè)表的內(nèi)核部分不是最新的,這就是為什么使用非連續(xù)物理內(nèi)存的效率會(huì)比較低了。
作者: embeddedlwp    時(shí)間: 2011-09-06 17:33
回復(fù) 16# allen303allen


    allen303allen兄對(duì)內(nèi)存管理的理解很透徹阿
作者: sky345824266    時(shí)間: 2015-08-07 10:44
附件訪(fǎng)問(wèn)不了, 無(wú)法打開(kāi)




歡迎光臨 Chinaunix (http://www.72891.cn/) Powered by Discuz! X3.2