天天看點

Android記憶體管理(四)Linux的記憶體管理機制

linux記憶體管理卷帙浩繁,本文隻能層層遞進地帶你領略冰山輪廓,通過本文你将了解到以下内容:

  • 為什麼需要管理記憶體
  • linux段頁管理機制
  • 記憶體碎片的産生機理

為什麼需要管理記憶體

老子的著名觀點是無為而治,簡單說就是不過多幹預而充分依靠自覺就可以有條不紊地運作,理想是美好的,現實是殘酷的。

在linux系統中如果以一種原始簡單的方式管理記憶體是存在一些問題的,我們來看幾個場景。

記憶體管理的問題

  • 程序空間隔離問題 假如現在有ABC三個程序運作在linux的記憶體空間,設定os給程序A配置設定的位址空間是0-20M 程序B位址空間30-80M,程序C位址空間90-120M,如圖:
Android記憶體管理(四)Linux的記憶體管理機制

在某些時候程式空間的通路可能出現問題,比如程序A通路了屬于程序B的空間,程序B通路了屬于程序C的空間,甚至修改了空間的值,這樣就會造成混亂和錯誤,是以實際中是不允許這種情況發生的。

  • 記憶體效率和記憶體不足問題

機器的記憶體是有限資源,而程序數量是無法确定的,如果在某些時候已經啟動的程序占據了所有記憶體空間,此時就無法啟動新程序了,因為沒有新記憶體可配置設定了,但是我們觀察到已經啟動的程序有時候是在睡大覺,也就是給了記憶體也不用,這樣效率确實是有點低,是以我們需要一個管理者把不用的記憶體倒騰出來,另外連續記憶體實在是很珍貴,很多時候我們沒法有效及時地配置設定連續記憶體,是以虛拟化和離散化可能會有效提高記憶體的使用率。

  • 程式定位調試和編譯運作問題

由于程式運作時的位置時不确定的,我們在定位問題、調試代碼、編譯執行時都會存在很多問題,我們希望每個程序有一緻且完整的位址空間,同樣的起始位置放置了堆、棧以及代碼段等,進而簡化編譯和執行過程中的 linker 連結器、loader 加載器的使用。

虛拟位址空間

為了解決上述的一些問題,linux系統引入了虛拟空間的概念,虛拟化的出現和硬體有密不可分的聯系,可以說是軟硬體組合的結果,虛拟位址空間就是在程式和實體空間所增加的中間層,這也是記憶體管理的重點。

磁盤 disk 作為一種大容量的存儲也作為"記憶體"的一部分參與程式的運作,記憶體管理系統會将不常用非活躍記憶體進行頁面換出,可以認為記憶體是磁盤的緩存,記憶體中保留了活躍的資料,進而間接擴充了有限的實體記憶體空間,這部分空間稱為虛拟記憶體是相對于實體記憶體而言的。

Android記憶體管理(四)Linux的記憶體管理機制

段頁管理機制

本文并不深入地将分段管理記憶體和分頁管理記憶體,因為将這些細節的優秀文章很多,感興趣的使用搜尋引擎一鍵即達。

段頁機制也不是一蹴而就的,經曆了單純實體分段、單純分頁、單純邏輯分段等階段,最終演進出來了分段和分頁結合的記憶體管理方式,段頁結合獲得了分段和分頁的優勢也避免了單一模式的弊端,是一種比較好的管理模式。

本文對于段頁管理機制隻想通俗地說明一些概念,段頁管理機制是分段式管理和分頁式管理的組合,段式管理是邏輯上的管理方式,分頁管理是偏實體上的管理方式。

計算機裡面的一些技術和實作都可以在現實生活中找到縮影,所謂藝術和科技源自生活大概就是這個意思吧。

舉個栗子:

在進行居民戶籍管理時都會有區縣市的概念,但是實際上并沒有這種實體,都是邏輯上的,增加了這些行政機關之後可以讓位址管理更加直接。

對于我們居民來說唯一的實體就是自己的房子住所,這是實體上的機關,是真實存在的,這也是最基本的機關。

對比linux段頁時管理來說,段是邏輯上的機關相當于區縣市的概念,頁是實體上的機關相當于小區/房屋的概念,這樣就友善很多。

多級頁表也很好了解,總的實體記憶體假如有4GB,頁大小為4KB,那麼就總共有2^20個頁,數量還是非常大的,這樣編号來建立索引尋址比較不友善,是以引入多級頁表,來減少存儲便于管理。

段頁機制加持下的邏輯位址和實體位址的映射關系簡圖,也就是虛拟位址到實體位址的對應關系:

