轉自:http://www.oenhan.com/kernel-deadlock-check
死鎖就是多個進程(線程)因為等待別的進程已占有的自己所需要的資源而陷入阻塞的一種狀態,死鎖狀態一旦形成,進程本身是解決不了的,需要外在的推動,才能解決,最重要的是死鎖不僅僅影響進程業務,而且還會占用系統資源,影響其他進程。所以內核中設計了內核死鎖檢測機制,一旦發現死鎖進程,就重啟OS,快刀斬亂麻解決問題。之所以使用重啟招數,還是在於分布式系統中可以容忍單點崩潰,不能容忍單點進程計算異常,否則進行死鎖檢測重啟OS就得不償失了。
內核提供自旋鎖、信號量等鎖形式的工具,具體不再贅述。
Linux內核死鎖主要分為分為兩種:D狀態死鎖和R狀態死鎖。
一、D狀態死鎖檢測
D狀態死鎖:進程長時間處於TASK_UNINTERRUPTIBLE而不恢復的狀態。進程處於TASK_UNINTERRUPTIBLE狀態,不響應其他信號(kill -9),保證一些內核原子操作不被意外中斷。但這種狀態時間長就表示進程異常了,需要處理。
內核D狀態死鎖檢測就是hung_task機制,主要代碼就在kernel/hung_task.c文件。
具體實現原理:
1.創建Normal級別的khungtaskd內核線程,在死循環中每隔sysctl_hung_task_timeout_secs時間后check一下,用schedule_timeout定時(節約定時器浪費的CPU)。
2.調用do_each_thread,while_each_thread宏遍歷所有的進程信息,如果有D狀態進程,則檢查最近切換次數和task計算是否一致,即最近是否有調度切換,如果一致,則沒有切換,打印相關信息,並根據sysctl_hung_task_panic開關決定是否重啟。
對應用戶態控制的proc接口有:
/proc/sys/kernel/hung_task_timeout_secs,hung_task_panic等。
二、R狀態死鎖檢測
R狀態死鎖:進程長時間處於TASK_RUNNING 狀態搶占CPU而不發生切換,一般是,進程關搶占后一直執行任務,或者進程關搶占后處於死循環或者睡眠,此時往往會導致多個CPU互鎖,整個系統異常。
補充:lockdep不是所謂的死鎖。
內核R狀態死鎖檢測機制就是lockdep機制,入口即是lockup_detector_init函數。
1.通過cpu_callback函數調用watchdog_enable,在每個CPU core上創建SCHED_FIFO級別的實時線程watchdog,其中使用了hrtimer定時器,控制檢查周期。
2.hrtimer定時器調用watchdog_timer_fn進行清狗的時間檢查,而線程則每次重置清狗時間,如果watchdog_timer_fn發現狗的重置時間已經和當前時間差出危險值,則根據開關進行panic處理。
對應用戶態控制的proc接口有:
/proc/sys/kernel/watchdog_thresh,softlockup_panic等。
整個死鎖檢測機制比較簡單,但cpu_callback函數結構性設計巧妙,可以在很多地方參考使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
static int __cpuinit
cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
{
int hotcpu = (unsigned long)hcpu;
switch (action) {
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
watchdog_prepare_cpu(hotcpu);
break;
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
if (watchdog_enabled)
watchdog_enable(hotcpu);
break;
#ifdef CONFIG_HOTPLUG_CPU
case CPU_UP_CANCELED:
case CPU_UP_CANCELED_FROZEN:
watchdog_disable(hotcpu);
break;
case CPU_DEAD:
case CPU_DEAD_FROZEN:
watchdog_disable(hotcpu);
break;
#endif /* CONFIG_HOTPLUG_CPU */
}
/*
* hardlockup and softlockup are not important enough
* to block cpu bring up. Just always succeed and
* rely on printk output to flag problems.
*/
return NOTIFY_OK;
}
|
—結束—