閱讀694 返回首頁    go 技術社區[雲棲]


[原創]systemtap腳本分析係統中dentry SLAB占用過高問題

摘要

利用systemtap腳本分析係統中dentry SLAB占用過高問題

原創文章:來自systemtap腳本分析係統中dentry SLAB占用過高問題

背景

長時間運行著的tengine主機有內存占用75%以上的報警.

操作係統版本: 2.6.32.el6.x86_64

原因定位

收集內存使用的相關信息如下:
因內存占用率報警mem:76.28%先看一下內存的總體使用狀況,從下圖中可以看出used占用較高,buffers/cached占用較少(關於cached占用過高的分析處理請參見另一篇文章), 這時首先想到是不是有進程內存泄漏,經查詢tengine和別的進程沒有發現有占用大量內存即不存在內存泄漏。
1

cat /proc/meminfo細化查看內存各部分的使用,可以發現slab占用過高約40GB,同時也可以觀察到SReclaimable也很高,即SReclaimable指可收回Slab的大小。
2

查看監控中的slab曲線一直處於增長狀態
3

詳細查看slab中各部分內存的占用,可以看出dentry占用的很多,大約在40GB內存。
4

dentry的作用:當讀寫文件時內核會為該文件對象建立一個dentry,並將其緩存起來,方便下一次讀寫時直接從內存中取出提高效率。

5

再詳細查看一下dentry的使用情況:
$cat /proc/sys/fs/dentry-state
209815031 209805757 45 0 0 0

從上查看dentry使用數量,可以發現有大量unused dentries占用率很高,沒有釋放掉。

以上數據的詳細含義可以man proc查看,下邊是可以從網上查到的中文含義
6

dentunusd:在緩衝目錄條目中沒有使用的條目數量.
file-nr:被係統使用的文件句柄數量.
inode-nr:使用的索引節點數量.
pty-nr:使用的pty數量.
1)dentunusd
dentunusd數據的數據來源是/proc/sys/fs/dentry-state的第二項數據.
要弄明白它的意義,我們首先要弄明白dcache(目錄高速緩存),因為係統中所有的inode都是通過文件名來訪問的,而為了解決文件名到inode轉換的時間,就引入了dcache.
它是VFS層為當前活動和最近使用的名字維護的一個cache.
dcache中所有處於unused狀態和negative(消極)狀態的dentry對象都通鏈入到dentry_unused鏈表中,這種dentry對象在回收內存時可能會被釋放.
如果我們在係統中運行ls -ltR /etc/會看到dentunusd的數量會多起來.
而通過mount -o remount /dev/sda1會看到dentunusd會迅速會回收.
2)file-nr
file-nr的的數據來源是/proc/sys/fs/file-nr文件的第一項數據.
實際上file-nr不是一個準確的值,file-nr每次增加的步長是64(64位係統),例如現在file-nr為2528,實際上可能隻打開了2527個文件,而此時你打開兩個文件後,它就會變成2592,而不是2530.
3)inode-nr
inode-nr的數據來源是/proc/sys/fs/inode-nr文件的第一項數據減去第二項數據的值.
inode-nr文件的第一項數據是已經分配過的INODE節點.第二項數據是空閑的INODE節點.
例如,inode-nr文件裏的值為:13720 7987
我們新建一個文件file1,此時inode-nr第一項數據會加1,就是13721,表示係統裏建立了這麼多的inode.
我們再刪除掉file1,此時就會變成13720.
空閑的INODE節點表示我們已經裏這麼多的INODE節點曾經有過被利用,但沒有被釋放.
所以INODE節點總數減去空閑的INODE,就是正在被用的INODE.
最後通過使用mount -o remount /dev/sda1命令,空閑節點會被刷新,所以inode-nr的值會有所變化.
4)pty-nr
pty-nr的數據來源是/proc/sys/kernel/pty/nr
表示登陸過的終端總數,如果我們登錄過10回,退出了3回,最後的結果還是10回.

也可用於sar命令查看dentunusd以秒為間隔查看dentry的變化。
$sar -v 1

