contributed by < linhoward0522
>
CMWQ
許多情況下需要非同步來執行 process
,這個情況下最常用的方法就是 workqueue
的 API
當需要這樣非同步來執行 process
時,會將工作放入 queue
中 。 一個獨立的thread
會進行非同步執行
queue
稱為 workqueue
thread
稱為 worker
workqueue
上有工作時, worker
會依序地執行與該工作關聯的功能workqueue
沒有工作時, worker
就會 idle
原本的 workqueue
有兩種方式:
multi thread
: 每個 CPU
有一個 worker
,每一個 multi thread
需要保持與 CPU
大致相同的 worker
數量single thread
: 整個系統內只有一個 worker
使用multi thread
方法會消耗許多的資源,且並行的效果不盡人意
Concurrency Managed Workqueue (cmwq)
是針對 workqueue
的改善,並著重於以下目標 :
workqueue
API 的兼容性CPU
共享所有的 Worker Pools
並根據不同需求提供彈性的並行等級,減少資源浪費worker pool
和 並行等級,使 API 使用者無需擔心這些細節work item
的概念。是一個簡單的結構包含指向非同步執行函式的函式指標,當想要非同步執行函式時,必須設置一個指向該函式的 work item
,並放入 workqueue
worker thread
依序會處理 workqueue
中的任務,若沒有任務 thread
會變為 idle
狀態,而由worker-pools
負責管理這些 threads
worker-pools
:
Bound
: 服務綁定在特定的 CPU
上的 work item
,在指定的 CPU
上執行。且又分兩個 worker-pools
,一種用於正常優先級的 work item
,另一種用於高優先級 work item
Unbound
: worker
可以在多個 CPU
上調度,且 worker-pools
裡面的 worker
數量是動態的worker-pools
的實作,管理並行等級(有多少任務同時工作)是一個問題。 CMWQ
會將並行保持在最低但足夠的等級,以節省資源worker
醒來或是睡覺時, worker-pool
都會收到通知,並持續追蹤當前可工作的 worker
work item
不會佔用 CPU
很多週期,表示保持足夠的並行性來防止工作停止是最佳的CPU
上有一個或多個可運行的 worker
, worker-pool
就不會開始執行新工作,但是當最後一個運行的 worker
進入睡眠狀態時,它會立即排程一個新的 worker
,這樣 CPU
就不會在有待處理的 work item
時閒置kthreads
的記憶體空間外,保持閒置的 worker
不會花費任何成本,因此 CMWQ
在釋放它們的系統資源之前會保留閒置的 worker
一段時間kecho
已使用 CMWQ
,請陳述其優勢和用法,應重現相關實驗kecho_mod.c
中的 71 行利用 alloc_workqueue
來創建 workqueue
其中 WQ_UNBOUND
flag 表示 work item
不需要綁定在特定的 CPU
上執行
進入到 unbound workqueue
的 work item
會被盡快執行,這樣雖然犧牲了 locality
,但有以下好處 :
bound workqueue
可能會創建大量的 worker
卻未使用到CPU
運行的工作Work items queued to an unbound wq are served by the special worker-pools which host workers which are not bound to any specific CPU. This makes the wq behave as a simple execution context provider without concurrency management. The unbound worker-pools try to start execution of work items as soon as possible. Unbound wq sacrifices locality but is useful for the following cases.
- Wide fluctuation in the concurrency level requirement is expected and using bound wq may end up creating large number of mostly unused workers across different CPUs as the issuer hops through different CPUs.
- Long running CPU intensive workloads which can be better managed by the system scheduler.
kecho_mod.c
中的 86 行利用 destroy_workqueue
來將 workqueue
釋放
Safely destroy a workqueue. All work currently pending will be done first.
echo_server.c
中的 109 行利用 INIT_WORK
來將任務初始化 ,透過 function pointer
指派任務內容
initialize all of a work item in one go
echo_server.c
中的 160 行利用 queue_work
來將任務加入 workqueue
中
We queue the work to the CPU on which it was submitted, but if the CPU dies it can be processed by another CPU.
kecho
和 user-echo-server
的效能kecho
的效能實驗
bench
不論設為true
或是false
,效能都差不多
user-echo-server
的效能可以看出因為
kecho
是在kernel
執行,並且又引入了CMWQ
,所以執行時間比user-echo-server
快很多
Concurrency Managed Workqueue (cmwq)
,改寫 kHTTPd
,分析效能表現和提出改進方案參考
kecho
來作修改
http_server.h
http_service
用來管理 workqueue
is_stopped
用來取代本來 worker
內的 kthread_should_stop
,改變 is_stopped
可以通知所有 worker
停止worker
使用 List
來維護,可以利用 The Linux Kernel API
來做新增或刪除khttpd
用來管理 work
main.c
原本 khttpd
核心模組是採用系統預設的 workqueue
,為了要引入 CMWQ
,必須在掛載以及卸載模組時用到 workqueue
,所以需要 alloc_workqueue
以及 destroy_workqueue
http_server.c
首先使用全域變數 is_stopped
,可以用來通知所有 worker
是否停止,以及一個指標指向 workqueue
http_server_worker
worker
,並使用 container_of
巨集來獲得 socket
資料while
迴圈使用 is_stopped
來判斷是否結束create_work
kmalloc
為每一個 work_item
動態配置 kernel space
的記憶體空間INIT_WORK
來將任務初始化 ,透過 function pointer
將 work_item
綁在對應的函式上list_add
來加入到 workqueue
中free_work
list_for_each_entry_safe
走訪整個 linked list
kernel_sock_shutdown
斷開 socket
的連線flush_work
等待 work_item
執行完畢sock_release
關閉 socket
kfree
釋放從 kmalloc
所配置的記憶體空間http_server_daemon
create_work
會為每一個連線的請求建立一個 work_item
進行處理queue_work
會將 work_item
放入 workqueue
中排隊daemon
結束,呼叫 free_work
來釋放掉建立連線所分配的記憶體空間使用 make check
來實驗原始版本,以及引入 CMWQ
後的效能
可以發現 requests/sec
提昇了將近一倍