Linux虛擬存儲系統

Linux虛擬存儲系統 

2012-07-19 22:25:27|  分類: Linux |  標籤:linux  存儲系統  虛擬內存  |字號訂閱linux

 
 

 

 

    Linux爲每個進程單獨維護了一個單獨的虛擬地址空間,形式如圖所示。面試

    其中內核虛擬存儲器包含內核中的代碼和數據結構。內核虛擬存儲器中的某些區域被映射到全部進程共享的物理頁面。例如每一個進程共享內核的代碼全局數據結構數據結構

    內核虛擬存儲器的其餘區域包含每一個進程都不相同的數據,好比頁表,內核在進程上下文中執行代碼時用到的棧。tcp

 

    Linux將虛擬存儲器組織成一些區域(也叫作段)的集合。一個區域就是已經存在的(已分配的)虛擬存儲器的連續片,這些頁是以某種方式相關聯的。好比說代碼段,數據段,堆,共享庫段以及用戶棧都是不一樣的區域。每一個存在的虛擬頁面都保存在某個區域中,而不屬於某個區域的虛擬頁是不存在的,而且不能被進程使用。ide

    在Linux系統中發生缺頁異常時,致使控制轉移到內核的異常處理程序,這個處理程序將執行如下步驟:函數

  1. 檢查虛擬地址A是合法的嗎?換句話說就是A是否是在某個區域內?因此缺頁異常處理程序首先搜索區域結構的鏈表,把A和每一個區域結構中的vm_start和vm_end作比較。若是這個地址是不合法的,那麼缺頁處理程序就會發出一個段錯誤,從而終止這個進程。
  2. 檢查存儲器訪問是否合法?就是說,該進程是否有讀,或者寫或執行這個區域內頁面的權限。若是試圖進行訪問是不合法的,那麼缺頁處理程序會觸發一個保護異常,從而終止這個進程。
  3. 若是不知足條件1和2,那麼此刻,內核知道了這個缺頁是因爲對合法的虛擬地址進行合法的訪問操做形成的。那個內核就選擇犧牲一個頁面,若是犧牲的這個頁面被修改過,那麼就將它交換出去,換入新的頁面並更新頁表。當缺頁處理程序返回的時候,CPU將沖洗啓動引發缺頁的那個指令,這條指令將再次發送地址A到MMU。此次MMU就能正常的翻譯A,而不會產生缺頁中斷了。

 

存儲器映射

    Linux經過將虛擬存儲器區域與一個磁盤上的對象關聯起來,以初始化這個虛擬存儲器區域的內容,這個過程就叫作存儲器映射ui

    一旦一個虛擬頁面被初始化了,它就在一個由內核維護的專門的交換文件之間換來換去。交換文件也叫作交換空間。須要意識到的很重要的一點是,在任什麼時候候,交換空間都限制着當前運行着的進程可以分配的虛擬頁面的總數。spa

共享對象翻譯

    存儲器映射的概念其實來源於一個聰明的發現:若是虛擬存儲器系統能夠集成到傳統的文件系統中,那麼就可以提供一種簡單而高效的把程序和數據加載到存儲器中的方法。orm

    進程這一抽象的概念可以爲每一個進程提供本身私有的虛擬地址空間,能夠避免受其餘進程的錯誤讀寫。不過有許多進程有一樣的只讀文本區域,並且許多程序須要訪問只讀運行時庫代碼的相同拷貝。好比每一個C程序都須要來自標準C庫的諸如printf這樣的函數。那麼若是每一個進程都在物理存儲器中保持這些經常使用代碼的複製拷貝,那就是一種極大的浪費。因此,咱們須要存儲器映射給咱們提供一種清晰的機制,來控制多個進程如何共享對象。

   一個對象能夠被映射到虛擬存儲器的一個區域,要麼做爲共享對象,要麼做爲私有對象。若是一個進程將一個共享對象映射到它的虛擬之地空間的一個區域內,那麼這個進程對這個區域的任何寫操做,對於那些也把這個共享對象映射到他們虛擬存儲器的其餘的進程來講也是可見的。並且這些變化會反應在磁盤上的原始對象中。

    對一個映射到私有對象的區域作改變的時候,對於其餘進程來講是不可見的,而且進程對這個區域所作的任何寫操做都不會反應在磁盤上的對象中。