12:07:09 AM dentunusd file-nr inode-nr pty-nr
12:07:10 AM 183107848 1088 42210 44
12:07:11 AM 183107871 1088 42245 44
12:07:12 AM 183116550 1152 42290 44
12:07:13 AM 183116581 1088 42343 44
12:07:14 AM 183116617 1216 42396 44
12:07:15 AM 183116059 1216 40585 44

關於linux dentry的介紹可以搜出來一大把,包括dentry占用過高,不外乎兩種處理方法,一種是直接通過proc係統清理dentry,命令如下:
sudo sh -c "echo 2 > /proc/sys/vm/drop_caches"
缺點是執行命令過程容易hang住幾分鍾,而且redhat官方也不建議使用這種方式清理cache等;另外一種方法是通過調整內核參數vm.vfs_cache_pressure,含義如下,先調整為vm.vfs_cache_pressure=10000觀察一天也沒有發現能使dentry降下去。
vm.vfs_cache_pressure控製內核回收用於dentry和inode cache內存的傾向。 默認值是100,內核會根據pagecache和swapcache的回收情況,讓dentry和inode cache的內存占用量保持在一個相對公平的百分比上。減小vfs_cache_pressure會讓內核更傾向於保留dentry和inode cache。當vfs_cache_pressure等於0,在內存緊張時內核也不會回收dentry和inode cache,這容易導致OOM。如果vfs_cache_pressure的值超過100,內核會更傾向於回收dentry和inode cache。

細節分析

