自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

紅帽Linux故障定位技術(shù)詳解與實(shí)例

系統(tǒng) Linux
在線故障定位就是在故障發(fā)生時(shí), 故障所處的操作系統(tǒng)環(huán)境仍然可以訪問(wèn),故障處理人員可通過(guò)console, ssh等方式登錄到操作系統(tǒng)上,在shell上執(zhí)行各種操作命令或測(cè)試程序的方式對(duì)故障環(huán)境進(jìn)行觀察,分析,測(cè)試,以定位出故障發(fā)生的原因。

紅帽Linux故障定位技術(shù)詳解與實(shí)例是本文要介紹的內(nèi)容,主要是來(lái)了解并學(xué)習(xí)紅帽linux故障定位技術(shù)的學(xué)習(xí),故障定位技術(shù)分為在線故障定位和離線故障定位,一起來(lái)看詳解。

1、故障定位(Debugging)場(chǎng)景分類

為便于描述問(wèn)題,將Linux上各種軟件故障定位的情形分成兩類

(1)在線故障故障定位

在線故障定位(online-debugging)就是在故障發(fā)生時(shí), 故障所處的操作系統(tǒng)環(huán)境仍然可以訪問(wèn),故障處理人員可通過(guò)console, ssh等方式登錄到操作系統(tǒng)上,在shell上執(zhí)行各種操作命令或測(cè)試程序的方式對(duì)故障環(huán)境進(jìn)行觀察,分析,測(cè)試,以定位出故障發(fā)生的原因

(2)離線故障定位

離線故障定位(offline-debugging)就是在故障發(fā)生時(shí),故障所處的操作系統(tǒng)環(huán)境已經(jīng)無(wú)法正常訪問(wèn),但故障發(fā)生時(shí)系統(tǒng)的全部或部分狀態(tài)已經(jīng)被系統(tǒng)本身所固有或事先設(shè)定的方式收集起來(lái),故障處理人員可通過(guò)對(duì)收集到的故障定位狀態(tài)信息進(jìn)行分析,定位出故障發(fā)生的原因

2、應(yīng)用進(jìn)程故障情形及處理

應(yīng)用進(jìn)程的故障一般不會(huì)影響操作系統(tǒng)運(yùn)行環(huán)境的正常使用(如果應(yīng)用代碼的bug導(dǎo)致了內(nèi)核的crash或hang,則屬于內(nèi)核存在漏洞),所以可采用在線故障定位的方法,靈活的進(jìn)行分析. 應(yīng)用代碼故障的情形有如下幾種:

(1)進(jìn)程異常終止

很多用戶認(rèn)為進(jìn)程異常終止情況無(wú)從分析,但實(shí)際上進(jìn)程異常終止情況都是有跡可尋的. 所有的進(jìn)程異常終止行為,都是通過(guò)內(nèi)核發(fā)信號(hào)給特定進(jìn)程或進(jìn)程組實(shí)現(xiàn)的. 可分成幾個(gè)類型進(jìn)行描述:

- SIGKILL. SIGKILL最特殊,因?yàn)樵撔盘?hào)不可被捕獲,同時(shí)SIGKILL不會(huì)導(dǎo)致被終止的進(jìn)程產(chǎn)生core文件, 但如果真正的是由內(nèi)核中發(fā)出的SIGKILL,則內(nèi)核一定會(huì)在dmesg中記錄下信息. 另外在內(nèi)核中使用SIGKILL的地方***,如oom_kill_process()中, 所以通過(guò)dmesg記錄并且分析內(nèi)核中使用SIGKILL的代碼,并不難分析原因

- SIGQUIT, SIGILL, SIGABRT, SIGBUS, SIGFPE, SIGSEGV. 這幾個(gè)信號(hào)在保留情況下會(huì)終止進(jìn)程并會(huì)產(chǎn)生core文件, 用戶根據(jù)core中的stack trace信息,能直接定位出導(dǎo)致終止信號(hào)的代碼位置. 另外, SIGQUIT,SIGABRT一般是由用戶代碼自己使用的,好的代碼一般會(huì)記錄日志. SIGILL, SIGBUS, SIGFPE, SIGSEGV, 都是由內(nèi)核中產(chǎn)生的,搜索內(nèi)核源碼,不難列出內(nèi)核中使用這幾個(gè)信號(hào)的地方, 如SIGILL 是非法指令,可能是浮點(diǎn)運(yùn)算產(chǎn)生的代碼被corrupted或文本區(qū)域的物理內(nèi)存corruption; SIGBUS多由MCE故障定位導(dǎo)致; SIGSEGV多由應(yīng)用代碼的指針變量被corrupted導(dǎo)致. 對(duì)于應(yīng)用的heap或stack的內(nèi)存被corrupted, 可用valgrind工具對(duì)應(yīng)用進(jìn)行profile, 通常能直接發(fā)現(xiàn)導(dǎo)致corruption的代碼

- SIGINT, SIGPIPE, SIGALRM, SIGTERM. 這幾個(gè)信號(hào)在保留情況下終止進(jìn)程但不會(huì)產(chǎn)生core文件. 對(duì)這幾個(gè)信號(hào),建議用戶一定要定義一個(gè)handler,以記錄產(chǎn)生問(wèn)題的上下文. 比較容易忽略的是SIGPIPE, 很多用戶程序在使用select()或poll()時(shí)只監(jiān)聽(tīng)read/write描述符,不監(jiān)聽(tīng)exception描述符,在對(duì)方TCP已經(jīng)關(guān)閉的情況下,仍然向socket中寫(xiě)入,導(dǎo)致SIGPIPE.

- 對(duì)于惡意的代嗎產(chǎn)生的進(jìn)程終止行為,如合作的一些進(jìn)程中,A向B發(fā)SIGKILL, 而沒(méi)做日志記錄,或者B直接判斷某條件而調(diào)用exit(), 也沒(méi)有做日志記錄.在應(yīng)用代碼量很大的情況下,通過(guò)分析代碼故障定位這種情形也許很難. SystemTap提供了解決這個(gè)問(wèn)題的一個(gè)比較好的方法,就是寫(xiě)用戶層的probes, 追蹤進(jìn)程對(duì)signal(), exit() 等系統(tǒng)調(diào)用的使用

(2)進(jìn)程阻塞,應(yīng)用無(wú)法正常推進(jìn)

這種情況,對(duì)于單個(gè)被阻塞的進(jìn)程而言,屬于正常狀態(tài), 但對(duì)于包含多個(gè)進(jìn)程的應(yīng)用整體而言,屬于異常. 應(yīng)用無(wú)法推進(jìn),說(shuō)明其中某一個(gè)進(jìn)程推進(jìn)的因素出現(xiàn)了問(wèn)題,導(dǎo)致其他依賴于它的進(jìn)程也要等待. 分析這種情形需要分析清楚進(jìn)程或事件之間的依賴關(guān)系,及數(shù)據(jù)的處理流. 首先要用gdb -p 的back trace功能查出各進(jìn)程阻塞的執(zhí)行路徑, 以確定每個(gè)進(jìn)程所處在的狀態(tài)機(jī)的位置.

通常而言,如果只考慮各個(gè)進(jìn)程的狀態(tài),則進(jìn)程之間可能形成了一種互相依賴的環(huán)形關(guān)系,如(P1發(fā)請(qǐng)求=>P2處理=>P2發(fā)反應(yīng)=>P1再請(qǐng)求=>P2處理=>P2再發(fā)反應(yīng)), 但應(yīng)用對(duì)workload, 一般是按一個(gè)個(gè)的transaction 或 session的方式進(jìn)行處理的,每個(gè)transaction都有起點(diǎn)和終點(diǎn), 我們需要用strace, tcpdump 等工具以及應(yīng)用的執(zhí)行日志進(jìn)行觀察,分析出當(dāng)前正被處理的transaction所被阻滯的位置,從而找出全部狀態(tài)機(jī)被阻塞的原因. 導(dǎo)致這種狀態(tài)機(jī)停止運(yùn)轉(zhuǎn)的原因有多個(gè):如和應(yīng)用通信的遠(yuǎn)端出現(xiàn)了問(wèn)題,后端數(shù)據(jù)庫(kù)/目錄等出現(xiàn)了問(wèn)題,應(yīng)用的某個(gè)進(jìn)程或線程處于非正常的blocking位置或直接終止,不再正常工作.

(3)用戶進(jìn)程形成死鎖

用戶進(jìn)程形成死鎖,如果沒(méi)有內(nèi)存上的故障定位,則完全是應(yīng)用自身的邏輯問(wèn)題. 死鎖的進(jìn)程或線程之間由于鎖的互相占有形成了環(huán)路。 這種情況發(fā)生時(shí),用gdb -p 的back trace的功能能直接確定死鎖的進(jìn)程全部阻塞在futex()等和鎖相關(guān)的系統(tǒng)調(diào)用上, 這些調(diào)用futex()的路徑可能是mutex, semaphore, conditional variable 等鎖函數(shù). 通過(guò)分析call trace 的代碼,能直接確定各進(jìn)程在執(zhí)行到該位置時(shí),可能已經(jīng)持有的全部鎖, 根據(jù)這個(gè)修改程序的代碼,消除死鎖環(huán)路,就可解決問(wèn)題.

注意,內(nèi)存故障也可導(dǎo)致假的死鎖的,如物理內(nèi)存故障可直接導(dǎo)致鎖變量的值為-1, 所以使用該鎖的進(jìn)程都會(huì)阻塞. 如果是代碼的bug導(dǎo)致的內(nèi)存corruption,可用valgrind工具檢查程序來(lái)發(fā)現(xiàn). 但如果是物理內(nèi)存的故障定位導(dǎo)致的corruption, 則需要硬件的支持,對(duì)于高端的PC, 如MCE功能的機(jī)器,當(dāng)物理內(nèi)存故障定位時(shí)能直接產(chǎn)生異?;驁?bào)告, 但對(duì)于低端PC服務(wù)器,除了運(yùn)行memtest工具進(jìn)行檢測(cè)外,沒(méi)有其他方法

(4)進(jìn)程長(zhǎng)期處于 'D' (UnInterruptible)狀態(tài)沒(méi)法退出

這種多是由內(nèi)核中的故障引起的. 內(nèi)核在很多執(zhí)行路徑中會(huì)將進(jìn)程至于'D'的狀態(tài),以確保關(guān)鍵的執(zhí)行路徑不被外部的信號(hào)中斷, 導(dǎo)致不必要的內(nèi)核中數(shù)據(jù)結(jié)構(gòu)狀態(tài)的不一致性. 但一般而言,進(jìn)程處于 'D' 狀態(tài)的時(shí)間不會(huì)太久, 因?yàn)闋顟B(tài)結(jié)束的條件(如timer觸發(fā),

IO操作完成等)很快會(huì)將進(jìn)程喚醒. 當(dāng)進(jìn)程長(zhǎng)期處于 'D',關(guān)鍵是要找出其阻塞的代碼位置, 用 sysrq 的t鍵功能可直接打印出系統(tǒng)中全部睡眠進(jìn)程的內(nèi)核執(zhí)行堆棧,如 echo 't' > /proc/sysrq-trigger, 其中包括出現(xiàn) 'D'狀態(tài)的進(jìn)程的內(nèi)核態(tài)堆棧. 找出代碼位置后,一般可直接分析出 'D' 狀態(tài)不能退出的原因, 如IO read操作因硬件或nfs故障而不能完成.

有可能導(dǎo)致 'D' 狀態(tài)的原因比較復(fù)雜,如‘D’的退出依賴于某變量的值,而該變量的值因某種原因被***corrupted掉了.

#p#

3、內(nèi)核故障情形及處理

(1)內(nèi)核panic

panic是內(nèi)核最直接的故障定位報(bào)告,發(fā)生panic時(shí),內(nèi)核已經(jīng)認(rèn)為故障定位已經(jīng)導(dǎo)致操作系統(tǒng)不再具備正常運(yùn)行的條件了. 當(dāng)發(fā)生panic時(shí),Linux會(huì)將所有CPU的中斷和進(jìn)程調(diào)度功能都關(guān)掉,所以這時(shí)系統(tǒng)是沒(méi)有任何反應(yīng)的,如果用戶啟動(dòng)的是圖形界面,則在屏幕上也看不到任何關(guān)于panic的信息.

我們通常遇到的,機(jī)器沒(méi)反應(yīng),ping不通的情況,絕大部分都是panic. Panic發(fā)生時(shí),內(nèi)核直接在console上打印導(dǎo)致panic的代碼位置的調(diào)用堆棧, 傳統(tǒng)的用戶用串口連接到機(jī)器上來(lái)收集console上的打印信息, 但串口這種方式,顯然用起來(lái)不方便, 現(xiàn)在的Linux, 如RHEL5,RHEL6, 都采用kdump的方法來(lái)收集panic時(shí)的信息. 在配置好kdump的情況下,panic時(shí)系統(tǒng)會(huì)用kexec加載并切換到一個(gè)新的內(nèi)核上(放置在預(yù)先分配的內(nèi)存位置),并用磁盤(pán)或網(wǎng)絡(luò)等將系統(tǒng)的全部或部分內(nèi)存數(shù)據(jù)保存起來(lái).

用kdump收集到panic的數(shù)據(jù)后,用戶用crash工具就能直接查看導(dǎo)致panic的代碼路徑.

panic一般是很直觀的,panic的堆棧信息能直接反映出導(dǎo)致bug的原因,如MCE故障,NMI故障, 數(shù)據(jù)結(jié)構(gòu)分配失敗等. 但有時(shí)panic是因?yàn)閮?nèi)核主動(dòng)發(fā)現(xiàn)了關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)不一致性,這種不一致性是什么時(shí)候,什么代碼導(dǎo)致的,并不清楚,可能還需要多次測(cè)試用SystemTap這樣的工具進(jìn)行捕捉

(2)多處理機(jī)環(huán)境內(nèi)核執(zhí)行路徑產(chǎn)生的死鎖

內(nèi)核死鎖和panic不一樣,產(chǎn)生死鎖時(shí),內(nèi)核并不主動(dòng)的使自己處于掛起狀態(tài). 但內(nèi)核死鎖發(fā)生時(shí),兩個(gè)以上的CPU的執(zhí)行路徑在內(nèi)核態(tài)不能推進(jìn)了,處于互相阻塞狀態(tài), 而且是100%的占用CPU(用的spin-lock),直接或間接的導(dǎo)致全部CPU上的進(jìn)程無(wú)法調(diào)度. 內(nèi)核死鎖又分兩種情況:

- 涉及到中斷上下文的死鎖. 這種情況的死鎖,最少一個(gè)CPU上的中斷被屏蔽了.系統(tǒng)可能沒(méi)法響應(yīng)ping請(qǐng)求. 由于有一個(gè)CPU已經(jīng)沒(méi)法響應(yīng)中斷,其上的local APIC定時(shí)中斷沒(méi)法工作,可以用NMI Watchdog的方法來(lái)檢測(cè)出來(lái)(檢查local APIC handler維護(hù)的計(jì)數(shù)器變量),NMI Watchdog可以在其處理程序中調(diào)用panic(), 用戶就可以用kdump收集內(nèi)存信息,從而分析各死鎖CPU上的調(diào)用堆棧,查處導(dǎo)致死鎖的邏輯原因.

- 不涉及中斷上下文的死鎖. 這種情況的死鎖,各CPU上的中斷都是正常的,系統(tǒng)能對(duì)ping請(qǐng)求作出反應(yīng),這時(shí)NMI Watchdog無(wú)法被觸發(fā). 在 2.6.16之前的內(nèi)核中,并沒(méi)有一種很好的方法來(lái)處理這種情形. 在RHEL5, RHEL6 內(nèi)核中, 每個(gè)CPU上提供了一個(gè)watchdog內(nèi)核線程,在死鎖出現(xiàn)的情況下,死鎖CPU上的watchdog內(nèi)核線程沒(méi)法被調(diào)度(即使它是***優(yōu)先級(jí)的實(shí)時(shí)進(jìn)程),它就沒(méi)法update相應(yīng)的counter變量,各CPU的NMI Watchdog中斷會(huì)周期性的檢查其CPU對(duì)應(yīng)的counter, 發(fā)現(xiàn)沒(méi)有updated, 會(huì)調(diào)用panic(),用戶就可用kdump收集內(nèi)存信息,分析各死鎖CPU上的調(diào)用堆棧,查處導(dǎo)致死鎖的邏輯原因.

(3)內(nèi)核的oops或warning

oops 和warning和panic類似的地方是,他們都是因內(nèi)核發(fā)現(xiàn)了不一致而主動(dòng)報(bào)告的異常. 但oops和warning導(dǎo)致的問(wèn)題嚴(yán)重程度要比panic輕很多,以致于內(nèi)核處理該問(wèn)題時(shí)不需要使系統(tǒng)掛起. 產(chǎn)生oops和warning, 內(nèi)核通常已經(jīng)在dmesg中記錄了相當(dāng)?shù)男畔?,特別是oops, 至少會(huì)打印出現(xiàn)故障的地方的call trace. Oops也可轉(zhuǎn)換成panic/kdump來(lái)進(jìn)行offline-debugging, 只要將/proc/sys/kernel下的panic_on_oops變量設(shè)置為1就行了.

產(chǎn)生oops和warning的直接原因有很多,如內(nèi)核中的segment fault, 或內(nèi)核發(fā)現(xiàn)的某數(shù)據(jù)結(jié)構(gòu)的counter值不對(duì), 而segment fault 和counter值的變化還有更深層次的原因,通常并不能從內(nèi)核dmesg的信息中看出來(lái),解決這種問(wèn)題的是要用SystemTap進(jìn)行probe, 如發(fā)現(xiàn)某counter的值不對(duì),就用SystemTap做一個(gè)probe來(lái)記錄所有代碼對(duì)該counter的訪問(wèn), 然后再進(jìn)行分析.

定位oops和warning會(huì)比定位應(yīng)用程序的內(nèi)存訪問(wèn)故障定位困難很多,因?yàn)樵趦?nèi)核并不能象用valgrind去trace應(yīng)用程序一樣跟蹤數(shù)據(jù)結(jié)構(gòu)的分配和使用情況.

2、其他(硬件相關(guān))故障

機(jī)器自動(dòng)重啟是一種常見(jiàn)的故障情形,一般是由硬件如物理內(nèi)存故障引起的,軟件的故障只會(huì)導(dǎo)致死鎖或panic, 內(nèi)核中幾乎沒(méi)有代碼在發(fā)現(xiàn)問(wèn)題的情況下去reboot機(jī)器. 在/proc/sys/kernel目錄下有個(gè)參數(shù)“panic”, 其值如果設(shè)置為非0,則在panic發(fā)生若干秒后,內(nèi)核會(huì)重啟機(jī)器. 現(xiàn)在高端的PC服務(wù)器,都在努力用軟件來(lái)處理物理內(nèi)存故障,如MCA的 “HWPoison”方法會(huì)將故障的物理頁(yè)隔離起來(lái),Kill掉故障頁(yè)所在的進(jìn)程就可以了,RHEL6現(xiàn)在已經(jīng)支持 “HWPoison”. 那些不具備MCA能力的機(jī)器,物理內(nèi)存故障時(shí),不會(huì)產(chǎn)生MCE異常,直接由硬件機(jī)制reboot機(jī)器

4、RHEL6 上的Debugging技術(shù)介紹

(1)Kdump故障定位收集和crash分析

kdump就是用來(lái)在內(nèi)核panic的情況下收集系統(tǒng)內(nèi)存信息的, 用戶也可在online情況下用sysrq的'c'鍵觸發(fā). Kdump 采用沒(méi)有污染的內(nèi)核來(lái)執(zhí)行dump工作,所以其比以前的diskdump, lkcd方法更可靠. 使用kdump,用戶可選擇將數(shù)據(jù)dump到本地盤(pán)或網(wǎng)絡(luò)上,也可通過(guò)定義makedumpfile的參數(shù)過(guò)濾要收集的內(nèi)存信息,已減少kdump所需要的停機(jī)時(shí)間

Crash就是對(duì)kdump的信息進(jìn)行分析的工具.其實(shí)際就是gdb的一個(gè)wrapper. 使用crash時(shí),***安裝kernel-debuginfo包,這樣能解析kdump收集的內(nèi)核數(shù)據(jù)的符號(hào)信息. 用crash來(lái)定位問(wèn)題的能力,完全取決于用戶對(duì)內(nèi)核代碼的理解和分析能力

參考 “#>man kdump.conf”, “#>man crash”, “#>man makedumpfile”學(xué)習(xí)怎樣使用kdump和crash. 訪問(wèn) http://ftp.redhat.com可下載debuginfo文件

(2)用systemTap定位bug

systemtap 屬于probe類的定位工具,它能對(duì)內(nèi)核或用戶代碼的指定位置進(jìn)行probe, 當(dāng)執(zhí)行到指定位置或訪問(wèn)指定位置的數(shù)據(jù)時(shí),用戶定義的probe函數(shù)自動(dòng)執(zhí)行,可打印出該位置的調(diào)用堆棧,參數(shù)值,變量值等信息. systemtap選擇進(jìn)行probe的位置很靈活,這是systemtap的強(qiáng)大功能所在. Systemtap的probe點(diǎn)可包括如下幾個(gè)方面:

- 內(nèi)核中全部系統(tǒng)調(diào)用,內(nèi)核及模塊中全部函數(shù)的入口或出口點(diǎn)

- 自定義的定時(shí)器probe點(diǎn)

- 內(nèi)核中任意指定的代碼或數(shù)據(jù)訪問(wèn)位置

- 特定用戶進(jìn)程中任意制定的代碼或數(shù)據(jù)訪問(wèn)位置

- 各個(gè)功能子系統(tǒng)預(yù)先設(shè)置的若干probe點(diǎn),如tcp,udp,nfs,signal各子系統(tǒng)都預(yù)先設(shè)置了很多探測(cè)點(diǎn)

systemTap的腳本用stap腳本語(yǔ)言來(lái)編寫(xiě),腳本代碼中調(diào)用stap提供的API進(jìn)行統(tǒng)計(jì),打印數(shù)據(jù)等工作,關(guān)于stap語(yǔ)言提供的API函數(shù),參考 “#> man stapfuncs”. 關(guān)于systemTap的功能和使用可參考 “#> man stap”, “#> man stapprobes”

(3)ftrace

ftrace 是linux內(nèi)核中利用tracepoints基礎(chǔ)設(shè)施實(shí)現(xiàn)的事件追蹤機(jī)制,它的作用在于能比較清楚的給出在一定時(shí)間內(nèi)系統(tǒng)或進(jìn)程所執(zhí)行的活動(dòng),如函數(shù)調(diào)用路徑,進(jìn)程切換流等. Ftrace可用于觀察系統(tǒng)各部分的latency,以便進(jìn)行實(shí)時(shí)應(yīng)用的優(yōu)化; 它也可以通過(guò)記錄一段時(shí)間內(nèi)的內(nèi)核活動(dòng)來(lái)幫助故障定位. 如用以下方法可trace某個(gè)進(jìn)程在一端時(shí)間的函數(shù)調(diào)用情況

  1. #> echo “function” > /sys/kernel/debug/tracing/current_tracer  
  2. #> echo “xxx” > /sys/kernel/debug/tracing/set_ftrace_pid  
  3. #> echo 1 > /sys/kernel/debug/tracing/tracing_enabled 

除tracing函數(shù)調(diào)用外,ftrace還可tracing系統(tǒng)的進(jìn)程切換,喚醒,塊設(shè)備訪問(wèn),內(nèi)核數(shù)據(jù)結(jié)構(gòu)分配等活動(dòng). 注意,tracing和profile是不同的,tracing記錄的是一段時(shí)間內(nèi)的全部活動(dòng),而不是統(tǒng)計(jì)信息,用戶可以通過(guò)/sys/kernel/debug/tracing下的buffer_size_kb設(shè)置緩沖區(qū)的大小, 以記錄更長(zhǎng)時(shí)間的數(shù)據(jù).

關(guān)于ftrace的具體使用可參考內(nèi)核源碼Documenation/trace下的內(nèi)容

(4)oprofile 和 perf

oprofile和perf都是對(duì)系統(tǒng)進(jìn)行profile(抽樣,統(tǒng)計(jì))的工具,它們主要用來(lái)解決系統(tǒng)和應(yīng)用的性能問(wèn)題. perf功能更強(qiáng)大,更全面,同時(shí)perf的用戶空間工具和內(nèi)核源碼一起維護(hù)和發(fā)布,讓用戶能及時(shí)的享受perf內(nèi)核新增加的特征. Perf 是在RHEL6中才有,RHEL5中沒(méi)有Perf. Oprofile和perf 都使用現(xiàn)代CPU中具有的硬件計(jì)數(shù)器進(jìn)行統(tǒng)計(jì)工作,但perf還可以使用內(nèi)核中定義的 “software counter”及 “trace points”, 所以能做更多的工作. Oprofile的抽樣工作利用 CPU的NMI中斷來(lái)進(jìn)行,而perf既可以利用NMI中斷也可利用硬件計(jì)數(shù)器提供的周期性中斷. 用戶能很容易用perf來(lái)oprofile一個(gè)進(jìn)程或系統(tǒng)的執(zhí)行時(shí)間分布,如

  1. #> perf top -f 1000 -p  

還可以利用系統(tǒng)定義的 “software counter”和各子系統(tǒng)的 “trace points” 對(duì)子系統(tǒng)進(jìn)行分析, 如

  1. #>perf stat -a -e kmem:mm_page_alloc -e kmem:mm_page_free_direct -e kmem:mm_pagevec_free sleep 6 

能統(tǒng)計(jì)6秒內(nèi)kmem子系統(tǒng)的活動(dòng) (這一點(diǎn)實(shí)際是利用ftrace提供的tracepoints來(lái)實(shí)現(xiàn))

我認(rèn)為有了perf, 用戶就沒(méi)必要使用oprofile了

#p#

5、用kdump工具內(nèi)核故障定位實(shí)例

A) 部署Kdump

部署 kdump 收集故障信息的步驟如下:

(1)設(shè)置好相關(guān)的內(nèi)核啟動(dòng)參數(shù)

在 /boot/grub/menu.lst 中加入如下內(nèi)容

  1. crashkernel=128M@16M nmi_watchdog=1 

其中crashkernel參數(shù)是用來(lái)為kdump的內(nèi)核預(yù)留內(nèi)存的; nmi_watchdog=1 是用來(lái)激活NMI中斷的, 我們?cè)谖创_定故障是否關(guān)閉了中斷的情況下, 需要部署NMI watchdog才能確保觸發(fā)panic. 重啟系統(tǒng)確保設(shè)置生效

(2)設(shè)置好相關(guān)的sysctl內(nèi)核參數(shù)

在/etc/sysctl.conf 中***加入一行

  1. kernel.softlookup_panic = 1  

該設(shè)置確保softlock發(fā)生時(shí)會(huì)調(diào)用panic, 從而觸發(fā)kdump行為執(zhí)行 #>sysctl -p 確保設(shè)置生效

(3)配置 /etc/kdump.conf

在 /etc/kdump.conf 中加入如下幾行內(nèi)容

  1. ext3 /dev/sdb1  
  2. core-collector makedumpfile -c –message-level 7 -d 31 -i /mnt/vmcoreinfo  
  3. path /var/crash  
  4. default reboot 

其中 /dev/sdb1 是用于放置dumpfile 的文件系統(tǒng), dumpfile 文件放置在/var/crash下, 要事先在/dev/sdb1分區(qū)下創(chuàng)建/var/crash 目錄. “-d 31”指定對(duì)dump內(nèi)容的過(guò)濾級(jí)別,這參數(shù)對(duì)于dump分區(qū)放不下全部?jī)?nèi)存內(nèi)容或用戶不想讓dumping中斷業(yè)務(wù)太長(zhǎng)時(shí)間時(shí)很重要. vmcoreinfo 文件放置在 /dev/sdb1 分區(qū)的 / 目錄下, 需要使用如下命令產(chǎn)生:

#>makedumpfile -g //vmcoreinfo -x /usr/lib/debug/lib/modules/2.6.18-128.el5.x86_64/vmlinux

“vmlinux” 文件是由kernel-debuginfo 包提供的,在運(yùn)行makedumpfile 之前需要安裝相應(yīng)內(nèi)核的 kernel-debuginfo 和 kernel-debuginfo-common 兩個(gè)包,該兩個(gè)包需從 http://ftp.redhat.com 下載. “default reboot” 用來(lái)告訴kdump, 收集完dump信息后重啟系統(tǒng)

(4)激活kdump

運(yùn)行 #>service kdump start 命令,你會(huì)看到,在成功完成的情況下會(huì)在/boot/目錄下生成一個(gè)initrd-2.6.18-128.el5.x86_64kdump.img 文件,該文件就是kdump加載的內(nèi)核的 initrd文件,收集dump信息的工作就是在該initrd的啟動(dòng)環(huán)境下進(jìn)行的. 查看/etc/init.d/kdump腳本的代碼,你可看到其中會(huì)調(diào)用mkdumprd命令創(chuàng)建用于dump的initrd文件

1、測(cè)試Kdump部署的有效性

為了測(cè)試kdump部署的有效性,本人寫(xiě)了如下一個(gè)內(nèi)核模塊,通過(guò)insmod 加載該內(nèi)核模塊, 就能產(chǎn)生一個(gè)內(nèi)核線程,在10秒左右后,占據(jù)100%的CPU,在20秒左右后觸發(fā)kdump. 系統(tǒng)重啟后,檢查/oracle分區(qū)/var/crash 目錄下的內(nèi)容,就能確認(rèn)vmcore文件是否生成.

  1. Zqfthread.c #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7.  
  8. MODULE_AUTHOR("frzhang@redhat.com");   
  9. MODULE_DESCRIPTION("A module to test ....");   
  10. MODULE_LICENSE("GPL");   
  11.  
  12. static struct task_struct *zqf_thread;   
  13. static int zqfd_thread(void *data);   
  14.  
  15. static int zqfd_thread(void *data)   
  16. {   
  17. int i=0;   
  18.  
  19. while (!kthread_should_stop()) {   
  20. i++;   
  21. if ( i < 10 ) {   
  22. msleep_interruptible(1000);   
  23. printk("%d seconds\n", i);   
  24. }   
  25. if ( i == 1000 ) // Running in the kernel   
  26. i = 11 ;   
  27. }   
  28. return 0;   
  29. }   
  30. static int __init zqfinit(void)   
  31. {   
  32. struct task_struct *p;   
  33.  
  34. p = kthread_create(zqfd_thread, NULL,"%s","zqfd");   
  35.  
  36. if ( p ) {   
  37. zqf_thread = p;   
  38. wake_up_process(zqf_thread); // actually start it up   
  39. return(0);   
  40. }   
  41.  
  42. return(-1);   
  43. }   
  44.  
  45. static void __exit zqffini(void)   
  46. {   
  47. kthread_stop(zqf_thread);   
  48. }   
  49.  
  50. module_init(zqfinit);   
  51. module_exit(zqffini)  
  52.  
  53. Makefile obj-m += zqfthread.o  
  54. Making #> make -C /usr/src/kernels/2.6.32-71.el6.x86_64/ M=`pwd` modules 

2、用crash 工具分析vmcore 文件

用crash 命令分析vmcore 的命令行格式如下所示. 用crash打開(kāi)vmcore后,主要是用dmesg及 bt 命令打印出問(wèn)題的執(zhí)行路徑的call trace, 用dis 反匯編出代碼,最終確認(rèn)call trace對(duì)應(yīng)的C源碼中的位置,再進(jìn)行邏輯分析.

  1. #>crash /usr/lib/debug/lib/modules/2.6.18-128.el5.x86_64/vmlinux /boot/System.map-2.6.18-128.el5.x86_64 ./vmcore 

#p#

6、使用kprobe來(lái)觀察內(nèi)核函數(shù)的執(zhí)行實(shí)例

kprobe是SystemTap對(duì)內(nèi)核函數(shù)進(jìn)行probing的功能在內(nèi)核中的實(shí)現(xiàn),由于內(nèi)核中提供了正式的API來(lái)使用kprobe,所以對(duì)很多內(nèi)核程序員來(lái)說(shuō),也許直接使用kprobe比使用SystemTap更方便. 內(nèi)核中提供了三種類型的kprobe處理函數(shù),分別是jprobe, kprobe, kretprobe, 下面的代碼用這三個(gè)probe觀察在TCP/IP的arp_process函數(shù)執(zhí)行中對(duì)ip_route_input()調(diào)用的返回結(jié)果.這個(gè)代碼還展示了在同一個(gè)函數(shù)probe的Entry handler和Ret handler之間共享參數(shù)的方法. 代碼如下:

 

  1. arp_probe.c /*  
  2. * arp_probe.c, by Qianfeng Zhang (frzhang@redhat.com)  
  3. */  
  4.  
  5. #include   
  6. #include   
  7. #include   
  8. #include   
  9. #include   
  10. #include   
  11. #include   
  12. #include   
  13.  
  14. MODULE_AUTHOR("frzhang@redhat.com");  
  15. MODULE_DESCRIPTION("A module to track the call results of ip_route_input() inside arp_process using jprobe and kretprobe");  
  16. MODULE_LICENSE("GPL");  
  17.  
  18. static int j_arp_process(struct sk_buff *skb)  
  19. {  
  20. struct net_device *dev = skb->dev;  
  21. struct in_device *in_dev;  
  22. int no_addr, rpf;  
  23.  
  24. in_dev = in_dev_get(dev);  
  25. no_addr = ( in_dev->ifa_list == NULL );  
  26. rpf = IN_DEV_RPFILTER(in_dev);  
  27. in_dev_put(in_dev);  
  28. printk("\narp_process() is called with interface device %s, in_dev(no_addr=%d,rpf=%d) \n", dev->name, no_addr, rpf);  
  29. jprobe_return();  
  30. return(0);  
  31. };  
  32.  
  33. static int j_fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,  
  34. struct net_device *dev, __be32 *spec_dst, u32 *itag, u32 mark)  
  35.  
  36. {  
  37. printk("fib_validate_source() is called with dst=0x%x, oif=%d \n", dst, oif);  
  38. jprobe_return();  
  39. return(0);  
  40. };  
  41.  
  42. static struct jprobe my_jp1 = {  
  43. .entry = j_arp_process,  
  44. .kp.symbol_name = "arp_process" 
  45. };  
  46.  
  47. static struct jprobe my_jp2 = {  
  48. .entry = j_fib_validate_source,  
  49. .kp.symbol_name = "fib_validate_source" 
  50. };  
  51.  
  52. static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)  
  53. {  
  54. printk("Calling: %s()\n", ri->rp->kp.symbol_name);  
  55. return(0);  
  56. };  
  57.  
  58. static int return_handler(struct kretprobe_instance *ri, struct pt_regs *regs)  
  59. {  
  60. int eax;  
  61.  
  62. eax = regs->ax & 0xffff ;  
  63. printk("Returning: %s() with a return value: 0x%lx(64bit) 0x%x(32bit)\n", ri->rp->kp.symbol_name, regs->ax, eax);  
  64.  
  65. return(0);  
  66. };  
  67.  
  68. static int fib_lookup_entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)  
  69. {  
  70. struct fib_result *resp;  
  71.  
  72. resp = (struct fib_result *) regs->dx;  
  73. printk("Calling: %s()\n", ri->rp->kp.symbol_name);  
  74. *((struct fib_result **)ri->data) = resp;  
  75.  
  76. return(0);  
  77. };  
  78.  
  79. static int fib_lookup_return_handler(struct kretprobe_instance *ri, struct pt_regs *regs)  
  80. {  
  81. struct fib_result *resp;  
  82. int eax;  
  83.  
  84. eax = regs->ax & 0xffff ;  
  85. resp = *((struct fib_result **) ri->data);  
  86. printk("Returning: fib_lookup() with a return value: 0x%lx(64bit) 0x%x(32bit), result->type: %d\n", regs->ax, eax, resp->type);  
  87.  
  88. return(0);  
  89. }  
  90.  
  91. static struct kretprobe my_rp1 = {  
  92. .handler = return_handler,  
  93. .entry_handler = entry_handler,  
  94. .kp.symbol_name = "ip_route_input_slow" 
  95. };  
  96.  
  97. static struct kretprobe my_rp2 = {  
  98. .handler = return_handler,  
  99. .entry_handler = entry_handler,  
  100. .kp.symbol_name = "fib_validate_source" 
  101. };  
  102.  
  103. static struct kretprobe my_rp3 = {  
  104. .handler = fib_lookup_return_handler,  
  105. .entry_handler = fib_lookup_entry_handler,  
  106. .kp.symbol_name = "fib_lookup",  
  107. .data_size = sizeof(struct fib_result *)  
  108. };  
  109.  
  110. static int __init init_myprobe(void)  
  111. {  
  112. int ret;  
  113.  
  114. printk("RTN_UNICAST is %d\n", RTN_UNICAST);  
  115. if ( (ret = register_jprobe(&my_jp1)) < 0) {  
  116. printk("register_jprobe %s failed, returned %d\n", my_jp1.kp.symbol_name, ret);  
  117. return(-1);  
  118. }  
  119.  
  120. if ( (ret = register_jprobe(&my_jp2)) < 0) {  
  121. printk("register_jprobe %s failed, returned %d\n", my_jp2.kp.symbol_name, ret);  
  122. return(-1);  
  123. }  
  124.  
  125. if ( (ret = register_kretprobe(&my_rp1)) < 0 ) {  
  126. printk("register_kretprobe %s failed, returned %d\n", my_rp1.kp.symbol_name, ret);  
  127. unregister_jprobe(&my_jp1);  
  128. unregister_jprobe(&my_jp2);  
  129. return(-1);  
  130. }  
  131.  
  132. if ( (ret = register_kretprobe(&my_rp2)) < 0 ) {  
  133. printk("register_kretprobe %s failed, returned %d\n", my_rp2.kp.symbol_name, ret);  
  134. unregister_jprobe(&my_jp1);  
  135. unregister_jprobe(&my_jp2);  
  136. unregister_kretprobe(&my_rp1);  
  137. return(-1);  
  138. }  
  139.  
  140. if ( (ret = register_kretprobe(&my_rp3)) < 0 ) {  
  141. printk("register_kretprobe %s failed, returned %d\n", my_rp3.kp.symbol_name, ret);  
  142. unregister_jprobe(&my_jp1);  
  143. unregister_jprobe(&my_jp2);  
  144. unregister_kretprobe(&my_rp1);  
  145. unregister_kretprobe(&my_rp2);  
  146. return(-1);  
  147. }  
  148.  
  149. return 0;  
  150. }  
  151.  
  152.  
  153. static void __exit rel_myprobe(void)  
  154. {  
  155. unregister_jprobe(&my_jp1);  
  156. unregister_jprobe(&my_jp2);  
  157. unregister_kretprobe(&my_rp1);  
  158. unregister_kretprobe(&my_rp2);  
  159. unregister_kretprobe(&my_rp3);  
  160. }  
  161.  
  162. module_init(init_myprobe);  
  163. module_exit(rel_myprobe);  
  164.  
  165. Makefile obj-m += arp_probe.o  
  166. Making #> make -C /usr/src/kernels/2.6.32-71.el6.x86_64/ M=`pwd` modules 

