一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux內(nèi)核:soft lockup是由于什么原因?qū)е碌哪兀?/h1>

提到soft lockup,大家都不會(huì)陌生:

BUG:softlockup-CPU#3stuckfor23s![kworker/332]

這個(gè)幾乎和panic,oops并列,也是非常難以排查甚至比panic更麻煩。至少panic之后你可以去分析一個(gè)靜態(tài)的尸體,然而soft lockup,那是一個(gè)動(dòng)態(tài)的過(guò)程,甚至轉(zhuǎn)瞬即逝,自帶自愈功能。

那么soft lockup是由于什么原因?qū)е碌哪兀?/p>

幾乎沒(méi)有這方面的文章,能找到的也只有個(gè)別的案例分析,所以我想趁著周末降至來(lái)寫一篇關(guān)于soft lockup的通用解釋。

首先澄清兩個(gè)關(guān)于soft lockup的誤區(qū):

soft lockup并不僅僅是由死循環(huán)引起的。

soft lockup并不是說(shuō)在一段代碼里執(zhí)行了23秒,22秒。

這里簡(jiǎn)單解釋一下上面的兩點(diǎn)。

事實(shí)上,死循環(huán)并不一定會(huì)導(dǎo)致soft lockup,比如Linux內(nèi)核生命周期內(nèi)的0號(hào)進(jìn)程就是一個(gè)死循環(huán),此外很多的內(nèi)核線程都是死循環(huán)。

此外,更難指望一段代碼可以執(zhí)行20多秒,要對(duì)現(xiàn)代計(jì)算機(jī)的速度有所概念。

soft lockup發(fā)生的真實(shí)場(chǎng)景是:

soft lockup是針對(duì)單獨(dú)CPU而不是整個(gè)系統(tǒng)的。

soft lockup指的是發(fā)生的CPU上在20秒(默認(rèn))中沒(méi)有發(fā)生調(diào)度切換。

第一點(diǎn)無(wú)須解釋,下面重點(diǎn)看第二點(diǎn)。

很顯然,只要讓一個(gè)CPU在20秒左右的時(shí)間內(nèi)都不發(fā)生進(jìn)程切換,就會(huì)觸發(fā)soft lockup,這個(gè)“20秒內(nèi)不切換”就是soft lockup發(fā)生的根因!

好了,現(xiàn)在我們來(lái)看20秒不切換的場(chǎng)景。

死循環(huán)的情況
這是最簡(jiǎn)單的場(chǎng)景,但細(xì)節(jié)往往不像看起來(lái)那么簡(jiǎn)單。比如你寫了一個(gè)死循環(huán)在內(nèi)核中執(zhí)行,它一定會(huì)導(dǎo)致soft lockup嗎?

我們來(lái)看一個(gè)內(nèi)核死循環(huán):

#include #include static int loop_func(void *arg){ int i = 0; while(!kthread_should_stop()) { i++; } return 0;} struct task_struct *kt;static int __init init_loop(void){ kt = kthread_run(loop_func, NULL, "loop_thread"); if (IS_ERR(kt)) { return -1; } return 0;} static void __exit exit_test(void){ kthread_stop(kt);} module_init(init_loop);module_exit(exit_loop);MODULE_LICENSE("GPL");

加載這個(gè)模塊,會(huì)soft lockup嗎?

我們知道,雖然loop thread是一個(gè)死循環(huán),但是它看起來(lái)正如一個(gè)普通用戶態(tài)進(jìn)程一樣,在執(zhí)行i++循環(huán)的時(shí)候,其實(shí)是可以被其它task搶占掉的,這是最基本的進(jìn)程調(diào)度的常識(shí)。

但是如果你真的去加載這個(gè)模塊,你會(huì)發(fā)現(xiàn)在有些機(jī)器上,它確實(shí)會(huì)soft lockup,但有的機(jī)器上不會(huì),這又是為什么?

這里的關(guān)鍵在于內(nèi)核搶占。你看下自己系統(tǒng)內(nèi)核的配置文件,如果下面的配置打開(kāi),意味著上述模塊的死循環(huán)不會(huì)造成soft lockup:

CONFIG_PREEMPT=y

如果這個(gè)配置沒(méi)有開(kāi),那么便刑不上內(nèi)核了,因?yàn)樗趦?nèi)核態(tài)執(zhí)行,所以沒(méi)有誰(shuí)可以搶占它,進(jìn)而發(fā)生soft lockup。