即然上述兩個辦法都不是完美的方法,就需要確認這麼多unused dentries到底是那些文件占用的? 按正常理解情況下linux內核不太可能釋放不了dentry。
首先通過查看內核dentry結構部分的代碼(fs/dcache.c),搞清楚了所有的unused dentries掛在了super_blocks 結構的s_dentry_lru鏈表上,可以通過遍曆此鏈表上的節點輸出這些節點代表的文件名,就可以知道dentries被那些文件占用。
思路:用systemtap工具做內核探測,將每個節點的文件名寫入到log文件。guru模式關鍵代碼如下:

        if (!list_empty(&sb->s_dentry_lru)) {
            list_for_each_entry(dent, &sb->s_dentry_lru, d_lru) {
                spin_lock(&dent->d_lock);
                if (dent->d_flags & DCACHE_REFERENCED) {
                    dcache_referenced_nr++;
                }
                spin_unlock(&dent->d_lock);

                memset(path, 0, 2048); 

                cp = dentry_path_stp(dent, path, 1024); //解析每個dentry的文件路徑,參考char *dentry_path(struct dentry *dentry, char *buf, int buflen)的實現
                if (!IS_ERR(cp)) {
                    if(strlen(cp))
                        //offset += snprintf(buf+offset, PAGE_SIZE-offset, "%s", cp);
                        ;
                    else
                        cp = path+1024;                        
                }  

                pp = path+1024;
                pp += sprintf(path+1024, "%d ", dcache_referenced_nr);

                if (offset + (pp-cp) + 3 >= PAGE_SIZE) {//\r\n space
                     fp->f_op->write(fp, buf, offset, &fp->f_pos);     //將文件路徑寫log
                     offset = 0;
                     memset(buf,0,PAGE_SIZE);
                }

                offset += snprintf(buf+offset, PAGE_SIZE-offset, "%s %s\r\n", cp, path+1024);
            }//end list_for

日誌分析

查看某一台主機上的dentry條目如下,約3.3億。
$cat /proc/sys/fs/dentry-state
338031179 338022259 45 0 0 0

systemtap腳本執行完成後,生成日誌文件,經分析/etc/pki/nssdb/xxxx,占用量最大1點多億條dentry.
$sudo grep "/etc/pki/nssdb" dcache.log |wc -l
131071404

$grep '/etc/pki/nssdb' dcache.log | more
/etc/pki/nssdb/._dOeSnotExist_-869749107.db 223
/etc/pki/nssdb/._dOeSnotExist_-869749108.db 224
/etc/pki/nssdb/._dOeSnotExist_-869749109.db 225
/etc/pki/nssdb/._dOeSnotExist_-869749110.db 226
/etc/pki/nssdb/._dOeSnotExist_-869749111.db 227
/etc/pki/nssdb/._dOeSnotExist_-869749112.db 228
/etc/pki/nssdb/._dOeSnotExist_-869749113.db 229
/etc/pki/nssdb/._dOeSnotExist_-869749114.db 230
/etc/pki/nssdb/._dOeSnotExist_-869749115.db 231
/etc/pki/nssdb/._dOeSnotExist_-869749116.db 232
/etc/pki/nssdb/._dOeSnotExist_-869749117.db 233
/etc/pki/nssdb/._dOeSnotExist_-869749118.db 234
/etc/pki/nssdb/._dOeSnotExist_-869749119.db 235
/etc/pki/nssdb/._dOeSnotExist_-869749120.db 236
/etc/pki/nssdb/._dOeSnotExist_-869749121.db 237
/etc/pki/nssdb/._dOeSnotExist_-869749122.db 238
/etc/pki/nssdb/._dOeSnotExist_-869749123.db 239
/etc/pki/nssdb/._dOeSnotExist_-869749124.db 240
/etc/pki/nssdb/._dOeSnotExist_-869749125.db 241
/etc/pki/nssdb/._dOeSnotExist_-869749126.db 242
/etc/pki/nssdb/._dOeSnotExist_-869749127.db 243
/etc/pki/nssdb/._dOeSnotExist_-869749128.db 244
/etc/pki/nssdb/._dOeSnotExist_-869749129.db 245
/etc/pki/nssdb/._dOeSnotExist_-869749130.db 246
……

從上邊可能看出隻有/etc/pki/nssdb/._dOeSnotExist_ 的使用不明確搜索一把,發現redhat 6係列的係統中存在的問題, bug分析詳細參考:
Can curl HTTPS requests make fewer access system calls?
https://bugzilla.redhat.com/show_bug.cgi?id=1044666

因為tengine主機上調用了大量的curl https做探測(curl的版本7.19),使用如下命令做了一下分析可以明確發現一次curl有上百次的訪問/etc/pki/nssdb/.xxx文件產生大量的dentry,

sudo strace -f -e trace=access curl 'https://www.taobao.com'

另外由於agent周期性的在拉取配置文件這些配置文件以當前時刻做文件名,也造成大量的dentry。兩者加起來的數量基本符合/proc/sys/fs/dentry-state查出來的數量。
至此問題的兩個主要原因已經比較清楚。

解決方案

  1. 保留係統和應用程序所需要的可用內存,如調整vm.min_free_kbytes = 4194304 vm.extra_free_kbytes = 4194304 使係統free的內存最少保持在8GB以上(注意6U係統才有extra_free_kbytes)
  2. 升級curl更高版本
  3. 升級nss-softokn 與nss-util rpm包到較高的版本 3.14.3-22,3.16.2.3(未驗證參考https://support.huawei.com/huaweiconnect/enterprise/thread-333119.html)
  4. 優化tengine的配置與日誌文件使盡可能少的產生文件dentry

案例小結

此例問題對所有linux主機上dentry占用較高分析有借鑒參考意義。

備注
內存回收主要的兩大機製:kswapd和“Low on Memory Reclaim”。即當係統中的可用內存很少時,守護進程kswapd被喚醒開始釋放頁麵,如果內存壓力很大,進程也會同步地釋放內存,這種情況被稱為direct-reclaim.
kswapd釋放內存頁麵的數量是有一定的比例的(有興趣可以查閱pages_min, pages_low and pages_high這些參數),但是當內存的分配占用率高於釋放率時,就引起了上述的問題即看到內存一直在增長。

最後更新:2017-07-19 00:02:48

  上一篇:go  阿裏雲優惠券怎麼用,阿裏雲幸運券使用方法以及阿裏雲8折碼
  下一篇:go  搜狗深度學習技術在廣告推薦領域的應用