可怕!那些你看不到的程式

聲東發表於2018-05-18

排查不一樣的問題,往往會有不一樣的難點。有的問題難在重現,但只要能重現一次,那麼問題就會迎刃而解;有的問題難在除錯,比如排查一個刷卡機內的SD卡,通過資料線連線到電腦上出現檔案系統不可見的問題。這可能需要研究刷卡機嵌入式作業系統,和桌面作業系統的檔案系統,儲存系統,以及裝置管理三層的實現,才能最終定位到問題。

對於阿里雲技術支援的同學來說,還有另外一種比較特別的技術難題。這類問題的難度來源於客戶的堅持:當我們的客戶對一個我們自己看起來無關緊要的問題盤根問底的時候,這個問題就會變得非常棘手。今天就跟大家分享一例這樣的問題。

是誰動了我的Cpu資源!

首先我簡單解釋一下客戶所看到的問題。如下圖第三行,top統計Cpu總體使用情況,使用了八個指標。這八個指標分別是,使用者空間程式(us),核心空間程式(sy),高nice值的使用者空間程式(ni),空閒(id),空閒等待io(wa),中斷上半部(hi),中斷下半部(si),以及steal時間(st)。理論上來講這八個指標之和,應該是100%。這八個指標當中,id和wa是Cpu空閒時間的統計,這兩個值之和越小,說明Cpu越忙碌。客戶這臺伺服器的id與wa之和是0,所以這臺伺服器的Cpu使用率是100%,其中佔比最大的是ni。

除了第三行Cpu總體統計指標之外,top會對Cpu的使用率,從程式維度上進行統計,也就是CPU這一列。因為這臺伺服器是16核的,所以每個程式(多執行緒)的Cpu使用率可以超過100%,同時所有程式Cpu使用率之和不能超過上線1600%(平均到每個核是100%)。

這個問題的“見鬼”之處在於,雖然這個系統裡執行著787個程式,但這些程式使用Cpu之和,卻遠小於1600%這個值。

晴天霹靂:問題現場丟失

剛準備深入探究這個問題的時候,不幸的事情發生了。客戶這臺機器重啟了。重啟之後問題消失!雖然問題現場丟失了,但客戶的質疑沒有改變。客戶強烈要求我們提供這臺伺服器Cpu打滿的原因。

備註:很多時候,我們在遇到難以解釋的問題的時候,往往傾向於把問題歸結到和這個問題相關的“黑盒”的部分。這也是為什麼,很多客戶在遇到不容易解釋的現象的時候,會懷疑原因在虛擬化層,或在物理機層,有時候甚至會懷疑阿里雲的產品是不是“缺斤短兩”了。

nice!

作為技術支援工程師,在沒有重現環境的情況下,為了滿足客戶的需求,我這邊做的第一件事情是,搞清楚ni這個指標的計算方法,跟客戶溝通這個指標背後的理論知識,然後期望客戶能夠理解,這個指標跟物理機沒有任何關係,純粹是虛擬機器內部行為。

nice是什麼

在第一部分,我介紹Cpu八個統計指標的時候,提到了ni是高nice值的使用者空間程式的Cpu使用率。nice值是什麼呢,簡單來講,nice值代表著一個程式使用Cpu資源的優先程度。每個程式都會有一個與之對應的nice值,nice值越高,那麼這個程式使用Cpu的優先順序就越低,獲得的處理器的時間相比較而言就會越少。而ni這個指標,統計的是系統中,所有nice值大於0的使用者空間程式的Cpu的使用率。

一般情況下程式預設的nice值是0,而當有些程式需要更高的執行優先順序的時候,我們會減小這些程式的nice值。當然有一些並不需要在高優先順序執行的程式,例如我們跑編譯程式gcc,去編譯一個核心,這個操作預計會花幾個小時,那麼我們可以增加這個gcc程式的nice值。

linux會把真正的使用者模式Cpu使用率拆分成兩部分顯示,nice值大於0的顯示為ni,小於等於0的顯示為us。

自己動手跑高ni

這裡我們做一個簡單的測試去驗證上邊的理論。我們使用for語句寫一個簡單的死迴圈程式loop,然後用objdump看程式碼編譯之後的彙編程式。這段彙編非常簡單,前兩行準備堆疊指標;第三行初始化一個變數,這個變數位於堆疊上rpb-0x4這個位置;然後第四第五行重複遞增這個變數。

00000000004004ed <main>:
 4004ed: 55 push %rbp
 4004ee: 48 89 e5 mov %rsp,%rbp
 4004f1: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
 4004f8: 83 45 fc 01 addl $0x1,-0x4(%rbp)
 4004fc: eb fa jmp 4004f8 <main+0xb>
 4004fe: 66 90 xchg %ax,%ax

loop程式一旦被排程到一個Cpu上,那麼這個Cpu就會被打滿。如下兩張圖,左邊是nice值為0的情況,右邊是nice值為19的情況。程式nice值可以在圖下邊NI這一列看到。

下邊是Cpu使用率拆分到每個核上的情況。

不滿意的客戶

