2012-07-19 22:25:27| 分類: Linux | 標籤:linux 存儲系統 虛擬內存 |字號大中小訂閱linux
Linux爲每個進程單獨維護了一個單獨的虛擬地址空間,形式如圖所示。面試
其中內核虛擬存儲器包含內核中的代碼和數據結構。內核虛擬存儲器中的某些區域被映射到全部進程共享的物理頁面。例如每一個進程共享內核的代碼和全局數據結構。數據結構
內核虛擬存儲器的其餘區域包含每一個進程都不相同的數據,好比頁表,內核在進程上下文中執行代碼時用到的棧。tcp
Linux將虛擬存儲器組織成一些區域(也叫作段)的集合。一個區域就是已經存在的(已分配的)虛擬存儲器的連續片,這些頁是以某種方式相關聯的。好比說代碼段,數據段,堆,共享庫段以及用戶棧都是不一樣的區域。每一個存在的虛擬頁面都保存在某個區域中,而不屬於某個區域的虛擬頁是不存在的,而且不能被進程使用。ide
在Linux系統中發生缺頁異常時,致使控制轉移到內核的異常處理程序,這個處理程序將執行如下步驟:函數
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須要如下幾個步驟:
#include <unistd.h>#include <sys/mman.h>void *mmap(void *start, size_t length, int prot, int flagint 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);}