上一篇咱們瞭解了內存在內核態是如何管理的,本篇文章咱們一塊兒來看下內存在用戶態的使用狀況,若是上一篇文章說是內核驅動工程師常常面對的內存管理問題,那本篇就是應用工程師常面對的問題。linux
相信你們都知道對用戶態的內存消耗對象是進程,應用開發者面對的全部代碼操做最後的落腳點都是進程,這也是說爲何內存和進程兩個知識點的重要性,理解了內存和進程兩大法寶,對全部軟件開發的理解都會有了全局觀(關於進程的知識之後再整理和你們分享)。web
下面閒話少說,開始本篇的內容——進程的內存消耗和泄漏算法
進程的虛擬地址空間VMA(Virtual Memory Area)
在linux操做系統中,每一個進程都經過一個task_struct的結構體描敘,每一個進程的地址空間都經過一個mm_struct描敘,c語言中的每一個段空間都經過vm_area_struct表示,他們關係以下 :shell
上圖中,task_struct中的mm_struct就表明進程的整個內存資源,mm_struct中的pgd爲頁表,mmap指針指向的vm_area_struct鏈表的每個節點就表明進程的一個虛擬地址空間,即一個VMA。一個VMA最終可能對應ELF可執行程序的數據段、代碼段、堆、棧、或者動態連接庫的某個部分。bash
VMA的分佈狀況能夠有經過pmap命令,及maps,smaps文件查看,以下圖:微信
另,VMA的具體內容可參考下圖。app
page fault的幾種可能性
咱們先來看張圖:編輯器
(此圖來源於宋寶華老師)ide
如,調用malloc申請100M內存,IA32下在0~3G虛擬地址中馬上就會佔用到大小爲100M的VMA,且符合堆的定義,這一段VMA的權限是R+W的。但因爲Lazy機制,這100M其實並無得到,這100M所有映射到一個物理地址相同的零頁,且在頁表中記錄的權限爲只讀的。當100M中任何一頁發生寫操做時,MMU會給CPU發page fault(MMU能夠從寄存器讀出發生page fault的地址;MMU能夠讀出發生page fault的緣由),Linux內核收到缺頁中斷,在缺頁中斷的處理程序中讀出虛擬地址和緣由,去VMA中查,發現是用戶程序在寫malloc的合法區域且有寫權限,Linux內核就真正的申請內存,頁表中對應一頁的權限也修改成R+W。工具
如,程序中有野指針飛到了此程序運行時進程的VMA之外的非法區域,硬件就會收到page fault,進程會收到SIGSEGV信號報段錯誤並終止。如,程序中有野指針飛到了此程序運行時進程的VMA之外的非法區域,硬件就會收到page fault,進程會收到SIGSEGV信號報段錯誤並終止。
如,代碼段在VMA中權限爲R+X,若是程序中有野指針飛到此區域去寫,則也會發生段錯誤。(另,malloc堆區在VMA中權限爲R+W,若是程序的PC指針飛到此區域去執行,一樣發生段錯誤。)
如,執行代碼段時會發生缺頁,Linux申請1頁內存,並從硬盤讀取出代碼段,此時產生了IO操做,爲major主缺頁。如,執行代碼段時會發生缺頁,Linux申請1頁內存,並從硬盤讀取出代碼段,此時產生了IO操做,爲major主缺頁。
(此圖來源於宋寶華老師)
綜上,page fault後,Linux會查VMA,也會比對VMA中和頁表中的權限,體現出VMA的重要做用。
malloc分配的原理
malloc的過程其實就是把VMA分配到各類段當中,這時候是沒有真正分配物理地址的。malloc 調用後,只是分配了內存的邏輯地址,在內核的mm_struct 鏈表中插入vm_area_struct結構體,沒有分配實際的內存。當分配的區域寫入數據是,引起頁中斷,創建物理頁和邏輯地址的映射。下圖表示了這個過程。
從操做系統角度來看,進程分配內存有兩種方式,分別由兩個系統調用完成:brk和mmap(不考慮共享內存)。
malloc小於128k的內存,使用brk分配內存,將_edata往高地址推(只分配虛擬空間,不對應物理內存(所以沒有初始化),第一次讀/寫數據時,引發內核缺頁中斷,內核才分配對應的物理內存,而後虛擬地址空間創建映射關係)
malloc大於128k的內存,使用mmap分配內存,在堆和棧之間找一塊空閒內存分配(對應獨立內存,並且初始化爲0)
內存的消耗VSS RSS PSS USS
首先,咱們評估一個進程的內存消耗都是指用戶空間的內存,不包括內核空間的內存消耗 。這裏咱們用工具 procrank先來看下Linux進程的內存佔用量 。
VSS -Virtual Set Size 虛擬耗用內存(包含共享庫佔用的內存)
RSS -Resident Set Size 實際使用物理內存(包含共享庫佔用的內存)
PSS -Proportional Set Size 實際使用的物理內存(比例分配共享庫佔用的內存)
USS -Unique Set Size 進程獨自佔用的物理內存(不包含共享庫佔用的內存)
下面再用一張圖來更好的解釋VSS,RSS,PSS,USS之間的區別:
有了對VSS,RSS,PSS,USS的瞭解,咱們趁熱打鐵來看下內存在進程中是如何被瓜分的:
(此圖來源於宋寶華老師)
1044,1045,1054三個進程,每一個進程都有一個頁表,對應其虛擬地址如何向real memory上去轉換。
process 1044的1,2,3都在虛擬地址空間,因此其VSS=1+2+3。
process 1044的4,5,6都在real memory上,因此其RSS=4+5+6。
分析real memory的具體瓜分狀況:
4 libc代碼段,1044,1045,1054三個進程都使用了libc的代碼段,被三個進程分享。
5 bash shell的代碼段,1044,1045都是bash shell,被兩個進程分享。
6 1044獨佔
因此,上圖中4+5+6並不全是1044進程消耗的內存,由於4明顯被3個進程指向,5明顯被2個進程指向,衍生出了PSS(按比例計算的駐留內存)的概念。進程1044的PSS爲4/3 +5/2 +6。
最後,進程1044獨佔且駐留的內存USS爲 6。
通常來講內存佔用大小有以下規律:VSS >= RSS >= PSS >= USS
【部份內容整理於宋寶華老師課程】
【推薦閱讀】
添加極客助手微信,加入技術交流羣

長按,掃碼,關注公衆號
本文分享自微信公衆號 - 人人都是極客(rrgeek)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。