我跟客戶溝通ni這個指標背後的理論知識和我的結論:這個問題和物理機沒有什麼關係。對於我的結論,客戶是不接受的。客戶強調,在機器重啟之前,他檢查了系統裡所有程式的Cpu的使用情況,他非常確定沒有發現任何異常。雖然當時系統裡有一百多個java程式,但是這些java程式的Cpu使用率都非常低。

時間大法,好!

以前處理系統夯機問題的時候,偶爾會走投無路。想象一下,一個複雜的系統中,執行著上千甚至上萬的程式。而夯機則意味著,系統裡的這些程式,像一團亂麻一樣,糾纏在了一起。這個時候,只有從這些程式中整理出依賴關係,才能知道哪些程式是夯機問題的trouble maker,而哪些程式又是夯機問題的受害者。理清這些關係,大部分情況下,我們是靠理清資源的持有與等待關係。

可惜的是,這種分析方法並不是萬能的。系統為了節省管理成本,只會有選擇的維護其中某些資源的持有與等待關係。

在我們不能用這種方法分析問題的時候,另外一種方法就派上了用場。這種方法就是分析程式進入等待狀態的先後順序。我們稱這種方法叫“時間大法”。

挖礦程式

在因為無法重現問題而“走投無路”的時候,“時間大法”給了我希望。首先,在sa日誌裡我找到了Cpu達到100%的開始時間是4月29日凌晨6點40。接著,我翻遍了系統裡幾乎所有的檔案,發現有兩個配置檔案在6點39被建立。而存放這兩個配置檔案的目錄,則有兩個非常可疑的庫檔案libxmr-stak-c.a和libxmr-stak-backend.a。Google這兩個檔案,發現這是門羅幣挖礦程式使用的名字。

還是不滿意的客戶

當把上邊的發現同步給客戶的時候,客戶還是覺得證據不足。而且客戶再次強調,他當時看了所有系統裡執行的程式,如果有可疑的程式使用Cpu異常的話,他肯定早發現了。因為客戶的堅持,壓力再次回到了我們這一邊。

隱藏linux程式方法一二三

如果客戶所說的是真實情況的話,那麼有什麼方法可以隱藏linux程式,讓客戶不能從ps或top的輸出中,讀到程式資訊呢?比較常用的三種方法是:建立程式的時候,把pid設定成為0;直接修改ps和top程式碼;或者hook libc裡readdir和opendir等函式(因為ps和top的實現,直接使用了readdir和opendir等libc庫函式,來讀取/proc檔案及其子目錄)。

這個時候我突然想起自己之前曾經看到過的,在6點39被更改的另外一個檔案ld.so.preload。第一次檢查這個檔案的時候,看到這個檔案裡被寫了一條libjdk.so,想當然的以為這個檔案和java有關,所以忽略了這條資訊。

我知道事情的真相了!

這個時候,事情的全貌就顯現出來了。在6點39分,有人給ld.so.preload增加了一個庫檔案。從那以後,所有的程式,啟動的時候都會首先載入這個庫,然後再載入其他庫。這就產生一個效果,如果程式呼叫一個外部函式,這個函式的實現本來在其他庫檔案裡,但是這個預先載入的庫實現了同樣的函式,那麼動態連結會先使用預先載入的這個庫裡定義的這個函式。

記得上一次使用這個技巧的時候,還是多年前在寫opengl trace工具的時候。後來轉投微軟系,linux上這些技巧就淡忘了。基本上來說,使用ld.so.preload,我們可以實現filter類工具,在filter工具中實現過濾,追蹤,引數檢查等功能。當然為了保證程式正常執行,我們的同名過濾函式,最終還是會呼叫原來的函式。

驗證了一下,系統裡所有的程式,因為重啟,都載入了libjdk這個庫檔案到自己的地址空間裡。下圖是讀bash程式/proc/<pid>/maps內容的輸出。

libjdk的雕蟲小技

這個庫libjdk和java沒有什麼關係,他非常小,實現也非常簡單。以致於我們甚至可以通過讀彙編來理解它的行為。就如之前猜測的一樣,這個庫hook了readdir之類的函式,對讀取/proc資料夾的操作做了過濾,所以客戶在使用top或者ps命令的時候,得到的結果都是被過濾過的結果。這裡不會對libjdk彙編程式碼進行深入分析,但是提供一個strings輸出的這個庫檔案裡包含的串。從這些串中,我們也能對這個庫的行為猜個大概。

後記

回顧這個問題的處理過程,憑良心講,這個問題本來並不算是什麼疑難雜症。可能抓個core dump,分分鐘就能搞定。但兩件事情極大的增加了這個問題的排查難度,一個是問題環境丟失,一個是客戶的堅持。

當然如果不是問題環境丟失,那麼我也不會去嘗試其他的排查思路,如果不是客戶的堅持,我也不會做到把彙編程式碼都拿出來做證據的這種程度。客戶的高要求,不斷的敦促,是我們不斷提升服務能力的重要驅動力。

備註:以上問題是在特殊情況下使用的排查方法。關於隱藏的挖礦程式使用ni高的問題,如果有重現環境且現象類似,進一步排查,可以通過雲監控看到隱藏的程式。另外修改ld.so.preload檔案之後,就可以使用top和ps命令來進一步排查可疑程式。


相關文章