都說Linux是吃內(nèi)存大戶,可你知道具體是哪些進(jìn)程吃掉了嗎?
一個(gè)經(jīng)常被問到的 Linux 問題:為啥 Linux 系統(tǒng)沒運(yùn)行多少程序,顯示的可用內(nèi)存這么少?
其實(shí) Linux 與 Windows 的內(nèi)存管理不同,會(huì)盡量緩存內(nèi)存以提高讀寫性能,通常叫做 Cache Memory。
比較老的資料都會(huì)介紹 Linux 的 Cache 占用很多并沒有關(guān)系,因?yàn)?Linux 會(huì)盡可能利用內(nèi)存進(jìn)行緩存。但是緩存的回收也是需要資源的,比較好的一篇文章是 Poor Zorro 寫的 Linux 內(nèi)存中的 Cache 真的能被回收么?。
雖然大部分情況下我們看到 Cache 占用很高時(shí)是沒有問題的,但是我們還是想弄清楚到底是哪個(gè)程序把 Cache 弄的那么高,這居然不是一件容易的事。
內(nèi)核的模塊在分配資源的時(shí)候,為了提高效率和資源的利用率,都是透過 Slab 來分配的。Slab 為結(jié)構(gòu)性緩存占用內(nèi)存,該項(xiàng)也經(jīng)常占用很大的內(nèi)存。不過借助 slabtop 工具,我們可以很方便的顯示內(nèi)核片緩存信息,該工具可以更直觀的顯示 /proc/slabinfo 下的內(nèi)容。
- # 顯示了一臺(tái)機(jī)器緩存中占用對(duì)象的情況
- $ slabtop -s c
- Active / Total Objects (% used) : 856448 / 873737 (98.0%)
- Active / Total Slabs (% used) : 19737 / 19737 (100.0%)
- Active / Total Caches (% used) : 67 / 89 (75.3%)
- Active / Total Size (% used) : 141806.80K / 145931.33K (97.2%)
- Minimum / Average / Maximum Object : 0.01K / 0.17K / 8.00K
- OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
- 416949 416949 100% 0.10K 10691 39 42764K buffer_head
- 5616 5545 98% 2.00K 351 16 11232K kmalloc-2048
- 9114 8990 98% 1.02K 294 31 9408K ext4_inode_cache
- 12404 12404 100% 0.57K 443 28 7088K radix_tree_node
- 10800 10731 99% 0.58K 400 27 6400K inode_cache
- 31290 29649 94% 0.19K 745 42 5960K dentry
- 3552 3362 94% 1.00K 111 32 3552K kmalloc-1024
- 1100 1055 95% 2.84K 100 11 3200K task_struct
- 1649 1481 89% 1.88K 97 17 3104K TCP
- 27000 27000 100% 0.11K 750 36 3000K sysfs_dir_cache
- 1380 1269 91% 2.06K 92 15 2944K sighand_cache
雖然上面的命令顯示了 Cache 中 Slab 的情況,但是還是沒有顯示什么程序占用的 Cache。
方案一:使用 Pcstat 來實(shí)現(xiàn)
經(jīng)過搜索,發(fā)現(xiàn) linux-ftools 這個(gè)工具可以顯示某個(gè)文件占用的 Cache 的情況, fincore 只是它其中的一個(gè)工具。
- $ fincore [options] files...
- --pages=false Do not print pages
- --summarize When comparing multiple files, print a summary report
- --only-cached Only print stats for files that are actually in cache.
- https://colobu.com/2017/03/07/what-is-in-linux-cached/root@xxxxxx:/var/lib/mysql/blogindex# fincore --pages=false --summarize --only-cached *
- stats for CLUSTER_LOG_2010_05_21.MYI: file size=93840384 , total pages=22910 , cached pages=1 , cached size=4096, cached perc=0.004365
- stats for CLUSTER_LOG_2010_05_22.MYI: file size=417792 , total pages=102 , cached pages=1 , cached size=4096, cached perc=0.980392
- stats for CLUSTER_LOG_2010_05_23.MYI: file size=826368 , total pages=201 , cached pages=1 , cached size=4096, cached perc=0.497512
- stats for CLUSTER_LOG_2010_05_24.MYI: file size=192512 , total pages=47 , cached pages=1 , cached size=4096, cached perc=2.127660
- stats for CLUSTER_LOG_2010_06_03.MYI: file size=345088 , total pages=84 , cached pages=43 , cached size=176128, cached perc=51.190476
- stats for CLUSTER_LOG_2010_06_04.MYD: file size=1478552 , total pages=360 , cached pages=97 , cached size=397312, cached perc=26.944444
- stats for CLUSTER_LOG_2010_06_04.MYI: file size=205824 , total pages=50 , cached pages=29 , cached size=118784, cached perc=58.000000
- stats for COMMENT_CONTENT_2010_06_03.MYI: file size=100051968 , total pages=24426 , cached pages=10253 , cached size=41996288, cached perc=41.975764
- stats for COMMENT_CONTENT_2010_06_04.MYD: file size=716369644 , total pages=174894 , cached pages=79821 , cached size=326946816, cached perc=45.639645
- stats for COMMENT_CONTENT_2010_06_04.MYI: file size=56832000 , total pages=13875 , cached pages=5365 , cached size=21975040, cached perc=38.666667
- stats for FEED_CONTENT_2010_06_03.MYI: file size=1001518080 , total pages=244511 , cached pages=98975 , cached size=405401600, cached perc=40.478751
- stats for FEED_CONTENT_2010_06_04.MYD: file size=9206385684 , total pages=2247652 , cached pages=1018661 , cached size=4172435456, cached perc=45.321117
- stats for FEED_CONTENT_2010_06_04.MYI: file size=638005248 , total pages=155763 , cached pages=52912 , cached size=216727552, cached perc=33.969556
- stats for FEED_CONTENT_2010_06_04.frm: file size=9840 , total pages=2 , cached pages=3 , cached size=12288, cached perc=150.000000
- stats for PERMALINK_CONTENT_2010_06_03.MYI: file size=1035290624 , total pages=252756 , cached pages=108563 , cached size=444674048, cached perc=42.951700
- stats for PERMALINK_CONTENT_2010_06_04.MYD: file size=55619712720 , total pages=13579031 , cached pages=6590322 , cached size=26993958912, cached perc=48.533080
- stats for PERMALINK_CONTENT_2010_06_04.MYI: file size=659397632 , total pages=160985 , cached pages=54304 , cached size=222429184, cached perc=33.732335
- stats for PERMALINK_CONTENT_2010_06_04.frm: file size=10156 , total pages=2 , cached pages=3 , cached size=12288, cached perc=150.000000
- ---
- total cached size: 32847278080
fincore 的工作原理是將指定文件的相應(yīng) Inode Data 與 Kernel 的 Page Cache Table 做對(duì)比,如果 Page Cache Table 有這個(gè) Inode 信息,就找到該 Inode 對(duì)應(yīng)的 Data Block 的大小。
因?yàn)?Kernel 的 Page Cache Table 只存儲(chǔ) Data Block 的引用而不是文件名,即文件的 Inode 信息。所以并沒有任何一個(gè)工具運(yùn)行一次就可以找出所有的文件使用緩存的情況。所以使用 linux-fincore 這個(gè)工具也只能加文件名來判斷該文件是否被緩存,如果緩存,大小是多少。問題是你不能隨便猜哪個(gè)文件是否被緩存吧。
Shanker 提供了一個(gè)腳本來解決此問題,那就是查看哪些進(jìn)程使用的物理內(nèi)存最多,就找到該進(jìn)程打開的文件,然后用 fincore 來查看這些文件的緩存使用率。
這個(gè)辦法在大部分情況下都可以找到占用 Cache 較多的程序和進(jìn)程。腳本內(nèi)容如下:
- #!/bin/bash
- #Author: Shanker
- #Time: 2016/06/08
- #set -e
- #set -u
- #you have to install linux-fincore
- if [ ! -f /usr/local/bin/linux-fincore ]
- then
- echo "You haven't installed linux-fincore yet"
- exit
- fi
- #find the top 10 processs' cache file
- ps -e -o pid,rss|sort -nk2 -r|head -10 |awk '{print $1}'>/tmp/cache.pids
- #find all the processs' cache file
- #ps -e -o pid>/tmp/cache.pids
- if [ -f /tmp/cache.files ]
- then
- echo "the cache.files is exist, removing now "
- rm -f /tmp/cache.files
- fi
- while read line
- do
- lsof -p $line 2>/dev/null|awk '{print $9}' >>/tmp/cache.files
- done</tmp/cache.pids
- if [ -f /tmp/cache.fincore ]
- then
- echo "the cache.fincore is exist, removing now"
- rm -f /tmp/cache.fincore
- fi
- for i in `cat /tmp/cache.files`
- do
- if [ -f $i ]
- then
- echo $i >>/tmp/cache.fincore
- fi
- done
- linux-fincore -s `cat /tmp/cache.fincore`
- rm -f /tmp/cache.{pids,files,fincore}
比較遺憾的是,linux-ftools 目前已經(jīng)不再維護(hù)了。在新版本的操作系統(tǒng)上沒法編譯好這個(gè)程序,所以這個(gè)方法失效了。
再次通過 Google 搜索,后來我找到了 pcstat 這個(gè)工具,pcstat 使用 Go 語言開發(fā),功能基本和 linux-ftools 一樣 。
項(xiàng)目地址:https://github.com/tobert/pcstat
然后我修改了 Shanker 的腳本,讓它使用 pcstat 來進(jìn)行處理,這樣就可以很好的找到 Cache 所占用的情況。修改后的腳本如下:
- #!/bin/bash
- #you have to install pcstat
- if [ ! -f /data0/brokerproxy/pcstat ]
- then
- echo "You haven't installed pcstat yet"
- echo "run \"go get github.com/tobert/pcstat\" to install"
- exit
- fi
- #find the top 10 processs' cache file
- ps -e -o pid,rss|sort -nk2 -r|head -10 |awk '{print $1}'>/tmp/cache.pids
- #find all the processs' cache file
- #ps -e -o pid>/tmp/cache.pids
- if [ -f /tmp/cache.files ]
- then
- echo "the cache.files is exist, removing now "
- rm -f /tmp/cache.files
- fi
- while read line
- do
- lsof -p $line 2>/dev/null|awk '{print $9}' >>/tmp/cache.files
- done</tmp/cache.pids
- if [ -f /tmp/cache.pcstat ]
- then
- echo "the cache.pcstat is exist, removing now"
- rm -f /tmp/cache.pcstat
- fi
- for i in `cat /tmp/cache.files`
- do
- if [ -f $i ]
- then
- echo $i >>/tmp/cache.pcstat
- fi
- done
- /data0/brokerproxy/pcstat `cat /tmp/cache.pcstat`
- rm -f /tmp/cache.{pids,files,pcstat}
腳本運(yùn)行成功后的顯示結(jié)果如下:
- +------------------------------------------+----------------+------------+-----------+---------+
- | Name | Size (bytes) | Pages | Cached | Percent |
- |------------------------------------------+----------------+------------+-----------+---------|
- | /data0/abcasyouknow/0307/abc | 10060771 | 2457 | 2457 | 100.000 |
- | /data0/abcasyouknow/0307/logs/abc.log | 1860 | 1 | 1 | 100.000 |
- | /data0/abcasyouknow/0307/logs/uuid.log | 326326364 | 79670 | 79670 | 100.000 |
- | /usr/bin/bash | 960384 | 235 | 194 | 082.553 |
- | /usr/lib/locale/locale-archive | 106065056 | 25895 | 211 | 000.815 |
- | /usr/lib64/libnss_files-2.17.so | 58288 | 15 | 15 | 100.000 |
- | /usr/lib64/libc-2.17.so | 2107760 | 515 | 336 | 065.243 |
- | /usr/lib64/libdl-2.17.so | 19512 | 5 | 5 | 100.000 |
- | /usr/lib64/libtinfo.so.5.9 | 174520 | 43 | 42 | 097.674 |
- | /usr/lib64/ld-2.17.so | 164336 | 41 | 41 | 100.000 |
- | /usr/lib64/gconv/gconv-modules.cache | 26254 | 7 | 7 | 100.000 |
- +------------------------------------------+----------------+------------+-----------+---------+
從結(jié)果我們可以看到 uuid.log 占用 Cache 比較多。這個(gè)文件是打開的,程序一直在往里面寫日志,Linux 應(yīng)該是把它緩存了。
方案二:使用 Vmtouch 來實(shí)現(xiàn)
除了上面提到的 pcstat 工具外,你還可以使用 vmtouch 來實(shí)現(xiàn)同樣的目的。vmtouch 是一個(gè)可以查詢到緩存的文件和目錄,并且能把文件推入緩存或者驅(qū)逐出緩存的工具。
項(xiàng)目地址:https://github.com/hoytech/vmtouch
安裝 Vmtouch
- $ git clone https://github.com/hoytech/vmtouch
- $ cd vmtouch
- $ make
- $ sudo make install
使用 Vmtouch
- vmtouch 命令語法
- $ vmtouch
- vmtouch: no files or directories specified
- vmtouch v1.0.2 - the Virtual Memory Toucher by Doug Hoyte
- Portable file system cache diagnostics and control
- Usage: vmtouch [OPTIONS] ... FILES OR DIRECTORIES ...
- Options:
- -t touch pages into memory
- -e evict pages from memory
- -l lock pages in physical memory with mlock(2)
- -L lock pages in physical memory with mlockall(2)
- -d daemon mode
- -m max file size to touch
- -p use the specified portion instead of the entire file
- -f follow symbolic links
- -h also count hardlinked copies
- -w wait until all pages are locked (only useful together with -d)
- -v verbose
- -q quiet
2. 一些使用的例子
由于 vmtouch 直接支持目錄級(jí)查詢,所以使用起來簡(jiǎn)單得多了。
- 查看 /tmp 目錄在內(nèi)存中的緩存
- $ vmtouch /tmp/
- vmtouch: WARNING: skipping non-regular file: /tmp/ssh-GgJnCEkWMQC2/agent.1068
- Files: 17
- Directories: 7
- Resident Pages: 4780/4780 18M/18M 100%
- Elapsed: 0.001006 seconds
如果需要查看更詳細(xì)信息,可以使用 -v 參數(shù)。
- $ vmtouch -v /tmp/
- 查看一個(gè)文件被緩存了多少
- $ vmtouch -v ~/Downloads/phoronix-test-suite_6.0.1_all.deb
- /home/neo/Downloads/phoronix-test-suite_6.0.1_all.deb
- [ ] 0/132
- Files: 1
- Directories: 0
- Resident Pages: 0/132 0/528K 0%
- Elapsed: 0.000117 seconds
- 把指定的文件緩存起來
- $ vmtouch -vt ~/Downloads/phoronix-test-suite_6.0.1_all.deb
- /home/neo/Downloads/phoronix-test-suite_6.0.1_all.deb
- [OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO] 132/132
- Files: 1
- Directories: 0
- Touched Pages: 132 (528K)
- Elapsed: 0.007935 seconds
- 把緩存中指定的數(shù)據(jù)驅(qū)逐出去
- $ vmtouch -ve ~/Downloads/phoronix-test-suite_6.0.1_all.deb
- Evicting /home/neo/Downloads/phoronix-test-suite_6.0.1_all.deb
- Files: 1
- Directories: 0
- Evicted Pages: 132 (528K)
- Elapsed: 0.000109 seconds
更多關(guān)于 vmtouch 使用的具體信息,你可以參考官網(wǎng):https://hoytech.com/vmtouch/
如果你還有更多 Linux 下查看 Cache 或 Buffer 占用的方法,請(qǐng)直接留言告訴我們喲!