開發與維運

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,節能工作隊列將會工作得更好。因為在後續調度其中,越來卻多的考慮了能源效率。

Leave a Reply

Your email address will not be published. Required fields are marked *