在Linux下,當你使用top、ps等命令查看某個進程的顯存使用情況時,經常會看到VIRT、RES、SHR等,它們是什么意思呢? 不同尺寸對工藝有什么影響?
查看進程使用的顯存
在進程看來,所有的顯存都是虛擬顯存,但是這個虛擬顯存對應多少數學顯存呢? 我們在Linux顯存管理中介紹過,并不是每一塊虛擬顯存都有對應的數學顯存,對應的數據可能在c盤的某個文件中,也可能在swap空間的某個區域中。 只有內核知道進程真正的數學內存使用情況,我們只能通過內核打開的一些套接字來獲取這些統計數據。
頂部
先看top的輸出(top使用的數據來自/proc/[pid]/statm),這里只是摘錄了一些數據:
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:進程使用的虛擬顯存大小
RES:系統為虛擬內存分配的數學內存大小,包括文件內存和顯存,其中包括進程自身分配使用的顯存,以及通過mmap與其他進程共享的顯存; 而file的顯存是指加載可執行文件和動態庫占用的顯存,以及通過方法調用mmap映射文件使用的顯存(當顯存和文件沒有寫回怎么看物理內存,那么這部分顯存就完成了),這部分顯存也有可能被其他進程共享。
SHR:RES的一部分,表示與其他進程共享的顯存,包括mmap共享的顯存和file的顯存。 通過priv方法調用mmap映射文件時,如果文件內容沒有改變,那么那部分內容就是SHR。 一旦文件內容改變,文件沒有回寫,那么這部分顯存是也不是SHR。
%MEM:等于RES/total*100%,其中total是指總的數學內存大小。
注意:由于SHR可能被多個進程共享,所以系統中所有進程的RES總和可能會超過數學內存的總數。 同理,所有進程的%MEM之和可能會超過100%。
從里面分析可以看出,VIRT的參考值意義不大,它只能反映程序的大小,而RES并不能完全代表一個進程實際占用的顯存空間,因為它還包括了SHR的一部分,比如三個bash進程共享一個libc動態庫,那么libc占用的顯存歸誰所有呢? 三個過程是否平分? 如果啟動一個bash占用4M的RES,其中3M被libc占用,因為三個進程共享libc的3M,所以啟動三個bash實際占用的顯存為3*(4-3)+3=6M ,而如果簡單的按照RES計算的話,三個進程會使用12M的空間。 因此,理解RES和SHR這兩個數據的含義,對于我們評估一臺服務器能運行多少個進程就顯得尤為重要。 不要一聽進程占用20M就認為系統能運行的進程數就是數學上的總內存。 數字乘以20M,雖然20M中有很大一部分可能是SHR。
注意:top 命令輸出中的 RES 和 pmap 輸出中的 RSS 是一回事。
地圖
里面的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
這里第一列是顯存的起始地址,第二列是地址大小,第三列是這個顯存的訪問權限,最后一列是要訪問的文件。 這里的地址都是虛擬地址,size也是虛擬地址的大小。
這里的輸出有很多[anon]行,說明c盤沒有對應的文件,一般是可執行文件或者動態庫中的bss段。 其實可能還有對應的文件,比如文件的data段。 程序的data段和bss段的介紹可以參考elf的相關資料。
里面可以看到bash、libc-2.23.so等文件有多行,但是每一行的權限是不同的。 這是因為每個動態庫或可執行文件都被分成很多段,有些只能讀取和執行。 在代碼段中,有一個可以讀寫的數據段,里面有一行如“16Krw—[anon]”怎么看物理內存,里面是libc-2.23.so的bss段一行。
[stack]表示進程使用的棧空間,這里看不到堆,因為pmap默認不單獨標示堆,因為堆是,所以從這里的大小可以推斷出堆是" —[匿名]”。
雖然從前面的結果中無法看出每個實際占用了多少化學顯存,但是為了看到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,是映射到內核的特殊段),空數組是文件段上一行的bss(但是gconv-.cache旁邊有兩行,可能跟共享顯存有關,不詳述)。
這些列標識大小大于或等于 RSS 的形式映射數學內存是什么以及多少
RSS欄表示實際占用的數學內存大小
top命令輸出的SHR顯存
最后我們看看pmap的輸出是由top命令輸出的SHR組成的。
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-/,其中/主要包括棧和堆,以及可執行文件和動態庫的data和bss段,stack+heap=1544+132=1675,其中已經占了絕大部分,所以data和bss段基本可以忽略,所以一般情況下,SHR≈RES-[heap]-[stack],因為stack通常比較小,前面的等式可以進一步約等于:SHR≈RES-[heap]。
總結
top命令可以看到一個進程占用的虛擬顯存空間、物理顯存空間以及與其他進程共享的化學顯存空間。 這里的共享空間包括通過mmap共享的顯存、共享的可執行文件和動態庫。 mmap命令可以看到更詳細的信息,比如可執行文件的大小和鏈接的動態庫,化學顯存占用了哪些段。
進程占用的虛擬地址空間的大小與程序的大小有關。 不僅僅是棧和堆段,其他段的大小基本都是固定的,但是在程序鏈接的時候就已經確定了,所以基本上只需要關注棧和堆段就可以了,因為stack相對于heap來說是比較小的,所以只要沒有stack的異常,只需要關注heap即可。
在實際工作過程中,雖然我們比較關心RSS用了多少,誰在用,簡單來說,如果我們不同時啟動多個進程(同一個程序),RSS是一個很好的實用工具 顯存使用一個參考值,但是如果你像那樣同時運行多個進程,那么將RSS占用的空間除以SHR是實際化學顯存占用的一個很好的參考值。 其實這些都是大概的計算值。
仍然很難準確評估一個進程占用多少顯存。 需要深入了解每個環節的流程,尤其是SHR部分共享哪些流程。 但是現在服務器上的顯存都是以G為單位的,所以一般情況下,一個大概的計算和合理的測試就可以滿足我們的需求。