中fuse_一个Fanotify和FUSE配合使用导致的问题
說明:本文所使用的內(nèi)核版本為3.10.0-1062.el7.x86_64。
前面文章介紹的fanotify機制可用于監(jiān)控文件操作產(chǎn)生的事件(比如open, read等),而這些文件會來自不同類型的文件系統(tǒng),其中比較特殊的一種是FUSE。那fanotify能不能支持對FUSE類型文件系統(tǒng)的監(jiān)控呢?
【現(xiàn)象】
還是拿fuse.sshfs來進行實驗,這里使用的"sshfs"是基于SFTP文件傳輸協(xié)議來掛載遠端目錄的(遠端為sftp server)。不過為了測試的方便(不用再找另一臺機器),采用的方式是將本機的一個目錄"/home/src"作為“遠端”,掛載到本地的"/mnt/src"目錄。
sshfs localhost:/home/src /mnt/dst
掛載成功后,啟動應(yīng)用層的fanotify監(jiān)控進程(一個用于文件安全分析的listerner),然后在"/mnt/dst"目錄下進行一些文件操作,很快,系統(tǒng)就卡死了。
【排查】
這種本機掛載sshfs的方式理論上是沒有問題的(就像socket既可以用于網(wǎng)絡(luò)上不同主機間的通信,也可用于同一主機上不同進程間的通信),但終歸不是一種常規(guī)的使用方式。那用真正的“遠端”目錄來掛載下試試呢?
sshfs :/home/src /mnt/dst
接下來按照與先前同樣的步驟進行操作,問題沒有再出現(xiàn)。隱隱覺得是因為本機掛載的方式形成了一種循環(huán),不過直覺歸直覺,總得找出確鑿的證據(jù)不是。
回到出現(xiàn)問題的“本機掛載”的方式,為了進一步縮小范圍,嘗試了幾種最簡單的文件操作,發(fā)現(xiàn)即便是在"/mnt/dst"目錄下通過"touch"命令創(chuàng)建一個文件,也可以讓問題100%地穩(wěn)定復(fù)現(xiàn)。那我們就使用這個"touch"操作,結(jié)合一些調(diào)試手段,來探究下系統(tǒng)卡死的根本原因。
【分析】
首先使用最易用的"strace"工具來追蹤下"touch"命令的執(zhí)行,跟預(yù)想的差不多,是在進行 ?open()系統(tǒng)調(diào)用的時候卡住的:
strace只能追蹤system call,要想知道open()之后到底發(fā)生了什么,得深入到內(nèi)核里面去看看,這就要用到Linux自帶的ftrace工具了。把"fanotify"和"fsnotify"相關(guān)的函數(shù)加入待觀測的列表,抓取"touch"命令執(zhí)行后的ftrace結(jié)果:
由于"fsnotify"的若干函數(shù)打印輸出過多,對主干的分析形成了一定的干擾,于是將下列三個函數(shù)的記錄排除在外:
調(diào)整之后可以看到,此過程所涉及的進程主要有"touch", "sshfs", "sftp-se"(即sftp server)和"listener"。
經(jīng)過對ftrace所展現(xiàn)的交互流程的梳理,感覺離真相已經(jīng)很接近了,但還是缺少了一些關(guān)鍵信息,沒有形成一個完整的邏輯鏈。不過,還剩一個終極手段沒有用,那就是在系統(tǒng)卡死的時候強制產(chǎn)生coredump,然后用crash工具來分析(前提是你有這個內(nèi)核的debuginfo)。
一般強制觸發(fā)crash的方式是在終端輸入"echo c > /proc/sysrq-trigger",但在系統(tǒng)卡死的這種情景下,搶時間點完成這個輸入有一些困難,如果是在KVM的環(huán)境中,可以通過以下命令,導(dǎo)出虛擬機的內(nèi)存轉(zhuǎn)儲:
virsh dump ?--memory-only ?--format=kdump-zlib
有了coredump和debuginfo,就可以開始hack了。從源頭入手,先來看下"touch"進程的back trace。很明顯,它對文件的open操作被fanotify劫持了,但還沒有等到listener的處理結(jié)果:
那為什么listener沒有返回結(jié)果呢?繼續(xù)看下listener進程中負責(zé)處理fanotify的線程的棧信息:
作為監(jiān)控listener,它會去嘗試打開下這個文件,以判斷這個文件的內(nèi)容是否安全,由于該文件屬于FUSE文件系統(tǒng),所以需要將操作的請求上報給用戶態(tài)的daemon進程(即sshfs)。listener陷入等待之后,沒有收到sshfs的返回結(jié)果。
那到底是listener的fuse request沒有發(fā)出去呢,還是sshfs沒有做出respond呢?這可以通過查看該"fuse_req"的狀態(tài)來確定,要查看狀態(tài)的值就得知道它的內(nèi)存地址,而在x64體系中,為了提高效率,函數(shù)的前面幾個參數(shù)通常使用寄存器傳遞,這就給直接從棧信息來獲取參數(shù)的地址帶來了困難。
因此,只能迂回一下,借助它的下級函數(shù)中保存的寄存器值來尋找和"fuse_req"的關(guān)聯(lián)。這里,"rbx", "r12"到"r15"都是潛在的突破口:
瀏覽wait_answer_interruptible()函數(shù)的源代碼,其中有兩處對"fuse_req"指針的引用:
void wait_answer_interruptible(struct fuse_conn *fc, struct fuse_req *req)
{
if (signal_pending(current))return;spin_unlock(&fc->lock);wait_event_interruptible(req->waitq, req->state == FUSE_REQ_FINISHED);spin_lock(&fc->lock);
}
總結(jié)
以上是生活随笔為你收集整理的中fuse_一个Fanotify和FUSE配合使用导致的问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python开发的模型部署_使用Pyth
- 下一篇: arcgis中欧氏距离操作_ArcGIS