一、簡介
Linux系統(tǒng)在運(yùn)行過程中,可能發(fā)生各種各樣的卡死情況。有的表現(xiàn)為某個(gè)或某些CPU無法調(diào)度其他進(jìn)程或無法響應(yīng)中斷,如正在CPU上運(yùn)行的進(jìn)程禁止了搶占或禁止了本地中斷后,但其需要的資源一直無法獲得(如發(fā)生了死鎖等情況),而一直占據(jù)著CPU;有的表現(xiàn)為某些重要進(jìn)程一直不能運(yùn)行,雖然不至于使某個(gè)或某些CPU上無法調(diào)度其他進(jìn)程,但由于重要進(jìn)程運(yùn)行異常,系統(tǒng)已無法正常進(jìn)行業(yè)務(wù)處理,例如重要進(jìn)程長期處于uninterruptible sleep狀態(tài)(也就是常說的D狀態(tài))或android systemserver的watchdog超時(shí)等情況。
本文主要討論進(jìn)程長期處于D狀態(tài)或重要進(jìn)程異??ㄗ〉?a target="_blank">檢測方法,即hungtask detect機(jī)制。而恢復(fù)機(jī)制,一般就是在檢測到異常時(shí),直接觸發(fā)整機(jī)重啟。
二、hungtask detect原理及流程
hungtask detect方法有多種,原理都很簡單。比如:
A、可以定時(shí)輪詢系統(tǒng)中的所有task,然后判斷處于D狀態(tài)的task的上下文切換次數(shù)是否和之前輪詢時(shí)的相等,如果相等則表明該task兩個(gè)輪詢間隔期間一直處于D狀態(tài),可以認(rèn)為該task有hang的情況。當(dāng)然,task hang住的情況,對于有些task來說沒有關(guān)系,可能其本身的邏輯就是如此,不會(huì)對系統(tǒng)中其它task產(chǎn)生影響;但對于一些我們認(rèn)為重要的進(jìn)程,如android中的systemserver、surfaceflinger等進(jìn)程,如果發(fā)生hang的情況,則一定會(huì)對用戶使用產(chǎn)生影響;還有task長時(shí)間處于io wait狀態(tài),同樣是一種異常狀態(tài),因?yàn)橐话銇碚fio應(yīng)盡快結(jié)束,而時(shí)間過長則表明io子系統(tǒng)很可能已經(jīng)異常。
B、如果只是判斷系統(tǒng)中的重要進(jìn)程是否卡住,也可以不檢查系統(tǒng)中所有task的狀態(tài),只需要關(guān)注重要進(jìn)程的運(yùn)行情況??梢宰屵@個(gè)重要進(jìn)程在規(guī)定時(shí)間內(nèi)模擬喂狗操作,若發(fā)現(xiàn)沒有及時(shí)喂狗,則認(rèn)為該重要進(jìn)程已經(jīng)卡住。
以下分別討論上面所述的兩種hungtask detect實(shí)現(xiàn)方式,所列代碼均為開源代碼,代碼鏈接見附錄參考文檔。
1、輪詢系統(tǒng)中的所有任務(wù)
這里對輪詢系統(tǒng)中的所有任務(wù)的hungtask detect方式進(jìn)行分析,代碼見參考文檔1,主要涉及代碼:
kernelhung_task.c (linux系統(tǒng)默認(rèn)實(shí)現(xiàn))
driverssocqcomhung_task_enh.c (在默認(rèn)實(shí)現(xiàn)上進(jìn)行vendor hook)
KCONFIG
libKconfig.debug (對應(yīng)hung_task.c )
driverssocqcomKconfig (對應(yīng)hung_task_enh.c)
代碼分析
kernelhung_task.c
A. 將panic_block(notifier_block結(jié)構(gòu)體)掛到panic_notifier_list通知鏈,當(dāng)系統(tǒng)發(fā)生panic時(shí),會(huì)通過該通知鏈通知注冊到該鏈的所有notifier_block,調(diào)用每個(gè)notifier_block的notifier_call成員函數(shù)。
對于這里的hungtask,就是在panic時(shí)調(diào)用hung_task_panic函數(shù)置did_panic為1,在hungtask檢測流程中發(fā)現(xiàn)did_panic為1,則直接退出。
B. hungtask_pm_notify_nb(notifier_block結(jié)構(gòu)體)掛到pm_chain_head通知鏈,當(dāng)系統(tǒng)發(fā)生pm狀態(tài)變化時(shí)調(diào)用hungtask_pm_notify,設(shè)置hung_detector_suspended變量。
C. 起內(nèi)核線程,運(yùn)行D狀態(tài)檢測函數(shù)watchdog(),下面分析。
A. 取sysctl_hung_task_timeout_secs和sysctl_hung_task_check_interval_secs最小值作為檢測時(shí)的interval,加上次檢測時(shí)間hung_last_checked,如達(dá)到或超過當(dāng)前時(shí)間jiffies則進(jìn)行hungtask check。
B. hungtask check函數(shù),下面詳細(xì)分析。
C. 進(jìn)入可中斷休眠,如有信號提前中斷喚起該線程,會(huì)在A處的時(shí)間判斷中確定是否進(jìn)行hungtask check。
A. 限制進(jìn)行hung check的task數(shù)量,本輪檢測的task數(shù)量達(dá)到該值后退出。
B. 如已經(jīng)運(yùn)行了HUNG_TASK_LOCK_BREAK時(shí)間,調(diào)用rcu_lock_break() 短暫退出rcu臨界區(qū)并調(diào)度出去,避免一次rcu grace period的時(shí)間過長,之后再調(diào)度回來時(shí)再次進(jìn)入rcu臨界區(qū)。由于調(diào)度出去再回來時(shí),正在檢測的task可能已經(jīng)釋放,所以在調(diào)度出去之前,需要使用get_task_struct增加task的task_struct結(jié)構(gòu)體的引用計(jì)數(shù),防止其被釋放,在通過pid_alive判斷task是否dead后,再調(diào)用put_task_struct減小引用計(jì)數(shù)。如果調(diào)度回來時(shí)發(fā)現(xiàn)task已經(jīng)dead,則退出本輪hung check。
C. 為符合GKI規(guī)范,此處通過vendor hook函數(shù),調(diào)用vendor實(shí)現(xiàn)的hook函數(shù),這里的實(shí)現(xiàn)是調(diào)用register函數(shù)注冊對應(yīng)hook函數(shù),qcom_before_check_tasks() 和qcom_check_tasks_done(),后面會(huì)有分析,主要就是判斷該task是否需要hungtask檢查,并獲得當(dāng)前iowait task的數(shù)量。
vendor hook函數(shù)注冊如下所示:
driverssocqcomhung_task_enh.c
D. 根據(jù)C處返回的need_check,如判斷需要進(jìn)行hungtask檢查,則調(diào)用check_hung_task(),后面會(huì)有分析。
E. 此處調(diào)用qcom_check_tasks_done,判斷在對所有task進(jìn)行hung_task_enh.max_iowait_timeout_cnt輪的檢測,如果連續(xù)地每輪都有大于等于hung_task_enh.max_iowait_task_cnt數(shù)量的task處于iowait狀態(tài),則直接觸發(fā)panic。
F. 之后的流程就是在本輪hungtask檢測結(jié)束后,跟蹤標(biāo)志狀態(tài)顯示task的鎖狀態(tài)及當(dāng)前各CPU上的棧。
接下來看下hook函數(shù)的具體內(nèi)容。
driverssocqcomhung_task_enh.c
A. 一個(gè)task根據(jù)其task_struct中的walt_task_struct的hung_detect_status成員判斷,如果在白名單(白名單模式)或不在黑名單(黑名單模式),則置need_check標(biāo)志,然后繼續(xù)判斷是否要增加iowait task數(shù)量的統(tǒng)計(jì)值。
B. 如果task處于iowait狀態(tài),且為D狀態(tài)、暫停狀態(tài)、跟蹤狀態(tài)之一,且到了檢查hungtask的時(shí)間,且為用戶空間進(jìn)程主線程,則增加iowait task數(shù)量的統(tǒng)計(jì)值。
接下來看check_hung_task()的具體內(nèi)容。
A. 如果task已凍結(jié)或?yàn)檎{(diào)用vfork的進(jìn)程(會(huì)處于D狀態(tài)直到等子進(jìn)程調(diào)用exit或exec)則跳過hungtask檢查。
B. task的自愿(nvcsw )和非自愿(nivcsw)上下文切換次數(shù)的和如果在檢測interval之間變動(dòng)過,則說明該task沒有hung住,即使task當(dāng)前為D狀態(tài)。直接返回,跳過該task。
C.打印sysctl_hung_task_warnings次task block信息后就不再打印,也就是說,更多的hungtask信息有可能不會(huì)再被看到。打印task block信息時(shí),會(huì)置hung_task_show_lock和hung_task_show_all_bt標(biāo)志,在退出本輪所有task的hungtask檢查后,會(huì)根據(jù)這些標(biāo)志打印task的鎖情況以及各CPU的backtrace。之后就退出了本輪的所有task的hungtask檢查。
2.只關(guān)注重要進(jìn)程
這里對第二種hungtask detect實(shí)現(xiàn)方式進(jìn)行分析,只判斷系統(tǒng)中的重要進(jìn)程是否卡住,代碼見參考文檔2,主要涉及驅(qū)動(dòng)代碼:
driversmiscmediatekmonitor_hanghang_detect.c
KCONFIG定義
driversmiscmediatekmonitor_hangKconfig (對應(yīng)hung_task.c )
代碼分析
driversmiscmediatekmonitor_hanghang_detect.c
A. 注冊hang monitor的misc device,名稱為RT_Monitor,通過其write接口控制hang monitor的使能,通過ioctl設(shè)置(類似watchdog kick操作)hang_detect_counter(后面分析的hang_detect線程會(huì)定時(shí)遞減這個(gè)counter,也就是說,如果不重置,一定時(shí)間之后就會(huì)認(rèn)為重要任務(wù) hang住了)和hang monitor的使能(hd_detect_enabled)。
B. 啟動(dòng)hang_detect和hang_detect1線程。hang_detect線程為檢測線程,下面分析。hang_detect1用來在檢測到hang時(shí)dump系統(tǒng)狀態(tài)。
繼續(xù)看下hang_detect線程的工作。
A處啟動(dòng)循環(huán),當(dāng)hang detect使能,且白名單中的task均在系統(tǒng)中時(shí),每HD_INTER秒(默認(rèn)為30秒)會(huì)對hang_detect_counter減一,減一前會(huì)檢查hang_detect_counter,當(dāng)小于等于0時(shí),會(huì)dump系統(tǒng)狀態(tài)或觸發(fā)BUG死機(jī)。
當(dāng)系統(tǒng)持續(xù)hang住時(shí),hang_detect_counter會(huì)先減到0,這時(shí)Hang_first_done是false(表示hang后的第一次處理還沒完成),所以會(huì)運(yùn)行wake_up_dump()(hang_detect線程)喚醒hang_detect1線程,hang_detect1線程dump系統(tǒng)狀態(tài)之后,置dump_bt_done為1,表示已經(jīng)完成dump backtrace,再喚醒wake_up_dump()(hang_detect線程),之后E處設(shè)置Hang_first_done為true,表示hang后的第一次處理已經(jīng)完成,之后hang_detect_counter會(huì)減到-1。
若系統(tǒng)還持續(xù)hang住,會(huì)走到B處,此處判斷條件時(shí)的意思是,如果之前hang_detect1線程dump系統(tǒng)狀態(tài)沒有正常執(zhí)行完成,則這里會(huì)再啟動(dòng)hang_detect2;否則,還會(huì)再喚醒hang_detect1線程,再次dump系統(tǒng)狀態(tài),方便和之前的進(jìn)行對比。之后走到D處,調(diào)用BUG觸發(fā)死機(jī)重啟以復(fù)位系統(tǒng),否則關(guān)鍵進(jìn)程可能一直卡住而不能自動(dòng)恢復(fù)。
wake_up_dump()
hang_detect1線程
hang_detect2線程
三、問題分析
上面介紹了兩種hungtask檢測的原理及方法,我們知道了不同系統(tǒng)可以如何判斷task已經(jīng)hang住,進(jìn)而觸發(fā)系統(tǒng)去顯示或保存現(xiàn)場狀態(tài)(如發(fā)現(xiàn)重要task持續(xù)hang住時(shí),會(huì)多次打印D狀態(tài)task的棧信息、或系統(tǒng)最終因?yàn)閔ungtask無法恢復(fù)而重啟時(shí)保存ramdump)及異?;謴?fù)。
一般可以通過kernel log中的task棧信息打印,看到hang住的關(guān)鍵task及其對應(yīng)的棧,并對比相隔一定時(shí)間的多次該task的棧情況,明確該task確實(shí)已經(jīng)異常,之后就可以根據(jù)棧的情況推測及尋找線索。如果開啟了ramdump,異常重啟時(shí)保存的ramdump也會(huì)對問題分析產(chǎn)生很大的幫助。
實(shí)際遇到的大多數(shù)問題,通過log中的task棧信息,一般只能粗略知道task hang住的大致現(xiàn)場及方向,還需要結(jié)合log中的其他信息、ramdump等進(jìn)行分析,可能還需要判斷問題發(fā)生場景、編譯調(diào)試版本復(fù)現(xiàn)問題抓取更多信息,或者排查可疑修改。
由hungtask原理可知,產(chǎn)生hungtask異常的直接原因是所關(guān)注的task長時(shí)間處于D狀態(tài)或無法調(diào)度運(yùn)行,一般就是task本身有異?;蛳到y(tǒng)有異常影響到了該task。以系統(tǒng)異常的情況居多,常見的可能原因有內(nèi)存不足、內(nèi)存分配異常、UFS器件異常、文件系統(tǒng)異常、spinlock或rwsem等各種鎖死鎖、中斷風(fēng)暴等等;task本身的異常,可能是其本身邏輯問題,需要具體分析。針對每種原因,大都有對應(yīng)的判斷及定位方法,可以輸出相應(yīng)的調(diào)試版本壓測復(fù)現(xiàn)分析。
以下舉兩個(gè)實(shí)際的例子看下hungtask問題發(fā)生的現(xiàn)象及分析方法。
1、開關(guān)機(jī)測試時(shí)死機(jī)
在開關(guān)機(jī)測試及各項(xiàng)功能測試時(shí)均出現(xiàn)了低概率死機(jī)問題,由于故障機(jī)分散,判斷為非硬件個(gè)體問題。查看故障機(jī)kernel log,發(fā)現(xiàn)死機(jī)之前,有hungtask打印。下面是其中一例死機(jī)前輸出的hungtask情況。
hungtask檢測輸出的hang住的其中幾個(gè)task的棧情況如下,該例中init進(jìn)程阻塞在了io上。
分析多例故障現(xiàn)場,hang住的task不一致,用T32分析對應(yīng)的ramdump,task會(huì)卡在對不同文件的io操作;出問題時(shí)hang住的task數(shù)量均較多。
解析ramdump得到cpu上的任務(wù)隊(duì)列情況,
發(fā)現(xiàn)一個(gè)swr irq(音頻功放注冊的中斷)在運(yùn)行,一個(gè)swr irq在pending,還有多個(gè)cfs task也在pending。查看系統(tǒng)中斷狀態(tài),觀察到開機(jī)的短時(shí)間內(nèi),產(chǎn)生了大量中斷,引起中斷風(fēng)暴,可能會(huì)嚴(yán)重影響系統(tǒng)中其他任務(wù)的執(zhí)行,包括io操作,由此導(dǎo)致比較隨機(jī)的開機(jī)時(shí)不同任務(wù)卡io的問題的發(fā)生。
之后去掉該音頻功放注冊的中斷(實(shí)際上這個(gè)中斷對應(yīng)的音頻功放并沒有使用,中斷引腳輸入狀態(tài)不定導(dǎo)致隨機(jī)的中斷異常),進(jìn)行開關(guān)機(jī)壓測,沒有復(fù)現(xiàn)問題。
2、Monkey測試時(shí)死機(jī)
某項(xiàng)目發(fā)現(xiàn)低概率hungtask問題,過一段時(shí)間就會(huì)有一、兩例出現(xiàn),在Money測試中概率有所上升,每輪中能穩(wěn)定出現(xiàn)一到兩例。每次出現(xiàn)問題的task不一定相同。下面舉一個(gè)例子:
這里時(shí)surfaceflinger出現(xiàn)了hungtask情況,其一直在嘗試獲取 kworker/u16:14 進(jìn)程所持的mutex。再查看kworker/u16:14,
可以看到該task在等rpm(處理系統(tǒng)中時(shí)鐘和電源請求的子系統(tǒng))操作的completion,但這里rpm ack一直沒有返回。查看其它異常時(shí)現(xiàn)場,共性都是rpm ack一直沒有返回。最后分析到,雖然系統(tǒng)還有足夠的H memory(HighAtomic),但ap和rpm通信用的glink分配內(nèi)存時(shí)無法使用這種遷移類型的memory;系統(tǒng)同時(shí)有許多zs_malloc失敗的情況,因?yàn)閦ram功能本身在進(jìn)行內(nèi)存分配時(shí),也沒有使用H memory,從而導(dǎo)致anon page回收異常。
最后,調(diào)整內(nèi)存回收參數(shù),該問題得以優(yōu)化。
四、總結(jié)
本文主要描述了什么是hungtask、hungtask檢測方法以及hungtask產(chǎn)生的原因,并通過兩個(gè)案例,展示了具體問題分析方法。
hungtask表現(xiàn)為某些重要進(jìn)程一直不能運(yùn)行,如長期處于uninterruptible sleep狀態(tài)(也就是常說的D狀態(tài)。可以采取多種方法檢測:定時(shí)輪詢系統(tǒng)中的所有task,然后判斷處于D狀態(tài)的task的上下文切換次數(shù)是否和之前輪詢時(shí)的相等,如果相等則表明該task兩個(gè)輪詢間隔期間一直處于D狀態(tài),可以認(rèn)為該task有hang的情況;或只關(guān)注重要進(jìn)程的運(yùn)行情況,讓這個(gè)重要進(jìn)程在規(guī)定時(shí)間內(nèi)模擬喂狗操作,若發(fā)現(xiàn)沒有及時(shí)喂狗,則認(rèn)為其有hang的情況。產(chǎn)生hungtask的直接原因是所關(guān)注的task長時(shí)間處于D狀態(tài)或無法調(diào)度運(yùn)行,task本身有異?;蛳到y(tǒng)有異常影響到了該task:對于系統(tǒng)異常,常見的可能原因有內(nèi)存不足、內(nèi)存分配異常、UFS器件異常、文件系統(tǒng)異常、spinlock或rwsem等各種鎖死鎖、中斷風(fēng)暴等等;task本身的異常,為其本身邏輯問題。
審核編輯:湯梓紅
-
cpu
+關(guān)注
關(guān)注
68文章
10854瀏覽量
211578 -
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209326 -
Detect
+關(guān)注
關(guān)注
0文章
6瀏覽量
7158
原文標(biāo)題:Hungtask原理及分析
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論