【轉】進程的虛擬內存,物理內存,共享內存


引 言: top命令作為Linux下最常用的性能分析工具之一,可以監控、收集進程的CPUIO、內存使用情況。比如我們可以通過top命令獲得一個進程使用了多少虛擬內存(VIRT)、物理內存(RES)、共享內存(SHR)。

    最近遇到一個內存問題,某軟件做性能分析需要獲取進程占用物理內存的實際大小(不包括和其他進程共享的部分),看似很簡單的問題,但經過研究分析后,發現背后有很多故事……

VIRT RES SHR的准確含義

 

三個內存指標,VRITRESSHR准確含義是什么?誰能告訴我們?MAN頁?Linux專家?SUSE工程師?Linus?誰能說出最正確答案?沒人!因為惟有源代碼才是最正確的答案。

 

那我們就去看下源碼吧,這就是開源軟件的最大的好處。

 

首先這三個數據的源頭,肯定是內核,進程的相關數據結構肯定是由內核維護。那么top作為一個用戶空間的程序,要想獲取內核空間的數據,就需要通過系統接口(API)獲取。而proc文件系統是Linux內核空間和用戶空間交換數據的一個途徑,而且是非常重要的一種途徑,這點和windows更傾向於基於函數調用的形式不同。

 

當你調用系統函數read讀取一個普通文件時,內核執行對應文件系統的代碼從磁盤傳送文件內容給你。

 

當你調用系統函數read讀取一個 proc文件時,內核執行對應的proc文件系統的代碼從內核的數據結構中傳送相關內容給你。proc文件和磁盤沒有關系。只是系統接口而已。

 

而一個進程的相關信息,Linux全部通過/proc/<pid>/內的文件告訴了我們。

 

如下,你可以使用普通的文件讀寫工具,比如cat獲取進程的各種信息。這比函數調用的方式靈活多了、豐富多了。

 

回到我們的問題,top命令顯示的進程信息,肯定也是通過proc獲取的,因為除此之外沒有其他途徑,沒有系統函數可以做這個事情,top也不可能越過用戶層直取內核獲取數據。

 

帶着以上信息,很快就可以從top的源碼中找到關鍵代碼:

 

啊哈,statm文件:

 

根據sscanf的順序,第一個值是VIRT,第二個值是RES,第三個值是SHR

 

等等,好像數值對不上,top顯示的SHR344k,而statm給出的是86

 

再來看一行關鍵代碼:

 

statm顯示的是頁數,top顯示的是KBX86下,一頁是4KB86 * 4 = 344。這就對了!

 

於是乎,我們找到了最關鍵的入口,接下來按圖索驥,看看內核是怎么產生statm文件內容就可以了。~~

 

 

proc_pid_statm函數負責產生statm文件內容,當你使用cat命令打印statm文件時,內核中的這個函數會執行。

proc_pid_statm獲取進程的mm_struct數據結構,而這個數據結構就是進程的內存描述符,通過它可以獲取進程內存使用、映射的全部信息。

     進一步考察task_statm函數,可以看到:

 

第一個值(VIRT)就是mm->total_vm,即進程虛存的總大小,這個比較清晰,只要進程申請了內存,無論是malloc還是堆棧還是全局,都會計入這個值;

第二個值(RES)是mm->file_rss+mm->anon_rss

第三個值(SHR)是mm->file_rss

 RES要和SHR結合者看,內核把物理內存分為了兩部分,一部分是映射至文件的,一部分是沒有映射至文件的即匿名內存,完全和共不共享沒有關系!

file_rss為什么叫做shared呢?應該是一種指示性表述,表示這部分內存可能是共享的。但並不代表真正共享了。那么到底哪些計入file_rss?通過查閱相關代碼,發現(可能有遺漏):

程序的代碼段。

動態庫的代碼段。

通過mmap做的文件映射。

通過mmap做的匿名映射,但指明了MAP_SHARED屬性。

通過shmget申請的共享內存。

 即進程通過以上方式占用的物理內存,計入file_rss,也就是topSHR字段。我們看到一般這些內存都是以共享方式存在。但如果某個動態庫只一個進程在使用,它的代碼段就沒有被共享着。

反過來再來看anon_rss統計的內容,是否就一定是獨占的?也不是,比如新fork之后的子進程,由於copy on write機制,在頁面被修改之前,和父進程共享。這部分值並不體現在top命令的SHR字段內。

 綜上所述top命令顯示的SHR字段,並不是准確描述了進程與其他進程共享使用的內存數量,是存在誤差的。 

那么如何獲取進程准確的共享內存數量?

獲取進程准確的共享內存數量

我們注意到在描述進程信息的proc/<pid>內,有一個smaps文件,里面展示了所有內存段的信息,其中有Shared_Clean Shared_Dirty Private_Clean Private_Dirty:幾個字段

找到相關代碼,可以看到,一個頁面如果映射數>=2計入Shared_* ; 如果=1計入Private_*。(臟頁計入*_Dirty,否則計入*_Clean

 統計smaps文件內所有段的Shared_*值的總和就是進程准確的共享內存數量!

     統計smaps文件內所有段的Private_*值的總和就是進程准確的獨占內存數量!

總結

通過以上分析,我們可以得到如下結論:

top命令通過解析/proc/<pid>/statm統計VIRTRESSHR字段值。

VIRT是申請的虛擬內存總量。

RES是進程使用的物理內存總和。

SHRRES映射至文件的物理內存總和。包括:

程序的代碼段。

動態庫的代碼段。

通過mmap做的文件映射。

通過mmap做的匿名映射,但指明了MAP_SHARED屬性。

通過shmget申請的共享內存。

/proc/<pid>/smapsShared_*統計的是RES映射數量>=2的物理內存。

/proc/<pid>/smapsPrivate_*統計的是RES映射數量=1的物理內存。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM