一、前言
上一節我們談了Linux的程式管理:
這一節我們將談下Linux的記憶體體系
二、概覽
程式執行過程中,Linux核心根據需要給程式分配一塊記憶體區域。程式就把這片區域作為工作區,按要求執行操作。這就像給你分配一張自己的桌子,你可以在桌子上擺放文件,備忘錄,開展自己的工作。區別在於,核心以更加動態的方式分配空間。系統上執行的程式經常是成千上萬的,但是記憶體卻是有限的。於是,Linux必須高效的處理記憶體問題。在本節中,將介紹Linux記憶體架構、地址佈局、以及Linux如何高效管理記憶體空間。
先簡單總結下,如果記憶體有限的情況下,比如8G,你開啟了大量的應用,勢必會導致電腦卡頓,執行緩慢,甚至無反應的!
三、記憶體體系
- 3.1 物理和虛擬記憶體
現實中,我們經常會面臨32位還是64位作業系統的選擇(雖然現在32位已經基本淘汰了),對使用者來說,它們間最大的差別是能否支援4GB以上的虛擬記憶體空間。站在效能的角度來理解32位和64位的系統,Linux對映實體記憶體到虛擬記憶體的區別是十分有趣的。如下圖所示,可以明顯看出記憶體對映方式在32位和64位系統上的區別。我們不去詳盡探索實體記憶體對映到虛擬記憶體的細節,對於效能調優,我們只需要瞭解一下Linux的記憶體架構的一些知識就夠了。
32位架構的機器上,Linux核心只能直接對映第一個GB的的實體記憶體(896M,因為還要考慮到保留的空間)。在此上的記憶體被稱作ZONE_NORMAL,這部分空間必須對映到最下面的1GB。這種對映對應用程式是完全透明的,但是分配記憶體頁到ZONE_HIGHMEM會造成一點 點效能損耗。
另一方面,在64位系統上,例如在IA-64上面,ZONE_NORMAL一直延伸到64GB或者128GB。如你所見,把記憶體頁從ZONE_HIGHMEM對映到ZONE_NORMAL這種損耗在64位系統上是不存在的。
下圖展示了32位和64位架構Linux系統的虛擬定址佈局:
在32位架構上,單個程式可以利用的最大地址空間是4GB,這是受到了32位虛擬記憶體對映的限制。在標準的32位環境中,虛擬地址被劃分為3GB的使用者空間和1GB的記憶體空間,現實中也存在一些4GB/4GB地址佈局。
再說64位架構,因為沒有記憶體限制存在,每個程式能夠都有可能使用巨大的地址空間。
- 3.2 虛擬記憶體管理器
由於作業系統把所有記憶體都對映成虛擬記憶體,所以,作業系統的實體記憶體架構對使用者和應用程式通常都是不可見的。如果我們要掌握Linux記憶體調優的辦法,就必須先理解Linux如何處理虛擬記憶體。如3.1所說的那樣,應用程式不使用實體記憶體,而是向Linux核心請求一個特定大小的記憶體對映,並且收到一個虛擬記憶體的對映。如下圖所示,虛擬記憶體不必要一定是實體記憶體的對映,如果某個應用程式使用了一塊超大的虛擬記憶體,這虛擬記憶體其中某一部分可能是由磁碟上的swap空間對映來的。
從圖中可以看出來,應用程式經常不是直接寫入磁碟子系統,而是首先寫入cache或者buffer,然後,在pdflush空閒的時候、或者某個檔案大小超出buffer和cache的時候,由pdflush核心執行緒把buffer或cache中的資料寫入磁碟。參考後面的寫入髒buffer部分。
Linux核心管理磁碟快取的方式,和核心寫資料到檔案系統的方式有緊密聯絡。和其它作業系統都只分配特定的部分記憶體作為磁碟快取的方式相比,Linux處理記憶體資源更加高效。
虛擬記憶體管理器預設配置把所有的可用空閒記憶體空間作為磁碟快取,所以,經常可以見到Linux系統明明擁有數GB級的記憶體,卻只有20M處於空閒狀態。
Linux同樣高效利用swap空間,當作業系統開始使用swap空間的時候,並不表示系統出現了記憶體瓶頸,而是證明了Linux如何有效的使用系統資源。參考頁幀回收(page frame reclaiming)。
下面簡單介紹下幾個重要概念:
- 頁幀分配(Page frame allocation)
頁是實體記憶體或虛擬記憶體中一組連續的線性地址,Linux核心以頁為單位處理記憶體,頁的大小通常是4KB。當一個程式請求一定量的頁面時,如果有可用的頁面,核心會直接把這些頁面分配給這個程式,否則,核心會從其它程式或者頁快取中拿來一部分給這個程式用。核心知道有多少頁可用,也知道它們的位置。
- 夥伴系統(Buddy system)
Linux核心使用名為夥伴系統(Buddy system)的機制維護空閒頁。夥伴系統維護空閒頁面,並且嘗試給發來頁面申請的程式分配頁面,它還努力保持記憶體區域是連續的。如果不考慮到零散的小頁面可能會導致記憶體碎片,而且在要分配一個連續的大記憶體頁時將變得很困難,這就可能導致記憶體使用效率降低和效能下降。下圖說明了夥伴系統如何分配記憶體頁:
如果嘗試分配記憶體頁失敗,就啟動回收機制。可以在/proc/buddyinfo檔案看到夥伴系統的資訊:
- 頁幀回收
如果在程式請求指定數量的記憶體頁時沒有可用的記憶體頁,核心就會嘗試釋放特定的記憶體頁(以前使用過,現在沒有使用,並且基於某些原則仍然被標記為活動狀態)給新的請求使用。這個過程叫做記憶體回收。kswapd核心執行緒和try_to_free_page()核心函式負責頁面回收。
kswapd通常在task interruptible狀態下休眠,當一個區域中的空閒頁低於閾值的時候,它就會被夥伴系統喚醒。它基於最近最少使用原則(LRU,Least Recently Used)在活動頁中尋找可回收的頁面。最近最少使用的頁面被首先釋放。它使用活動列表和非活動列表來維護候選頁面。kswapd掃描活動列表,檢查頁面的近期使用情況,近期沒有使用的頁面被放入非活動列表中。使用vmstat -a命令可以檢視有分別有多少記憶體被認為是活動和非活動狀態:
kswapd還要遵循另外一個原則。頁面主要有兩種用途:頁面快取(page cahe)和程式地址空間(process address space)。頁面快取是指對映到磁碟檔案的頁面;程式地址空間的頁面(又叫做匿名記憶體,因為不是任何檔案的對映,也沒有名字)使用來做堆疊使用的。在回收記憶體時,kswapd更偏向於回收頁面快取。
如果大部分的頁面快取和程式地址空間來自於記憶體回收,在某些情況下,可能會影響效能。我們可以通過修改/proc/sys/vm/swappiness檔案來控制這個行為:
- swap分割槽
在發生頁面回收時,屬於程式地址空間的處於非活動列表的候選頁面會發生page out。擁有交換空間本身是很正常的事情。在其它作業系統中,swap無非是保證作業系統可以分配超出實體記憶體大小的空間,但是Linux使用swap的空間的辦法更加高效。虛擬記憶體由實體記憶體和磁碟子系統或者swap分割槽組成。在Linux中,如果虛擬記憶體管理器意識到記憶體頁已經分配了,但是已經很久沒有使用,它就把記憶體頁移動到swap空間。
Page out和swap out:“page out”和“swap out”很容易混淆。“page out”意思是把一些頁面
(整個地址空間的一部分)交換到swap;"swap out"意味著把所有的地址空間交換到swap。
複製程式碼
四、更通俗的理解
虛擬記憶體由實體記憶體和磁碟子系統或者swap分割槽組成。這話的意思是,實體記憶體,磁碟還有swap分割槽這些物理裝置合在一起被抽象虛擬成了一個大記憶體,叫做虛擬記憶體。程式在虛擬記憶體上分配的地址會被對映道物理裝置上,所以可能部分在實體記憶體,可能在swap分割槽,可能是磁碟上(個人理解)。
基於上述,有一下幾點理解:
-
每個程式都有自己獨立的4G記憶體空間而這4G記憶體空間只是虛擬記憶體空間,每次訪問記憶體空間的某個地址,都需要把地址翻譯為實際實體記憶體地址
-
一個新程式建立的時候,將會建立起自己的記憶體空間,此程式的資料,程式碼等從磁碟拷貝到自己的程式空間,哪些資料在哪裡,都由程式控制表中的task_struct記錄,task_struct中記錄中一條連結串列,記錄中記憶體空間的分配情況,哪些地址有資料,哪些地址無資料,哪些可讀,哪些可寫,都可以通過這個連結串列記錄
-
所有程式共享同一實體記憶體,每個程式只把自己目前需要的虛擬記憶體空間對映並儲存到實體記憶體上(參考3.1)。
-
程式要知道哪些虛擬記憶體地址上的資料在實體記憶體上,哪些不在,還有在實體記憶體上的位置,需要用頁表來記錄
-
頁表的每一個表項分兩部分,第一部分記錄此頁是否在實體記憶體上,第二部分記錄實體記憶體頁的地址(如果在的話)
-
當程式訪問某個虛擬地址,去看頁表,如果發現對應的資料不在實體記憶體中,則缺頁異常。缺頁異常的處理過程,就是把程式需要的資料從磁碟上拷貝到實體記憶體中,如果記憶體已經滿了,沒有空地方了,那就找一個頁覆蓋,當然如果被覆蓋的頁曾經被修改過,需要將此頁寫回磁碟
五、下一節是???
談完Linux的記憶體體系,下一節將會談一下Linux的檔案系統