女人18毛片a级毛片一区二区-男男瑟瑟视频-午夜亚洲一区二区,国产福利91精品,日韩精品在线免费视频,国产欧美在线一区

沃趣科技產(chǎn)品與技術(shù)資源中心
產(chǎn)品白皮書丨最佳實(shí)踐合集丨行業(yè)報(bào)告丨技術(shù)文章
資源 > 故障分析|看Linux如何判斷系統(tǒng)“死機(jī)”

故障分析|看Linux如何判斷系統(tǒng)“死機(jī)”

2023年06月14日

前言



在系統(tǒng)的日常運(yùn)維中,最令人頭疼的莫過(guò)于各種應(yīng)用程序或者操作系統(tǒng)hung住不響應(yīng)的問(wèn)題。對(duì)于處在用戶態(tài)的程序相對(duì)來(lái)說(shuō)還比較容易排查,而一旦程序hung在內(nèi)核態(tài)或者操作系統(tǒng)本身hung住,kill信號(hào)甚至硬件中斷都無(wú)法響應(yīng),此時(shí)我們能做的就只有重啟了。然而重啟并不能解決根本問(wèn)題,更麻煩的是這種情況下我們幾乎拿不到任何有用的信息,后續(xù)原因的分析和排查更是步履維艱。好在Linux內(nèi)核早已提供了一系列的機(jī)制來(lái)幫助我們分析此類問(wèn)題。下面我們就來(lái)看下如何配置使用這些機(jī)制以及它們的實(shí)現(xiàn)原理。



不可中斷睡眠


■概念


?  第一種比較常見(jiàn)的現(xiàn)象是:進(jìn)程長(zhǎng)時(shí)間處于D(不可中斷睡眠)狀態(tài)。依賴于它的進(jìn)程也會(huì)因?yàn)榈却枞D敲词裁词荄狀態(tài)呢?顧名思義:首先它是一種睡眠狀態(tài),也就意味著處于此狀態(tài)的進(jìn)程不會(huì)消耗CPU。其次睡眠的原因是因?yàn)榈却承┵Y源(比如鎖或者磁盤IO),這也是我們看到非常多D狀態(tài)的進(jìn)程都處在處理IO操作的原因。最后一點(diǎn)就是它不能被中斷,這個(gè)要區(qū)別于“硬件中斷”的中斷,是指不希望在其獲取到資源或者超時(shí)前被終止。因此他不會(huì)被信號(hào)喚醒,也就不會(huì)響應(yīng)kill -9這類信號(hào)。這也是它跟S(可中斷睡眠)狀態(tài)的區(qū)別。


??進(jìn)程進(jìn)入D狀態(tài)發(fā)生在內(nèi)核代碼或者底層驅(qū)動(dòng)代碼中,典型的場(chǎng)景是與硬件進(jìn)行通信。通常情況下我們可以通過(guò)top命令觀察到進(jìn)程快速的的進(jìn)入退出D狀態(tài),但當(dāng)硬件故障或者驅(qū)動(dòng)bug出現(xiàn)時(shí)就會(huì)導(dǎo)致進(jìn)程長(zhǎng)時(shí)間處于D狀態(tài)無(wú)法退出,依賴或等待它的其他進(jìn)程都被阻塞卡死。


  • 處于D狀態(tài)的進(jìn)程

1.jpg


■機(jī)制


??內(nèi)核通常會(huì)創(chuàng)建一個(gè)khungtaskd的守護(hù)進(jìn)程,它會(huì)周期性的檢查所有進(jìn)程的狀態(tài)和上下文切換,進(jìn)而判斷是否有進(jìn)程長(zhǎng)時(shí)間處于D狀態(tài)。我們可以通過(guò)配置如下內(nèi)核參數(shù)來(lái)控制檢測(cè)的超時(shí)時(shí)間、告警打印,以及是否觸發(fā)panic,以幫助問(wèn)題的后續(xù)分析。


# 超時(shí)時(shí)間
kernel.hung_task_timeout_secs = 120
# 告警打印的次數(shù)
kernel.hung_task_warnings = 10
# 是否觸發(fā)系統(tǒng)panic
kernel.hung_task_panic = 0
# 檢測(cè)的最大進(jìn)程數(shù),系統(tǒng)中進(jìn)程數(shù)超過(guò)此值時(shí)會(huì)忽略超出的部分
kernel.hung_task_check_count = 4194304


  • 進(jìn)程處于D狀態(tài)的超時(shí)打印

2.jpg


■實(shí)現(xiàn)


    khungtaskd對(duì)應(yīng)的代碼在hung_task.c中,主要實(shí)現(xiàn)邏輯:

  1. 每隔一段時(shí)間(hung_task_timeout_secs定義的超時(shí)時(shí)間),檢查系統(tǒng)中所有進(jìn)程

  2. 針對(duì)處于D狀態(tài)的進(jìn)程,記錄并檢查它的上下文切換次數(shù),如果和上次記錄的上下文切換次數(shù)相同,則說(shuō)明此進(jìn)程在超時(shí)時(shí)間內(nèi)一直處于D狀態(tài)。

  3. 根據(jù)配置選擇打印告警并觸發(fā)系統(tǒng)panic