Android記憶體管理(四)Linux的記憶體管理機制

記憶體管理單元( MMU Memory Management Unit )是硬體層元件,主要提供将虛拟位址映射為實體位址。

MMU 的工作流程:CPU 生成邏輯位址交給分段單元,分段單元進行處理将邏輯位址轉換為線性位址,再線性位址交給分頁單元,分頁單元根據頁表映射轉換記憶體實體位址,其中可能出現缺頁中斷。

缺頁中斷( Page Fault )是隻當軟體試圖通路一個虛拟位址時,經過段頁轉換為實體位址之後,此時發現該頁并沒有在記憶體中,這時 cpu 就會報出中斷,再進行相關虛拟記憶體的調入工作或者配置設定工作,如果出現異常也可能直接中斷。

實體記憶體和記憶體碎片

前面說的段頁管理機制算是虛拟空間的部分,然而linux記憶體管理的另外一個重要部分就是實體記憶體的管理了,也就是如何配置設定和回收實體記憶體,這就涉及到一些記憶體配置設定算法和配置設定器。

Android記憶體管理(四)Linux的記憶體管理機制

實體記憶體配置設定器

配置設定器和配置設定算法就像公司财務,記憶體就像公司資金,如何把資金合理使用是财務的本職工作,如何把實體記憶體合理使用是配置設定器的分内之事。

Android記憶體管理(四)Linux的記憶體管理機制

記憶體碎片分類和機理

如果我們不知道記憶體碎片是什麼,試想一下我們常說的碎片化的時間,也就是那些雖然空閑但是沒有被利用的時間,其實記憶體也是如此。

Android記憶體管理(四)Linux的記憶體管理機制

無論是時間還是記憶體被碎片化之後都無法被有效利用,是以合理管理減少碎片對我們來說是至關重要的,這也是實體記憶體配置設定算法和配置設定器的研究重點。

按照碎片的位置和産生原因,記憶體碎片分為外部碎片和内部碎片,我們看下這兩種碎片的直覺展示:

Android記憶體管理(四)Linux的記憶體管理機制

從圖中可以知道,外部碎片是程序與程序間未配置設定的記憶體空間,外部碎片的出現和程序頻繁的配置設定和釋放記憶體有直接關系,這個很好了解,模拟一下配置設定不同空間的程序不同時間釋放就可以看到外部碎片的産生了。

内部碎片主要因為配置設定器粒度問題以及一些位址限制導緻實際配置設定的記憶體大于所需記憶體,這樣在程序内部就會出現記憶體空洞。

雖然虛拟位址讓程序使用的記憶體在實體記憶體上是離散的,但是很多時候程序需要一定量連續實體記憶體,如果大量碎片存在,就會造成無法啟動程序的問題,如圖Process7需要一塊連續的實體記憶體卻無法被配置設定:

Android記憶體管理(四)Linux的記憶體管理機制

在Linux系統下,監控記憶體常用的指令是free、top等,下面是一個free指令的執行結果:

Android記憶體管理(四)Linux的記憶體管理機制

要了解Linux的記憶體管理,首先要明白上例中各個名詞的意義:

  • total:實體記憶體的總大小。
  • used:已經使用的實體記憶體多小。
  • free:空閑的實體記憶體值。
  • shared:多個程序共享的記憶體值。
  • buffers / cached:用于磁盤緩存的大小(這部分是從實體記憶體中劃出來的)。
  • 第二行Mem:代表實體記憶體使用情況。
  • 第三行(-/+ buffers/cached):代表磁盤緩存使用狀态。
  • 第四行:Swap表示交換空間記憶體使用狀态(這部分實際上是從磁盤上虛拟出來的邏輯記憶體)。

free指令輸出的記憶體狀态,可以從兩個角度來看:核心角度、應用層角度。

1. 從核心角度來檢視記憶體的狀态:

就是核心目前可以直接配置設定到,不需要額外的操作,即free指令第二行 Mem 的輸出。從上例中可見,41940 + 16360492 = 16402432,也就是說Mem行的 free + used = total,注意,這裡的free并不包括buffers和cached。

2. 從應用層角度來檢視記憶體的狀态:

也就是Linux上運作的程式可以使用的記憶體大小,即free指令第三行 -/+ buffers/cache 的輸出。再來做一個計算41940+(465404+12714880)=13222224,即Mem行的free + buffers + cached = -/+ buffers/cache行的free,也就是說應用可用的實體記憶體值是Mem行的free、buffers和cached三者之和,可見-/+ buffers/cache行的free是包括buffers和cached的。

