空閑CPU在做什么?
我們知道操作系統(tǒng)的特性是:在任何時(shí)間內(nèi),CPU有且只有一個(gè)任務(wù)在運(yùn)行。如果沒(méi)有一個(gè)任務(wù)在運(yùn)行,CPU在做什么的?
事實(shí)證明,這種情況非常普遍,對(duì)于大多數(shù)個(gè)人計(jì)算機(jī)來(lái)說(shuō),它實(shí)際上是常態(tài):睡眠過(guò)程的進(jìn)程,都在等待某些條件喚醒,而近100%的CPU時(shí)間正在這個(gè)神秘的“空閑任務(wù)”中。事實(shí)上,如果CPU一直忙于普通用戶(hù),那通常是有問(wèn)題的,或者是惡意軟件在霸占CPU。
為了保持設(shè)計(jì)的一致性,OS開(kāi)發(fā)人員創(chuàng)建了一個(gè)空閑任務(wù),當(dāng)沒(méi)有其他工作時(shí),該任務(wù)將被安排運(yùn)行。我們?cè)贚inux 啟動(dòng)過(guò)程中已經(jīng)看到,空閑任務(wù)是進(jìn)程0,它是計(jì)算機(jī)首次打開(kāi)時(shí)運(yùn)行的第一條指令的直接后代。它在rest_init中 初始化,其中init_idle_bootup_task初始化空閑調(diào)度類(lèi)。
簡(jiǎn)而言之,Linux支持不同的調(diào)度類(lèi),例如實(shí)時(shí)進(jìn)程,常規(guī)用戶(hù)進(jìn)程等。當(dāng)選擇進(jìn)程成為活動(dòng)任務(wù)時(shí),將按優(yōu)先級(jí)順序查詢(xún)這些類(lèi)。這樣,“核反應(yīng)堆控制代碼”總是在Web瀏覽器之前運(yùn)行。但是,這些類(lèi)通常會(huì)返回NULL,這意味著它們沒(méi)有合適的運(yùn)行過(guò)程 - 它們都在睡覺(jué)。但是最后運(yùn)行的空閑調(diào)度類(lèi)永遠(yuǎn)不會(huì)失敗:它總是返回空閑任務(wù)。
這一切都很好,但讓我們來(lái)看看這個(gè)空閑任務(wù)究竟在做什么。cpu_idle_loop,如下:
cpu_idle_loop
- while(1){ while(!need_resched()){ cpuidle_idle_call(); }
- / *
- [注意:切換到其他任務(wù)。當(dāng)
- 再次選擇空閑任務(wù)運(yùn)行時(shí),我們將返回此循環(huán)。]
- * /
- schedule_preempt_disabled();
- }
我已經(jīng)省略了很多細(xì)節(jié),我們稍后會(huì)仔細(xì)研究任務(wù)切換,但是如果你閱讀了代碼,你會(huì)得到它的重要信息:只要不需要重新安排,即改變活動(dòng)任務(wù),CPU就會(huì)一直空閑。以經(jīng)過(guò)的時(shí)間來(lái)衡量,這個(gè)循環(huán)及其在其他操作系統(tǒng)中的表兄弟可能是計(jì)算歷史中執(zhí)行最多的代碼片段。對(duì)于英特爾處理器,傳統(tǒng)上保持空閑意味著運(yùn)行暫停指令:
native_halt
- static inline void native_halt(void)
- {
- asm volatile("hlt": : :"memory");
- }
hlt停止處理器中的代碼執(zhí)行并將其置于暫停狀態(tài)。奇怪的是,全世界數(shù)以百萬(wàn)計(jì)的類(lèi)似英特爾的CPU正在花費(fèi)大部分時(shí)間停止工作,即使在他們通電的情況下也是如此。它也不是非常有效的節(jié)約能源,這導(dǎo)致芯片制造商為處理器開(kāi)發(fā)更深層次的睡眠狀態(tài),從而在更長(zhǎng)的喚醒延遲中消耗更少的功耗。內(nèi)核的cpuidle子系統(tǒng)負(fù)責(zé)利用這些節(jié)能模式。
現(xiàn)在,一旦我們告訴CPU停止或睡眠,我們需要以某種方式讓它恢復(fù)活力。如果您已閱讀過(guò)我以前的文章,您可能會(huì)懷疑涉及到中斷,實(shí)際上它們也是如此。中斷會(huì)刺激CPU退出暫停狀態(tài)并恢復(fù)運(yùn)行。所以把這些放在一起,這是你的電腦在閱讀這篇文章時(shí)所做的大部分工作
除定時(shí)器中斷外的其他中斷也會(huì)使處理器再次運(yùn)行。如果你點(diǎn)擊一個(gè)網(wǎng)頁(yè)就會(huì)發(fā)生這種情況,例如:你的鼠標(biāo)發(fā)出一個(gè)中斷,它的驅(qū)動(dòng)程序處理它,突然一個(gè)進(jìn)程可以運(yùn)行,因?yàn)樗行碌妮斎搿4藭r(shí)need_resched()返回true,并且啟動(dòng)空閑任務(wù)以支持您的瀏覽器任務(wù)。
這是隨時(shí)間變化的空閑循環(huán):
在這個(gè)例子中,內(nèi)核將定時(shí)器中斷編程為每4毫秒(ms)發(fā)生一次。這是滴答期。這意味著我們每秒獲得250個(gè)滴答,因此滴答率或滴答頻率為250 Hz。這是在英特爾處理器上運(yùn)行的Linux的典型值,100赫茲是另一個(gè)人群的最?lèi)?ài)。這在CONFIG_HZ構(gòu)建內(nèi)核時(shí)在選項(xiàng)中定義。
現(xiàn)在看起來(lái)對(duì)于空閑CPU來(lái)說(shuō)看起來(lái)像是一項(xiàng)非常多的毫無(wú)意義的工作,而且確實(shí)如此。如果沒(méi)有來(lái)自外界的新鮮輸入,CPU將繼續(xù)陷入這種地獄般的小睡狀態(tài),在您的筆記本電腦電池耗盡時(shí),每秒鐘會(huì)被喚醒250次。如果這是在虛擬機(jī)中運(yùn)行,我們將從主機(jī)CPU燒掉電源和時(shí)鐘周期。
這里的解決方案是動(dòng)態(tài)勾選,以便當(dāng)CPU空閑時(shí),定時(shí)器中斷被取消激活或重新編程,以便在內(nèi)核知道將要工作的地方發(fā)生(例如,進(jìn)程可能有一個(gè)定時(shí)器)在5秒內(nèi)到期,所以我們不能睡過(guò)去)。這也稱(chēng)為無(wú)滴答模式。
最后,假設(shè)您在系統(tǒng)中有一個(gè)活動(dòng)進(jìn)程,例如長(zhǎng)時(shí)間運(yùn)行的CPU密集型任務(wù)。這幾乎與空閑系統(tǒng)完全相同:上述圖保持大致相同,只需將一個(gè)進(jìn)程替換為空閑任務(wù),圖景就是準(zhǔn)確的。在那種情況下,每隔4毫秒中斷任務(wù)是沒(méi)有意義的:它只是操作系統(tǒng)抖動(dòng),從而減慢了你的工作量。Linux還可以在這個(gè)單進(jìn)程場(chǎng)景中停止固定速率滴答,即所謂的自適應(yīng)滴答模式。最終,固定速率蜱可能會(huì)消失完全。
這對(duì)于一個(gè)文章來(lái)說(shuō)已經(jīng)足夠發(fā)散了。內(nèi)核的空閑行為是操作系統(tǒng)難題的重要組成部分,它與我們將看到的其他復(fù)雜情況非常相似,因此這有助于我們構(gòu)建正在運(yùn)行的內(nèi)核的圖景。
