static int __init hung_task_init(void)
{
   ... ...
   watchdog_task = kthread_run(watchdog, NULL, "khungtaskd");    //創(chuàng)建khungtaskd進(jìn)程
 ... ...
}

static int watchdog(void *dummy)
{
 ... ...
   for ( ; ; ) {
       while (schedule_timeout_interruptible(timeout_jiffies(timeout)))    //休眠一段時(shí)間
           timeout = sysctl_hung_task_timeout_secs;
       ... ...
       check_hung_uninterruptible_tasks(timeout);    //開(kāi)始檢查進(jìn)程
   }
}

static void check_hung_task(struct task_struct *t, unsigned long timeout)
{
   unsigned long switch_count = t->nvcsw + t->nivcsw;    //計(jì)算上下文切換次數(shù)
   ... ...

   if (switch_count != t->last_switch_count) {        //和上次切換次數(shù)進(jìn)行比較
       t->last_switch_count = switch_count;
       return;
   }
   ... ...

   printk(KERN_ERR "INFO: task %s:%d blocked for more than "        //打印告警
               "%ld seconds.\n", t->comm, t->pid, timeout);
   printk(KERN_ERR "\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\""
               " disables this message.\n");

 ... ...
   if (sysctl_hung_task_panic) {
       trigger_all_cpu_backtrace();
       panic("hung_task: blocked tasks");    //觸發(fā)系統(tǒng)panic
   }
}



軟鎖和硬鎖


■概念


??另外一種比較常見(jiàn)的情況就是:一個(gè)進(jìn)程一直占用CPU,其他進(jìn)程始終無(wú)法被調(diào)度執(zhí)行,極端情況下甚至無(wú)法響應(yīng)中斷,此時(shí)系統(tǒng)可能就會(huì)完全hung住不響應(yīng)任何用戶操作。這種情況還是發(fā)生在內(nèi)核代碼或者驅(qū)動(dòng)代碼bug中。應(yīng)用程序不會(huì)出現(xiàn)這個(gè)問(wèn)題,就好像我們寫一個(gè)死循序程序不會(huì)導(dǎo)致系統(tǒng)hung住一樣。


??要理解這種情況,首先得明白Linux是個(gè)搶占式內(nèi)核,進(jìn)程之間可以相互搶占CPU,其次Linux會(huì)為每個(gè)CPU core設(shè)置一個(gè)固定周期的時(shí)鐘中斷,這個(gè)中斷是一個(gè)很重要的搶占的時(shí)機(jī),時(shí)鐘中斷處理程序會(huì)判斷下面需要讓哪個(gè)進(jìn)程搶到CPU。一個(gè)處在用戶態(tài)的進(jìn)程執(zhí)行一段時(shí)間后,時(shí)鐘中斷觸發(fā),進(jìn)程調(diào)度算法(例如:CFS)可能就會(huì)將CPU分配給其他進(jìn)程,從而不會(huì)讓這個(gè)進(jìn)程一直占用CPU。而一個(gè)處在內(nèi)核態(tài)的進(jìn)程則不同,首先它可以屏蔽中斷響應(yīng),這就直接去除了搶占的時(shí)機(jī),其次它也可以顯示的關(guān)閉搶占,同時(shí)如果是個(gè)內(nèi)核進(jìn)程,他的優(yōu)先級(jí)高于普通進(jìn)程并且調(diào)度策略也不同于CFS,以上這些情況下如果不主動(dòng)讓出CPU,其他進(jìn)程就無(wú)法獲取執(zhí)行,最終就會(huì)導(dǎo)致問(wèn)題出現(xiàn)。


上面描述的現(xiàn)象在linux里面稱為:軟鎖(soft_lockup)和硬鎖(hard_lockup)


  • soft_lockup

CPU被某個(gè)進(jìn)程長(zhǎng)時(shí)間占用,其他進(jìn)程得不到調(diào)用。例如:長(zhǎng)時(shí)間禁用內(nèi)核搶占


  • soft_lockup告警打印

3.jpg


  • hard_lockup

CPU被某個(gè)進(jìn)程長(zhǎng)時(shí)間占用,其他進(jìn)程得不到調(diào)用,同時(shí)也不響應(yīng)中斷。例如:長(zhǎng)時(shí)間屏蔽中斷響應(yīng)


  • hard_lockup告警打印

4.jpg



■機(jī)制


?Linux內(nèi)核通過(guò)watchdog機(jī)制來(lái)檢查系統(tǒng)中是否出現(xiàn)soft_lockup和hard_lockup。watchdog的主要思想是:通過(guò)優(yōu)先級(jí)更高的任務(wù)來(lái)觀察優(yōu)先級(jí)較低的任務(wù)(進(jìn)程/中斷)是否被成功執(zhí)行調(diào)度,因此可以通過(guò)中斷來(lái)觀察進(jìn)程是否被正常調(diào)度,而通過(guò)NMI(不可屏蔽中斷)來(lái)觀察中斷是否被響應(yīng)。 