fork函數

   理解了虛擬存儲器和存儲器映射以後,咱們就能夠清晰的知道fork函數是如何建立一個帶有本身獨立虛擬地址空間的新進程的了。

    當fork函數被當前進程(也就是父進程)調用的時候,內核爲新進程(子進程)建立各類數據結構,並分配給它一個惟一的PID。爲了給這個新進程建立虛擬存儲器,它建立了當前進程的mm_struct結構、區域結構、和頁表的原樣拷貝。它將兩個進程的頁面都標記爲只讀,並將兩個進程中的每一個區域結構都標記爲私有的寫時拷貝。

    當fork在新進程中返回的時候,新進程如今的虛擬存儲器恰好和調用fork時存在的虛擬存儲器相同。當這父子進程中的任何一個後來進行寫操做的時候,寫時拷貝機制就會建立新頁面,所以,子進程對任何數據的操做(好比更改數據大小),對父進程是沒有影響的。

execve函數

    虛擬存儲器和存儲器映射在將程序加載到存儲器到的過程當中扮演者重要的角色。假設當前的進程中執行了以下的調用: execve(「a.out」, NULL, NULL);

execve函數在當前的進程中加載並運行包含在可執行目標文件a.out中的程序,用a.out程序有效的替代了當前程序。加載並運行a.out須要如下幾個步驟:

  1. 刪除已存在的用戶區域:刪除當前進程虛擬地址的用戶部分中的已存在的區域結構。
  2. 映射私有區域:爲新程序的文本,數據,bss,和棧區建立新的區域結構。全部這些新的區域都是私有的,寫時拷貝的。文本和數據區域被映射爲a.out文件中的文本和數據區。bss區域是請求二進制零的,映射到匿名文件,其大小包含在a.out中。棧和堆區域也是請求二進制零的,初始長度爲0.
  3. 映射共享區域:若是a.out程序和共享對象(或目標)連接,好比標準C庫libc.so,那麼這些對象都是動態連接到這個程序的,而後在映射到用戶虛擬地址空間中的共享區域內。
  4. 設置程序計數器(PC):exevce作的最後一件事情就是設置當前進程上下文中的程序計數器,使之指向文本區域的入口點。
mmap函數(用戶級存儲映射)
    Unix進程可使用mmap函數來建立新的虛擬存儲器區域,並將對象映射到這些區域中。
#include <unistd.h> 
#include <sys/mman.h> 
void *mmap(void *start, size_t length, int prot, int flag 
           int fd, off_t offset); 

mmap函數要求內核建立一個新的虛擬存儲器區域,最好是從地址start開始的一個區域,並將文件描述符fd指定的對象的一個連續的片chunk映射到這個新的區域。連續的對象的片的大小爲length字節,從距文件開始處偏移量爲offset字節的地方開始。start地址僅僅是一個暗示,一般被定義爲NULL(表明由內核來決定)。

參數prot包含描述新映射的虛擬存儲器區域的訪問權限位(在相應結構中的vm_prot位)

  • PROT_EXEC:這個區域內的頁面由能夠被CPU執行的指令組成。
  • PROT_READ:這個區域內的頁面可讀。
  • PROT_WRITE:這個區域內的頁面可寫。
  • PROT_NONE:這個區域內的頁面不能被訪問。

參數flag由描述被映射對象類型的位組成。若是設置了MAP_ANON標記爲,那麼被映射的對象就是一個匿名對象,而相應的虛擬頁面是請求二進制零的。MAP_PRIVATE表示被映射的對象是一個私有的,寫時拷貝的對象。而MAP_SHARED表示是一個共享的對象。

能夠寫一個C程序,使用mmap函數將一個任意大小的磁盤文件拷貝到stdout。

int main(int argc, char **argv) 
{ 
  struct stat stat; 
  int fd; 
  if(argc != 2) 
  { 
    printf("Usage Wrong"); 
    exit(0); 
  } 
  fd = Open(argv[1], O_RDONLY, 0); 
  fstat(fd, &stat); 
  char * bufp; 
  bufp = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, 
              fd, 0); 
  Write(1, bufp, stat.st_size); 
  exit(0); 
}
 
 
 
 
 
 
 
 
 
相關文章
相關標籤/搜索