1. 程式人生 > >Linux 系統內存分析

Linux 系統內存分析

連續 結構 介紹 合並 程序 數量 linux內存 del 單獨

1. 內存基本介紹

1.計算機基本結構:

電腦之父——馮·諾伊曼提出了計算機的五大部件:輸入設備、輸出設備、存儲器、運算器和控制器

如圖:

技術分享圖片

輸入設備:鍵盤鼠標等

CPU:是計算機的運算核心和控制核心,讓電腦的各個部件順利工作,起到協調和控制作用。

存儲器:一系列的存儲設備,硬盤,內存等

輸出設備:如打印機,揚聲器等

2.存儲器:

我們看一下系統中存儲器的層次結構:

技術分享圖片

圖中L0-L5分別表示系統中所有存儲器的層次結構,其中包括高速緩存,主存,磁盤等。 越上層的存儲器造價越高,速度也越快,也更加靠近cpu。 正常情況下,我們在開發應用程序時,使用的是主存(L3)。

在開發應用程序時,我們接觸最多的應該是內存和磁盤文件,也知道訪問磁盤存儲器的時候,需要進行I/O操作,相比內存十分耗時間,那兩者具體相差多少,我們可以看一下下面這一段描述(DRAM為主存):

技術分享圖片

也就是說,在這個例子裏面,磁盤訪問時間是內存的10萬倍,當然,數據不是絕對的,在不同情況下,數據應該會存在偏差,我們大概有個概念就可以。

3.內存和磁盤的主要作用:

硬盤:存儲資料和軟件等數據的設備,有容量大,斷電數據不丟失的特點。也被人們稱之為“數據倉庫”。

內存:1. cpu無法直接訪問硬盤,必須通過內存,因此內存一個作用是負責硬盤等硬件上的數據與CPU之間數據交換處理;2. 緩存系統中的臨時數據。3. 斷電後數據會丟失。

2. Linux 系統內存管理

一. 物理尋址與虛擬尋址

計算機系統的主存(內存)被組織成一個由M個連續的字節大小的單元組成的數組。每個字節都擁有唯一的物理地址,cpu直接對物理地址進行尋址稱為物理尋址

,很早期的pc使用的是物理尋址,示意圖如下:

技術分享圖片

為通用計算設計的現代處理器使用的是虛擬尋址。根據虛擬尋址,cpu在訪問主存之前,需將虛擬地址轉換成物理地址(通過地址翻譯MMU)。示意圖如下:

技術分享圖片

也就是說,在虛擬尋址的系統中,一個數據對象擁有兩個地址空間,關於地址空間,我們看一下下面這一段話:

技術分享圖片

那這兩個地址空間多大呢? 首先,我們知道一個32位的系統,每一個地址對應的數據空間為32位,也就是4個字節。一個地址用32位二進制表示,那麽所有地址的可能性就是為2的32次方,大小為4G。因此,虛擬地址的地址空間和物理地址的地址空間取決於虛擬地址和物理地址的位數

二. 系統引入虛擬地址的原因

我們以Linux系統為例,來解釋一下為什麽要引入虛擬地址,直接用物理地址不是更快嗎? 最根本的原因是因為Linux系統是一個多任務系統,而虛擬地址可以很好的保證系統進程的並發性,獨立性。

Linux 進程內存分配結構圖(原因中有涉及):

技術分享圖片

原因:

1. 簡化存儲器管理。每個進程一個獨立頁表,獨立虛擬地址空間

技術分享圖片

1.1. 簡化鏈接。因為進程虛擬地址空間獨立,以32位系統為例,每個進程分配的虛擬內存均為4G,其中堆段,棧,數據段,代碼段各自所在的位置是一致的,這樣可以簡化鏈接器的設計與實現。如圖:10.10

技術分享圖片

1.2. 簡化共享。一般而言,每個進程都有自己私有代碼,數據,堆以及棧區域,是不和其他進程共享的,那這時候就需要多個物理頁面來進行存儲,但是在一些情況下,還是會需要進程之間來共享代碼和數據。例如:操作系統的內核代碼,c程序中的標準庫代碼,比如printf。操作系統將不同進程中適當的虛擬頁面映射到相同的物理頁面,從而達到多個進程共享這部分代碼的一個拷貝。而不是在每個進程中都包括單獨的內核和c標準庫拷貝。

1.3. 簡化存儲器分配。分配的物理頁面可以不連續。

技術分享圖片

1.4. 簡化加載。 和鏈接差不多,進程加載elf文件到存儲器的虛擬地址是相同的。

技術分享圖片

2. 保護存儲器。控制用戶進程對存儲器的訪問,對某些特殊區段,用戶進程禁止訪問修改,保證安全性和獨立性

技術分享圖片

三. 虛擬存儲系統如何工作

同任何緩存一樣,虛擬存儲系統必須有某種方法來判定一個虛擬頁是否存在於DRAM中,如果存在,還需要確定這個虛擬頁存放在哪個物理頁。如果沒用命中,系統必須判斷這個虛擬頁存放在磁盤哪個位置,在物理頁中選擇一個犧牲頁,並將虛擬頁拷貝到DRAM,替換這個犧牲頁。要完成這部分工作,需要哪些角色參與進來呢? 看下面介紹:

技術分享圖片

技術分享圖片

看上圖,可以清楚到看到,虛擬內存是如何映射到物理地址和磁盤的。下面我們來介紹一個概念頁命中,當cpu從虛擬內存中讀取一個字節,如果通過頁表可以成功映射到物理存儲器,則稱為頁命中,具體見下圖:

技術分享圖片

如果虛擬頁沒有映射到物理內存,而是映射到磁盤,那這種現象就叫做缺頁。這時候,地址翻譯硬件,判斷虛擬頁未緩存到DRAM中,然後會觸發一個缺頁異常(也叫缺頁中斷)。將虛擬頁重新映射到DRAM。 在磁盤與存儲器之間傳送頁的活動叫做頁面調度,涉及的算法叫頁面置換算法(如:OPT,FIFO,LRU) 。 缺頁中斷過程如下。

缺頁前:

技術分享圖片

缺頁後:

技術分享圖片

Linux系統層面的內存管理調度大概就介紹到這裏,下面我們進入進程層面的內存分析!

3. Linux 進程級內存管理

首先,我們再次把進程的空間結構圖附上:

技術分享圖片

一. 內核態和用戶態

我們知道程序訪問的地址都是虛擬地址,用32位操作系統來講,系統訪問的地址空間為4G,linux會將4G分為兩部分,如上圖所示,其中從0x00000000 到 0xbfffffff的線性地址為用戶空間,0xc0000000 到 0xffffffff為內核空間

進程在用戶態只能訪問0~3G,只有進入內核態才能訪問3G~4G

用戶空間:在Linux中,每個用戶進程都可以訪問4GB的線性虛擬內存空間。其中從0到3GB的虛存地址是用戶空間,通過每個進程自己的頁目錄、頁表,用戶進程可以直接訪問。

內核空間:從3GB到4GB的虛存地址為內核態空間,存放供內核訪問的代碼和數據,用戶態進程不能訪問,也就是說對於用戶代碼來說是不可見的,只有內核態進程才能尋址。所有進程從3GB到4GB的虛擬空間都是一樣的,linux以此方式讓內核態進程共享代碼段和數據段。(頁表就存放在內核虛擬空間)

二. 進程用戶態空間

如果不是做內核開發,在寫代碼的過程中,我們主要涉及到的是用戶態空間,在代碼層面可以進行優化和分析的也是用戶態空間,內核空間主要由系統進行管理,因此我們著重介紹一下用戶態空間,至於內核空間,其中可能涉及到一些比較好的算法思路(夥伴,slab等),如果有時間可以單獨去學習其思想。

程序空間

1. 堆段

對於堆段,程序層面我們平常會使用new,delete,malloc,free方式來申請內存。而Linux內核會為進程分配一段內存地址,隨著進程申請內存增加,進程會通過系統調用brk,讓內核來拓展這段內存空間;當進程釋放內存時,進程又通過系統調用brk,來告訴內核縮減這段內存,內核將其一部分物理地址進行回收。當程序調用內存申請接口(malloc)時,具體分配流程如下:

技術分享圖片

1.1. 小塊內存分配

對於堆段的內存分配,如果是小塊內存分配,為了減少內存碎片,glibc庫對於某些相鄰的內存肯進行合並,但是為了節省cpu和內存,對於過小的內存並不會進行合並,具體閾值可以通過接口進行設置,如下接口:

技術分享圖片

1.2. 大塊內存分配

如果是大塊內存分配,當申請內存數量大於一個閾值,glibc會采用mmaps為進程分配一塊虛擬空間,而不是采用brk,來拓展棧頂指針。如下接口:

技術分享圖片

1.3. 內存釋放

至於內存釋放,當我們調用free時,系統並不會立即將內存回收,而是將其cache,保留到下次使用。原因是頻繁申請釋放會造成大量的系統調用,會影響進程效率。對於free後,是否系統回收,也可以通過接口設置。如下接口:

技術分享圖片

1.4. 內存空洞

還有一個概念我們需要知道,就是內存空洞,就是一段內存,中間的釋放了,但是堆頂沒用釋放,導致所有的內存無法被系統回收。 對於內存空洞,見下面這段文字描述:

技術分享圖片

註:如何區分內存空洞和內存泄漏

技術分享圖片

從上面描述知道,對於內存空洞,我們只需了解即可,按照就近原則釋放即可。當然對於內存空洞,其實是有另外的內存管理機制可以解決的,如果感興趣,可以另外了解一下。

2. 棧段

用於維護函數調用的上下文空間,一般為 8M ,可通過 ulimit –s 查看(函數堆棧打印就是基於此實現) 。 那在使用的過程中,我們需註意,1. 盡量避免在棧空間申請大量內存;2. 盡量避免遞歸使用

3. 數據段

也就是我們進程空間中的.bss和.data,主要用來保存全局變量、靜態變量,兩者區別:

技術分享圖片

4. 代碼段

代碼段是整個系統共享的,位於進程只讀段。

5. 共享映射

我們看進程的空間結構圖可以知道,還有一個文件映射區域,這裏只要是動態庫、共享內存等映射物理空間的內存,一般是 mmap 函數所分配的虛擬地址空間。

四. 總結

文章中出現的圖片和相關文字描述截圖主要出自書籍<<深入理解計算機系統>>, <<嵌入式Linux性能詳解>>,兩本書都非常不錯!

文章主要是對內存做了一個整體介紹,自底向上,從系統層面,進程層面,到用戶代碼層面,進行了詳細的分析和總結,使得我們對Linux內存管理有了一個系統的認識!

2018年9月15日12:33:02

Linux 系統內存分析