??我們可以通過(guò)如下內(nèi)核參數(shù)來(lái)配置檢查條件和是否觸發(fā)panic,以幫助問(wèn)題的后續(xù)分析。


# 開(kāi)啟watchdog
kernel.watchdog = 1
# hardlockup超時(shí)時(shí)間,softlockup超時(shí)時(shí)間=2*watchdog_thresh
kernel.watchdog_thresh = 10
# 是否觸發(fā)panic
kernel.hardlockup_panic = 1
kernel.softlockup_panic = 1



■實(shí)現(xiàn)


??watchdog對(duì)應(yīng)的代碼在watchdog.c中,主要實(shí)現(xiàn)邏輯:


  1. 為每個(gè)CPU core創(chuàng)建一個(gè)(watchdog/%u)內(nèi)核進(jìn)程,它會(huì)周期性的更新watchdog_touch_ts變量

  2. 設(shè)置一個(gè)時(shí)鐘中斷,它會(huì)周期性的更新hrtimer_interrupts變量 
    同時(shí)負(fù)責(zé)檢測(cè)soft_lockup,通過(guò)查看watchdog_touch_ts的值是否被更新,來(lái)判斷(watchdog/%u)進(jìn)程是否被執(zhí)行,從而判斷CPU是否被其他進(jìn)程一直占用

  3. 設(shè)置一個(gè)NMI(不可屏蔽中斷),它會(huì)每watchdog_thresh秒觸發(fā)一次 
    同時(shí)負(fù)責(zé)檢測(cè)hard_lockup,通過(guò)查看hrtimer_interrupts的值是否被更新,來(lái)判斷是否出現(xiàn)hard_lockup


對(duì)應(yīng)內(nèi)核代碼


#watchdog.c
static int watchdog_enable_all_cpus(void)
{
 ... ...
   if (!watchdog_running) {
       err = smpboot_register_percpu_thread(&watchdog_threads);    //創(chuàng)建(watchdog/%u)內(nèi)核進(jìn)程
   ... ...
}

static void __touch_watchdog(void)
{
   __this_cpu_write(watchdog_touch_ts, get_timestamp());        //更新watchdog_touch_ts
}

static void watchdog_enable(unsigned int cpu)
{
   ... ...
   hrtimer->function = watchdog_timer_fn;    //設(shè)置時(shí)鐘中斷
 watchdog_nmi_enable(cpu);        //設(shè)置NMI(非屏蔽中斷)
   hrtimer_start(hrtimer, ns_to_ktime(sample_period), HRTIMER_MODE_REL_PINNED);    //啟動(dòng)時(shí)鐘中斷

}

static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
{
   ... ...
   watchdog_interrupt_count();        //更新hrtimer_interrupts

   wake_up_process(__this_cpu_read(softlockup_watchdog));

   ... ...
   duration = is_softlockup(touch_ts);        //檢查softlockup
 ... ...
}

static bool is_hardlockup(void)    //檢查hardlockup
{
   unsigned long hrint = __this_cpu_read(hrtimer_interrupts);

   if (__this_cpu_read(hrtimer_interrupts_saved) == hrint)
       return true;

   __this_cpu_write(hrtimer_interrupts_saved, hrint);
   return false;
}



總結(jié)


以上介紹了三種最常見(jiàn)的導(dǎo)致進(jìn)程或系統(tǒng)hung住的場(chǎng)景和相關(guān)的背景及原理。在遇到這些情況的時(shí)候我們可以更快速的判斷出現(xiàn)問(wèn)題的基本原因和可能的地方。同時(shí)也介紹了linux內(nèi)核提供的一些機(jī)制,幫助我們檢查并收集必要的日志和信息。有了這些信息,我們就可以通過(guò)分析日志、利用kdump等工具來(lái)進(jìn)一步排查問(wèn)題的最終原因。


上述代碼源自Redhat-7.5,kernel版本:linux-3.10.0-862.el7




讓數(shù)據(jù)庫(kù)基礎(chǔ)設(shè)施更簡(jiǎn)單
加速企業(yè)數(shù)字化轉(zhuǎn)型建設(shè)及落地
立即咨詢

沃趣科技

中立的企業(yè)級(jí)數(shù)據(jù)庫(kù)云
十年磨一劍十年來(lái)始終如一的專注數(shù)據(jù)庫(kù)生態(tài)領(lǐng)域
夯實(shí)技術(shù)底蘊(yùn)打造最適合時(shí)代的數(shù)據(jù)庫(kù)基礎(chǔ)設(shè)施
業(yè)績(jī)持續(xù)領(lǐng)先目前已累計(jì)服務(wù)超3000家企業(yè)客戶

留言咨詢

完善信息,我們第一時(shí)間跟您聯(lián)系
姓名
手機(jī)
公司
所在地區(qū)
咨詢問(wèn)題