1. 程式人生 > >linux進程內存到底怎麽看 剖析top命令顯示的VIRT RES SHR值

linux進程內存到底怎麽看 剖析top命令顯示的VIRT RES SHR值

這就是 img pca 內存使用情況 res 內核空間 mar 執行 圖片

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

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

1 VIRT RES SHR的準確含義

技術分享圖片

三個內存指標,VRIT,RES,SHR準確含義是什麽?誰能告訴我們?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顯示的SHR是344k,而statm給出的是86!

再來看一行關鍵代碼:

技術分享圖片

statm顯示的是頁數,top顯示的是KB。X86下,一頁是4KB,86 * 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?通過查閱相關代碼,發現(可能有遺漏):

l 程序的代碼段。

l 動態庫的代碼段。

l 通過mmap做的文件映射。

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

l 通過shmget申請的共享內存。

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

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

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

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

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

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

技術分享圖片

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

技術分享圖片

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

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

3 總結

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

l top命令通過解析/proc/<pid>/statm統計VIRT和RES和SHR字段值。

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

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

l SHR是RES中”映射至文件”的物理內存總和。包括:

程序的代碼段。

動態庫的代碼段。

通過mmap做的文件映射。

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

通過shmget申請的共享內存。

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

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

linux進程內存到底怎麽看 剖析top命令顯示的VIRT RES SHR值