Linux 虛擬內存和物理內存的理解

微笑首先,讓咱們看下虛擬內存:前端

 

第一層理解node

1.         每一個進程都有本身獨立的4G內存空間,各個進程的內存空間具備相似的結構算法

 

2.       一個新進程創建的時候,將會創建起本身的內存空間,此進程的數據,代碼等從磁盤拷貝到本身的進程空間,哪些數據在哪裏,都由進程控制表中的task_struct記錄,task_struct中記錄中一條鏈表,記錄中內存空間的分配狀況,哪些地址有數據,哪些地址無數據,哪些可讀,哪些可寫,均可以經過這個鏈表記錄數組

 

3.       每一個進程已經分配的內存空間,都與對應的磁盤空間映射緩存

Linux 使用虛擬地址空間,大大增長了進程的尋址空間,由低地址到高地址分別爲: 
只讀段:該部分空間只能讀,不可寫;(包括:代碼段、rodata 段(C常量字符串和#define定義的常量) )
數據段:保存全局變量、靜態變量的空間; 
堆 :就是平時所說的動態內存, malloc/new 大部分都來源於此。其中堆頂的位置可經過函數 brk 和 sbrk 進行動態調整。 
文件映射區域 :動態庫、共享內存等映射物理空間的內存,通常是 mmap 函數所分配的虛擬地址空間。 
棧:用於維護函數調用的上下文空間,通常爲 8M ,可經過 ulimit –s 查看。 
內核虛擬空間:用戶代碼不可見的內存區域,由內核管理(頁表就存放在內核虛擬空間)
數據結構


 

問題:架構

計算機明明沒有那麼多內存(n個進程的話就須要n*4G)內存函數

創建一個進程,就要把磁盤上的程序文件拷貝到進程對應的內存中去,對於一個程序對應的多個進程這種狀況,浪費內存!佈局

第二層理解
性能

1.         每一個進程的4G內存空間只是虛擬內存空間,每次訪問內存空間的某個地址,都須要把地址翻譯爲實際物理內存地址

2.         全部進程共享同一物理內存,每一個進程只把本身目前須要的虛擬內存空間映射並存儲到物理內存上。

3.         進程要知道哪些內存地址上的數據在物理內存上,哪些不在,還有在物理內存上的哪裏,須要用頁表來記錄

4.         頁表的每個表項分兩部分,第一部分記錄此頁是否在物理內存上,第二部分記錄物理內存頁的地址(若是在的話)

5.         當進程訪問某個虛擬地址,去看頁表,若是發現對應的數據不在物理內存中,則缺頁異常

6.         缺頁異常的處理過程,就是把進程須要的數據從磁盤上拷貝到物理內存中,若是內存已經滿了,沒有空地方了,那就找一個頁覆蓋,固然若是被覆蓋的頁曾經被修改過,須要將此頁寫回磁盤

 

總結:

優勢:

1.既然每一個進程的內存空間都是一致並且固定的,因此連接器在連接可執行文件時,能夠設定內存地址,而不用去管這些數據最終實際的內存地址,這是有獨立內存空間的好處

2.當不一樣的進程使用一樣的代碼時,好比庫文件中的代碼,物理內存中能夠只存儲一份這樣的代碼,不一樣的進程只須要把本身的虛擬內存映射過去就能夠了,節省內存

3.在程序須要分配連續的內存空間的時候,只須要在虛擬內存空間分配連續空間,而不須要實際物理內存的連續空間,能夠利用碎片。

 

另外,事實上,在每一個進程建立加載時,內核只是爲進程「建立」了虛擬內存的佈局,具體就是初始化進程控制表中內存相關的鏈表,實際上並不當即就把虛擬內存對應位置的程序數據和代碼(好比.text .data段)拷貝到物理內存中,只是創建好虛擬內存和磁盤文件之間的映射就好(叫作存儲器映射),等到運行到對應的程序時,纔會經過缺頁異常,來拷貝數據。還有進程運行過程當中,要動態分配內存,好比malloc時,也只是分配了虛擬內存,即爲這塊虛擬內存對應的頁表項作相應設置,當進程真正訪問到此數據時,才引起缺頁異常。

 

補充理解:

虛擬存儲器涉及三個概念: 虛擬存儲空間,磁盤空間,內存空間


能夠認爲虛擬空間都被映射到了磁盤空間中,(事實上也是按須要映射到磁盤空間上,經過mmap),而且由頁表記錄映射位置,當訪問到某個地址的時候,經過頁表中的有效位,能夠得知此數據是否在內存中,若是不是,則經過缺頁異常,將磁盤對應的數據拷貝到內存中,若是沒有空閒內存,則選擇犧牲頁面,替換其餘頁面。

 

mmap是用來創建從虛擬空間到磁盤空間的映射的,能夠將一個虛擬空間地址映射到一個磁盤文件上,當不設置這個地址時,則由系統自動設置,函數返回對應的內存地址(虛擬地址),當訪問這個地址的時候,就須要把磁盤上的內容拷貝到內存了,而後就能夠讀或者寫,最後經過manmap能夠將內存上的數據換回到磁盤,也就是解除虛擬空間和內存空間的映射,這也是一種讀寫磁盤文件的方法,也是一種進程共享數據的方法 共享內存

 

 

微笑接下來咱們來討論下物理內存:

 

 

在內核態申請內存比在用戶態申請內存要更爲直接,它沒有采用用戶態那種延遲分配內存技術。內核認爲一旦有內核函數申請內存,那麼就必須馬上知足該申請內存的請求,而且這個請求必定是正確合理的。相反,對於用戶態申請內存的請求,內核老是儘可能延後分配物理內存,用戶進程老是先得到一個虛擬內存區的使用權,最終經過缺頁異常得到一塊真正的物理內存。

1.物理內存的內核映射


IA32架構中內核虛擬地址空間只有1GB大小(從3GB到4GB),所以能夠直接將1GB大小的物理內存(即常規內存)映射到內核地址空間,但超出1GB大小的物理內存(即高端內存)就不能映射到內核空間。爲此,內核採起了下面的方法使得內核可使用全部的物理內存。


       1).高端內存不能所有映射到內核空間,也就是說這些物理內存沒有對應的線性地址。不過,內核爲每一個物理頁框都分配了對應的頁框描述符,全部的頁框描述符都保存在mem_map數組中,所以每一個頁框描述符的線性地址都是固定存在的。內核此時可使用alloc_pages()和alloc_page()來分配高端內存,由於這些函數返回頁框描述符的線性地址。


       2).內核地址空間的後128MB專門用於映射高端內存,不然,沒有線性地址的高端內存不能被內核所訪問。這些高端內存的內核映射顯然是暫時映射的,不然也只能映射128MB的高端內存。當內核須要訪問高端內存時就臨時在這個區域進行地址映射,使用完畢以後再用來進行其餘高端內存的映射。
因爲要進行高端內存的內核映射,所以直接可以映射的物理內存大小隻有896MB,該值保存在high_memory中。內核地址空間的線性地址區間以下圖所示:

 

 
從圖中能夠看出,內核採用了三種機制將高端內存映射到內核空間:永久內核映射,固定映射和vmalloc機制。

2.物理內存管理機制
基於物理內存在內核空間中的映射原理,物理內存的管理方式也有所不一樣。內核中物理內存的管理機制主要有夥伴算法,slab高速緩存和vmalloc機制。其中夥伴算法和slab高速緩存都在物理內存映射區分配物理內存,而vmalloc機制則在高端內存映射區分配物理內存。
夥伴算法
夥伴算法負責大塊連續物理內存的分配和釋放,以頁框爲基本單位。該機制能夠避免外部碎片。
per-CPU頁框高速緩存
內核常常請求和釋放單個頁框,該緩存包含預先分配的頁框,用於知足本地CPU發出的單一頁框請求。
slab緩存
slab緩存負責小塊物理內存的分配,而且它也做爲高速緩存,主要針對內核中常常分配並釋放的對象。
vmalloc機制
vmalloc機制使得內核經過連續的線性地址來訪問非連續的物理頁框,這樣能夠最大限度的使用高端物理內存。

3.物理內存的分配
內核發出內存申請的請求時,根據內核函數調用接口將啓用不一樣的內存分配器。
       3.1 分區頁框分配器
分區頁框分配器 (zoned page frame allocator) ,處理對連續頁框的內存分配請求。分區頁框管理器分爲兩大部分:前端的管理區分配器和夥伴系統,以下圖:

 
管理區分配器負責搜索一個能知足請求頁框塊大小的管理區。在每一個管理區中,具體的頁框分配工做由夥伴系統負責。爲了達到更好的系統性能,單個頁框的申請工做直接經過per-CPU頁框高速緩存完成。
該分配器經過幾個函數和宏來請求頁框,它們之間的封裝關係以下圖所示。

 
這些函數和宏將核心的分配函數__alloc_pages_nodemask()封裝,造成知足不一樣分配需求的分配函數。其中,alloc_pages()系列函數返回物理內存首頁框描述符,__get_free_pages()系列函數返回內存的線性地址。
       3.2 slab分配器
slab 分配器最初是爲了解決物理內存的內部碎片而提出的,它將內核中經常使用的數據結構看作對象。slab分配器爲每一種對象創建高速緩存。內核對該對象的分配和釋放均是在這塊高速緩存中操做。一種對象的slab分配器結構圖以下:

 
能夠看到每種對象的高速緩存是由若干個slab組成,每一個slab是由若干個頁框組成的。雖然slab分配器能夠分配比單個頁框更小的內存塊,但它所需的全部內存都是經過夥伴算法分配的。
slab高速緩存分專用緩存和通用緩存。專用緩存是對特定的對象,好比爲內存描述符建立高速緩存。通用緩存則是針對通常狀況,適合分配任意大小的物理內存,其接口即爲kmalloc()。
       3.3 非連續內存區內存的分配
內核經過vmalloc()來申請非連續的物理內存,若申請成功,該函數返回連續內存區的起始地址,不然,返回NULL。vmalloc()和kmalloc()申請的內存有所不一樣,kmalloc()所申請內存的線性地址與物理地址都是連續的,而vmalloc()所申請的內存線性地址連續而物理地址則是離散的,兩個地址之間經過內核頁表進行映射。
vmalloc()的工做方式理解起來很簡單:
        1).尋找一個新的連續線性地址空間;
        2).依次分配一組非連續的頁框;
        3).爲線性地址空間和非連續頁框創建映射關係,即修改內核頁表;
vmalloc()的內存分配原理與用戶態的內存分配類似,都是經過連續的虛擬內存來訪問離散的物理內存,而且虛擬地址和物理地址之間是經過頁表進行鏈接的,經過這種方式能夠有效的使用物理內存。可是應該注意的是,vmalloc()申請物理內存時是當即分配的,由於內核認爲這種內存分配請求是正當並且緊急的;相反,用戶態有內存請求時,內核老是儘量的延後,畢竟用戶態跟內核態不在一個特權級。

相關文章
相關標籤/搜索