我們對(duì)上述的死循環(huán)代碼是否會(huì)觸發(fā)soft lockup已經(jīng)很明確了,下面我們看另一種情況。

如果死循環(huán)不在內(nèi)核線程上下文,而是在軟中斷上下文,會(huì)怎樣?

很顯然,軟中斷不能被進(jìn)程搶占,所以一定會(huì)soft lockup。

當(dāng)然,如果真的發(fā)生了死循環(huán)導(dǎo)致的soft lockup,那肯定是在一個(gè)循環(huán)代碼中執(zhí)行超過(guò)20秒了,不說(shuō)20秒,如果無(wú)人干涉,200000秒都是有的…

現(xiàn)在我們來(lái)看另一種復(fù)雜的情況,即timer的情況。在討論timer時(shí),我假設(shè)系統(tǒng)的內(nèi)核搶占是開(kāi)啟的,這樣更容易分類討論,否則,如果關(guān)閉了內(nèi)核搶占,那么事情會(huì)變得更加嚴(yán)重。

timer的情況

我們先看下面的timer回調(diào)函數(shù):

static void timer_func(unsigned long data){ mdelay(1); mod_timer(&timer, jiffies + 200);}

僅僅執(zhí)行1ms的函數(shù),它會(huì)導(dǎo)致超過(guò)20秒不調(diào)度切換的soft lockup嗎?

初看,應(yīng)該不會(huì),但是如果我們?cè)敿?xì)看了Linux內(nèi)核timer的執(zhí)行原理,就會(huì)明白:

pending在一個(gè)CPU上的所有過(guò)期timer是順序遍歷執(zhí)行的。

一輪timer的順序遍歷執(zhí)行是持有自旋鎖的。

這意味著在執(zhí)行一輪過(guò)期timer的過(guò)程中,watchdog實(shí)時(shí)線程將無(wú)法被調(diào)度從而喂狗,這意味著:

同一CPU上的過(guò)期timer積累到一定量,其回調(diào)函數(shù)的延時(shí)之和大于20秒,將會(huì)soft lockup。

我們需要進(jìn)一步了解一下Linux timer的工作機(jī)制。

可以把timer的執(zhí)行過(guò)程抽象成下面的邏輯:

run_timers(){ while (now > base.early_jiffies) { for_each_timer(timer, base.list) { detach_timer(timer) forward_early_jiffies(base) call_timer_fn(timer) } }}

