Linux進程的內存使用狀況

在linux下,使用top,ps等命令查看進程的內存使用狀況時,常常看到VIRT,RES,SHR等,他們都表明什麼意思呢?不一樣的大小對進程有什麼影響呢?這篇文章未來聊一聊這個問題。閱讀本篇前建議先閱讀Linux內存管理,瞭解一些Linux下內存的基本概念,如什麼是anonymous和file backed映射等。html

查看進程所使用的內存

在進程的眼裏,全部的內存都是虛擬內存,可是這些虛擬內存所對應的物理內存是多少呢?正如咱們在Linux內存管理中所介紹的那樣,並非每塊虛擬內存都有對應的物理內存,可能對應的數據在磁盤上的一個文件中,或者交換空間上的一塊區域裏。一個進程真正的物理內存使用狀況只有內核知道,咱們只能經過內核開放的一些接口來獲取這些統計數據。node

top

先看看top的輸出(top用到的數據來自於/proc/[pid]/statm),這裏只是摘錄了幾條數據:linux

PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 2530 root      20   0       0      0      0 S  0.3  0.0   0:02.69 kworker/0:0
 2714 dev       20   0   41824   3700   3084 R  0.3  0.7   0:00.02 top
 3008 dev       20   0   22464   5124   3356 S  0.0  1.0   0:00.02 bash

VIRT:進程所使用的虛擬內存大小apache

RES:系統爲虛擬內存分配的物理內存大小,包括file backed和anonymous內存,其中anonymous包含了進程本身分配和使用的內存,以及和別的進程經過mmap共享的內存;而file backed的內存就是指加載可執行文件和動態庫所佔的內存,以及經過private方式調用mmap映射文件所使用的內存(當在內存中修改了這部分數據且沒有寫回文件,那麼這部份內存就變成了anonymous),這部份內存也可能跟別的進程共享。segmentfault

SHR:RES的一部分,表示和別的進程共享的內存,包括經過mmap共享的內存和file backed的內存。當經過prive方式調用mmap映射一個文件時,若是沒有修改文件的內容,那麼那部份內容就是SHR的,一旦修改了文件內容且沒有寫回文件,那麼這部份內存就是anonymous且非SHR的。bash

%MEM:等於RES/total*100%,這裏total指總的物理內存大小。服務器

注意:因爲SHR可能會被多個進程所共享,因此係統中全部進程的RES加起來可能會超過總的物理內存數量,因爲一樣的緣由,全部進程的%MEM總和可能超過100%。app

從上面的分析能夠看出,VIRT的參考意義不大,它只能反應出程序的大小,而RES也不能徹底的表明一個進程真正佔用的內存空間,由於它裏面還包含了SHR的部分,好比三個bash進程共享了一個libc動態庫,那麼libc所佔用的內存算誰的呢?三個進程平分嗎?若是啓動一個bash佔用了4M的RES,其中3M是libc佔用的,因爲三個進程都共享那3M的libc,那麼啓動3個bash實際佔用的內存將是3*(4-3)+3=6M,可是若是單純的按照RES來算的話,三個進程就用了12M的空間。因此理解RES和SHR這兩個數據的含義對咱們在評估一臺服務器能跑多少個進程時尤爲重要,不要一看到apache的進程佔用了20M,就認爲系統能跑的apache進程數就是總的物理內存數除以20M,其實這20M裏面有可能有很大一部分是SHR的。測試

注意:top命令輸出中的RES和pmap輸出中的RSS是一個東西。code

pmap

上面top命令只是給出了一個進程大概佔用了多少的內存,而pmap能更詳細的給出內存都是被誰佔用了。pmap命令輸出的內容來自於/proc/[pid]/maps和/proc/[pid]/smaps這兩個文件,第一個文件包含了每段的一個大概描述,然後一個文件包含了更詳細的信息。

這裏用pmap看看當前bash的內存使用狀況,:

#這裏$$表明當前bash的進程ID,下面只顯示了部分輸出結果
dev@dev:~$ pmap  $$
2805:   bash
0000000000400000    976K r-x-- bash
00000000006f3000      4K r---- bash
00000000006f4000     36K rw--- bash
00000000006fd000     24K rw---   [ anon ]
0000000000be4000   1544K rw---   [ anon ]
......
00007f1fa0e9e000   2912K r---- locale-archive
00007f1fa1176000   1792K r-x-- libc-2.23.so
00007f1fa1336000   2044K ----- libc-2.23.so
00007f1fa1535000     16K r---- libc-2.23.so
00007f1fa1539000      8K rw--- libc-2.23.so
00007f1fa153b000     16K rw---   [ anon ]
......
00007f1fa196c000    152K r-x-- ld-2.23.so
00007f1fa1b7e000     28K r--s- gconv-modules.cache
00007f1fa1b85000     16K rw---   [ anon ]
00007f1fa1b8f000      8K rw---   [ anon ]
00007f1fa1b91000      4K r---- ld-2.23.so
00007f1fa1b92000      4K rw--- ld-2.23.so
00007f1fa1b93000      4K rw---   [ anon ]
00007ffde903a000    132K rw---   [ stack ]
00007ffde90e4000      8K r----   [ anon ]
00007ffde90e6000      8K r-x--   [ anon ]
ffffffffff600000      4K r-x--   [ anon ]
 total            22464K

