天天看點

Linux核心工作隊列探秘

工作隊列的節能特性最早由3.11核心引入,此後,50多個子系統和裝置驅動開始使用它。而節能工作隊列則被廣泛用于手持裝置(如平闆電腦,智能手機)。ARM平台上,在Android系統中使用節能工作隊列,可以顯著降低能源消耗。

在Linux kernel中,工作隊列是常見的延後執行機制,經常出現在異步執行上下文中。上下文由核心工作線程提供,當有任務被放入隊列(入隊操作)時,工作線程将會被喚醒。核心實作時,工作隊列由strut workqueue_struct表示,而任務由strut work_struct表示。work_struct中包含一個回調函數,該函數将會被工作線程調用,以表示任務被執行。一旦工作隊列上的所有任務執行完畢,工作線程又繼續睡眠。

下面是工作隊列相關的常見API:

bool queue_work(...);

bool queue_work_on(...);

bool queue_delayed_work(...);

bool queue_delayed_work_on(...);

queue_work_on()和queue_delayed_work_on()指定了任務由哪個cpu上的工作線程執行,另兩個函數允許任務運作在任意cpu上。對于前兩個函數,任務将會被立即執行;而對于後兩個函數,任務需要等待一段時間才會被執行。

綁定工作隊列的缺陷

在核心中,一種常見的使用工作隊列的場景是處理周期性的工作:不斷重複執行隊列任務,并由回調函數重新将任務放入隊列。下面是一段示範程式:

1. static void foohandler(struct work_struct *work)
2. {
3.    struct delayed_work *dwork = to_delayed_work(work);
4.    /* Do some work here */
5.    queue_delayed_work(system_wq, dwork, 10);
6. }
7. void foo_init(void)
8. {
9.    struct delayed_work *dwork = kmalloc(sizeof(*dwork), GFP_KERNEL);
10.    INIT_DEFERRABLE_WORK(dwork, foo_handler);
11.    queue_delayed_work(system_wq, dwork, 10);
}           

讀者可能會認為,任務将會被任意cpu執行(由排程器選出一個最合适的cpu)。遺憾的是,這不完全正确。工作隊列機制傾向于将任務放入local cpu(即,執行queue_delayed_work()的那個cpu),除非local cpu被wq_unbound_cpumask屏蔽了。舉個例子,在8核平台上,上面示範程式中的回調函數總是在一個cpu上執行,盡管該cpu處于idle狀态且存在其它cpu處于運作狀态。

wq_unbound_cpumask表示可以執行“工作隊列任務”的cpu集合,注意,隻有當該任務沒有通過API(xxx_work_on())指定到某個特定的cpu時,該掩碼才生效。該掩碼可以通過 /sys/devices/virtual/workqueue/cpumask設定。

從節能的角度看,一個正在執行正常程式的cpu被中斷,然後執行工作隊列任務,這是可接受的。反之,如果喚醒一個處于idle狀态的cpu,然後僅僅更新時鐘和将任務放入隊列,這将消耗更多能源。cpu綁定有時并不能帶來好的性能,因為被綁定的cpu并不一定是排程器認為的負載最輕的cpu,此時排程器不能進行負載均衡。

工作隊列的節能特性

預設情況下,工作隊列的節能特性是關閉的。使能該特性有兩種方式:

核心啟動參數 workqueue.power_efficient=true

編譯核心時打開開關 CONFIGWQPOWER_EFFICIENT = y

一旦使能節能模式,我們就可以在調用 alloc_workqueue() 時傳入WQ_POWER_EFFICIENT标志,建立節能工作隊列。核心中還維護了兩個全局的節能工作隊列:system_power_efficient_wq 和 system_freezable_power_efficient_wq,當使用者不想建立自己私有的隊列時,可以使用它們。

不同于之前的local cpu政策,節能模式下,任務入隊時,總是由排程器提供一個target cpu,然後将任務放入target cpu上的工作隊列。是以,現在任務可以在不同的cpu執行了。

不幸的是,這并不意味着排程器總是選擇一個最優的cpu去執行工作隊列任務。排程器的排程算法非常複雜,但總體上,它在考慮cache親和性的基礎上,傾向于選擇一個負載最輕的cpu。如果,工作隊列任務沒有被快速執行完,任務還有可能會被排程器遷移到别的cpu上。

節能特性的實作依賴于cpu排程器,但cpu排程器更主要的設計點是性能,其次才在排程政策中加入了能效方面的考慮。是以,目前實作的節能工作隊列顯然沒有采用最優的節能政策,但它在能效方面确實表現得更好了。

很自然的,我們會想到,是否所有的工作隊列都應該工作在節能模式下呢?節能工作隊列有一個明顯的缺點:每次執行任務都在不同的cpu上,cache親和性被破壞,可能會導緻大量cache miss(取決于任務的訪存特性),這會顯著降低性能。但有的時候,隊列任務對cache miss不敏感,排程器的負載均衡操作反而能顯著降低隊列任務的響應延遲。考慮到上述兩方面,在使用節能隊列時需要仔認真地評估。

測試資料

在32-bit ARM big.LITTLE平台上運作benchmark,該平台具有4個Cortex A7核和4個Cortex A15核。除了用aplay在背景播放音樂外,整個系統沒有其它負載。測試核心采用Linaro公司的ubuntu-devel版本,此外還打了一些排程器更新檔。測試結果顯示,節能工作隊列的能源效率平均提高15.7%。具體資料如下:

Vanilla kernel +                  Vanilla Kernel +
scheduler patches +           scheduler patches       
                                          power-efficient wq
 A15 cluster      0.322866            0.2289042
 A7 cluster       2.619137            2.2514632
Total            2.942003            2.4803674
           

如果使用upstream kernel,節能工作隊列将會工作得更好。因為在後續排程其中,越來卻多的考慮了能源效率。

繼續閱讀