很簡(jiǎn)單的流程,內(nèi)核把當(dāng)前過(guò)期的timer執(zhí)行到結(jié)束。run_timers可以在軟中斷上下文中執(zhí)行,也可以在softirqd內(nèi)核線程上下文中執(zhí)行,為了營(yíng)造soft lockup,我們假設(shè)它是在時(shí)鐘中斷退出時(shí)的軟中斷上下文中執(zhí)行的(記住之前還有個(gè)假設(shè),即系統(tǒng)是開(kāi)啟內(nèi)核搶占的?。?,此時(shí),run_timers不能被watchdog搶占。

如果一個(gè)timer中耗時(shí)1ms,那么一個(gè)循環(huán)需要20000個(gè)timer遍歷執(zhí)行,才能湊齊20秒的不能被搶占的時(shí)間,進(jìn)而引發(fā)soft lockup。我的天,20000個(gè)timer,不可思議!

其實(shí)根本就不需要20000個(gè)timer,200個(gè)足矣!

問(wèn)題就出現(xiàn)在call_timer_fn,它實(shí)際上是調(diào)用該timer回調(diào)函數(shù)的封裝!我們知道,timer回調(diào)函數(shù)中執(zhí)行了mod_timer的操作,它的邏輯如下:

mod_timer(timer, expires){ list_add_timer(timer, expires, base.list)}

它事實(shí)上是把timer又插回了list,如果我們把這個(gè)list看作是一條時(shí)間線的話,它事實(shí)上只是往后移了expires這么遠(yuǎn)的距離:

假設(shè)所有timer的expire都是固定的常量,如果:

我們的timer的足夠多,多到按照其expires重新requeue時(shí)恰好能填補(bǔ)中間的那段空隙。

我們的timer回調(diào)函數(shù)耗時(shí)恰好和timer的expires流逝速率相一致。

那么,兩個(gè)甚至多個(gè)batch就合并成了一個(gè)batch,這意味著一輪timer的執(zhí)行將不會(huì)結(jié)束!

我們來(lái)試一下:

#include #include #include static int stop = 1; // timer的數(shù)量static int size = 1;module_param(size, int, 0644);MODULE_PARM_DESC(size, "size"); // timer的expiresstatic int interval = 200;module_param(interval, int, 0644);MODULE_PARM_DESC(interval, ""); // 回調(diào)函數(shù)耗時(shí)static int dt = 100;module_param(dt, int, 0644);MODULE_PARM_DESC(dt, ""); struct wrapper { struct timer_list timer; spinlock_t lock;}; struct wrapper *wr; static void timer_func(unsigned long data){ int i = data; struct wrapper *w = &wr[i]; spin_lock_bh(&(w->lock)); if (stop == 0) { udelay(dt); // 以忙等模擬耗時(shí) } spin_unlock_bh(&(w->lock)); w->timer.data = i; if (stop == 0) { mod_timer(&(w->timer), jiffies + interval); }} static int __init maint_init(void){ int i; wr = (struct wrapper *)kzalloc(size*sizeof(struct wrapper), GFP_KERNEL); for (i = 0; i < size; i++) { struct wrapper *w = &wr[i]; spin_lock_init(&(w->lock)); init_timer(&(w->timer)); w->timer.expires = jiffies + 20; w->timer.function = timer_func; w->timer.data = i; add_timer(&(w->timer)); } stop = 0; return 0;} static void __exit maint_exit(void){ int i; stop = 1; udelay(100); for (i = 0; i < size; i++) { struct wrapper *w = &wr[i]; del_timer_sync(&(w->timer)); } kfree(wr); } module_init(maint_init);module_exit(maint_exit);MODULE_LICENSE("GPL");

我的測(cè)試虛擬機(jī)HZ為1000,這意味1ms將會(huì)產(chǎn)生一次時(shí)鐘中斷,我們以每個(gè)timer函數(shù)持鎖執(zhí)行1ms,一共400個(gè)timer來(lái)加載模塊,看下結(jié)果:


單核跑滿,這意味著timer已經(jīng)拼接成龍,20秒后,我們將看到soft lockup:


事實(shí)上,每個(gè)timer回調(diào)函數(shù)delay 800us,一共200個(gè)timer即可觸發(fā)soft lockup!使用這個(gè)代碼,你基本可以確定你要測(cè)試的機(jī)器的timer執(zhí)行時(shí)間的安全閾值。

這就是timer導(dǎo)致的soft lockup的動(dòng)力學(xué)。

關(guān)于HZ1000
1ms間隔的時(shí)鐘中斷對(duì)于服務(wù)器而言是悲哀的,1ms的時(shí)間無(wú)法容納太多的timer,也不允許每個(gè)timer中有哪怕稍微的合理耗時(shí),1ms一次中斷很容易觸發(fā)run_timers在軟中斷上下文中被執(zhí)行,但很遺憾,這就是事實(shí)。

拋開(kāi)timer不談,HZ1000更多的意義在于快速響應(yīng)事件而不是增加系統(tǒng)吞吐,這對(duì)服務(wù)器的單機(jī)性能是有傷害的!

說(shuō)了這么多,現(xiàn)在讓我們考慮一下現(xiàn)實(shí)。

除了不要在內(nèi)核中寫死循環(huán)之外,我們也不應(yīng)該讓timer回調(diào)函數(shù)執(zhí)行過(guò)久,特別是系統(tǒng)中timer特別多,且expires特別短的情況下。

回到現(xiàn)實(shí)中,我們來(lái)看一個(gè)實(shí)例。

假設(shè)你使用的內(nèi)核版本還不支持TCP的lockless listener,那么我們特別要注意一個(gè)函數(shù),即inet_csk_reqsk_queue_prune:

這是一個(gè)在TCP的per listener的timer中執(zhí)行的函數(shù)。

這個(gè)函數(shù)的實(shí)現(xiàn)采用兩層循環(huán),循環(huán)耗時(shí)取決于:

外層循環(huán):該listener的backlog大小,受程序配置控制。

內(nèi)層循環(huán):該listener的半連接隊(duì)列的大小,受系統(tǒng)快照控制。

如果系統(tǒng)中的listener特別多,在收到SYN掃描攻擊時(shí),特別容易陷入soft lockup的深淵!幸運(yùn)的是,這個(gè)問(wèn)題已經(jīng)在TCP lockless listener的版本中修了,它的效果如下:

將per listener的半連接隊(duì)列timer換成了per request timer,減少了回調(diào)函數(shù)處理耗時(shí)。

per request timer增加了timer的數(shù)量,會(huì)不會(huì)抵消縮短回調(diào)耗時(shí)帶來(lái)的收益,需要攻擊來(lái)驗(yàn)證。

我們看一個(gè)相關(guān)issue和patch:
https://patchwork.ozlabs.org/patch/452426/

好了,再次回到核心主題。

觸發(fā)soft lockup的當(dāng)然不止死循環(huán)和timer,我只是用這兩個(gè)來(lái)說(shuō)明soft lockup的動(dòng)力學(xué),即超過(guò)2倍的kernel.watchdog_thresh時(shí)間不能進(jìn)行進(jìn)程調(diào)度,就會(huì)觸發(fā)soft lockup告警。至于說(shuō)stuck for 23s!那只是表象,并不是如其字面表達(dá)的那樣,23秒的時(shí)間在執(zhí)行一段代碼。

此外,頻繁的spinlock,rwlock也會(huì)導(dǎo)致soft lockup,我這有一個(gè)關(guān)于IPv6路由查詢機(jī)制的實(shí)例,詳情參見(jiàn):
https://blog.csdn.net/dog250/article/details/91046131

總之,所有的情況將不勝枚舉,也不可能通過(guò)一篇文章來(lái)展示,所以說(shuō),遇到此類問(wèn)題,還是要有一個(gè)明確的排查思路或者說(shuō)范式,才能快速定位問(wèn)題的根因并且解決之。

當(dāng)然了,經(jīng)理并不關(guān)注這些爛八七糟的東西。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    11080

    瀏覽量

    217090
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11511

    瀏覽量

    213815

原文標(biāo)題:Linux內(nèi)核為什么會(huì)發(fā)生soft lockup?

文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    網(wǎng)絡(luò)光纖出問(wèn)題一般是什么原因導(dǎo)致

    (如道路開(kāi)挖)、重物碾壓、動(dòng)物啃咬(如老鼠咬斷)。 案例:某小區(qū)寬帶中斷,經(jīng)排查發(fā)現(xiàn)施工隊(duì)挖斷主干光纖,導(dǎo)致整棟樓斷網(wǎng)。 檢測(cè)方法:使用OTDR(光時(shí)域反射儀)定位斷點(diǎn)位置。 光纖彎曲過(guò)度 原因:布線時(shí)彎曲半徑過(guò)小( 案例:辦公室網(wǎng)絡(luò)時(shí)斷
    的頭像 發(fā)表于 06-17 10:05 ?421次閱讀
    網(wǎng)絡(luò)光纖出問(wèn)題一般是<b class='flag-5'>什么原因</b><b class='flag-5'>導(dǎo)致</b>的<b class='flag-5'>呢</b>

    ADS1118讀取內(nèi)部溫度傳感器溫度值偏高,有什么原因導(dǎo)致偏高?

    ADS1118讀取內(nèi)部溫度傳感器溫度值偏高,相對(duì)實(shí)際板上的溫度偏高幾度,這樣正常嗎,有什么原因導(dǎo)致偏高
    發(fā)表于 01-03 08:20

    TLC7135發(fā)燙、發(fā)熱是什么原因導(dǎo)致的?

    HI,TI:如上圖設(shè)計(jì),使用穩(wěn)壓電源供電接入+5V和-5V,開(kāi)電后大約1分鐘左右,TLC7135開(kāi)始發(fā)燙,+5V的電流也從70mA(+5V也供電給MCU)變?yōu)?30mA。請(qǐng)問(wèn)我的設(shè)計(jì)是否合理?是什么原因導(dǎo)致這個(gè)問(wèn)題?
    發(fā)表于 01-03 07:00

    什么原因會(huì)導(dǎo)致IP地址沖突?

    IP地址沖突是運(yùn)維常見(jiàn)的問(wèn)題,很多人并不知道是什么原因會(huì)產(chǎn)生IP地址沖突故障,也不知道怎么樣去解決這種問(wèn)題。 要想去解決這類問(wèn)題,需要了解IP地址基礎(chǔ)知識(shí),往期文章講了多次,這次大概說(shuō)一下,互聯(lián)網(wǎng)
    的頭像 發(fā)表于 12-01 23:00 ?738次閱讀

    AD1293芯片在進(jìn)行YY0885測(cè)試的時(shí)候輸入阻抗影響了波形,是什么原因導(dǎo)致?

    我們使用了AD1293芯片,在進(jìn)行YY0885測(cè)試的時(shí)候,加入了620KΩ的輸入阻抗后,10Hz的正弦波形只畫出來(lái)三個(gè)周期就變成直線了,是什么原因導(dǎo)致?
    發(fā)表于 11-22 08:19

    ADS1256IDBR出現(xiàn)轉(zhuǎn)換失敗,沒(méi)有響應(yīng)的問(wèn)題,請(qǐng)問(wèn)是什么原因導(dǎo)致?

    您好,ADS1256IDBR出現(xiàn)轉(zhuǎn)換失敗,沒(méi)有響應(yīng)的問(wèn)題,請(qǐng)問(wèn)是什么原因導(dǎo)致?
    發(fā)表于 11-19 07:35

    什么原因會(huì)導(dǎo)致單片機(jī)系統(tǒng)死機(jī)

    ______________________________________ 什么原因會(huì)導(dǎo)致單片機(jī)系統(tǒng)死機(jī)
    發(fā)表于 10-16 22:32

    什么原因會(huì)導(dǎo)致單片機(jī)程序跑飛

    ______________________________________ 什么原因會(huì)導(dǎo)致單片機(jī)程序跑飛
    發(fā)表于 10-16 22:31

    電芯抽芯是什么原因導(dǎo)致

    電芯抽芯,通常指的是電池電芯在制造、使用或存儲(chǔ)過(guò)程中,由于各種原因導(dǎo)致電芯內(nèi)部結(jié)構(gòu)發(fā)生變形或損傷,從而使得電芯內(nèi)部的活性材料與集流體分離,最終導(dǎo)致電芯性能下降或失效的現(xiàn)象。這種現(xiàn)象在鋰
    的頭像 發(fā)表于 09-24 09:36 ?1420次閱讀

    運(yùn)放輸出失真是什么原因導(dǎo)致的?

    下圖為原理圖: 當(dāng)按下按鍵時(shí),測(cè)試三極管集電極的波形如下: 若把R27改為10K后,就不會(huì)除了上圖紅色方框的失真現(xiàn)象。這是由什么原因導(dǎo)致
    發(fā)表于 09-14 08:03

    INA128放大倍數(shù)不對(duì)是什么原因導(dǎo)致的?

    INA128放大倍數(shù)不對(duì)是什么原因導(dǎo)致的?
    發(fā)表于 08-30 07:34

    什么原因導(dǎo)致壓力傳感器漂移?

    什么原因導(dǎo)致壓力傳感器漂移的?我們?cè)谠O(shè)計(jì)的時(shí)候怎么才能消除壓力傳感器漂移
    的頭像 發(fā)表于 08-22 18:00 ?1755次閱讀
    <b class='flag-5'>什么原因</b><b class='flag-5'>導(dǎo)致</b>壓力傳感器漂移?

    程序跑到H723ZGT6的flash擦除那一段命令就死機(jī),什么原因導(dǎo)致?

    除了死機(jī)外,keil彈出對(duì)話框:Cannot access target .Shutting down debug session。 請(qǐng)問(wèn)這個(gè)什么原因導(dǎo)致?
    發(fā)表于 08-14 08:09

    OPA388低頻振蕩是什么原因導(dǎo)致的?

    采樣轉(zhuǎn)換。 目前遇到的問(wèn)題是,可能是由于輸入分壓電阻太大, 導(dǎo)致經(jīng)過(guò)OPA緩沖后,會(huì)有一個(gè)大概頻率為0.05的低頻振蕩,請(qǐng)教大家,這是什么原因導(dǎo)致的,又如何避免次問(wèn)題。
    發(fā)表于 08-09 06:37

    INA826檢測(cè)時(shí)出現(xiàn)較大幅度偏移,導(dǎo)致結(jié)果偏大或偏小是什么原因導(dǎo)致

    電池化成產(chǎn)品上使用許多INA826,用于電池通道電流檢測(cè)。目前發(fā)現(xiàn)INA826檢測(cè)時(shí)出現(xiàn)較大幅度偏移,導(dǎo)致結(jié)果偏大或偏小,而且檢測(cè)通道不固定。請(qǐng)問(wèn)一下,是什么原因導(dǎo)致?
    發(fā)表于 08-02 07:35