對于應用程式來說,buffers/cached占有的記憶體是可用的,因為buffers/cached是為了提高檔案讀取的性能,當應用程式需要用到記憶體的時候,buffers/cached會很快地被回收,以供應用程式使用。

實體記憶體和虛拟記憶體 實體記憶體就是系統硬體提供的記憶體大小,是真正的記憶體。在linux下還有一個虛拟記憶體的概念,虛拟記憶體就是為了滿足實體記憶體的不足而提出的政策,它是利用磁盤空間虛拟出的一塊邏輯記憶體,用作虛拟記憶體的磁盤空間被稱為 交換空間(Swap Space) 。

linux的記憶體管理采取的是分頁存取機制,為了保證實體記憶體能得到充分的利用,核心會在适當的時候将實體記憶體中不經常使用的資料塊自動交換到虛拟記憶體中,而将經常使用的資訊保留到實體記憶體。而進行這種交換所遵循的依據是“LRU”算法(Least Recently Used,最近最少使用算法)。

最後介紹下Buffers和Cached有什麼用

在任何系統中,檔案的讀寫都是一個耗時的操作,當應用程式需要讀寫檔案中的資料時,作業系統先配置設定一些記憶體,将資料從磁盤讀入到這些記憶體中,然後應用程式讀寫這部分記憶體資料,之後系統再将資料從記憶體寫到磁盤上。如果是大量檔案讀寫甚至重複讀寫,系統讀寫性能就變得非常低下,在這種情況下,Linux引入了緩存機制。

buffers與cached都是從實體記憶體中分離出來的,主要用于實作磁盤緩存,用來儲存系統曾經打開過的檔案以及檔案屬性資訊,這樣當作業系統需要讀取某些檔案時,會首先在buffers與cached記憶體區查找,如果找到,直接讀出傳送給應用程式,否則,才從磁盤讀取,通過這種緩存機制,大大降低了對磁盤的IO操作,提高了作業系統的資料通路性能。而這種磁盤高速緩存則是基于兩個事實:第一,記憶體通路速度遠遠高于磁盤通路速度;第二,資料一旦被通路,就很有可能短期内再次被通路。

另外,buffers與cached緩存的内容也是不同的。buffers是用來緩沖塊裝置做的,它隻記錄檔案系統的中繼資料(metadata)以及 tracking in-flight pages,而cached是用來給檔案做緩沖。更通俗一點說:buffers主要用來存放目錄裡面有什麼内容,檔案的屬性以及權限等等。而cached直接用來記憶我們打開過的檔案和程式。

為了驗證我們的結論是否正确,可以通過vi打開一個非常大的檔案,看看cached的變化,然後再次vi這個檔案,感覺一下是不是第二次打開的速度明顯快于第一次?

接着執行下面的指令:

find /* -name  *.conf           

複制

看看buffers的值是否變化,然後重複執行find指令,看看兩次顯示速度有何不同。

Linux記憶體管理的哲學

Free memory is wasted memory.

Linux的哲學是盡可能多的使用記憶體,減少磁盤IO,因為記憶體的速度比磁盤快得多。 Linux總是在力求緩存更多的資料和資訊,記憶體不夠時,将一些不經常使用的資料轉移到交換分區(Swap Space)中以釋放更多可用實體記憶體,當然,如果交換分區的資料再次被讀寫時,又會被轉移到實體記憶體中,這種設計思路提高了系統的整體性能。而Windows的處理方式是,記憶體和虛拟記憶體一起使用,不是以記憶體操作為主,結果就是IO的負擔比較大,可能拖慢處理速度。

Linux和Windows在記憶體管理機制上的差別

在Linux系統使用過程中,你會發現,無論你的電腦記憶體配置多麼優越,仍然不時的發生可用記憶體吃緊的現象,感覺記憶體不夠用了,其實不然。這是Linux記憶體管理的優秀特性,無論實體記憶體有多大,Linux都将其充分利用,将一些程式調用過的硬碟資料緩存到記憶體,利用記憶體讀寫的高速性提高系統的資料通路性能。而Window隻在需要記憶體時,才為應用配置設定記憶體,不能充分利用大容量的記憶體空間。 換句話說,每增加一些記憶體,Linux都能将其利用起來,充分發揮硬體投資帶來的好處,而Windows隻将其作為擺設。

是以說,一般我們不需要太關注Linux的記憶體占用情況,而如果Swap占用率一直居高不下的話,就很有可能真的是需要擴充記憶體了。