這裏第一列是內存的起始地址,第二列是mapping的地址大小,第三列是這段內存的訪問權限,最後一列是mapping到的文件。這裏的地址都是虛擬地址,大小也是虛擬地址大小。

這裏的輸出有不少的[ anon ]行,表示在磁盤上沒有對應的文件,這些通常都是可執行文件或者動態庫裏的bss段。固然有對應文件的mapping也有多是anonymous,好比文件的數據段。關於程序的數據段和bss段的介紹請參考elf的相關資料

上面能夠看到bash、libc-2.23.so等文件出現了多行,但每行的權限不同,這是由於每一個動態庫或者可執行文件裏面都分不少段,有隻能讀和執行的代碼段,有能讀寫的數據段,還有好比這一行「00007f1fa153b000 16K rw--- [ anon ]」,就是它上面一行libc-2.23.so的bss段。

[ stack ]表示進程用到的棧空間,而heap在這裏看不到,由於pmap默認狀況下不單獨標記heap出來,因爲heap是anonymous,因此從這裏的大小能夠推測出來,heap就是「0000000000be4000 1544K rw--- [ anon ]」。

其實從上面的結果根本看不出實際上每段佔用了多少物理內存,要想看到RSS,須要使用-X參數,下面看看更詳細的輸出:

dev@dev:~$ pmap -X $$
2805:   bash
         Address Perm   Offset Device  Inode  Size  Rss  Pss Referenced Anonymous Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked Mapping
        00400000 r-xp 00000000  fc:00 390914   976  888  526        888         0              0               0    0       0      0 bash
        006f3000 r--p 000f3000  fc:00 390914     4    4    4          4         4              0               0    0       0      0 bash
        006f4000 rw-p 000f4000  fc:00 390914    36   36   36         36        36              0               0    0       0      0 bash
        006fd000 rw-p 00000000  00:00      0    24   24   24         24        24              0               0    0       0      0
        00be4000 rw-p 00000000  00:00      0  1544 1544 1544       1544      1544              0               0    0       0      0 [heap]
    .....
    7f1fa0e9e000 r--p 00000000  fc:00 136340  2912  400   83        400         0              0               0    0       0      0 locale-archive
    7f1fa1176000 r-xp 00000000  fc:00 521726  1792 1512   54       1512         0              0               0    0       0      0 libc-2.23.so
    7f1fa1336000 ---p 001c0000  fc:00 521726  2044    0    0          0         0              0               0    0       0      0 libc-2.23.so
    7f1fa1535000 r--p 001bf000  fc:00 521726    16   16   16         16        16              0               0    0       0      0 libc-2.23.so
    7f1fa1539000 rw-p 001c3000  fc:00 521726     8    8    8          8         8              0               0    0       0      0 libc-2.23.so
    7f1fa153b000 rw-p 00000000  00:00      0    16   12   12         12        12              0               0    0       0      0
    ......
    7f1fa196c000 r-xp 00000000  fc:00 521702   152  144    4        144         0              0               0    0       0      0 ld-2.23.so
    7f1fa1b7e000 r--s 00000000  fc:00 132738    28   28    9         28         0              0               0    0       0      0 gconv-modules.cache
    7f1fa1b85000 rw-p 00000000  00:00      0    16   16   16         16        16              0               0    0       0      0
    7f1fa1b8f000 rw-p 00000000  00:00      0     8    8    8          8         8              0               0    0       0      0
    7f1fa1b91000 r--p 00025000  fc:00 521702     4    4    4          4         4              0               0    0       0      0 ld-2.23.so
    7f1fa1b92000 rw-p 00026000  fc:00 521702     4    4    4          4         4              0               0    0       0      0 ld-2.23.so
    7f1fa1b93000 rw-p 00000000  00:00      0     4    4    4          4         4              0               0    0       0      0
    7ffde903a000 rw-p 00000000  00:00      0   136   24   24         24        24              0               0    0       0      0 [stack]
    7ffde90e4000 r--p 00000000  00:00      0     8    0    0          0         0              0               0    0       0      0 [vvar]
    7ffde90e6000 r-xp 00000000  00:00      0     8    4    0          4         0              0               0    0       0      0 [vdso]
