剖析top命令顯示的VIRT RES SHR值

http://yalung929.blog.163.com/blog/static/203898225201212981731971/ 

http://www.fuzhijie.me/?p=741 

 

引 言: top命令做爲Linux下最經常使用的性能分析工具之一,能夠監控、收集進程的CPU、IO、內存使用狀況。好比咱們能夠經過top命令得到一個進程使用了多少虛擬內存(VIRT)、物理內存(RES)、共享內存(SHR)。windows

最近遇到一個諮詢問題,某產品作性能分析須要獲取進程佔用物理內存的實際大小(不包括和其餘進程共享的部分),看似很簡單的問題,但通過研究分析後,發現背後有不少故事……數據結構

1 VIRT RES SHR的準確含義

 

剖析top命令顯示的VIRT RES SHR值 - yalung - Y A L U N G
 

三個內存指標,VRIT,RES,SHR準確含義是什麼?誰能告訴咱們?MAN頁?Linux專家?SUSE工程師?Linus?誰能說出最正確答案?沒人!由於唯有源代碼纔是最正確的答案。函數

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

首先這三個數據的源頭,確定是內核,進程的相關數據結構確定是由內核維護。那麼top做爲一個用戶空間的程序,要想獲取內核空間的數據,就須要經過系統接口(API)獲取。而proc文件系統是Linux內核空間和用戶空間交換數據的一個途徑,並且是很是重要的一種途徑,這點和windows更傾向於基於函數調用的形式不一樣。post

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

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

而一個進程的相關信息,Linux所有經過/proc/<pid>/內的文件告訴了咱們。blog

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

 

剖析top命令顯示的VIRT RES SHR值 - yalung - Y A L U N G
 

 

回到咱們的問題,top命令顯示的進程信息,確定也是經過proc獲取的,由於除此以外沒有其餘途徑,沒有系統函數能夠作這個事情,top也不可能越過用戶層直取內核獲取數據。進程

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

 

剖析top命令顯示的VIRT RES SHR值 - yalung - Y A L U N G
 
 

啊哈,statm文件:

 

剖析top命令顯示的VIRT RES SHR值 - yalung - Y A L U N G
 

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

等等,好像數值對不上,top顯示的SHR是344k,而statm給出的是86!

再來看一行關鍵代碼:

 

剖析top命令顯示的VIRT RES SHR值 - yalung - Y A L U N G
 
 

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

 

因而乎,咱們找到了最關鍵的入口,接下來按圖索驥,看看內核是怎麼產生statm文件內容就能夠了。~~

 

剖析top命令顯示的VIRT RES SHR值 - yalung - Y A L U N G
 

 

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

proc_pid_statm獲取進程的mm_struct數據結構,而這個數據結構就是進程的內存描述符,經過它能夠獲取進程內存使用、映射的所有信息。

     進一步考察task_statm函數,能夠看到:

 

剖析top命令顯示的VIRT RES SHR值 - yalung - Y A L U N G
 

第一個值(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:幾個字段。

 

剖析top命令顯示的VIRT RES SHR值 - yalung - Y A L U N G
 
 

 

找到相關代碼,能夠看到,一個頁面若是映射數>=2計入Shared_* ; 若是=1計入Private_*。(髒頁計入*_Dirty,不然計入*_Clean)

 

 

剖析top命令顯示的VIRT RES SHR值 - yalung - Y A L U N G
 

     統計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的物理內存。

相關文章
相關標籤/搜索