久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

利用異常表處理LINUX內(nèi)核態(tài)缺頁異v常

 木芙蓉的圖書館 2011-07-21

在程序的執(zhí)行過程中,,因為遇到某種障礙而使 CPU 無法最終訪問到相應(yīng)的物理內(nèi)存單元,,即無法完成從虛擬地址到物理地址映射的時候,CPU 會產(chǎn)生一次缺頁異常,,從而進行相應(yīng)的缺頁異常處理,。基于 CPU 的這一特性,,Linux 采用了請求調(diào)頁(Demand Paging)和寫時復(fù)制(Copy On Write)的技術(shù),。

  1. 請求調(diào)頁是一種動態(tài)內(nèi)存分配技術(shù),它把頁框的分配推遲到不能再推遲為止,。這種技術(shù)的動機是:進程開始運行的時候并不訪問地址空間中的全部內(nèi)容,。事實上,有 一部分地址也許永遠也不會被進程所使用,。程序的局部性原理也保證了在程序執(zhí)行的每個階段,,真正使用的進程頁只有一小部分,對于臨時用不到的頁,,其所在的頁 框可以由其它進程使用,。因此,請求分頁技術(shù)增加了系統(tǒng)中的空閑頁框的平均數(shù),,使內(nèi)存得到了很好的利用,。從另外一個角度來看,,在不改變內(nèi)存大小的情況下,請 求分頁能夠提高系統(tǒng)的吞吐量,。當進程要訪問的頁不在內(nèi)存中的時候,,就通過缺頁異常處理將所需頁調(diào)入內(nèi)存中。

  2. 寫時復(fù)制主要應(yīng)用于系統(tǒng)調(diào)用fork,,父子進程以只讀方式共享頁框,,當其中之一要修改頁框時,內(nèi)核才通過缺頁異常處理程序分配一個新的頁框,,并將頁框標記 為可寫,。這種處理方式能夠較大的提高系統(tǒng)的性能,這和Linux創(chuàng)建進程的操作過程有一定的關(guān)系,。在一般情況下,,子進程被創(chuàng)建以后會馬上通過系統(tǒng)調(diào)用 execve將一個可執(zhí)行程序的映象裝載進內(nèi)存中,此時會重新分配子進程的頁框,。那么,,如果fork的時候就對頁框進行復(fù)制的話,顯然是很不合適的,。

  在上述的兩種情況下出現(xiàn)缺頁異常,,進程運行于用戶態(tài),異常處理程序可以讓進程從出現(xiàn)異常的指令處恢復(fù)執(zhí)行,,使用戶感覺不到異常的發(fā)生,。當然,也 會有異常無法正?;謴?fù)的情況,,這時,異常處理程序會進行一些善后的工作,,并結(jié)束該進程,。也就是說,運行在用戶態(tài)的進程如果出現(xiàn)缺頁異常,,不會對操作系統(tǒng)核 心的穩(wěn)定性造成影響,。那么對于運行在核心態(tài)的進程如果發(fā)生了無法正常恢復(fù)的缺頁異常,,應(yīng)該如何處理呢,?是否會導(dǎo)致系統(tǒng)的崩潰呢?是否能夠解決好內(nèi)核態(tài)缺頁 異常對于操作系統(tǒng)核心的穩(wěn)定性來說會產(chǎn)生很大的影響,,如果一個誤操作就會造成系統(tǒng)的Oops,,這對于用戶來說顯然是不能容忍的。本文正是針對這個問題,介 紹了一種Linux內(nèi)核中所采取的解決方法,。

  在讀者繼續(xù)往下閱讀之前,,有一點需要先說明一下,本文示例中所選的代碼取自于Linux-2.4.0,,編譯環(huán)境是gcc-2.96,,objdump的版本是2.11.93.0.2,具體的版本信息可以通過以下的命令進行查詢:

$ gcc -v
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/2.96/specs
gcc version 2.96 20000731 (Red Hat Linux 7.3 2.96-110)
$ objdump -v
GNU objdump 2.11.93.0.2 20020207
Copyright 2002 Free Software Foundation, Inc.

   GCC的擴展功能

  由于本文中會用到GCC的擴展功能,,即匯編器as中提供的.section偽操作,,在文章開始之前我再作一個簡要的介紹。此偽操作對于不同的可 執(zhí)行文件格式有不同的解釋,,我也不一一列舉,,僅對我們所感興趣的Linux中常用的ELF格式的用法加以描述,其指令格式如下:

 .section NAME[, "FLAGS"]
  
  大家所熟知的C程序一般由以下的幾個部分組成:代碼段(text section),、初始化數(shù)據(jù)段(data section),、非初始化數(shù)據(jù)段(bss section)、棧(heap)以及堆(stack),,具體的地址空間布局可以參考《UNIX環(huán)境高級編程》一書,。

  在Linux內(nèi)核中,通過使用.section的偽操作,,可以把隨后的代碼匯編到一個由NAME指定的段中。而FLAGS字段則說明了該段的屬性,,它可以用下面介紹的單個字符來表示,,也可以是多個字符的組合。

'a' 可重定位的段,;
  'w' 可寫段,;
  'x' 可執(zhí)行段;
  'W' 可合并的段,;
  's' 共享段,。

  舉個例子來說明,讀者在后面會看到的:.section .fixup, "ax",。這樣的一條指令定義了一個名為.fixup的段,,隨后的指令會被加入到這個段中,該段的屬性是可重定位并可執(zhí)行,。

  內(nèi)核缺頁異常處理
  運行在核心態(tài)的進程經(jīng)常需要訪問用戶地址空間的內(nèi)容,,但是誰都無法保證內(nèi)核所得到的這些從用戶空間傳入的地址信息是"合法"的。為了保護內(nèi)核不受錯誤信息的攻擊,,需要驗證這些從用戶空間傳入的地址信息的正確性,。

  在老版本的Linux中,這個工作是通過函數(shù)verify_area來完成的:

extern inline int verify_area(int type, const void * addr, unsigned long size)

  該函數(shù)驗證了是否可以以type中說明的訪問類型(read or write)訪問從地址addr開始,、大小為size的一塊虛擬存儲區(qū)域,。為了做到這一點,,verify_read首先需要找到包含地址addr的虛擬存 儲區(qū)域(vma)。一般的情況下(正確運行的程序)這個測試都會成功返回,,在少數(shù)情況下才會出現(xiàn)失敗的情況,。也就是說,大部分的情況下內(nèi)核在一些無用的驗 證操作上花費了不算短的時間,,這從操作系統(tǒng)運行效率的角度來說是不可接受的,。

  為了解決這個問題,現(xiàn)在的Linux設(shè)計中將驗證的工作交給虛存中的硬件設(shè)備來完成,。當系統(tǒng)啟動分頁機制以后,,如果一條指令的虛擬地址所對應(yīng)的 頁框(page frame)不在內(nèi)存中或者訪問的類型有錯誤,就會發(fā)生缺頁異常,。處理器把引起缺頁異常的虛擬地址裝到寄存器CR2中,,并提供一個出錯碼,指示引起缺頁異 常的存儲器訪問的類型,,隨后調(diào)用Linux的缺頁異常處理函數(shù)進行處理,。

  Linux中進行缺頁異常處理的函數(shù)如下:

asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
  ……………………
  __asm__("movl %%cr2,%0":"=r" (address));
  ……………………
  vma = find_vma(mm, address);
  if (!vma)
  goto bad_area;
  if (vma->vm_start <= address)
  goto good_area;
  if (!(vma->vm_flags & VM_GROWSDOWN))
  goto bad_area;
  if (error_code & 4) {
  if (address + 32 < regs->esp)
  goto bad_area;
  ……………………
bad_area:
  ……………………
no_context:
/* Are we prepared to handle this kernel fault? */
  if ((fixup = search_exception_table(regs->eip)) != 0) {
  regs->eip = fixup;
  return;
  }
  ………………………
}

  首先讓我們來看看傳給這個函數(shù)調(diào)用的兩個參數(shù):它們都是通過entry.S在堆棧中建立的(arch/i386/kernel /entry.S),參數(shù)regs指向保存在堆棧中的寄存器,,error_code中存放著異常的出錯碼,,具體的堆棧布局參見圖一(堆棧的生成過程請參考 《Linux內(nèi)核源代碼情景分析》一書)

  該函數(shù)首先從CPU的控制寄存器CR2中獲取出現(xiàn)缺頁異常的虛擬地址。由于缺頁異常處理程序需要處理的缺頁異常類型很多,,分支也很復(fù)雜,。基于本文的主旨,,我們只關(guān)心以下的幾種內(nèi)核缺頁異常處理的情況:

  1." 程序要訪問的內(nèi)核地址空間的內(nèi)容不在內(nèi)存中,,先跳轉(zhuǎn)到標號vmalloc_fault,如果當前訪問的內(nèi)容所對應(yīng)的頁目錄項不在內(nèi)存中,,再跳轉(zhuǎn)到標號no_context,;

  2. 缺頁異常發(fā)生在中斷或者內(nèi)核線程中,跳轉(zhuǎn)到標號no_context,;

  3. 程序在核心態(tài)運行時訪問用戶空間的數(shù)據(jù),,被訪問的數(shù)據(jù)不在內(nèi)存中

  a) 出現(xiàn)異常的虛擬地址在進程的某個vma中,但是系統(tǒng)內(nèi)存無法分配空閑頁框(page frame),,則先跳轉(zhuǎn)到標號out_of_memory,,再跳轉(zhuǎn)到標號no_context;

  b) 出現(xiàn)異常的虛擬地址不屬于進程任一個vma,,而且不屬于堆棧擴展的范疇,,則先跳轉(zhuǎn)到標號bad_area,最終也是到達標號no_context。

  從上面的這幾種情況來看,,我們關(guān)注的焦點最后集中到標號no_context處,,即對函數(shù)search_exception_table的調(diào) 用。這個函數(shù)的作用就是通過發(fā)生缺頁異常的指令(regs->eip)在異常表(exception table)中尋找下一條可以繼續(xù)運行的指令(fixup),。這里提到的異常表包含一些地址對,,地址對中的前一個地址表示出現(xiàn)異常的指令的地址,后一個表 示當前一個指令出現(xiàn)錯誤時,,程序可以繼續(xù)得以執(zhí)行的修復(fù)地址,。

  如果這個查找操作成功的話,缺頁異常處理程序?qū)⒍褩V械姆祷氐刂罚╮egs->eip)修改成修復(fù)地址并返回,,隨后,,發(fā)生異常的進程將按照fixup中安排好的指令繼續(xù)執(zhí)行下去。當然,,如果無法找到與之匹配的修復(fù)地址,,系統(tǒng)只有打印出出錯信息并停止運作。

  那么,,這個所謂的修復(fù)地址又是如何生成的呢,?是系統(tǒng)自動生成的嗎?答案當然是否定的,,這些修復(fù)指令都是編程人員通過as提供的擴展功能寫進內(nèi)核源碼中的,。下面我們就來分析一下其實現(xiàn)機制。


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多