① linux內核線程死鎖或死循環之後如何讓系統宕機重啟
在開發內核模塊或驅動時,如果處理失誤,導致內核線程中出現死鎖或者死循環,你會發現,除了重啟之外,你沒有任何可以做的。這時你的輸入不起任何作用,終端(不是指遠程的ssh工具)只會在那重復的輸出類似「BUG: soft lockup - CPU#0 stuck for 67s! [fclustertool:2043]」,更無奈的是你重啟之後導致系統掛起的堆棧信息也看不到,你所能做的就是一遍遍的加調試信息,一遍遍的重啟機器(這是我的經歷,現在想想很傻)。 這種情況你肯定不是第一個遇到的,所以內核肯定會提供處理這種情況的一些機制。但是如何來找到這些機制在哪個地方,或者說根據什麼信息去google呢?最有用的就是這句話「BUG: soft lockup - CPU#0 stuck for 67s! [fclustertool:2043]」,因為這句話提供你的信息量很大。首先,這條信息可以輸出,說明即使發生死鎖或者死循環,還是有代碼可以執行。第二,可以通過這個日誌信息,找到對應的處理函數,這個函數所在的模塊就是用來處理CPU被過度使用時用到的。所以通過這個事情,可以看到內核列印出的只言片語都有可能成為你解決問題的關鍵,一定要從重視這些信息,從中找出有用的東西。 我經常看的內核版本是官方的2.6.32內核,這個版本中我找到的函數是softlockup_tick(),這個函數在時鍾中斷的處理函數run_local_timers()中調用。這個函數會首先檢查watchdog線程是否被掛起,如果不是watchdog線程,會檢查當前佔有CPU的線程佔有的時間是否超過系統配置的閾值,即softlockup_thresh。如果當前佔有CPU的時間過長,則會在系統日誌中輸出我們上面看到的那條日誌。接下來才是最關鍵的,就是輸出模塊信息、寄存器信息和堆棧信息,檢查softlockup_panic的值是否為1。如果softlockup_panic為1,則調用panic()讓內核掛起,輸出OOPS信息。代碼如下所示:/** This callback runs from the timer interrupt, and checks * whether the watchdog thread has hung or not:*/void softlockup_tick(void){int this_cpu = smp_processor_id(); unsigned long touch_timestamp = per_cpu(touch_timestamp, this_cpu); unsigned long print_timestamp; struct pt_regs *regs = get_irq_regs(); unsigned long now; /* Warn about unreasonable delays: */ if (now <= (touch_timestamp + softlockup_thresh))return; per_cpu(print_timestamp, this_cpu) = touch_timestamp; spin_lock(&print_lock); printk(KERN_ERR BUG: soft lockup - CPU#%d stuck for %lus! [%s:%d]\n, this_cpu, now - touch_timestamp, current-comm, task_pid_nr(current)); print_moles(); print_irqtrace_events(current);if (regs)show_regs(regs);elsemp_stack(); spin_unlock(&print_lock); if (softlockup_panic) panic(softlockup: hung tasks);} 但是softlockup_panic的值默認竟然是0,所以在出現死鎖或者死循環的時候,會一直只輸出日誌信息,而不會宕機,這個真是好坑啊!所以你得手動修改/proc/sys/kernel/softlockup_panic的值,讓內核可以在死鎖或者死循環的時候可以宕機。如果你的機器中安裝了kmp,在重啟之後,你會得到一份內核的core文件,這時從core文件中查找問題就方便很多了,而且再也不用手動重啟機器了。如果你的內核是標准內核的話,可以通過修改/proc/sys/kernel/softlockup_thresh來修改超時的閾值,如果是CentOS內核的話,對應的文件是/proc/sys/kernel/watchdog_thresh。CentOS內核和標准內核還有一個地方不一樣,就是處理CPU佔用時間過長的函數,CentOS下是watchdog_timer_fn()函數。 這里介紹下lockup的概念。lockup分為soft lockup和hard lockup。 soft lockup是指內核中有BUG導致在內核模式下一直循環的時間超過10s(根據實現和配置有所不同),而其他進程得不到運行的機會。hard softlockup是指內核已經掛起,可以通過watchdog這樣的機制來獲取詳細信息。這兩個概念比較類似。如果你想了解更多關於lockup的信息,可以參考這篇文檔: 注意上面說的這些,都是在內核線程中有效,對用戶態的死循環沒用。如果要監視用戶態的死循環,或者內存不足等資源的情況,強烈推薦軟體層面的watchdog。具體的操作可以參考下面的文章,都寫的非常好,非常實用:
② 請教linux下用戶態進程調度問題
在進行Linux系統操作的時候,有時候會遇到一次用戶態進程死循環,即系統反應遲鈍、進程掛死等問題,那麼遇到這些問題又該如何解決呢?下面小編就給大家介紹下一次用戶態進程死循環的問題該如何處理。
Linux下如何處理一次用戶態進程死循環問題
1、問題現象
業務進程(用戶態多線程程序)掛死,操作系統反應遲鈍,系統日誌沒有任何異常。從進程的內核態堆棧看,看似所有線程都卡在了內核態的如下堆棧流程中:
[root@vmc116 ~]# cat /proc/27007/task/11825/stack
[《ffffffff8100baf6》] retint_careful+0x14/0x32
[《ffffffffffffffff》] 0xffffffffffffffff
2、問題分析
1)內核堆棧分析
從內核堆棧看,所有進程都阻塞在 retint_careful上,這個是中斷返回過程中的流程,代碼(匯編)如下:
entry_64.S
代碼如下:
ret_from_intr:
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF
decl PER_CPU_VAR(irq_count)
/* Restore saved previous stack */
popq %rsi
CFI_DEF_CFA rsi,SS+8-RBP /* reg/off reset after def_cfa_expr */
leaq ARGOFFSET-RBP(%rsi), %rsp
CFI_DEF_CFA_REGISTER rsp
CFI_ADJUST_CFA_OFFSET RBP-ARGOFFSET
。。。
retint_careful:
CFI_RESTORE_STATE
bt $TIF_NEED_RESCHED,%edx
jnc retint_signal
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_NONE)
pushq_cfi %rdi
SCHEDULE_USER
popq_cfi %rdi
GET_THREAD_INFO(%rcx)
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF
jmp retint_check
這其實是用戶態進程在用戶態被中斷打斷後,從中斷返回的流程,結合retint_careful+0x14/0x32,進行反匯編,可以確認阻塞的點其實就在
SCHEDULE_USER
這其實就是調用schele()進行調度,也就是說當進程走到中斷返回的流程中時,發現需要調度(設置了TIF_NEED_RESCHED),於是在這里發生了調度。
有一個疑問:為什麼在堆棧中看不到schele()這一級的棧幀呢?
因為這里是匯編直接調用的,沒有進行相關棧幀壓棧和上下文保存操作。
2)進行狀態信息分析
從top命令結果看,相關線程實際一直處於R狀態,CPU幾乎完全耗盡,而且絕大部分都消耗在用戶態:
[root@vmc116 ~]# top
top - 09:42:23 up 16 days, 2:21, 23 users, load average: 84.08, 84.30, 83.62
Tasks: 1037 total, 85 running, 952 sleeping, 0 stopped, 0 zombie
Cpu(s): 97.6%us, 2.2%sy, 0.2%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 32878852k total, 32315464k used, 563388k free, 374152k buffers
Swap: 35110904k total, 38644k used, 35072260k free, 28852536k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
27074 root 20 0 5316m 163m 14m R 10.2 0.5 321:06.17 z_itask_templat
27084 root 20 0 5316m 163m 14m R 10.2 0.5 296:23.37 z_itask_templat
27085 root 20 0 5316m 163m 14m R 10.2 0.5 337:57.26 z_itask_templat
27095 root 20 0 5316m 163m 14m R 10.2 0.5 327:31.93 z_itask_templat
27102 root 20 0 5316m 163m 14m R 10.2 0.5 306:49.44 z_itask_templat
27113 root 20 0 5316m 163m 14m R 10.2 0.5 310:47.41 z_itask_templat
25730 root 20 0 5316m 163m 14m R 10.2 0.5 283:03.37 z_itask_templat
30069 root 20 0 5316m 163m 14m R 10.2 0.5 283:49.67 z_itask_templat
13938 root 20 0 5316m 163m 14m R 10.2 0.5 261:24.46 z_itask_templat
16326 root 20 0 5316m 163m 14m R 10.2 0.5 150:24.53 z_itask_templat
6795 root 20 0 5316m 163m 14m R 10.2 0.5 100:26.77 z_itask_templat
27063 root 20 0 5316m 163m 14m R 9.9 0.5 337:18.77 z_itask_templat
27065 root 20 0 5316m 163m 14m R 9.9 0.5 314:24.17 z_itask_templat
27068 root 20 0 5316m 163m 14m R 9.9 0.5 336:32.78 z_itask_templat
27069 root 20 0 5316m 163m 14m R 9.9 0.5 338:55.08 z_itask_templat
27072 root 20 0 5316m 163m 14m R 9.9 0.5 306:46.08 z_itask_templat
27075 root 20 0 5316m 163m 14m R 9.9 0.5 316:49.51 z_itask_templat
。。。
3)進程調度信息
從相關線程的調度信息看:
[root@vmc116 ~]# cat /proc/27007/task/11825/schedstat
15681811525768 129628804592612 3557465
[root@vmc116 ~]# cat /proc/27007/task/11825/schedstat
15682016493013 129630684625241 3557509
[root@vmc116 ~]# cat /proc/27007/task/11825/schedstat
15682843570331 129638127548315 3557686
[root@vmc116 ~]# cat /proc/27007/task/11825/schedstat
15683323640217 129642447477861 3557793
[root@vmc116 ~]# cat /proc/27007/task/11825/schedstat
15683698477621 129645817640726 3557875
發現相關線程的調度統計一直在增加,說明相關線程一直是在被調度運行的,結合其狀態也一直是R,推測很可能在用戶態發生了死循環(或者非睡眠死鎖)。
這里又有問題:為什麼從top看每個線程的CPU佔用率只有10%左右,而不是通常看到的死循環進程導致的100%的佔用率?
因為線程數很多,而且優先順序都一樣,根據CFS調度演算法,會平均分配時間片,不會讓其中一個線程獨佔CPU。結果為多個線程間輪流調度,消耗掉了所有的cpu。。
另一個問題:為什麼這種情況下,內核沒有檢測到softlockup?
因為業務進程的優先順序不高,不會影響watchdog內核線程(最高優先順序的實時線程)的調度,所以不會產生softlockup的情況。
再一個問題:為什麼每次查看線程堆棧時,總是阻塞在retint_careful,而不是其它地方?
因為這里(中斷返回的時候)正是調度的時機點,在其它時間點不能發生調度(不考慮其它情況~),而我們查看線程堆棧的行為,也必須依賴於進程調度,所以我們每次查看堆棧時,正是查看堆棧的進程(cat命令)得到調度的時候,這時正是中斷返回的時候,所以正好看到的阻塞點為retint_careful。
4)用戶態分析
從上面的分析看,推測應該是用戶態發生了死鎖。
用戶態確認方法:
部署debug信息,然後gdb attach相關進程,確認堆棧,並結合代碼邏輯分析。
最終確認該問題確為用戶態進程中產生了死循環。
③ 如何退出linux環境下的死循環程序
ctrl+c 強制結束
或者另起一個終端 然後 ps -ef|grep 程序名 然後得到進程號 用kill -9 進程號