ffffffffff600000 r-xp 00000000  00:00      0     4    0    0          0         0              0               0    0       0      0 [vsyscall]
                                             ===== ==== ==== ========== ========= ============== =============== ==== ======= ======
                                             22468 5084 2578       5084      1764              0               0    0       0      0 KB
  • 權限字段多了一個s和p的標記,s表示是和別人共享的內存空間,讀寫會影響到其餘進程,而p表示這是本身私有的內存空間,讀寫這部份內存不會對其餘進程形成影響。

  • 輸出標示出了[heap]段,而且也說明了後面幾個[anon]表明的什麼意思(vvar,vdso,vsyscall都是映射到內核的特殊段),mapping字段爲空的都是上一行mapping文件裏面的bss段(但是gconv-modules.cache後面有兩行anonymous mapping,可能跟共享內存有關係,沒有深究)。

  • Anonymous列標示出了哪些是而且有多少是Anonymous方式映射的物理內存,其大小小於等於RSS

  • RSS列表示實際佔用的物理內存大小

top命令輸出的SHR內存

最後來看看top命令輸出的SHR到底由pmap的哪些輸出構成

dev@dev:~$ pmap -d $$
3108:   bash
Address           Kbytes Mode  Offset           Device    Mapping
0000000000400000     976 r-x-- 0000000000000000 0fc:00000 bash
00000000006f3000       4 r---- 00000000000f3000 0fc:00000 bash
00000000006f4000      36 rw--- 00000000000f4000 0fc:00000 bash
00000000006fd000      24 rw--- 0000000000000000 000:00000   [ anon ]
0000000000c23000    1544 rw--- 0000000000000000 000:00000   [ anon ]
......
00007f53af18e000      16 rw--- 0000000000000000 000:00000   [ anon ]
00007f53af198000       8 rw--- 0000000000000000 000:00000   [ anon ]
00007f53af19a000       4 r---- 0000000000025000 0fc:00000 ld-2.23.so
00007f53af19b000       4 rw--- 0000000000026000 0fc:00000 ld-2.23.so
00007f53af19c000       4 rw--- 0000000000000000 000:00000   [ anon ]
00007ffc5a94b000     132 rw--- 0000000000000000 000:00000   [ stack ]
00007ffc5a9b7000       8 r---- 0000000000000000 000:00000   [ anon ]
00007ffc5a9b9000       8 r-x-- 0000000000000000 000:00000   [ anon ]
ffffffffff600000       4 r-x-- 0000000000000000 000:00000   [ anon ]
mapped: 22464K    writeable/private: 1848K    shared: 28K

dev@dev:~$ top -p $$
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 3108 dev       20   0   22464   5028   3264 S  0.0  1.0   0:00.02 bash

從上面的輸出可看出SHR ≈ RES - writeable/private,其中writeable/private主要包含stack和heap以及可執行文件和動態庫的data和bss段,而stack+heap=1544+132=1675,這已經佔了絕大部分,從而data和bss段之類的基本上能夠忽略了,因此通常狀況下,SHR ≈ RES - [heap] - [stack],因爲stack通常都比較小,上面的等式能夠進一步約等於:SHR ≈ RES - [heap]。

總結

top命令能看到一個進程佔用的虛擬內存空間、物理內存空間以及和別的進程共享的物理內存空間,這裏共享的空間包括經過mmap共享的內存以及共享的可執行文件以及動態庫。而mmap命令能看到更詳細的信息,好比可執行文件和它所連接的動態庫大小,以及物理內存都是被哪些段給佔用了。

進程佔用的虛擬地址空間大小跟程序的規模有關,除了stack和heap段,其餘段的大小基本上都是固定的,而且在程序連接的時候就已經肯定了,因此基本上只要關注stack和heap段就能夠了,因爲stack相對heap來講很小,因此只要沒什麼stack異常,只須要關注heap。

在實際的工做過程當中,其實咱們更關心的是RSS用了多少,都被誰用了,簡單點說,若是咱們沒有同時啓動多個進程(同一個程序),RSS就是一個很好的實際物理內存使用參考值,但若是是像apache那樣同時跑不少個進程,那麼RSS減去SHR所佔用的空間就是一個很好的實際物理內存佔用參考值,固然這都是大概估算值。

要想精確評估一個進程到底佔了多少內存,仍是很難的,須要對進程的每一個段有深刻的理解,尤爲是SHR部分都有哪些進程在一塊兒共享,不過如今服務器上的內存都是以G爲單位的,因此通常狀況下大概的估算一下加上合理的測試就能知足咱們的需求了。

參考

相關文章
相關標籤/搜索