Linux故障定位技術(shù)詳解與實(shí)例的內(nèi)容介紹完了,希望通過(guò)本文紅帽Linux故障定位技術(shù)的學(xué)習(xí)能對(duì)你有所幫助!

原文地址:http://beareyes.com.cn/2/lib/201109/27/20110927182_0.htm

責(zé)任編輯:程站 來(lái)源: BEAREYES.COM
相關(guān)推薦

2013-07-25 14:50:03

2009-09-10 18:55:07

2010-08-17 15:09:45

綜合布線系統(tǒng)故障

2009-02-09 10:40:00

通信光纜故障維修

2023-01-05 07:54:49

vivo故障定位

2014-04-24 10:14:32

紅帽

2010-09-13 13:12:57

CSS定位

2023-07-07 10:37:43

自動(dòng)駕駛技術(shù)

2010-06-22 10:28:04

linux at命令

2010-09-10 13:07:51

CSS DIV絕對(duì)定位CSS DIV固定定位

2010-06-24 16:55:47

Linux chgrp

2023-06-14 08:49:22

PodKubernetes

2018-02-07 09:25:50

Linux命令touch

2010-06-18 10:33:03

Linux Acces

2018-07-04 10:21:21

2009-12-25 10:31:31

Linux網(wǎng)絡(luò)故障

2010-04-22 18:09:38

Aix系統(tǒng)

2009-09-03 15:39:15

紅帽集成虛擬化RHEL Linux

2017-08-28 14:43:28

Kubernetes技術(shù)紅帽

2013-01-14 16:00:29

Linux系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)