歡迎光臨
每天分享高質量文章

當 CPU 空閑時它都在做什麼? | Linux 中國

大量的睡眠行程,它們都在等待某種情況下被喚醒,差不多在 100% 的 CPU 時間中,都處於虛構的“空閑任務”中。
— Gustavo Duarte


編譯自 | https://manybutfinite.com/post/what-does-an-idle-cpu-do/ 
 作者 | Gustavo Duarte
 譯者 | qhwdw

在 上篇文章中[1] 我說了作業系統行為的基本原理是,在任何一個給定的時刻,在一個 CPU 上有且只有一個任務是活動的。但是,如果 CPU 無事可做的時候,又會是什麼樣的呢?

事實證明,這種情況是非常普遍的,對於絕大多數的個人電腦來說,這確實是一種常態:大量的睡眠行程,它們都在等待某種情況下被喚醒,差不多在 100% 的 CPU 時間中,都處於虛構的“空閑任務”中。事實上,如果一個普通使用者的 CPU 處於持續的繁忙中,它可能意味著有一個錯誤、bug、或者運行了惡意軟體。

因為我們不能違反我們的原理,一些任務需要在一個 CPU 上啟用。首先是因為,這是一個良好的設計:持續很長時間去遍歷核心,檢查是否一個活動任務,這種特殊情況是不明智的做法。最好的設計是沒有任何例外的情況。無論何時,你寫一個 if 陳述句,Nyan Cat 就會喵喵喵。其次,我們需要使用空閑的 CPU 去做一些事情,讓它們充滿活力,你懂得,就是建立天網計劃唄。

因此,保持這種設計的連續性,並領先於那些邪惡計劃一步,作業系統開發者建立了一個空閑任務,當沒有其它任務可做時就排程它去執行。我們可以在 Linux 的 引導過程[2] 中看到,這個空閑任務就是行程 0,它是由計算機開啟電源時執行的第一個指令直接派生出來的。它在 rest_init[3] 中初始化,在 initidlebootup_task[4] 中初始化空閑排程類scheduling class

簡而言之,Linux 支援像實時行程、普通使用者行程等等的不同排程類。當選擇一個行程變成活動任務時,這些類按優先順序進行查詢。透過這種方式,核反應堆的控制程式碼總是優先於 web 瀏覽器執行。儘管在通常情況下,這些類傳回 NULL,意味著它們沒有合適的任務需要去執行 —— 它們總是處於睡眠狀態。但是空閑排程類,它是持續執行的,從不會失敗:它總是傳回空閑任務。

好吧,我們來看一下這個空閑任務到底做了些什麼。下麵是 cpuidleloop[5],感謝開源能讓我們看到它的程式碼:

  1. while (1) {

  2.    while(!need_resched()) {

  3.        cpuidle_idle_call();

  4.    }

  5.    /*

  6.    [Note: Switch to a different task. We will return to this loop when the idle task is again selected to run.]

  7.    */

  8.    schedule_preempt_disabled();

  9. }

cpuidleloop

我省略了很多的細節,稍後我們將去瞭解任務切換,但是,如果你閱讀了這些原始碼,你就會找到它的要點:由於這裡不需要重新排程(即改變活動任務),它一直處於空閑狀態。以所經歷的時間來計算,這個迴圈和其它作業系統中它的“堂兄弟們”相比,在計算的歷史上它是執行的最多的程式碼片段。對於 Intel 處理器來說,處於空閑狀態意味著執行著一個 halt[6] 指令:

  1. static inline void native_halt(void)

  2.    {

  3.    asm volatile("hlt": : :"memory");

  4.    }

native_halt

hlt 指令停止處理器中的程式碼執行,並將它置於 halt 的狀態。奇怪的是,全世界各地數以百萬計的 Intel 類的 CPU 們花費大量的時間讓它們處於 halt 的狀態,甚至它們在通電的時候也是如此。這並不是高效、節能的做法,這促使晶片製造商們去開發處理器的深度睡眠狀態,以帶來著更少的功耗和更長休眠時間。內核的 cpuidle 子系統[7] 是這些節能樣式能夠產生好處的原因。

現在,一旦我們告訴 CPU 去 halt(睡眠)之後,我們需要以某種方式讓它醒來。如果你讀過 上篇文章《你的作業系統什麼時候執行?》[1] ,你可能會猜到中斷會參與其中,而事實確實如此。中斷促使 CPU 離開 halt 狀態傳回到啟用狀態。因此,將這些拼到一起,下圖是當你閱讀一個完全呈現的 web 網頁時,你的系統主要做的事情:

除定時器中斷外的其它中斷也會使處理器再次發生變化。如果你再次點選一個 web 頁面就會產生這種變化,例如:你的滑鼠發出一個中斷,它的驅動會處理它,並且因為它產生了一個新的輸入,突然行程就可運行了。在那個時刻, need_resched() 傳回 true,然後空閑任務因你的瀏覽器而被踢出而終止執行。

如果我們獃獃地看著這篇文章,而不做任何事情。那麼隨著時間的推移,這個空閑迴圈就像下圖一樣:

在這個示例中,由核心計劃的定時器中斷會每 4 毫秒發生一次。這就是滴答tick週期。也就是說每秒鐘將有 250 個滴答,因此,這個滴答速率(頻率)是 250 Hz。這是執行在 Intel 處理器上的 Linux 的典型值,而其它作業系統喜歡使用 100 Hz。這是由你構建核心時在 CONFIG_HZ 選項中定義的。

對於一個空閑 CPU 來說,它看起來似乎是個無意義的工作。如果外部世界沒有新的輸入,在你的膝上型電腦的電池耗盡之前,CPU 將始終處於這種每秒鐘被喚醒 250 次的地獄般折磨的小憩中。如果它執行在一個虛擬機器中,那我們正在消耗著宿主機 CPU 的效能和寶貴的時鐘週期。

在這裡的解決方案是 動態滴答[8],當 CPU 處於空閑狀態時,定時器中斷被 暫停或重計劃[9],直到核心知道將有事情要做時(例如,一個行程的定時器可能要在 5 秒內過期,因此,我們不能再繼續睡眠了),定時器中斷才會重新發出。這也被稱為無滴答樣式

最後,假設在一個系統中你有一個活動行程,例如,一個長時間執行的 CPU 密集型任務。那樣幾乎就和一個空閑系統是相同的:這些示意圖仍然是相同的,只是將空閑任務替換為這個行程,並且相應的描述也是準確的。在那種情況下,每 4 毫秒去中斷一次任務仍然是無意義的:它只是作業系統的效能抖動,甚至會使你的工作變得更慢而已。Linux 也可以在這種單一行程的場景中停止這種固定速率的滴答,這被稱為 自適應滴答[10] 樣式。最終,這種固定速率的滴答可能會 完全消失[11]

對於閱讀一篇文章來說,CPU 基本是無事可做的。內核的這種空閑行為是作業系統難題的一個重要部分,並且它與我們看到的其它情況非常相似,因此,這將幫助我們理解一個執行中的核心。


via: https://manybutfinite.com/post/what-does-an-idle-cpu-do/

作者:Gustavo Duarte[13] 譯者:qhwdw 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出

LCTT 譯者

qhwdw ? ? ? ?
共計翻譯:57 篇
貢獻時間:93 天


推薦文章

< 左右滑動檢視相關文章 >

點選圖片、輸入文章 ID 或識別二維碼直達

贊(0)

分享創造快樂